From 939744a0f07615b669721a80405518b6e5e38ed3 Mon Sep 17 00:00:00 2001 From: Quinten Steenhuis Date: Thu, 8 Oct 2026 23:19:36 -0400 Subject: [PATCH 1/2] Preserve existing court party identity through import and filing --- docs/developer-notes/existing-case-import.md | 93 + efile_app/efile/api/filing_views.py | 2 +- efile_app/efile/api/suffolk_api_views.py | 5 +- efile_app/efile/middleware.py | 16 + .../migrations/0038_existing_case_identity.py | 40 + efile_app/efile/models.py | 9 + efile_app/efile/services/drafts.py | 22 +- efile_app/efile/services/efsp_payload.py | 7 +- efile_app/efile/services/existing_cases.py | 524 ++++ efile_app/efile/services/extracted_parties.py | 8 +- efile_app/efile/services/fee_quotes.py | 15 +- efile_app/efile/services/filing_path.py | 12 +- efile_app/efile/services/handoff.py | 17 +- efile_app/efile/services/people.py | 38 +- efile_app/efile/static/js/filing-payload.js | 13 +- .../templates/efile/case_confirmation.html | 17 +- efile_app/efile/templates/efile/parties.html | 172 +- efile_app/efile/templates/efile/review.html | 18 +- .../existing_cases/2005SC000985-raw.json | 802 ++++++ .../existing_cases/2019-D-0000655-raw.json | 2469 +++++++++++++++++ .../existing_cases/2019-SC-0001642-raw.json | 583 ++++ .../existing_cases/2024DC00000572-raw.json | 584 ++++ .../existing_cases/2024EV001752-raw.json | 803 ++++++ .../existing_cases/2024SC00003388-raw.json | 582 ++++ .../fixtures/existing_cases/2025SC5-raw.json | 695 +++++ .../existing_cases/kane-party-types.json | 127 + .../existing_cases/lake-party-types.json | 472 ++++ .../existing_cases/marion-party-types.json | 152 + .../existing_cases/winnebago-party-types.json | 82 + efile_app/efile/tests/helpers.py | 20 + .../efile/tests/test_case_confirmation.py | 15 +- efile_app/efile/tests/test_detours.py | 8 +- efile_app/efile/tests/test_existing_cases.py | 563 ++++ .../efile/tests/test_filing_plan_actions.py | 10 +- .../efile/tests/test_review_submit_flow.py | 5 + efile_app/efile/views/case_confirmation.py | 44 +- efile_app/efile/views/parties.py | 68 +- efile_app/efile/views/party_details.py | 3 + efile_app/efile/views/review.py | 4 + efile_app/efile/views/session_api.py | 2 +- efile_app/js-tests/filing-payload.test.js | 61 + efile_app/tests/existing-case-flow.js | 118 + 42 files changed, 9197 insertions(+), 103 deletions(-) create mode 100644 docs/developer-notes/existing-case-import.md create mode 100644 efile_app/efile/migrations/0038_existing_case_identity.py create mode 100644 efile_app/efile/services/existing_cases.py create mode 100644 efile_app/efile/tests/fixtures/existing_cases/2005SC000985-raw.json create mode 100644 efile_app/efile/tests/fixtures/existing_cases/2019-D-0000655-raw.json create mode 100644 efile_app/efile/tests/fixtures/existing_cases/2019-SC-0001642-raw.json create mode 100644 efile_app/efile/tests/fixtures/existing_cases/2024DC00000572-raw.json create mode 100644 efile_app/efile/tests/fixtures/existing_cases/2024EV001752-raw.json create mode 100644 efile_app/efile/tests/fixtures/existing_cases/2024SC00003388-raw.json create mode 100644 efile_app/efile/tests/fixtures/existing_cases/2025SC5-raw.json create mode 100644 efile_app/efile/tests/fixtures/existing_cases/kane-party-types.json create mode 100644 efile_app/efile/tests/fixtures/existing_cases/lake-party-types.json create mode 100644 efile_app/efile/tests/fixtures/existing_cases/marion-party-types.json create mode 100644 efile_app/efile/tests/fixtures/existing_cases/winnebago-party-types.json create mode 100644 efile_app/efile/tests/test_existing_cases.py create mode 100644 efile_app/tests/existing-case-flow.js diff --git a/docs/developer-notes/existing-case-import.md b/docs/developer-notes/existing-case-import.md new file mode 100644 index 00000000..afa85f44 --- /dev/null +++ b/docs/developer-notes/existing-case-import.md @@ -0,0 +1,93 @@ +# Importing existing court parties + +Implementation and validation for [issue #291](https://github.com/SuffolkLITLab/LITEFile/issues/291#issuecomment-6069909727). + +## Case-detail contract + +`services/existing_cases.py` separates authenticated retrieval, normalization, preview, atomic application, and envelope reconciliation. It uses the raw detail endpoint: + +```text +GET /jurisdictions/{jurisdiction}/cases/courts/{court}/cases/{tracking_id} +``` + +The seven authorized Illinois staging examples were queried using both the raw response and `X-API-VERSION: JSON-V1`. Raw responses remain the import source: JSON-V1 does not preserve available contacts, and its individual ID extraction does not explicitly select the `CASEPARTYID` category. + +Two differences found during live validation are covered by fixtures: + +- Search returns the EFM UUID needed by detail retrieval, fees, and submission. Detail responses place a local court-system ID in their top-level `caseTrackingID`. The original EFM UUID appears in `caseLineageCase`. Import verifies that identity, the selected docket number (allowing punctuation differences), and the returned court. It preserves the EFM UUID as `previous_case_id` and records the local ID separately in the snapshot. +- Marion, Kane, and Winnebago return additional parties with roles absent from the selected case type's current party-type list. Import verifies those roles against the court's full `/party_types` catalog. It does not reinterpret those parties as a plaintiff or defendant. Required-role checks still use the selected case type's requirements. + +## Field mapping + +| Raw detail field | Durable destination | Behavior | +| --- | --- | --- | +| Search UUID, verified against top-level ID or `caseLineageCase` | Draft `previous_case_id`; snapshot `tracking_id` | Used for both fees and submission; never replaced with the local ID | +| Top-level detail `caseTrackingID` | Snapshot `court_local_case_id` | Retained separately | +| `caseCourt.organizationIdentification.identificationID` | Snapshot `court` | Must match the selected court | +| `caseDocketID`, `caseTitleText` | Draft docket and title; snapshot | Court values replace search/browser suggestions on confirmation | +| `caseCategoryText`, Tyler augmentation `caseTypeText` | Draft category/type codes; snapshot | Court classifications drive the outgoing envelope | +| Individual `personOtherIdentification` or organization's nested `identification` with category `CASEPARTYID` | Party `external_party_id` | Category-specific selection; unrelated IDs are ignored | +| `EntityPerson` / `EntityOrganization` | Person fields / `organization_name` | Organizations serialize with `person_type: business` | +| `personGivenName`, `personMiddleName`, `personSurName`, `personNameSuffixText` | First, middle, last name, suffix | Preserved without name-based deduplication | +| `organizationName` | Organization name | Preserved as one name | +| `caseParticipantRoleCode` | Party type code and verified catalog name | Missing/unrecognized roles make the import partial | +| `ContactEmailID`, `ContactTelephoneNumber.telephoneNumberFullID` | Email and phone | First available supported value | +| `ContactMailingAddress` / `ContactAddress`, structured delivery points, city, state, postal code, country | Mailing address fields | Optional information; missing country remains unknown | +| `caseOtherEntityAttorney`, `CASEPARTYATTORNEYID` or `ATTORNEYID`, bar identification, represented-party references | Party `representation.attorneys` | Normalized attorney identities with their ID category, supported names/contacts, and associations retained | +| No returned attorney association | `representation.status: unknown` | Does not imply self-representation | + +Unsupported personal attributes, historical documents, prior filing codes, payments, and service selections are not imported. The current proxy person input exposes no representation-change fields. The UI preserves and describes returned representation; it does not send guessed pro-se or attorney changes. Existing-party IDs reference the court's existing associations. `is_form_filler` retains its separate firm-filer meaning. + +The bounded snapshot includes schema version, court and case identity, jurisdiction, source endpoint, retrieval time, status, problems, and normalized parties. An absent snapshot is `not_loaded`; a verified empty roster is `loaded`; missing essential fields are `partial`; retrieval/authentication/format failures are `failed`. Credentials and raw responses are not saved in the draft or logged by the import service. + +## Workflow and identity rules + +Confirmation previews the server-fetched roster and applies it when the filer confirms. Plan-linked cases use the same confirmation step. Active older drafts cannot proceed to People, Fees, or Review until the court import is confirmed; envelope preparation also enforces this requirement. Submitted drafts cannot be imported into or rewritten. + +Imported parties have `source: court`, independently of whether their ID is present. Their ordinary edit/remove actions are unavailable and direct requests are rejected. “This is me” links the account to the existing row without renaming or deleting it. “Who are you filing for?” selects durable court rows independently of that account link. Account contact information remains separate. A separate “Add a new party” action creates `source: added`; the new party's role must be chosen from the court's current published choices. If a required role is absent from the verified roster, the UI explains the missing role and asks for an explicit addition or support; it does not fabricate a placeholder. Server required-role validation still runs. + +Atomic imports serialize on the draft and reuse court rows by ID. A conditional database constraint prevents duplicate nonblank IDs within a draft and source case. Equal names with different IDs remain distinct. Case changes/rejection clear court and explicitly added case-specific parties, links, and quotes while retaining uploads and account contact details. A stale detail response cannot overwrite another selection. + +Extraction suggestions cannot edit a confirmed court roster. The People screen asks the filer to compare document caption evidence with the court roster. Legacy draft writes cannot replace imported parties or verified case classifications. + +The shared fee/submission preparation receives the authoritative draft. It verifies case identity, party names and roles against the snapshot, filing-party selection, unique IDs, and document references. Existing rows serialize with `tyler_id` and `is_new: false` in either party collection. Explicitly added rows are reconciled against their saved draft row and serialize with `is_new: true`, without a court ID. Internal draft-row identifiers are removed before forwarding. Each party appears once. Quote fingerprints include imported IDs, names, representation, and filing-party selections. + +## Illinois staging results + +Validated on October 8, 2026 (America/New_York), against `https://efile-test.suffolklitlab.org`, using authorized local test credentials. Each test used its captured court roster, a selected existing filing party, a current Motion filing code, a non-confidential document type, the required filing component, the repository's public test PDF, and an existing staging waiver payment account. + +The browser payload module generated the envelopes; the shared server preparation reconciled them against persisted imported drafts before the live fee and filing requests. Every outgoing court participant had the original `CASEPARTYID` and `is_new: false`, and every document referenced the selected entry in `users`. Returned `caseId` values matched the original search UUIDs. Raw responses, payloads, and credentials stayed under `/tmp`; committed fixtures replace names, contacts, party/attorney IDs, and bar numbers with synthetic values. + +| Court | Supplied case number | Imported parties | Motion code | Fee response | Submission response | Staging envelope | +| --- | --- | ---: | --- | --- | --- | --- | +| Marion | `2025SC5` | 3 | `143177` | 200 | 200 | `324947` | +| Kane | `2024EV001752` | 4 | `77747` | 200 | 200 | `324948` | +| Kane | `2005SC000985` | 4 | `6344` | 200 | 200 | `324949` | +| Winnebago | `2019-D-0000655` | 5 | `6344` | 200 | 200 | `324950` | +| Winnebago | `2019-SC-0001642` | 2 | `196589` | 200 | 200 | `324951` | +| Lake | `2024SC00003388` | 2 | `53156` | 200 after timeout/retry | 200 | `324952` | +| Lake | `2024DC00000572` | 2 | `53159` | 200 | 200 | `324953` | + +All seven submissions returned an envelope ID and one filing ID. This verifies staging envelope acceptance, not a later clerk disposition. No production filing was made. + +## Regression checks + +`efile/tests/test_existing_cases.py` covers the seven sanitized detail responses and full role catalogs, category-specific ID selection, organizations and equal names, import idempotency, uniqueness, required roles, immutable details, account linking, explicit new parties, lifecycle changes, stale results, resume, failures, and substituted payload identities. + +Its browser test runs the actual Django views with sanitized court responses: lookup, confirmation, read-only roster, native keyboard filing-party selection, account linking, and Review. The JavaScript suite checks both party collections and document references. Existing routing tests now provide a trusted detail response rather than assuming confirmation can bypass import. + +From `efile_app/`: + +```bash +uv run pytest -q efile/tests/test_existing_cases.py +uv run pytest -q efile/tests/test_existing_cases.py efile/tests/test_fee_quotes.py efile/tests/test_handoff.py efile/tests/test_people_flow.py efile/tests/test_review_submit_flow.py +uv run pytest -q efile/tests --ignore=efile/tests/test_integration.py --ignore=efile/tests/tests.py -m 'not integration' +npm run test:unit +uv run ruff check . +uv run ty check +uv run python manage.py makemigrations --check --dry-run +``` + +The repeatable backend suite passed with **1,675 passed and three integration tests deselected**. The final affected regression suite passed with **167 tests**, including the real-view browser flow and attorney-association mapping. JavaScript unit tests passed with **88 tests**. Ruff, Ty, changed-file ESLint, template formatting, migration consistency, and `git diff --check` passed. The repository-wide JavaScript lint command also scans an unrelated local broken virtual environment; ESLint was therefore run against every changed JavaScript file. + +The browser test requires the repository's npm dependencies and installed Playwright Chromium. The live seven-case matrix was performed separately from the repeatable fixture/browser suite. Migration `0038_existing_case_identity` must be applied before deploying the application changes. diff --git a/efile_app/efile/api/filing_views.py b/efile_app/efile/api/filing_views.py index 1c5be240..92265bdc 100644 --- a/efile_app/efile/api/filing_views.py +++ b/efile_app/efile/api/filing_views.py @@ -166,7 +166,7 @@ def payment_fees(request): # Must match what submit_final_filing sends, or fees are quoted # against a payload that differs from the one actually filed. try: - prepare_efile_payload(efile_data, jurisdiction_id, court_id) + prepare_efile_payload(efile_data, jurisdiction_id, court_id, draft=draft) except PayloadValidationError as error: # Known-bad payload: answer with the specific reason rather than # letting the EFSP reply with a code-list error no filer can act on. diff --git a/efile_app/efile/api/suffolk_api_views.py b/efile_app/efile/api/suffolk_api_views.py index 7f35d460..7c66e4be 100644 --- a/efile_app/efile/api/suffolk_api_views.py +++ b/efile_app/efile/api/suffolk_api_views.py @@ -160,9 +160,8 @@ def lookup_case(request): success = True if not case_info.get("caseTrackingID") and not case_info.get("caseCategoryText"): - logger.warning(f"Could not extract case information from API response for case {case_number}: {api_data}") - # Return what we have from the API for debugging - case_info = api_data[0] if api_data and len(api_data) > 0 else {} + logger.warning("Could not extract case information from API response") + case_info = {} success = False response = JsonResponse( diff --git a/efile_app/efile/middleware.py b/efile_app/efile/middleware.py index 178a8a75..f630359e 100644 --- a/efile_app/efile/middleware.py +++ b/efile_app/efile/middleware.py @@ -22,6 +22,22 @@ def process_view(self, request, view_func, view_args, view_kwargs): statuses = (FilingDraft.Status.SUBMITTED,) try: resolve_explicit_draft(request, jurisdiction=view_kwargs.get("jurisdiction"), statuses=statuses) + if request.resolver_match.url_name in { + "parties", + "party_details", + "case_questions", + "payment", + "case_review", + }: + from django.shortcuts import redirect + + from efile.services.current_drafts import get_current_draft + from efile.services.existing_cases import import_ready + from efile.workflow import ExistingCase + + draft = get_current_draft(request, jurisdiction=view_kwargs.get("jurisdiction")) + if draft and draft.existing_case == ExistingCase.EXISTING and not import_ready(draft): + return redirect("case_confirmation", jurisdiction=draft.jurisdiction) except DraftIdentityError as error: return self.process_exception(request, error) diff --git a/efile_app/efile/migrations/0038_existing_case_identity.py b/efile_app/efile/migrations/0038_existing_case_identity.py new file mode 100644 index 00000000..f70ea644 --- /dev/null +++ b/efile_app/efile/migrations/0038_existing_case_identity.py @@ -0,0 +1,40 @@ +# Generated by Django 5.2.17 on 2026-10-08 22:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("efile", "0037_efsp_code_copies_and_jobs"), + ] + + operations = [ + migrations.AddField( + model_name="filingdraft", + name="existing_case_snapshot", + field=models.JSONField(blank=True, default=dict), + ), + migrations.AddField( + model_name="filingparty", + name="representation", + field=models.JSONField(blank=True, default=dict), + ), + migrations.AddField( + model_name="filingparty", + name="source", + field=models.CharField(default="manual", max_length=20), + ), + migrations.AddField( + model_name="filingparty", + name="source_case_id", + field=models.CharField(blank=True, max_length=255), + ), + migrations.AddConstraint( + model_name="filingparty", + constraint=models.UniqueConstraint( + condition=models.Q(("source", "court"), models.Q(("external_party_id", ""), _negated=True)), + fields=("draft", "source_case_id", "external_party_id"), + name="unique_imported_case_party", + ), + ), + ] diff --git a/efile_app/efile/models.py b/efile_app/efile/models.py index 5984df40..07ab5d24 100644 --- a/efile_app/efile/models.py +++ b/efile_app/efile/models.py @@ -311,6 +311,7 @@ class Status(models.TextChoices): document_type_code = models.CharField(max_length=100, blank=True) document_type_name = models.CharField(max_length=255, blank=True) + existing_case_snapshot = models.JSONField(default=dict, blank=True) previous_case_id = models.CharField(max_length=255, blank=True) docket_number = models.CharField(max_length=255, blank=True) case_title = models.CharField(max_length=500, blank=True) @@ -580,6 +581,9 @@ class FilingParty(models.Model): party_type = models.CharField(max_length=100, blank=True) party_type_name = models.CharField(max_length=255, blank=True) external_party_id = models.CharField(max_length=255, blank=True) + source = models.CharField(max_length=20, default="manual") + source_case_id = models.CharField(max_length=255, blank=True) + representation = models.JSONField(default=dict, blank=True) # Set on the review screen, where the people read off the document are # first shown: "this one is me". It is recorded on the party rather than @@ -632,6 +636,11 @@ class Meta: ordering = ["role", "sort_order", "created_at"] constraints = [ models.UniqueConstraint(fields=["draft", "role", "sort_order"], name="unique_party_order_per_draft_role"), + models.UniqueConstraint( + fields=["draft", "source_case_id", "external_party_id"], + condition=models.Q(source="court") & ~models.Q(external_party_id=""), + name="unique_imported_case_party", + ), ] verbose_name_plural = "Filing parties" diff --git a/efile_app/efile/services/drafts.py b/efile_app/efile/services/drafts.py index 27ca3381..45ab8103 100644 --- a/efile_app/efile/services/drafts.py +++ b/efile_app/efile/services/drafts.py @@ -208,6 +208,9 @@ def write_case_data( """ data = dict(case_data or {}) + FilingDraft.objects.select_for_update().get(pk=draft.pk) + draft.refresh_from_db() + old_identity = (draft.court_code, draft.previous_case_id, draft.existing_case) update_fields: list[str] = [] for field, sources in _DRAFT_FIELD_SOURCES.items(): @@ -221,6 +224,18 @@ def write_case_data( setattr(draft, field, value) update_fields.append(field) + if old_identity != (draft.court_code, draft.previous_case_id, draft.existing_case): + from efile.services.existing_cases import clear_case_import + + clear_case_import(draft) + + elif draft.existing_case_snapshot.get("status") == "loaded": + snapshot = draft.existing_case_snapshot + for field in ("case_category_code", "case_type_code", "docket_number", "case_title"): + if getattr(draft, field) != snapshot[field]: + setattr(draft, field, snapshot[field]) + update_fields.append(field) + if "optional_services" in data: services = data.get("optional_services") or [] if draft.optional_services != services: @@ -270,7 +285,7 @@ def _write_parties(draft: FilingDraft, data: dict[str, Any]) -> None: party_type = _first_present(data, _PETITIONER_PARTY_TYPE_KEYS) if party_type is not _MISSING: values["party_type"] = _as_str(party_type) - if values: + if values and not draft.existing_case_snapshot: FilingParty.objects.update_or_create(draft=draft, role=role, sort_order=0, defaults=values) @@ -334,6 +349,11 @@ def read_case_data(draft: FilingDraft | None) -> dict[str, Any]: filing_parties = [ { "id": party.pk, + "external_party_id": party.external_party_id, + "source": party.source, + "source_case_id": party.source_case_id, + "representation": party.representation, + "is_self": party.is_self, "role": party.role, # Whether the filing is made on behalf of this party -- the filer # themselves when they are one, someone they are filing for when diff --git a/efile_app/efile/services/efsp_payload.py b/efile_app/efile/services/efsp_payload.py index ae8f300a..10403dae 100644 --- a/efile_app/efile/services/efsp_payload.py +++ b/efile_app/efile/services/efsp_payload.py @@ -125,11 +125,14 @@ def parse_optional_services(services): return parsed -def prepare_efile_payload(efile_data, jurisdiction_id, court_id): +def prepare_efile_payload(efile_data, jurisdiction_id, court_id, *, draft=None): """Apply every server-side fixup an EFSP request needs. Mutates ``efile_data``. Raises ``PayloadValidationError`` when the payload is knowably invalid. """ + from efile.services.existing_cases import reconcile_case_parties + + reconcile_case_parties(efile_data, draft, jurisdiction_id, court_id) lookups = _EfspLookups() _clean_case_identifiers(efile_data) validate_party_formats(efile_data, jurisdiction_id, court_id) @@ -154,6 +157,8 @@ def validate_party_formats(payload, jurisdiction, court): for index, party in enumerate(parties): if not isinstance(party, dict): continue + if party.get("tyler_id") and party.get("is_new") is False: + continue address = party.get("address") or {} name = party.get("name") or {} if not isinstance(address, dict) or not isinstance(name, dict): diff --git a/efile_app/efile/services/existing_cases.py b/efile_app/efile/services/existing_cases.py new file mode 100644 index 00000000..b3041098 --- /dev/null +++ b/efile_app/efile/services/existing_cases.py @@ -0,0 +1,524 @@ +"""Authenticated court-case import and authoritative envelope identity. + +The raw ECF response is intentional: JSON-V1 currently loses contacts and can +choose a non-CASEPARTYID identification for an individual. Only supported fields +enter the bounded snapshot; raw records and credentials are never persisted. +""" + +import re +from urllib.parse import quote + +import requests +from django.conf import settings +from django.db import transaction +from django.utils import timezone + +from efile.models import FilingDraft, FilingParty +from efile.party_sides import side_for_party_type_name +from efile.services.fee_quotes import fee_inputs_token, invalidate_fee_quote +from efile.utils.proxy_connection import get_headers +from efile.workflow import ExistingCase + + +class CaseImportError(ValueError): + pass + + +def unwrap(value): + while isinstance(value, dict) and "value" in value: + value = value["value"] + return value + + +def text(value): + value = unwrap(value) + return str(value).strip() if isinstance(value, str | int) and not isinstance(value, bool) else "" + + +def objects(value): + if isinstance(value, dict): + yield value + for item in value.values(): + yield from objects(item) + elif isinstance(value, list): + for item in value: + yield from objects(item) + + +def field(value, key): + return next((text(item[key]) for item in objects(value) if item.get(key) is not None), "") + + +def entity_id(entity, category="CASEPARTYID"): + ids = { + text(item.get("identificationID")) + for item in objects(entity) + if text(item.get("identificationCategory")).upper() == category + } + ids.discard("") + return next(iter(ids)) if len(ids) == 1 else "" + + +def contacts(entity): + result = {} + # Search only this entity, never attorney/represented-party references. + for item in objects(entity): + kind = str(item.get("name") or "").split("}")[-1] + value = unwrap(item) + if kind == "ContactEmailID": + result.setdefault("email", text(value)) + elif kind == "ContactTelephoneNumber": + result.setdefault("phone", field(value, "telephoneNumberFullID")) + elif kind in {"ContactMailingAddress", "ContactAddress"}: + address = unwrap(value.get("addressRepresentation")) if isinstance(value, dict) else None + if not isinstance(address, dict): + continue + delivery = address.get("addressDeliveryPoint") or [] + lines = [field(line, "streetFullText") or text(line) for line in delivery] + result.update( + { + "address_line_1": lines[0] if lines else "", + "address_line_2": lines[1] if len(lines) > 1 else "", + "city": text(address.get("locationCityName")), + "state": text(address.get("locationState")), + "zip_code": text(address.get("locationPostalCode")), + "country": text(address.get("locationCountry")), + } + ) + return result + + +def entity_fields(entity): + if "organizationName" in entity: + return {"organization_name": text(entity.get("organizationName")), **contacts(entity)} + name = entity.get("personName") or {} + if not isinstance(name, dict): + raise CaseImportError("The court returned an unreadable party name. Retry loading the case.") + return { + "first_name": text(name.get("personGivenName")), + "middle_name": text(name.get("personMiddleName")), + "last_name": text(name.get("personSurName")), + "suffix": text(name.get("personNameSuffixText")), + **contacts(entity), + } + + +def normalize_case(raw, *, court, tracking_id, party_types, docket_number=""): + case = unwrap(raw) + lineage_ids = { + text(item.get("caseTrackingID")) + for item in objects(case) + if isinstance(item.get("caseLineageCase"), list) + for item in item["caseLineageCase"] + } + if not isinstance(case, dict) or tracking_id not in {text(case.get("caseTrackingID")), *lineage_ids}: + raise CaseImportError("The court returned a different case. Search for your case again.") + if docket_number and re.sub(r"[^a-z0-9]", "", text(case.get("caseDocketID")).lower()) != re.sub( + r"[^a-z0-9]", "", docket_number.lower() + ): + raise CaseImportError("The court returned a different case number. Search for your case again.") + returned_court = field( + next((x for x in objects(case) if "caseCourt" in x), {}).get("caseCourt"), "identificationID" + ) + if not returned_court or returned_court != court: + raise CaseImportError("The court returned a different court. Search for your case again.") + augmentation = next((x for x in objects(case) if "caseParticipant" in x), None) + if augmentation is None or not isinstance(augmentation["caseParticipant"], list): + raise CaseImportError("The court did not return a party roster. Retry loading the case or sign in again.") + if not isinstance(party_types, list) or any( + not isinstance(p, dict) or not p.get("code") or not isinstance(p.get("name"), str) for p in party_types + ): + raise CaseImportError("We could not verify the court's party roles. Retry loading the case.") + if len(augmentation["caseParticipant"]) > 1000: + raise CaseImportError("This case has more parties than can be loaded here. Contact support.") + types = {str(p["code"]): p["name"] for p in party_types} + attorneys = {} + associations = {} + attorney_records = augmentation.get("caseOtherEntityAttorney") or [] + if ( + not isinstance(attorney_records, list) + or len(attorney_records) > 1000 + or any(not isinstance(a, dict) for a in attorney_records) + ): + raise CaseImportError("The court returned unreadable attorney information. Retry loading the case.") + for attorney in attorney_records: + reference = attorney.get("roleOfPersonReference") or {} + references = attorney.get("caseRepresentedPartyReference") or [] + if ( + not isinstance(reference, dict) + or not isinstance(references, list) + or len(references) > 1000 + or any(not isinstance(ref, dict) for ref in references) + ): + raise CaseImportError("The court returned unreadable attorney information. Retry loading the case.") + entity = reference.get("ref") + if not isinstance(entity, dict): + continue + id_category = "CASEPARTYATTORNEYID" + attorney_id = entity_id(entity, id_category) + if not attorney_id: + id_category = "ATTORNEYID" + attorney_id = entity_id(entity, id_category) + if not attorney_id: + continue + attorneys[attorney_id] = { + "id": attorney_id, + "id_category": id_category, + **entity_fields(entity), + "bar_number": field(attorney.get("judicialOfficialBarMembership"), "identificationID"), + } + for reference in references: + ref = reference.get("ref") + if isinstance(ref, dict): + associations.setdefault(entity_id(ref), []).append(attorney_id) + parties, problems, seen = [], [], set() + for index, wrapped in enumerate(augmentation["caseParticipant"]): + participant = unwrap(wrapped) + if not isinstance(participant, dict): + raise CaseImportError("The court returned an unreadable party. Retry loading the case.") + role = text(participant.get("caseParticipantRoleCode")) + if role.upper() == "ATTY": + continue + entity = unwrap(participant.get("entityRepresentation")) + if not isinstance(entity, dict): + raise CaseImportError("The court returned an unreadable party. Retry loading the case.") + party_id = entity_id(entity) + if party_id and party_id in seen: + raise CaseImportError("The court returned conflicting party IDs. Retry loading the case.") + seen.add(party_id) + values: dict[str, str] = { + key: "" + for key in ( + "first_name", + "middle_name", + "last_name", + "suffix", + "organization_name", + "email", + "phone", + "address_line_1", + "address_line_2", + "city", + "state", + "zip_code", + "country", + ) + } + values.update(entity_fields(entity)) + values.update(external_party_id=party_id, party_type=role, party_type_name=types.get(role, "")) + for key, value in values.items(): + limit = FilingParty._meta.get_field(key).max_length + if limit and len(value) > limit: + raise CaseImportError( + "The court returned party information that is too long to import. Contact support." + ) + if not party_id: + problems.append(f"Party {index + 1} has no court party ID.") + if role not in types: + problems.append(f"Party {index + 1} has an unrecognized court role ({role or 'missing'}).") + if not (values.get("organization_name") or (values.get("first_name") and values.get("last_name"))): + problems.append(f"Party {index + 1} has an incomplete court name.") + attorney_ids = associations.get(party_id, []) + parties.append( + { + **values, + "external_party_id": party_id, + "party_type": role, + "party_type_name": types.get(role, ""), + "representation": { + "status": "represented" if attorney_ids else "unknown", + "attorneys": [attorneys[i] for i in attorney_ids], + }, + } + ) + if len(parties) > 500: + raise CaseImportError("This case has more parties than can be loaded here. Contact support.") + case_fields = { + "docket_number": text(case.get("caseDocketID")), + "case_title": text(case.get("caseTitleText")), + "case_category_code": text(case.get("caseCategoryText")), + "case_type_code": text(augmentation.get("caseTypeText")), + } + for key, value in case_fields.items(): + if len(value) > FilingDraft._meta.get_field(key).max_length: + raise CaseImportError("The court returned case information that is too long to import. Contact support.") + if not value and key != "case_title": + problems.append(f"The court did not return the case's {key.replace('_', ' ')}.") + return { + "schema_version": 1, + "court_local_case_id": text(case.get("caseTrackingID")), + "court": court, + "tracking_id": tracking_id, + "status": "partial" if problems else "loaded", + "problems": problems, + **case_fields, + "parties": parties, + } + + +def fetch_case(draft, token): + if not token: + raise CaseImportError("Sign in again to load your court case.") + url = ( + f"{settings.EFSP_URL}/jurisdictions/{quote(draft.jurisdiction, safe='')}/cases/" + f"courts/{quote(draft.court_code, safe='')}/cases/{quote(draft.previous_case_id, safe='')}" + ) + headers = get_headers() + headers[f"tyler-token-{draft.jurisdiction}"] = token + try: + response = requests.get(url, headers=headers, timeout=30) + response.raise_for_status() + raw = response.json() + except (requests.RequestException, ValueError) as error: + raise CaseImportError("We could not load the court's parties. Retry or sign in again.") from error + # Classification and roles come from this response, not browser assertions. + try: + codes = requests.get( + f"{settings.EFSP_URL}/jurisdictions/{draft.jurisdiction}/codes/courts/" + f"{quote(draft.court_code, safe='')}/party_types", + headers=headers, + timeout=15, + ) + codes.raise_for_status() + types = codes.json() + if not isinstance(types, list): + raise ValueError + snapshot = normalize_case( + raw, + court=draft.court_code, + tracking_id=draft.previous_case_id, + party_types=types, + docket_number=draft.docket_number, + ) + except (requests.RequestException, ValueError) as error: + if isinstance(error, CaseImportError): + raise + raise CaseImportError("We could not verify the court's party roles. Retry loading the case.") from error + snapshot.update( + source_environment=settings.EFSP_URL, jurisdiction=draft.jurisdiction, retrieved_at=timezone.now().isoformat() + ) + return snapshot + + +@transaction.atomic +def clear_case_import(draft): + draft.parties.filter(source__in=["court", "added"]).delete() + draft.parties.update(is_filing_party=False, is_self=False) + draft.existing_case_snapshot = {} + invalidate_fee_quote(draft, save=False) + draft.save( + update_fields=[ + "existing_case_snapshot", + "quoted_fee_total", + "quoted_fee_breakdown", + "quoted_fee_fingerprint", + "updated_at", + ] + ) + + +@transaction.atomic +def apply_case(draft, snapshot): + current = FilingDraft.objects.select_for_update().get(pk=draft.pk) + if current.status not in {FilingDraft.Status.DRAFT, FilingDraft.Status.ERROR}: + raise CaseImportError("This filing can no longer be edited.") + if (current.court_code, current.previous_case_id, current.existing_case) != ( + snapshot["court"], + snapshot["tracking_id"], + ExistingCase.EXISTING, + ): + raise CaseImportError("The selected case changed while loading. Reload your case.") + if current.existing_case_snapshot.get("tracking_id") not in {None, snapshot["tracking_id"]}: + clear_case_import(current) + previous_fee_inputs = fee_inputs_token(current) + # Extracted suggestions stay in extracted_guesses, not as duplicate parties. + current.parties.exclude(role="filer").exclude(source__in=["court", "added"]).delete() + current.parties.filter(role="filer").update(party_type="", party_type_name="", is_filing_party=False) + existing = {p.external_party_id: p for p in current.parties.filter(source="court") if p.external_party_id} + keep = [] + next_order = max(current.parties.values_list("sort_order", flat=True), default=-1) + 1 + for index, values in enumerate(snapshot["parties"]): + party = existing.get(values["external_party_id"]) + if party is None: + party = FilingParty(draft=current, role="other", sort_order=next_order + index) + for key, value in values.items(): + setattr(party, key, value) + party.source = "court" + party.source_case_id = snapshot["tracking_id"] + party.party_side = side_for_party_type_name(party.party_type_name) + party.save() + keep.append(party.pk) + current.parties.filter(source="court").exclude(pk__in=keep).delete() + current.existing_case_snapshot = snapshot + for key in ("docket_number", "case_title", "case_category_code", "case_type_code"): + setattr(current, key, snapshot[key]) + if fee_inputs_token(current) != previous_fee_inputs: + invalidate_fee_quote(current, save=False) + current.save() + draft.refresh_from_db() + + +def import_ready(draft): + snapshot = draft.existing_case_snapshot or {} + return ( + snapshot.get("status") == "loaded" + and snapshot.get("confirmed") is True + and snapshot.get("court") == draft.court_code + and snapshot.get("tracking_id") == draft.previous_case_id + and snapshot.get("source_environment") == settings.EFSP_URL + ) + + +def load_case(draft, token, *, apply=True): + try: + snapshot = fetch_case(draft, token) + if apply: + apply_case(draft, snapshot) + else: + with transaction.atomic(): + current = FilingDraft.objects.select_for_update().get(pk=draft.pk) + if (current.court_code, current.previous_case_id, current.existing_case) != ( + snapshot["court"], + snapshot["tracking_id"], + ExistingCase.EXISTING, + ) or current.status not in {FilingDraft.Status.DRAFT, FilingDraft.Status.ERROR}: + raise CaseImportError("The selected case changed while loading. Reload your case.") + current.existing_case_snapshot = snapshot + current.save(update_fields=["existing_case_snapshot", "updated_at"]) + draft.refresh_from_db() + except CaseImportError: + with transaction.atomic(): + current = FilingDraft.objects.select_for_update().get(pk=draft.pk) + if (current.court_code, current.previous_case_id, current.existing_case) == ( + draft.court_code, + draft.previous_case_id, + ExistingCase.EXISTING, + ) and current.status in {FilingDraft.Status.DRAFT, FilingDraft.Status.ERROR}: + current.existing_case_snapshot = { + "status": "failed", + "court": current.court_code, + "tracking_id": current.previous_case_id, + } + invalidate_fee_quote(current, save=False) + current.save( + update_fields=[ + "existing_case_snapshot", + "quoted_fee_total", + "quoted_fee_breakdown", + "quoted_fee_fingerprint", + "updated_at", + ] + ) + draft.refresh_from_db() + raise + + +def reconcile_case_parties(payload, draft, jurisdiction, court): + from efile.services.efsp_payload import PayloadValidationError + + existing = bool(payload.get("previous_case_id")) or (draft and draft.existing_case == ExistingCase.EXISTING) + if not existing: + if any(p.get("tyler_id") for key in ("users", "other_parties") for p in payload.get(key, [])): + raise PayloadValidationError("A new case cannot use another case's party IDs.") + return + if draft is None or not import_ready(draft): + raise PayloadValidationError("Confirm your court case and load its parties before calculating fees or filing.") + if (jurisdiction, court, payload.get("previous_case_id"), payload.get("user_started_case")) != ( + draft.jurisdiction, + draft.court_code, + draft.previous_case_id, + False, + ): + raise PayloadValidationError("The filing does not match the confirmed court case.") + for wire, attr in ( + ("efile_case_category", "case_category_code"), + ("efile_case_type", "case_type_code"), + ("docket_number", "docket_number"), + ): + payload[wire] = getattr(draft, attr) + expected = {p.external_party_id: p for p in draft.parties.filter(source="court")} + expected.update({f"draft:{p.pk}": p for p in draft.parties.filter(source="added")}) + selected = {key for key, p in expected.items() if p.is_filing_party} + if not selected: + raise PayloadValidationError("Choose the existing party you are filing for.") + seen = set() + for collection in ("users", "other_parties"): + for party in payload.get(collection, []): + party_id = party.get("tyler_id") + if not party_id and party.get("is_new") is True: + party_id = f"draft:{party.get('_draft_party_id')}" + if not party_id or party_id not in expected or party_id in seen: + raise PayloadValidationError( + "The filing contains a missing, duplicate, or substituted court party ID. Reload the parties." + ) + stored = expected[party_id] + if stored.source == "court": + verified = next( + (p for p in draft.existing_case_snapshot["parties"] if p["external_party_id"] == party_id), None + ) + if verified is None or any( + getattr(stored, key) != verified.get(key, "") + for key in ("party_type", "first_name", "middle_name", "last_name", "suffix", "organization_name") + ): + raise PayloadValidationError("The court party roster needs to be reloaded before filing.") + if party.get("is_new") is not (stored.source == "added") or ( + stored.source_case_id != draft.previous_case_id + or (stored.source == "added" and bool(party.get("tyler_id"))) + ): + raise PayloadValidationError("The party's court identity cannot be changed.") + name = { + "first": stored.organization_name or stored.first_name, + "middle": stored.middle_name, + "last": stored.last_name, + "suffix": stored.suffix, + } + if party.get("name") != name or party.get("party_type") != stored.party_type: + raise PayloadValidationError("Court party names and roles cannot be changed in this filing.") + if (collection == "users") != (party_id in selected): + raise PayloadValidationError("The selected filing parties changed. Reload the parties.") + # Ignore client additions to court identity/representation fields. + email = (stored.email or draft.notice_email or draft.user.email) if collection == "users" else stored.email + party.clear() + party.update( + is_new=stored.source == "added", + name=name, + party_type=stored.party_type, + person_type="business" if stored.organization_name else "individual", + email=email or "", + ) + if stored.source == "court": + party["tyler_id"] = stored.external_party_id + if stored.phone: + party["phone_number"] = stored.phone + if stored.address_line_1 and stored.city and stored.state and stored.zip_code: + party["address"] = { + "address": stored.address_line_1, + "unit": stored.address_line_2, + "city": stored.city, + "state": stored.state, + "zip": stored.zip_code, + "country": stored.country, + } + if collection == "users": + party["is_form_filler"] = False + seen.add(party_id) + if seen != set(expected): + raise PayloadValidationError("Some existing court parties are missing. Reload the parties.") + references = [f"users[{i}]" for i in range(len(payload.get("users", [])))] + for bundle in payload.get("al_court_bundle", []): + if bundle.get("filing_parties") != references: + raise PayloadValidationError("A document does not reference the selected existing filing parties.") + + +@transaction.atomic +def confirm_case(draft): + current = FilingDraft.objects.select_for_update().get(pk=draft.pk) + snapshot = current.existing_case_snapshot + if (current.court_code, current.previous_case_id, current.existing_case) != ( + draft.court_code, + draft.previous_case_id, + ExistingCase.EXISTING, + ) or snapshot.get("status") != "loaded": + raise CaseImportError("The selected case changed while loading. Reload your case.") + apply_case(current, {**snapshot, "confirmed": True}) + draft.refresh_from_db() diff --git a/efile_app/efile/services/extracted_parties.py b/efile_app/efile/services/extracted_parties.py index 1e4bb596..f6c772ec 100644 --- a/efile_app/efile/services/extracted_parties.py +++ b/efile_app/efile/services/extracted_parties.py @@ -289,6 +289,12 @@ def save_reviewed_parties(draft: FilingDraft, rows: list[dict[str, str]]) -> Non this screen must not lose the address they typed on the party screen. """ + if ( + draft.existing_case_snapshot.get("confirmed") + or FilingParty.objects.filter(draft=draft, source="court").exists() + ): + # Caption suggestions remain in extracted_guesses for review. + return existing = {party.pk: party for party in FilingParty.objects.filter(draft=draft, role="other")} kept: set[int] = set() next_order = max((party.sort_order for party in existing.values()), default=-1) + 1 @@ -308,7 +314,7 @@ def save_reviewed_parties(draft: FilingDraft, rows: list[dict[str, str]]) -> Non # a person to add to the case. if not name or is_placeholder_name(name): continue - party = FilingParty(draft=draft, role="other", sort_order=next_order + index) + party = FilingParty(draft=draft, role="other", source="document", sort_order=next_order + index) else: kept.add(party.pk) diff --git a/efile_app/efile/services/fee_quotes.py b/efile_app/efile/services/fee_quotes.py index f5c620a7..d77caffc 100644 --- a/efile_app/efile/services/fee_quotes.py +++ b/efile_app/efile/services/fee_quotes.py @@ -54,7 +54,20 @@ def fee_inputs(draft: FilingDraft, *, payment_account_id: str | None = None) -> for document in FilingDocument.objects.filter(draft=draft).order_by("role", "sort_order", "pk") ] parties = sorted( - (party.role, party.party_type, bool(party.is_filing_party), bool(party.organization_name)) + ( + party.role, + party.party_type, + bool(party.is_filing_party), + bool(party.organization_name), + party.external_party_id, + party.source_case_id, + json.dumps(party.representation, sort_keys=True), + party.first_name, + party.middle_name, + party.last_name, + party.suffix, + party.organization_name, + ) for party in FilingParty.objects.filter(draft=draft) ) return { diff --git a/efile_app/efile/services/filing_path.py b/efile_app/efile/services/filing_path.py index 9c88479e..ec7d052a 100644 --- a/efile_app/efile/services/filing_path.py +++ b/efile_app/efile/services/filing_path.py @@ -96,6 +96,13 @@ def change_filing_path(draft: FilingDraft, new_path: str) -> FilingPathChange: update_fields = ["existing_case", "updated_at"] draft.existing_case = current + from efile.services.existing_cases import clear_case_import + + if draft.existing_case_snapshot or FilingParty.objects.filter(draft=draft, source="court").exists(): + had_quote = bool(draft.quoted_fee_total or draft.quoted_fee_breakdown or draft.quoted_fee_fingerprint) + clear_case_import(draft) + if had_quote: + change.cleared.append("fees") if current != ExistingCase.EXISTING and (draft.previous_case_id or draft.docket_number or draft.case_title): # A new case has no number or title until the court opens it, and a @@ -156,7 +163,10 @@ def clear_changed_classification(draft, court_code, case_category_code, case_typ requested_optional_services=[], filing_requires_amount_in_controversy=False, ) - FilingParty.objects.filter(draft=draft).update(party_type="", party_type_name="") + from efile.services.existing_cases import clear_case_import + + clear_case_import(draft) + FilingParty.objects.filter(draft=draft).exclude(source="court").update(party_type="", party_type_name="") draft.case_subtype_code = "" draft.case_subtype_name = "" draft.document_type_code = "" diff --git a/efile_app/efile/services/handoff.py b/efile_app/efile/services/handoff.py index bfb1775e..387df2f7 100644 --- a/efile_app/efile/services/handoff.py +++ b/efile_app/efile/services/handoff.py @@ -453,6 +453,8 @@ def choose(obj, field, options, hints): ) party_options = _codes(draft.jurisdiction, f"{draft.court_code}/case_types/{draft.case_type_code}/party_types") for party in draft.parties.all(): + if party.source == "court": + continue if party.role == "filer" and not (party.is_self or party.is_filing_party): continue hints = [party.party_role_hint] @@ -492,6 +494,15 @@ def need(path, value, message, view): need(field, getattr(draft, field), f"Choose {label}.", "extraction_review") if draft.existing_case == ExistingCase.EXISTING: need("previous_case_id", draft.previous_case_id, "Find and confirm the existing court case.", "case_lookup") + if draft.previous_case_id: + from efile.services.existing_cases import import_ready + + need( + "_court_case_import", + import_ready(draft), + "Load and confirm the court's existing parties.", + "case_confirmation", + ) need("main_document", draft.documents.filter(role="lead").exists(), "Add the main PDF.", "upload_documents") for doc in draft.documents.all(): for field, label in ( @@ -529,8 +540,10 @@ def need(path, value, message, view): f"Add the name for this party: {party}.", "parties", ) - address_ok = address_is_complete(party) or ( - address_is_blank(party) and not party_address_requirement(draft, party).required + address_ok = ( + party.source == "court" + or address_is_complete(party) + or (address_is_blank(party) and not party_address_requirement(draft, party).required) ) need(f"parties.{party.pk}.address", address_ok, f"Complete the address for {party}.", "parties") for question in get_case_questions(draft): diff --git a/efile_app/efile/services/people.py b/efile_app/efile/services/people.py index 4900f203..c614b97a 100644 --- a/efile_app/efile/services/people.py +++ b/efile_app/efile/services/people.py @@ -6,6 +6,7 @@ import requests from django.conf import settings +from django.db import transaction from efile.models import FilingDocument, FilingDraft, FilingParty from efile.party_sides import PARTY_SIDE_KEYWORDS, PartySide, side_for_party_type_name @@ -34,6 +35,8 @@ def party_is_complete(party: FilingParty, *, draft=None, party_types=None) -> bool: has_name = bool(party.organization_name or (party.first_name and party.last_name)) + if getattr(party, "source", "") == "court": + return bool(party.external_party_id and party.party_type and has_name) if getattr(party, "role", "") == "filer" and not party.party_type: # Someone filing for a party they are not. They have no party type to # be missing, and no caption address to complete: their name and @@ -67,7 +70,10 @@ def filer_is_party(draft: FilingDraft) -> bool: """ filer = FilingParty.objects.filter(draft=draft, role="filer").first() - return bool(filer and filer.party_type) + return ( + bool(filer and filer.party_type) + or FilingParty.objects.filter(draft=draft, source__in=["court", "added"], is_self=True).exists() + ) def filing_parties(draft: FilingDraft) -> list[FilingParty]: @@ -89,6 +95,7 @@ def filing_parties(draft: FilingDraft) -> list[FilingParty]: return [filer] if filer is not None and filer.party_type else [] +@transaction.atomic def set_filing_parties(draft: FilingDraft, parties) -> None: """Record who this filing is on behalf of, and no one else. @@ -97,6 +104,7 @@ def set_filing_parties(draft: FilingDraft, parties) -> None: themselves behind as a second filing party. """ + FilingDraft.objects.select_for_update().get(pk=draft.pk) wanted = {party.pk for party in parties} for party in FilingParty.objects.filter(draft=draft): if party.is_filing_party != (party.pk in wanted): @@ -195,6 +203,7 @@ def claim_replaces_a_name(filer: FilingParty | None, party: FilingParty | None) return bool(party_display_name(party)) and not names_match(filer, party) +@transaction.atomic def claim_party_as_filer(draft: FilingDraft, party: FilingParty, *, use_party_name: bool = False) -> None: """Answer "that party is me": become them, and stop listing them twice. @@ -209,8 +218,18 @@ def claim_party_as_filer(draft: FilingDraft, party: FilingParty, *, use_party_na the default keeps their own. """ + FilingDraft.objects.select_for_update().get(pk=draft.pk) + party = FilingParty.objects.filter(pk=party.pk, draft=draft).first() filer = FilingParty.objects.filter(draft=draft, role="filer").first() - if filer is None: + if filer is None or party is None: + return + if party.source in {"court", "added"}: + FilingParty.objects.filter(draft=draft, source__in=["court", "added"]).update(is_self=False) + party.is_self = True + party.save(update_fields=["is_self", "updated_at"]) + filer.party_type = filer.party_type_name = "" + filer.save(update_fields=["party_type", "party_type_name", "updated_at"]) + set_filing_parties(draft, [party]) return name_fields: list[str] = [] if use_party_name and party_display_name(party): @@ -260,7 +279,7 @@ def discard_empty_parties(draft: FilingDraft) -> int: empty = [ party for party in FilingParty.objects.filter(draft=draft, role="other", party_type="") - if not party_display_name(party) and not party.party_side + if party.source != "court" and not party_display_name(party) and not party.party_side ] for party in empty: party.delete() @@ -415,7 +434,7 @@ def apply_party_sides(draft: FilingDraft, party_types: list[dict[str, Any]]) -> if not party_types: return for party in FilingParty.objects.filter(draft=draft, role="other", party_type=""): - if not party.party_side: + if party.source == "court" or not party.party_side: continue match = party_type_for_party(party, party_types) if match is None: @@ -443,7 +462,7 @@ def _filer_duplicates(draft: FilingDraft) -> list[FilingParty]: return [ party for party in FilingParty.objects.filter(draft=draft, role="other") - if _comparable(party_display_name(party)) == filer_name + if party.source != "court" and _comparable(party_display_name(party)) == filer_name ] @@ -481,7 +500,10 @@ def absorb_filer_duplicates(draft: FilingDraft) -> str: put to them on the parties screen instead; see :func:`filer_name_match`. """ - if not filer_is_party(draft): + if ( + not filer_is_party(draft) + or FilingParty.objects.filter(draft=draft, source__in=["court", "added"], is_self=True).exists() + ): return side_named_for_filer(draft) duplicates = _filer_duplicates(draft) @@ -510,6 +532,10 @@ def absorb_filer_duplicates(draft: FilingDraft) -> str: def ensure_required_parties(draft: FilingDraft, party_types: list[dict[str, Any]]) -> None: + if draft.existing_case_snapshot: + # A verified roster cannot acquire new people through placeholders. + # Existing-case additions require the filer's explicit Add action. + return parties = FilingParty.objects.filter(draft=draft) covered = set(parties.exclude(party_type="").values_list("party_type", flat=True)) last_order = parties.filter(role="other").order_by("-sort_order").values_list("sort_order", flat=True).first() diff --git a/efile_app/efile/static/js/filing-payload.js b/efile_app/efile/static/js/filing-payload.js index e3b2b0fa..c9a4672e 100644 --- a/efile_app/efile/static/js/filing-payload.js +++ b/efile_app/efile/static/js/filing-payload.js @@ -50,6 +50,9 @@ const FilingPayload = { }, partyFromDraft(party) { + if (party.source === "court" && !party.external_party_id) { + throw new Error("Reload the court case: an existing party has no court ID."); + } const address = { address: party.address_line_1 || "", unit: party.address_line_2 || "", @@ -59,6 +62,9 @@ const FilingPayload = { country: party.country || "US" }; return { + ...(party.source === "added" ? { + _draft_party_id: party.id + } : {}), party_type: party.party_type, // An organization has one name where a person has three, and the // EFSP only reads `name.first` that way when the entry says it is @@ -83,6 +89,9 @@ const FilingPayload = { } : {}), email: party.email || "", phone_number: party.phone || "", + ...(party.external_party_id ? { + tyler_id: party.external_party_id + } : {}), is_new: !party.external_party_id }; }, @@ -207,7 +216,7 @@ const FilingPayload = { const mainUser = filerIsFilingParty ? this.accountUser(userData, partyType) : null; const users = filingParties.length ? filingParties.map((party) => ( - party.role === "filer" ? mainUser : this.filingPartyFromDraft(party, noticeEmail) + party.role === "filer" && !party.external_party_id ? mainUser : this.filingPartyFromDraft(party, noticeEmail) )) : [mainUser]; // Add second user if needed for name changes @@ -244,7 +253,7 @@ const FilingPayload = { .filter((party) => party.role !== "filer" && party.party_type && !filingParties.includes(party)) .map((party) => this.partyFromDraft(party)); - if (other_parties.length === 0 && caseData.other_first_name && caseData.other_party_type) { + if (!durableParties.length && other_parties.length === 0 && caseData.other_first_name && caseData.other_party_type) { const legacyAddress = { address: caseData.other_address_line_1 || "", unit: caseData.other_address_line_2 || "", diff --git a/efile_app/efile/templates/efile/case_confirmation.html b/efile_app/efile/templates/efile/case_confirmation.html index d4456b13..fff87f33 100644 --- a/efile_app/efile/templates/efile/case_confirmation.html +++ b/efile_app/efile/templates/efile/case_confirmation.html @@ -62,6 +62,16 @@

{% translate "Is this your court case?" %}

+ {% if import_error %} + + {% translate "Sign in again" %} + {% endif %} + {% if court_parties %} +

{% translate "Already on the court case" %}

+ + {% endif %} {% if availability_message %}{% endif %}
{% csrf_token %} @@ -82,7 +92,12 @@

{% translate "Is this your court case?" %}

name="confirmed" value="yes" {% if availability_message %}disabled{% endif %}> - {% translate "Yes, this is my case" %} + {% if import_error %} + {% translate "Retry loading the case" %} + {% else %} + {% translate "Yes, this is my case" %} + {% endif %} +
diff --git a/efile_app/efile/templates/efile/parties.html b/efile_app/efile/templates/efile/parties.html index eb5e0d76..15fc0128 100644 --- a/efile_app/efile/templates/efile/parties.html +++ b/efile_app/efile/templates/efile/parties.html @@ -11,7 +11,9 @@

{% translate "Who is involved in this case?" %}

{% include "efile/partials/filing_guidance.html" %}

- {% if case_has_parties %} + {% if existing_court_case %} + {% translate "The court has already recorded these parties. Choose who you are filing for, or link yourself to an existing party." %} + {% elif case_has_parties %} {% translate "You already told us who is in this case. The one thing left is your own part in it — the list below is here to check, not to fill in again." %} {% else %} {% translate "Start with your own part in this case. Then we will ask about any other people, one at a time." %} @@ -60,52 +62,54 @@

{% translate "Who is involved in this case?" %}

{% translate "You — the person filing" %} -
- {% ui_text "parties.role_question" %} -

{% ui_text "parties.role_help" %}

- {% if guessed_party_type %} -
- - {% blocktranslate with name=guessed_party_type.name %}Based on this filing, you are likely the {{ name }}. We are not certain, so choose below to confirm.{% endblocktranslate %} - -
- {% endif %} -
- {% for party_type in party_types %} - - {% endfor %} - {% if party_types %} - + {% if not existing_court_case %} +
+ {% ui_text "parties.role_question" %} +

{% ui_text "parties.role_help" %}

+ {% if guessed_party_type %} +
+ + {% blocktranslate with name=guessed_party_type.name %}Based on this filing, you are likely the {{ name }}. We are not certain, so choose below to confirm.{% endblocktranslate %} + +
{% endif %} -
-
+
+ {% for party_type in party_types %} + + {% endfor %} + {% if party_types %} + + {% endif %} +
+ + {% endif %}
+ {% if not filing_for_someone_else and not existing_court_case %}hidden{% endif %}> {% translate "Who are you filing for?" %}

{% translate "The court records every filing as being made on behalf of a party. Choose the person you are filing for." %} @@ -159,12 +163,21 @@

{% translate "Who is involved in this case?" %}

name="action" value="save_role" {% if not party_types %}disabled{% endif %}>{% translate "Save role" %} - {% translate "Clicking your court role saves it and updates the party list below. With a keyboard, select your role, then choose Save role or Continue. If you are filing for someone else, complete their information and choose Save role." %} + + {% if existing_court_case %} + {% translate "Choose the party you are filing for, then choose Save role or Continue. Use This is me to link yourself to a court party." %} + {% else %} + {% translate "Clicking your court role saves it and updates the party list below. With a keyboard, select your role, then choose Save role or Continue. If you are filing for someone else, complete their information and choose Save role." %} + {% endif %} + + {% if caption_discrepancy %} +

{% translate "Your document also contains party names. Check its caption against the court roster below; the court roster is used for this filing." %}

+ {% endif %}

{% translate "Party list" %}

- {% if not filer.party_type %} + {% if not filer.party_type and not existing_court_case %}
{% if case_has_parties %}

- {% if parties_from_document %} + {% if existing_court_case %} + {% translate "These parties are already on the court case. Choose who this filing is for. Their court details are read-only." %} + {% elif parties_from_document %} {% translate "These are the people we read from your document and you confirmed. Change anything that is wrong." %} {% else %} {% translate "These are the people you told us about. Change anything that is wrong." %} @@ -204,12 +224,16 @@

{% translate "Party list" %}

{% if item.party.organization_name %} {{ item.party.organization_name }} {% else %} - {{ item.party.first_name }} {{ item.party.last_name }} + {{ item.name }} {% endif %} {% if item.party.role == "filer" and not item.party.party_type %} - {% translate "You — filing this, but not a party in the case" %} + {% if existing_court_case %} + {% translate "Your account contact information" %} + {% else %} + {% translate "You — filing this, but not a party in the case" %} + {% endif %} {% else %} {% firstof item.party.party_type_name item.party.get_party_side_display "Role not chosen" %} {% if item.party.role == "filer" %} @@ -228,17 +252,25 @@

{% translate "Party list" %}

{% translate "Needs details" %} {% endif %} + {% if item.party.source == "court" %} + {% translate "Already on the court case" %}{% if item.party.is_self %} · {% translate "This is you" %}{% endif %} + + {% if item.party.representation.status == "represented" %} + {% translate "Attorney information on file" %} + {% else %} + {% translate "Representation not provided by the court" %} + {% endif %} + + {% endif %} {% if item.party.role == "other" %} {% if not filer.party_type and item.claimable %} {% comment %} - The court's required parties are often all taken by people the - document named, so becoming one of them is claiming a row rather - than adding another person. Claiming a row whose name is not - yours replaces a person in the case, so it is not offered under - the same words, and claim-party.js asks before it happens. + Court and explicitly added rows link to the account without + changing their names. Document suggestions retain the name + choice dialog used when replacing a suggested person. {% endcomment %}
{% translate "Party list" %}
{% endif %} - {% translate "Edit" %} -
- {% csrf_token %} - - - - -
+ {% if item.party.source != "court" %} + {% translate "Edit" %} +
+ {% csrf_token %} + + + + +
+ {% endif %} {% endif %} {% endfor %} diff --git a/efile_app/efile/templates/efile/review.html b/efile_app/efile/templates/efile/review.html index 620e9b40..e62ffa4d 100644 --- a/efile_app/efile/templates/efile/review.html +++ b/efile_app/efile/templates/efile/review.html @@ -127,7 +127,16 @@

{% translate "Your information" %}

{{ filer.phone }} {% endif %} - {% if filer.party_type %} + {% if existing_court_case %} + {% if filer_court_party %} +

{% blocktranslate with name=filer_court_party %}Your account is linked to {{ name }}.{% endblocktranslate %}

+ {% endif %} + {% if filing_for %} +

{% blocktranslate with names=filing_for|join:", " %}This filing is on behalf of {{ names }}.{% endblocktranslate %}

+ {% else %} + {% translate "Choose who you are filing for" %} + {% endif %} + {% elif filer.party_type %} {{ filer.party_type_name|default:filer.party_type }} {% else %}

@@ -139,7 +148,12 @@

{% translate "Your information" %}

- {% translate "Add me as a party" %} + + {% if existing_court_case %} + {% translate "Choose an existing court party" %} + {% else %} + {% translate "Add me as a party" %} + {% endif %} {% endif %} {% endif %} diff --git a/efile_app/efile/tests/fixtures/existing_cases/2005SC000985-raw.json b/efile_app/efile/tests/fixtures/existing_cases/2005SC000985-raw.json new file mode 100644 index 00000000..175fe3a8 --- /dev/null +++ b/efile_app/efile/tests/fixtures/existing_cases/2005SC000985-raw.json @@ -0,0 +1,802 @@ +{ + "value": { + "caseTrackingID": { + "value": "2189961" + }, + "caseDocketID": { + "value": "2005-SC-000985" + }, + "caseTitleText": { + "value": "Sample plaintiff v. Sample defendant" + }, + "caseCategoryText": { + "value": "7421" + }, + "rest": [ + { + "value": { + "caseCourt": { + "organizationIdentification": { + "name": "{http://niem.gov/niem/niem-core/2.0}OrganizationIdentification", + "value": { + "identificationID": { + "value": "kane" + } + } + } + }, + "caseLineageCase": [ + { + "caseTrackingID": { + "value": "2b15d645-04e5-45b2-9d5f-dc717d33085e" + } + } + ] + } + }, + { + "value": { + "caseParticipant": [ + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "2692c552-07c0-504d-90ef-ed807771a1ea" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "ATTY" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityOrganization", + "value": { + "organizationIdentification": { + "name": "{urn:tyler:ecf:extensions:Common}OrganizationOtherIdentification", + "value": { + "identification": [ + { + "identificationID": { + "value": "a50cfa61-b48a-5779-b86b-bdd35c84bccc" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ] + } + }, + "organizationName": { + "value": "Sample organization" + }, + "rest": [ + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}OrganizationAugmentation", + "value": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + ] + } + }, + "caseParticipantRoleCode": { + "value": "10406" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "d2f3660c-d82d-537a-a087-d4ee30c7f473" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "5616" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityOrganization", + "value": { + "organizationIdentification": { + "name": "{urn:tyler:ecf:extensions:Common}OrganizationOtherIdentification", + "value": { + "identification": [ + { + "identificationID": { + "value": "8071fad8-5ea5-5bd4-aa5c-3dec5ab2a95e" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ] + } + }, + "organizationName": { + "value": "Sample organization" + }, + "rest": [ + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}OrganizationAugmentation", + "value": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + ] + } + }, + "caseParticipantRoleCode": { + "value": "5616" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityOrganization", + "value": { + "organizationIdentification": { + "name": "{urn:tyler:ecf:extensions:Common}OrganizationOtherIdentification", + "value": { + "identification": [ + { + "identificationID": { + "value": "45d119df-c4ba-5aab-bb09-850cdfbf8d0b" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ] + } + }, + "organizationName": { + "value": "Sample organization" + }, + "rest": [ + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}OrganizationAugmentation", + "value": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + ] + } + }, + "caseParticipantRoleCode": { + "value": "10446" + } + } + } + ], + "caseOtherEntityAttorney": [ + { + "roleOfPersonReference": { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "2692c552-07c0-504d-90ef-ed807771a1ea" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "judicialOfficialBarMembership": { + "judicialOfficialBarIdentification": { + "identificationID": { + "value": "123456" + } + } + }, + "caseOfficialRoleText": { + "value": "LEAD" + }, + "caseRepresentedPartyReference": [ + { + "ref": { + "organizationIdentification": { + "name": "{urn:tyler:ecf:extensions:Common}OrganizationOtherIdentification", + "value": { + "identification": [ + { + "identificationID": { + "value": "a50cfa61-b48a-5779-b86b-bdd35c84bccc" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ] + } + }, + "organizationName": { + "value": "Sample organization" + }, + "rest": [ + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}OrganizationAugmentation", + "value": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + ] + } + } + ] + } + ], + "caseTypeText": { + "value": "27610" + } + } + } + ] + }, + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CivilCase-4.0}CivilCase" +} diff --git a/efile_app/efile/tests/fixtures/existing_cases/2019-D-0000655-raw.json b/efile_app/efile/tests/fixtures/existing_cases/2019-D-0000655-raw.json new file mode 100644 index 00000000..aaae2363 --- /dev/null +++ b/efile_app/efile/tests/fixtures/existing_cases/2019-D-0000655-raw.json @@ -0,0 +1,2469 @@ +{ + "value": { + "caseTrackingID": { + "value": "181681695" + }, + "caseDocketID": { + "value": "2019-D-0000655" + }, + "caseTitleText": { + "value": "Sample plaintiff v. Sample defendant" + }, + "caseCategoryText": { + "value": "6190" + }, + "rest": [ + { + "value": { + "caseCourt": { + "organizationIdentification": { + "name": "{http://niem.gov/niem/niem-core/2.0}OrganizationIdentification", + "value": { + "identificationID": { + "value": "winnebago" + } + } + } + }, + "caseLineageCase": [ + { + "caseTrackingID": { + "value": "6fbcd5ce-cce6-4d08-a0e0-1f2efa4fc0f9" + } + } + ] + } + }, + { + "value": { + "caseParticipant": [ + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "5af99db4-d440-5872-ae8e-f007b06a4692" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "ATTY" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "efeb1f6a-7a44-5c0d-8b5a-91a4b888885f" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "103300" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "baf95bff-8bff-565d-9337-28c59249eff1" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "ATTY" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "5f740d61-a1fb-5218-8e23-89629eb3cd2f" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "59056" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "37488c98-867f-51dc-8766-ecf1dacbd6a2" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "ATTY" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "628aa269-6a2f-5093-99c2-a2e3c0d7d1c5" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "ATTY" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "b6475022-7d76-595a-9c41-24788db9800b" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "59053" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "75e61297-6b02-5247-8570-4bc512bfe6e5" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "ATTY" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "cda326d4-e871-54dc-8ed4-cfd275eb1ccf" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "103301" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "7105fec0-19d5-58cc-a155-1ef36544cf0f" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "ATTY" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "af7b4cff-abd0-5206-8530-3a46792f28fa" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "103298" + } + } + } + ], + "caseOtherEntityAttorney": [ + { + "roleOfPersonReference": { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "7105fec0-19d5-58cc-a155-1ef36544cf0f" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "judicialOfficialBarMembership": { + "judicialOfficialBarIdentification": { + "identificationID": { + "value": "123456" + } + } + }, + "caseOfficialRoleText": { + "value": "LEAD" + }, + "caseRepresentedPartyReference": [ + { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "af7b4cff-abd0-5206-8530-3a46792f28fa" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + } + ] + }, + { + "roleOfPersonReference": { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "75e61297-6b02-5247-8570-4bc512bfe6e5" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "judicialOfficialBarMembership": { + "judicialOfficialBarIdentification": { + "identificationID": { + "value": "123456" + } + } + }, + "caseOfficialRoleText": { + "value": "LEAD" + }, + "caseRepresentedPartyReference": [ + { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "cda326d4-e871-54dc-8ed4-cfd275eb1ccf" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + } + ] + }, + { + "roleOfPersonReference": { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "628aa269-6a2f-5093-99c2-a2e3c0d7d1c5" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "judicialOfficialBarMembership": { + "judicialOfficialBarIdentification": { + "identificationID": { + "value": "123456" + } + } + }, + "caseRepresentedPartyReference": [ + { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "b6475022-7d76-595a-9c41-24788db9800b" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + } + ] + }, + { + "roleOfPersonReference": { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "37488c98-867f-51dc-8766-ecf1dacbd6a2" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "judicialOfficialBarMembership": { + "judicialOfficialBarIdentification": { + "identificationID": { + "value": "123456" + } + } + }, + "caseOfficialRoleText": { + "value": "LEAD" + }, + "caseRepresentedPartyReference": [ + { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "b6475022-7d76-595a-9c41-24788db9800b" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + } + ] + }, + { + "roleOfPersonReference": { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "baf95bff-8bff-565d-9337-28c59249eff1" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "judicialOfficialBarMembership": { + "judicialOfficialBarIdentification": { + "identificationID": { + "value": "123456" + } + } + }, + "caseOfficialRoleText": { + "value": "LEAD" + }, + "caseRepresentedPartyReference": [ + { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "5f740d61-a1fb-5218-8e23-89629eb3cd2f" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + } + ] + }, + { + "roleOfPersonReference": { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "5af99db4-d440-5872-ae8e-f007b06a4692" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "judicialOfficialBarMembership": { + "judicialOfficialBarIdentification": { + "identificationID": { + "value": "123456" + } + } + }, + "caseOfficialRoleText": { + "value": "LEAD" + }, + "caseRepresentedPartyReference": [ + { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "efeb1f6a-7a44-5c0d-8b5a-91a4b888885f" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + } + ] + } + ], + "caseTypeText": { + "value": "163013" + } + } + } + ] + }, + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CivilCase-4.0}CivilCase" +} diff --git a/efile_app/efile/tests/fixtures/existing_cases/2019-SC-0001642-raw.json b/efile_app/efile/tests/fixtures/existing_cases/2019-SC-0001642-raw.json new file mode 100644 index 00000000..7073fa12 --- /dev/null +++ b/efile_app/efile/tests/fixtures/existing_cases/2019-SC-0001642-raw.json @@ -0,0 +1,583 @@ +{ + "value": { + "caseTrackingID": { + "value": "181663660" + }, + "caseDocketID": { + "value": "2019-SC-0001642" + }, + "caseTitleText": { + "value": "Sample plaintiff v. Sample defendant" + }, + "caseCategoryText": { + "value": "6198" + }, + "rest": [ + { + "value": { + "caseCourt": { + "organizationIdentification": { + "name": "{http://niem.gov/niem/niem-core/2.0}OrganizationIdentification", + "value": { + "identificationID": { + "value": "winnebago" + } + } + } + }, + "caseLineageCase": [ + { + "caseTrackingID": { + "value": "3ac4b346-42a5-4a72-9cd6-6a126bb9d0ba" + } + } + ] + } + }, + { + "value": { + "caseParticipant": [ + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "529040a7-2f75-51b3-a8ea-bd26d27da2a7" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "ATTY" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "550aa214-7f64-5fda-b70a-40e18a9de053" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "59054" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityOrganization", + "value": { + "organizationIdentification": { + "name": "{urn:tyler:ecf:extensions:Common}OrganizationOtherIdentification", + "value": { + "identification": [ + { + "identificationID": { + "value": "035e2f18-2eff-524e-a61f-46867775e2f7" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ] + } + }, + "organizationName": { + "value": "Sample organization" + }, + "rest": [ + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}OrganizationAugmentation", + "value": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + ] + } + }, + "caseParticipantRoleCode": { + "value": "59055" + } + } + } + ], + "caseOtherEntityAttorney": [ + { + "roleOfPersonReference": { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "529040a7-2f75-51b3-a8ea-bd26d27da2a7" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "judicialOfficialBarMembership": { + "judicialOfficialBarIdentification": { + "identificationID": { + "value": "123456" + } + } + }, + "caseOfficialRoleText": { + "value": "LEAD" + }, + "caseRepresentedPartyReference": [ + { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "550aa214-7f64-5fda-b70a-40e18a9de053" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + } + ] + } + ], + "caseTypeText": { + "value": "103332" + } + } + } + ] + }, + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CivilCase-4.0}CivilCase" +} diff --git a/efile_app/efile/tests/fixtures/existing_cases/2024DC00000572-raw.json b/efile_app/efile/tests/fixtures/existing_cases/2024DC00000572-raw.json new file mode 100644 index 00000000..10176de1 --- /dev/null +++ b/efile_app/efile/tests/fixtures/existing_cases/2024DC00000572-raw.json @@ -0,0 +1,584 @@ +{ + "value": { + "caseTrackingID": { + "value": "6134794" + }, + "caseDocketID": { + "value": "2024DC00000572" + }, + "caseTitleText": { + "value": "Sample plaintiff v. Sample defendant" + }, + "caseCategoryText": { + "value": "190919" + }, + "rest": [ + { + "value": { + "caseCourt": { + "organizationIdentification": { + "name": "{http://niem.gov/niem/niem-core/2.0}OrganizationIdentification", + "value": { + "identificationID": { + "value": "lake" + } + } + } + }, + "caseLineageCase": [ + { + "caseTrackingID": { + "value": "c14090e7-ad60-43ae-8821-6fa4ef4a0011" + } + } + ] + } + }, + { + "value": { + "caseParticipant": [ + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "bc207308-698d-5bcb-91f9-fb405ffc11f8" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "ATTY" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "73cfad07-8408-5590-8696-3b731aaf5e9d" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "5628" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "e1dae246-9115-5c9e-98bf-b0d751a3b490" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "5627" + } + } + } + ], + "caseOtherEntityAttorney": [ + { + "roleOfPersonReference": { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "bc207308-698d-5bcb-91f9-fb405ffc11f8" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "judicialOfficialBarMembership": { + "judicialOfficialBarIdentification": { + "identificationID": { + "value": "123456" + } + } + }, + "caseOfficialRoleText": { + "value": "LEAD" + }, + "caseRepresentedPartyReference": [ + { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "73cfad07-8408-5590-8696-3b731aaf5e9d" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + } + ] + } + ], + "caseTypeText": { + "value": "242453" + } + } + } + ] + }, + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:DomesticCase-4.0}DomesticCase" +} diff --git a/efile_app/efile/tests/fixtures/existing_cases/2024EV001752-raw.json b/efile_app/efile/tests/fixtures/existing_cases/2024EV001752-raw.json new file mode 100644 index 00000000..eaf76a8e --- /dev/null +++ b/efile_app/efile/tests/fixtures/existing_cases/2024EV001752-raw.json @@ -0,0 +1,803 @@ +{ + "value": { + "caseTrackingID": { + "value": "4752350" + }, + "caseDocketID": { + "value": "2024-EV-001752" + }, + "caseTitleText": { + "value": "Sample plaintiff v. Sample defendant" + }, + "caseCategoryText": { + "value": "200542" + }, + "rest": [ + { + "value": { + "caseCourt": { + "organizationIdentification": { + "name": "{http://niem.gov/niem/niem-core/2.0}OrganizationIdentification", + "value": { + "identificationID": { + "value": "kane" + } + } + } + }, + "caseLineageCase": [ + { + "caseTrackingID": { + "value": "8534ec52-0fa8-4e77-91ca-55af362742c3" + } + } + ] + } + }, + { + "value": { + "caseParticipant": [ + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "71a08532-5778-58bb-8027-508ed3b73f49" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "ATTY" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityOrganization", + "value": { + "organizationIdentification": { + "name": "{urn:tyler:ecf:extensions:Common}OrganizationOtherIdentification", + "value": { + "identification": [ + { + "identificationID": { + "value": "3e54d37e-4732-5d4d-bcdb-9e618be79309" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ] + } + }, + "organizationName": { + "value": "Sample organization" + }, + "rest": [ + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}OrganizationAugmentation", + "value": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + ] + } + }, + "caseParticipantRoleCode": { + "value": "10406" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "59ca0ea7-ff8f-5636-949a-a239ee870557" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "5616" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "b4770227-ab1f-5a6f-94cf-34413703ff28" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "5616" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityOrganization", + "value": { + "organizationIdentification": { + "name": "{urn:tyler:ecf:extensions:Common}OrganizationOtherIdentification", + "value": { + "identification": [ + { + "identificationID": { + "value": "0fcca70b-2f5a-54f3-94e1-ccbdff67b0cd" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ] + } + }, + "organizationName": { + "value": "Sample organization" + }, + "rest": [ + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}OrganizationAugmentation", + "value": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + ] + } + }, + "caseParticipantRoleCode": { + "value": "5616" + } + } + } + ], + "caseOtherEntityAttorney": [ + { + "roleOfPersonReference": { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "71a08532-5778-58bb-8027-508ed3b73f49" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "judicialOfficialBarMembership": { + "judicialOfficialBarIdentification": { + "identificationID": { + "value": "123456" + } + } + }, + "caseOfficialRoleText": { + "value": "LEAD" + }, + "caseRepresentedPartyReference": [ + { + "ref": { + "organizationIdentification": { + "name": "{urn:tyler:ecf:extensions:Common}OrganizationOtherIdentification", + "value": { + "identification": [ + { + "identificationID": { + "value": "3e54d37e-4732-5d4d-bcdb-9e618be79309" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ] + } + }, + "organizationName": { + "value": "Sample organization" + }, + "rest": [ + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}OrganizationAugmentation", + "value": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + ] + } + } + ] + } + ], + "caseTypeText": { + "value": "318655" + } + } + } + ] + }, + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CivilCase-4.0}CivilCase" +} diff --git a/efile_app/efile/tests/fixtures/existing_cases/2024SC00003388-raw.json b/efile_app/efile/tests/fixtures/existing_cases/2024SC00003388-raw.json new file mode 100644 index 00000000..8d8900fc --- /dev/null +++ b/efile_app/efile/tests/fixtures/existing_cases/2024SC00003388-raw.json @@ -0,0 +1,582 @@ +{ + "value": { + "caseTrackingID": { + "value": "6134381" + }, + "caseDocketID": { + "value": "2024SC00003388" + }, + "caseTitleText": { + "value": "Sample plaintiff v. Sample defendant" + }, + "caseCategoryText": { + "value": "6198" + }, + "rest": [ + { + "value": { + "caseCourt": { + "organizationIdentification": { + "name": "{http://niem.gov/niem/niem-core/2.0}OrganizationIdentification", + "value": { + "identificationID": { + "value": "lake" + } + } + } + }, + "caseLineageCase": [ + { + "caseTrackingID": { + "value": "116095f0-ec03-427a-a55e-ed61caf55a7c" + } + } + ] + } + }, + { + "value": { + "caseParticipant": [ + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "105f37e9-bb88-53b2-a2df-f1952843f6f4" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "ATTY" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityOrganization", + "value": { + "organizationIdentification": { + "name": "{urn:tyler:ecf:extensions:Common}OrganizationOtherIdentification", + "value": { + "identification": [ + { + "identificationID": { + "value": "e49a339d-1889-5608-8b14-d09756f42c2a" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ] + } + }, + "organizationName": { + "value": "Sample organization" + }, + "rest": [ + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}OrganizationAugmentation", + "value": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + ] + } + }, + "caseParticipantRoleCode": { + "value": "5624" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "5869be7b-a45f-5ce0-b961-14fe12b4ec3f" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "5616" + } + } + } + ], + "caseOtherEntityAttorney": [ + { + "roleOfPersonReference": { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "105f37e9-bb88-53b2-a2df-f1952843f6f4" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "judicialOfficialBarMembership": { + "judicialOfficialBarIdentification": { + "identificationID": { + "value": "123456" + } + } + }, + "caseOfficialRoleText": { + "value": "LEAD" + }, + "caseRepresentedPartyReference": [ + { + "ref": { + "organizationIdentification": { + "name": "{urn:tyler:ecf:extensions:Common}OrganizationOtherIdentification", + "value": { + "identification": [ + { + "identificationID": { + "value": "e49a339d-1889-5608-8b14-d09756f42c2a" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ] + } + }, + "organizationName": { + "value": "Sample organization" + }, + "rest": [ + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}OrganizationAugmentation", + "value": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + ] + } + } + ] + } + ], + "caseTypeText": { + "value": "242604" + } + } + } + ] + }, + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CivilCase-4.0}CivilCase" +} diff --git a/efile_app/efile/tests/fixtures/existing_cases/2025SC5-raw.json b/efile_app/efile/tests/fixtures/existing_cases/2025SC5-raw.json new file mode 100644 index 00000000..5e7a6361 --- /dev/null +++ b/efile_app/efile/tests/fixtures/existing_cases/2025SC5-raw.json @@ -0,0 +1,695 @@ +{ + "value": { + "caseTrackingID": { + "value": "IL016025J2025SC5" + }, + "caseDocketID": { + "value": "2025SC5" + }, + "caseTitleText": { + "value": "Sample plaintiff v. Sample defendant" + }, + "caseCategoryText": { + "value": "6198" + }, + "rest": [ + { + "value": { + "caseCourt": { + "organizationIdentification": { + "name": "{http://niem.gov/niem/niem-core/2.0}OrganizationIdentification", + "value": { + "identificationID": { + "value": "marion" + } + } + } + }, + "caseLineageCase": [ + { + "caseTrackingID": { + "value": "bd9d69d6-027e-42fa-9237-d4ba2f04f80e" + } + } + ] + } + }, + { + "value": { + "caseParticipant": [ + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "75124f76-65fd-551a-bfd2-e02cd341d782" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "ATTY" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "aad9179f-d732-56f3-8b66-ac3804999857" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "20646" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "832e489f-c2fd-5023-8ddc-8de87cbb2844" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "20641" + } + } + }, + { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}CaseParticipant", + "value": { + "entityRepresentation": { + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CommonTypes-4.0}EntityPerson", + "value": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "edd25b73-5ba0-55b9-accb-1df3d0264046" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "caseParticipantRoleCode": { + "value": "136614" + } + } + } + ], + "caseOtherEntityAttorney": [ + { + "roleOfPersonReference": { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "75124f76-65fd-551a-bfd2-e02cd341d782" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYATTORNEYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + }, + "judicialOfficialBarMembership": { + "judicialOfficialBarIdentification": { + "identificationID": { + "value": "123456" + } + } + }, + "caseOfficialRoleText": { + "value": "LEAD" + }, + "caseRepresentedPartyReference": [ + { + "ref": { + "personName": { + "personGivenName": { + "value": "Sample" + }, + "personMiddleName": { + "value": "" + }, + "personSurName": { + "value": "Party" + }, + "personNameSuffixText": { + "value": "Jr" + } + }, + "personOtherIdentification": [ + { + "identificationID": { + "value": "aad9179f-d732-56f3-8b66-ac3804999857" + }, + "identificationCategory": { + "name": "{http://niem.gov/niem/niem-core/2.0}IdentificationCategoryText", + "value": { + "value": "CASEPARTYID" + } + } + } + ], + "personAugmentation": { + "contactInformation": [ + { + "contactMeans": [ + { + "name": "{urn:tyler:ecf:extensions:Common}ServiceConsentEmail", + "value": { + "value": "" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactTelephoneNumber", + "value": { + "telephoneNumberRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}FullTelephoneNumber", + "value": { + "telephoneNumberFullID": { + "value": "5555550100" + } + } + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactEmailID", + "value": { + "value": "sample@example.com" + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}ContactMailingAddress", + "value": { + "addressRepresentation": { + "name": "{http://niem.gov/niem/niem-core/2.0}StructuredAddress", + "value": { + "addressDeliveryPoint": [ + { + "name": "{http://niem.gov/niem/niem-core/2.0}LocationStreet", + "value": { + "streetFullText": { + "value": "123 Sample Street" + } + } + }, + { + "name": "{http://niem.gov/niem/niem-core/2.0}AddressSecondaryUnitText", + "value": { + "value": "Suite 1" + } + } + ], + "locationCityName": { + "value": "Sample city" + }, + "locationState": { + "value": "IL" + }, + "locationCountry": { + "value": "US" + }, + "locationPostalCode": { + "value": "60000" + } + } + } + } + } + ] + } + ] + } + } + } + ] + } + ], + "caseTypeText": { + "value": "310346" + } + } + } + ] + }, + "name": "{urn:oasis:names:tc:legalxml-courtfiling:schema:xsd:CivilCase-4.0}CivilCase" +} diff --git a/efile_app/efile/tests/fixtures/existing_cases/kane-party-types.json b/efile_app/efile/tests/fixtures/existing_cases/kane-party-types.json new file mode 100644 index 00000000..e3529b29 --- /dev/null +++ b/efile_app/efile/tests/fixtures/existing_cases/kane-party-types.json @@ -0,0 +1,127 @@ +[ + { + "code": "5606", + "name": "Administrator", + "isrequired": false + }, + { + "code": "5612", + "name": "Counter Defendant", + "isrequired": false + }, + { + "code": "5615", + "name": "Decedent", + "isrequired": false + }, + { + "code": "5616", + "name": "Defendant", + "isrequired": false + }, + { + "code": "5617", + "name": "Disabled Adult", + "isrequired": false + }, + { + "code": "5621", + "name": "Guardian Ad Litem", + "isrequired": false + }, + { + "code": "5623", + "name": "Minor", + "isrequired": false + }, + { + "code": "5627", + "name": "Respondent", + "isrequired": false + }, + { + "code": "5628", + "name": "Petitioner", + "isrequired": false + }, + { + "code": "10361", + "name": "Agent", + "isrequired": false + }, + { + "code": "10366", + "name": "As Subrogee of", + "isrequired": false + }, + { + "code": "10373", + "name": "Counter Plaintiff", + "isrequired": false + }, + { + "code": "10384", + "name": "Executor", + "isrequired": false + }, + { + "code": "10386", + "name": "Fourth Party Defendant", + "isrequired": false + }, + { + "code": "10388", + "name": "Fourth Party Plaintiff", + "isrequired": false + }, + { + "code": "10391", + "name": "Garnishee", + "isrequired": false + }, + { + "code": "10393", + "name": "Guardian", + "isrequired": false + }, + { + "code": "10400", + "name": "Missing Person", + "isrequired": false + }, + { + "code": "10403", + "name": "Parent", + "isrequired": false + }, + { + "code": "10406", + "name": "Plaintiff", + "isrequired": false + }, + { + "code": "10424", + "name": "Third Party Defendant", + "isrequired": false + }, + { + "code": "10427", + "name": "Third Party Plaintiff", + "isrequired": false + }, + { + "code": "10446", + "name": "Other", + "isrequired": false + }, + { + "code": "137491", + "name": "Ward", + "isrequired": false + }, + { + "code": "145881", + "name": "Disabled Person", + "isrequired": false + } +] diff --git a/efile_app/efile/tests/fixtures/existing_cases/lake-party-types.json b/efile_app/efile/tests/fixtures/existing_cases/lake-party-types.json new file mode 100644 index 00000000..7d0ac4f3 --- /dev/null +++ b/efile_app/efile/tests/fixtures/existing_cases/lake-party-types.json @@ -0,0 +1,472 @@ +[ + { + "code": "5609", + "name": "Attorney", + "isrequired": false + }, + { + "code": "5616", + "name": "Defendant", + "isrequired": false + }, + { + "code": "5623", + "name": "Minor", + "isrequired": false + }, + { + "code": "5624", + "name": "Plaintiff", + "isrequired": false + }, + { + "code": "5627", + "name": "Respondent", + "isrequired": false + }, + { + "code": "5628", + "name": "Petitioner", + "isrequired": false + }, + { + "code": "7404", + "name": "Trustee", + "isrequired": false + }, + { + "code": "10400", + "name": "Missing Person", + "isrequired": false + }, + { + "code": "10403", + "name": "Parent", + "isrequired": false + }, + { + "code": "50472", + "name": "Counter Plaintiff", + "isrequired": false + }, + { + "code": "50474", + "name": "Temporary Guardian", + "isrequired": false + }, + { + "code": "50475", + "name": "Third Party Defendant", + "isrequired": false + }, + { + "code": "50476", + "name": "Third Party Plaintiff", + "isrequired": false + }, + { + "code": "50478", + "name": "Father", + "isrequired": false + }, + { + "code": "50479", + "name": "Alleged Putative Father", + "isrequired": false + }, + { + "code": "50481", + "name": "Fourth Party Defendant", + "isrequired": false + }, + { + "code": "50482", + "name": "Fourth Party Plaintiff", + "isrequired": false + }, + { + "code": "50483", + "name": "Administrator", + "isrequired": false + }, + { + "code": "50484", + "name": "Claimant", + "isrequired": false + }, + { + "code": "50485", + "name": "Counter Defendant", + "isrequired": false + }, + { + "code": "50486", + "name": "Decedent (Deceased)", + "isrequired": false + }, + { + "code": "50487", + "name": "Disabled", + "isrequired": false + }, + { + "code": "50488", + "name": "Executor", + "isrequired": false + }, + { + "code": "50490", + "name": "Guardian", + "isrequired": false + }, + { + "code": "60280", + "name": "SEARCH", + "isrequired": false + }, + { + "code": "60281", + "name": "FORFEITED ITEM", + "isrequired": false + }, + { + "code": "66286", + "name": "Adult", + "isrequired": false + }, + { + "code": "74422", + "name": "Brother", + "isrequired": false + }, + { + "code": "74423", + "name": "Foster Father", + "isrequired": false + }, + { + "code": "74424", + "name": "Foster Mother", + "isrequired": false + }, + { + "code": "74427", + "name": "Involved Party", + "isrequired": false + }, + { + "code": "74430", + "name": "Respondent Mother", + "isrequired": false + }, + { + "code": "74431", + "name": "Respondent Step Father", + "isrequired": false + }, + { + "code": "74432", + "name": "Respondent Step Mother", + "isrequired": false + }, + { + "code": "74433", + "name": "Seizure Warrant", + "isrequired": false + }, + { + "code": "74434", + "name": "Sister", + "isrequired": false + }, + { + "code": "74442", + "name": "Third Party Respondent", + "isrequired": false + }, + { + "code": "74443", + "name": "Uncle", + "isrequired": false + }, + { + "code": "74448", + "name": "Case Worker", + "isrequired": false + }, + { + "code": "74452", + "name": "Cross Claimant", + "isrequired": false + }, + { + "code": "74453", + "name": "Cross Defendant", + "isrequired": false + }, + { + "code": "74454", + "name": "Cross Plaintiff", + "isrequired": false + }, + { + "code": "74458", + "name": "Error", + "isrequired": false + }, + { + "code": "74463", + "name": "Foster Relative", + "isrequired": false + }, + { + "code": "74471", + "name": "Objector", + "isrequired": false + }, + { + "code": "74475", + "name": "Respondent Father", + "isrequired": false + }, + { + "code": "74476", + "name": "Respondent In Discovery", + "isrequired": false + }, + { + "code": "74486", + "name": "Supervised Executor", + "isrequired": false + }, + { + "code": "100509", + "name": "Administrator Of Estate", + "isrequired": false + }, + { + "code": "100510", + "name": "Administrator To Collect", + "isrequired": false + }, + { + "code": "100511", + "name": "Counter Petitioner", + "isrequired": false + }, + { + "code": "100512", + "name": "Counter Respondent", + "isrequired": false + }, + { + "code": "100513", + "name": "Delinquent", + "isrequired": false + }, + { + "code": "100514", + "name": "Fifth Party Defendant", + "isrequired": false + }, + { + "code": "100515", + "name": "Fifth Party Plaintiff", + "isrequired": false + }, + { + "code": "100516", + "name": "Filer", + "isrequired": false + }, + { + "code": "100517", + "name": "Heir", + "isrequired": false + }, + { + "code": "100518", + "name": "Interested Party", + "isrequired": false + }, + { + "code": "100519", + "name": "Intervening Defendant", + "isrequired": false + }, + { + "code": "100520", + "name": "Intervening Petitioner", + "isrequired": false + }, + { + "code": "100521", + "name": "Mother", + "isrequired": false + }, + { + "code": "100522", + "name": "Special Representative", + "isrequired": false + }, + { + "code": "100523", + "name": "State", + "isrequired": false + }, + { + "code": "100524", + "name": "Successor Trustee", + "isrequired": false + }, + { + "code": "100525", + "name": "Administrator With Will Annexed", + "isrequired": false + }, + { + "code": "100526", + "name": "Aunt", + "isrequired": false + }, + { + "code": "100527", + "name": "Catholic Charities", + "isrequired": false + }, + { + "code": "100528", + "name": "Dept Of Children/Family Services", + "isrequired": false + }, + { + "code": "100529", + "name": "Independent Administrator", + "isrequired": false + }, + { + "code": "100530", + "name": "Independent Administrator To Collect", + "isrequired": false + }, + { + "code": "100531", + "name": "Independent Administrator-Will Annexed", + "isrequired": false + }, + { + "code": "100532", + "name": "Independent Executor", + "isrequired": false + }, + { + "code": "100533", + "name": "Independent Executor With Will Annexed", + "isrequired": false + }, + { + "code": "100534", + "name": "Limited & Temporary Guardian", + "isrequired": false + }, + { + "code": "100535", + "name": "Limited Guardian", + "isrequired": false + }, + { + "code": "100536", + "name": "Mortgagee", + "isrequired": false + }, + { + "code": "100537", + "name": "Mortgagor", + "isrequired": false + }, + { + "code": "100538", + "name": "Plenary Guardian", + "isrequired": false + }, + { + "code": "100539", + "name": "Relative", + "isrequired": false + }, + { + "code": "100540", + "name": "Special Administrator", + "isrequired": false + }, + { + "code": "100541", + "name": "Special Administrator to Collect", + "isrequired": false + }, + { + "code": "100542", + "name": "Standby Guardian", + "isrequired": false + }, + { + "code": "100543", + "name": "Step-Father", + "isrequired": false + }, + { + "code": "100544", + "name": "Step-Mother", + "isrequired": false + }, + { + "code": "100545", + "name": "Successor Administrator", + "isrequired": false + }, + { + "code": "100546", + "name": "Successor Administrator With Will Annexe", + "isrequired": false + }, + { + "code": "100547", + "name": "Successor Executor", + "isrequired": false + }, + { + "code": "100548", + "name": "Successor Guardian", + "isrequired": false + }, + { + "code": "100549", + "name": "Successor Independent Administrator", + "isrequired": false + }, + { + "code": "100550", + "name": "Successor Independent Executor", + "isrequired": false + }, + { + "code": "100551", + "name": "Successor Limited Guardian", + "isrequired": false + }, + { + "code": "100552", + "name": "Successor Plenary Guardian", + "isrequired": false + }, + { + "code": "100553", + "name": "Supervised Administrator", + "isrequired": false + }, + { + "code": "246247", + "name": "Living Person", + "isrequired": false + } +] diff --git a/efile_app/efile/tests/fixtures/existing_cases/marion-party-types.json b/efile_app/efile/tests/fixtures/existing_cases/marion-party-types.json new file mode 100644 index 00000000..4dac9de4 --- /dev/null +++ b/efile_app/efile/tests/fixtures/existing_cases/marion-party-types.json @@ -0,0 +1,152 @@ +[ + { + "code": "5603", + "name": "Third Party Plaintiff", + "isrequired": false + }, + { + "code": "5626", + "name": "Third Party Defendant", + "isrequired": false + }, + { + "code": "7067", + "name": "Adoptive Parent", + "isrequired": false + }, + { + "code": "7132", + "name": "Biological Parent", + "isrequired": false + }, + { + "code": "7137", + "name": "Legal Father", + "isrequired": false + }, + { + "code": "7395", + "name": "Guardian", + "isrequired": false + }, + { + "code": "7403", + "name": "Minor", + "isrequired": false + }, + { + "code": "8577", + "name": "Putative Father", + "isrequired": false + }, + { + "code": "8750", + "name": "Counter Plaintiff/Petitioner", + "isrequired": false + }, + { + "code": "8754", + "name": "Counter Defendant/Respondent", + "isrequired": false + }, + { + "code": "9602", + "name": "Counter-Claimant", + "isrequired": false + }, + { + "code": "9603", + "name": "Third-Party Litigant", + "isrequired": false + }, + { + "code": "9833", + "name": "Administrator", + "isrequired": false + }, + { + "code": "9835", + "name": "Decedent", + "isrequired": false + }, + { + "code": "9838", + "name": "Executor", + "isrequired": false + }, + { + "code": "9839", + "name": "Guardian Ad Litem", + "isrequired": false + }, + { + "code": "9843", + "name": "Ward", + "isrequired": false + }, + { + "code": "9844", + "name": "Other", + "isrequired": false + }, + { + "code": "20640", + "name": "Adoptive Child", + "isrequired": false + }, + { + "code": "20641", + "name": "Defendant/Respondent", + "isrequired": false + }, + { + "code": "20642", + "name": "Fourth Party Defendant", + "isrequired": false + }, + { + "code": "20643", + "name": "Fourth Party Plaintiff", + "isrequired": false + }, + { + "code": "20644", + "name": "Intervenor-Defendant", + "isrequired": false + }, + { + "code": "20645", + "name": "Intervenor-Plaintiff", + "isrequired": false + }, + { + "code": "20646", + "name": "Plaintiff/Petitioner", + "isrequired": false + }, + { + "code": "136614", + "name": "Non-litigant", + "isrequired": false + }, + { + "code": "179603", + "name": "Legal Mother", + "isrequired": false + }, + { + "code": "179604", + "name": "Legal Parent", + "isrequired": false + }, + { + "code": "283697", + "name": "Biological Mother", + "isrequired": false + }, + { + "code": "283698", + "name": "Biological Father", + "isrequired": false + } +] diff --git a/efile_app/efile/tests/fixtures/existing_cases/winnebago-party-types.json b/efile_app/efile/tests/fixtures/existing_cases/winnebago-party-types.json new file mode 100644 index 00000000..9fd52c74 --- /dev/null +++ b/efile_app/efile/tests/fixtures/existing_cases/winnebago-party-types.json @@ -0,0 +1,82 @@ +[ + { + "code": "5605", + "name": "Ward", + "isrequired": false + }, + { + "code": "59053", + "name": "Petitioner", + "isrequired": false + }, + { + "code": "59054", + "name": "Plaintiff", + "isrequired": false + }, + { + "code": "59055", + "name": "Defendant", + "isrequired": false + }, + { + "code": "59056", + "name": "Respondent", + "isrequired": false + }, + { + "code": "79731", + "name": "Third Party Defendant", + "isrequired": false + }, + { + "code": "79736", + "name": "Putative Father", + "isrequired": false + }, + { + "code": "79739", + "name": "Third Party Plaintiff", + "isrequired": false + }, + { + "code": "79759", + "name": "Minor", + "isrequired": false + }, + { + "code": "103298", + "name": "Other Party", + "isrequired": false + }, + { + "code": "103299", + "name": "Decendent", + "isrequired": false + }, + { + "code": "103300", + "name": "Counter Defendant", + "isrequired": false + }, + { + "code": "103301", + "name": "Counter Plaintiff", + "isrequired": false + }, + { + "code": "103302", + "name": "Disabled Person", + "isrequired": false + }, + { + "code": "103303", + "name": "Subject", + "isrequired": false + }, + { + "code": "328017", + "name": "Interested Party", + "isrequired": false + } +] diff --git a/efile_app/efile/tests/helpers.py b/efile_app/efile/tests/helpers.py index 602d8d67..28866790 100644 --- a/efile_app/efile/tests/helpers.py +++ b/efile_app/efile/tests/helpers.py @@ -21,3 +21,23 @@ def reviewed_document(**fields): fields.setdefault("preparation", "unchanged") fields.setdefault("preparation_reviewed_at", timezone.now()) return FilingDocument.objects.create(**fields) + + +def loaded_case_snapshot(draft, *, confirmed=True): + """A trusted empty court response for tests of routing rather than import.""" + from django.conf import settings + + return { + "schema_version": 1, + "status": "loaded", + "confirmed": confirmed, + "court": draft.court_code, + "tracking_id": draft.previous_case_id, + "source_environment": settings.EFSP_URL, + "parties": [], + "problems": [], + "docket_number": draft.docket_number, + "case_title": draft.case_title, + "case_category_code": draft.case_category_code, + "case_type_code": draft.case_type_code, + } diff --git a/efile_app/efile/tests/test_case_confirmation.py b/efile_app/efile/tests/test_case_confirmation.py index 93feae49..f73976b0 100644 --- a/efile_app/efile/tests/test_case_confirmation.py +++ b/efile_app/efile/tests/test_case_confirmation.py @@ -66,13 +66,26 @@ def test_case_lookup_result_is_persisted_on_the_draft(client, django_user_model) @pytest.mark.django_db -def test_case_confirmation_accepts_case_and_converges_on_checklist(client, django_user_model): +def test_case_confirmation_accepts_case_and_converges_on_checklist(client, django_user_model, monkeypatch): draft = prepare_client(client, django_user_model) draft.previous_case_id = "tracking-123" draft.docket_number = "2025MR000123" draft.case_title = "Ada Lovelace v. Example LLC" draft.save() + from efile.services.existing_cases import apply_case + from efile.tests.test_existing_cases import normalized + + snapshot = normalized() + draft.court_code = snapshot["court"] + draft.previous_case_id = snapshot["tracking_id"] + draft.docket_number = snapshot["docket_number"] + draft.save() + monkeypatch.setattr( + "efile.views.case_confirmation.load_case", + lambda draft, token, apply=False: apply_case(draft, {**snapshot, "confirmed": False}), + ) + response = client.post( reverse("case_confirmation", kwargs={"jurisdiction": "illinois"}), {"confirmed": "yes"}, diff --git a/efile_app/efile/tests/test_detours.py b/efile_app/efile/tests/test_detours.py index 8150b418..40691320 100644 --- a/efile_app/efile/tests/test_detours.py +++ b/efile_app/efile/tests/test_detours.py @@ -123,7 +123,7 @@ def test_adding_a_person_on_a_handoff_detour_stays_on_the_people_screens(client, assert f"party={added.pk}" in response.url and "return_to=handoff" in response.url -def test_finding_a_case_on_a_handoff_detour_still_asks_to_confirm_it(client, draft): +def test_finding_a_case_on_a_handoff_detour_still_asks_to_confirm_it(client, draft, monkeypatch): draft.existing_case = "existing" draft.save() response = client.post( @@ -136,6 +136,12 @@ def test_finding_a_case_on_a_handoff_detour_still_asks_to_confirm_it(client, dra assert "return_to=handoff" in redirect_url page = client.get(redirect_url) assert b'name="return_to" value="handoff"' in page.content + from efile.tests.helpers import loaded_case_snapshot + + monkeypatch.setattr( + "efile.services.existing_cases.fetch_case", lambda draft, token: loaded_case_snapshot(draft, confirmed=False) + ) + with patch("efile.views.case_confirmation.draft_unavailable_message", return_value=""): confirmed = client.post(redirect_url, {"confirmed": "yes", "return_to": "handoff"}) assert confirmed.url == reverse("handoff_review", args=[draft.pk]) diff --git a/efile_app/efile/tests/test_existing_cases.py b/efile_app/efile/tests/test_existing_cases.py new file mode 100644 index 00000000..b80aed12 --- /dev/null +++ b/efile_app/efile/tests/test_existing_cases.py @@ -0,0 +1,563 @@ +"""Court identity regressions, independent of staging availability.""" + +import copy +import json +from pathlib import Path + +import pytest +from django.conf import settings +from django.db import IntegrityError, transaction +from django.urls import reverse + +from efile.models import FilingDraft, FilingParty +from efile.services.drafts import read_case_data, write_case_data +from efile.services.efsp_payload import PayloadValidationError +from efile.services.existing_cases import ( + CaseImportError, + apply_case, + entity_id, + import_ready, + normalize_case, + reconcile_case_parties, +) +from efile.services.extracted_parties import save_reviewed_parties +from efile.services.filing_path import change_filing_path +from efile.services.people import ( + absorb_filer_duplicates, + claim_party_as_filer, + discard_empty_parties, + ensure_required_parties, + party_is_complete, + set_filing_parties, +) +from efile.tests.test_people_flow import authorize +from efile.workflow import ExistingCase + +FIXTURES = Path(__file__).parent / "fixtures" / "existing_cases" + + +def normalized(filename="2025SC5-raw.json"): + raw = json.loads((FIXTURES / filename).read_text()) + case = raw["value"] + court = next(item["value"]["caseCourt"] for item in case["rest"] if "caseCourt" in item["value"]) + court = court["organizationIdentification"]["value"]["identificationID"]["value"] + types = json.loads((FIXTURES / (court + "-party-types.json")).read_text()) + lineage = next(item["value"]["caseLineageCase"] for item in case["rest"] if "caseCourt" in item["value"]) + tracking = lineage[0]["caseTrackingID"]["value"] + snapshot = normalize_case( + raw, court=court, tracking_id=tracking, party_types=types, docket_number=case["caseDocketID"]["value"] + ) + snapshot.update(confirmed=True, source_environment=settings.EFSP_URL) + return snapshot + + +@pytest.fixture +def imported(client, django_user_model): + snapshot = normalized() + user = django_user_model.objects.create_user( + username="existing-owner", email="contact@example.com", tyler_jurisdiction="illinois" + ) + draft = FilingDraft.objects.create( + user=user, + jurisdiction="illinois", + existing_case=ExistingCase.EXISTING, + court_code=snapshot["court"], + previous_case_id=snapshot["tracking_id"], + docket_number=snapshot["docket_number"], + ) + apply_case(draft, snapshot) + FilingParty.objects.create( + draft=draft, role="filer", first_name="Account", last_name="Contact", email="contact@example.com" + ) + client.force_login(user) + authorize(client, draft) + return draft + + +@pytest.mark.parametrize("filename", sorted(p.name for p in FIXTURES.glob("*-raw.json"))) +def test_sanitized_staging_variations(filename): + snapshot = normalized(filename) + assert snapshot["status"] == "loaded" + counts = { + "2025SC5-raw.json": 3, + "2024EV001752-raw.json": 4, + "2005SC000985-raw.json": 4, + "2019-D-0000655-raw.json": 5, + "2019-SC-0001642-raw.json": 2, + "2024SC00003388-raw.json": 2, + "2024DC00000572-raw.json": 2, + } + assert len(snapshot["parties"]) == counts[filename] + assert all(p["external_party_id"] for p in snapshot["parties"]) + assert len({p["external_party_id"] for p in snapshot["parties"]}) == len(snapshot["parties"]) + assert all(p["representation"]["status"] in {"unknown", "represented"} for p in snapshot["parties"]) + + +def test_id_selects_category_not_last_identification(): + assert ( + entity_id( + { + "personOtherIdentification": [ + { + "identificationCategory": {"value": {"value": "CASEPARTYID"}}, + "identificationID": {"value": "court-id"}, + }, + { + "identificationCategory": {"value": {"value": "OTHER"}}, + "identificationID": {"value": "wrong-id"}, + }, + ] + } + ) + == "court-id" + ) + + +def test_case_attorney_associations_names_and_contacts_are_preserved(): + snapshot = normalized() + represented = [p for p in snapshot["parties"] if p["representation"]["status"] == "represented"] + assert len(represented) == 1 + attorneys = [a for p in represented for a in p["representation"]["attorneys"]] + assert len(attorneys) == 1 + assert all(a["id_category"] == "CASEPARTYATTORNEYID" for a in attorneys) + assert all(a["id"] and a["bar_number"] == "123456" for a in attorneys) + assert all(a["first_name"] == "Sample" and a["email"] == "sample@example.com" for a in attorneys) + assert len({a["id"] for a in attorneys}) == 1 + + +@pytest.mark.django_db +def test_reimport_preserves_rows_selections_and_equal_names(imported): + selected = imported.parties.filter(source="court").first() + set_filing_parties(imported, [selected]) + ids = list(imported.parties.filter(source="court").values_list("pk", flat=True)) + apply_case(imported, normalized()) + assert list(imported.parties.filter(source="court").values_list("pk", flat=True)) == ids + selected.refresh_from_db() + assert selected.is_filing_party + assert imported.parties.filter(source="court").count() == 3 + assert import_ready(imported) + assert read_case_data(imported)["filing_parties"][1]["external_party_id"] + with pytest.raises(IntegrityError), transaction.atomic(): + FilingParty.objects.create( + draft=imported, + role="other", + sort_order=99, + source="court", + source_case_id=selected.source_case_id, + external_party_id=selected.external_party_id, + ) + + +@pytest.mark.django_db +def test_claim_and_cleanup_preserve_court_identity(imported): + party = imported.parties.filter(source="court").first() + original = (party.pk, party.first_name, party.external_party_id) + claim_party_as_filer(imported, party) + absorb_filer_duplicates(imported) + discard_empty_parties(imported) + party.refresh_from_db() + assert (party.pk, party.first_name, party.external_party_id) == original + assert party.is_self and party.is_filing_party + assert not imported.parties.get(role="filer").party_type + assert party_is_complete(party) + types = [{"code": p.party_type, "name": "Role", "required": True} for p in imported.parties.filter(source="court")] + ensure_required_parties(imported, types) + assert imported.parties.count() == 4 + + +@pytest.mark.django_db +def test_stale_import_cannot_restore_another_case(imported): + snapshot = normalized() + write_case_data(imported, {"previous_case_id": "case-B"}) + assert not imported.parties.filter(source="court").exists() + with pytest.raises(CaseImportError, match="changed"): + apply_case(imported, snapshot) + imported.refresh_from_db() + assert imported.previous_case_id == "case-B" + assert not imported.existing_case_snapshot + + +@pytest.mark.django_db +def test_switch_to_new_clears_import_and_quote(imported): + imported.quoted_fee_total = "12" + imported.save() + change_filing_path(imported, ExistingCase.NEW) + assert not imported.parties.filter(source="court").exists() + assert imported.parties.get(role="filer").email == "contact@example.com" + assert not imported.existing_case_snapshot + assert not imported.quoted_fee_total + + +@pytest.mark.django_db +def test_crafted_party_edit_remove_and_extraction_do_not_change_court(imported, client, monkeypatch): + monkeypatch.setattr( + "efile.views.parties.get_party_types", lambda draft: [{"code": "1", "name": "Role", "required": False}] + ) + party = imported.parties.filter(source="court").first() + before = read_case_data(imported)["filing_parties"] + url = reverse("parties", kwargs={"jurisdiction": "illinois"}) + assert client.post(url, {"action": "remove", "party_id": party.pk}).status_code == 302 + details = reverse("party_details", kwargs={"jurisdiction": "illinois"}) + assert client.post(f"{details}?party={party.pk}", {"first_name": "Tampered"}).status_code == 302 + save_reviewed_parties(imported, [{"id": party.pk, "name": "Tampered", "side": "other"}]) + write_case_data(imported, {"other_first_name": "Tampered", "other_party_type": "wrong"}) + assert read_case_data(imported)["filing_parties"] == before + response = client.get(url) + assert response.status_code == 200 + assert b"Already on the court case" in response.content + assert f"?party={party.pk}".encode() not in response.content + + +def payload_for(draft): + from efile.services.drafts import read_case_data + + rows = read_case_data(draft)["filing_parties"] + parties = [ + { + "tyler_id": p["external_party_id"], + "is_new": False, + "party_type": p["party_type"], + "name": { + "first": p["organization_name"] or p["first_name"], + "middle": p["middle_name"], + "last": p["last_name"], + "suffix": p["suffix"], + }, + } + for p in rows + if p["source"] == "court" + ] + set_filing_parties(draft, [draft.parties.filter(source="court").first()]) + return { + "previous_case_id": draft.previous_case_id, + "user_started_case": False, + "users": parties[:1], + "other_parties": parties[1:], + "al_court_bundle": [{"filing_parties": ["users[0]"]}], + } + + +@pytest.mark.django_db +@pytest.mark.parametrize("mutation", ["foreign", "rename", "new", "duplicate", "missing", "reference", "case"]) +def test_reject_substituted_identity(imported, mutation): + payload = payload_for(imported) + if mutation == "foreign": + payload["users"][0]["tyler_id"] = "foreign-id" + if mutation == "rename": + payload["users"][0]["name"]["first"] = "Tampered" + if mutation == "new": + payload["users"][0]["is_new"] = True + if mutation == "duplicate": + payload["other_parties"].append(copy.deepcopy(payload["users"][0])) + if mutation == "missing": + payload["other_parties"].pop() + if mutation == "reference": + payload["al_court_bundle"][0]["filing_parties"] = ["users[99]"] + if mutation == "case": + payload["previous_case_id"] = "different-case" + with pytest.raises(PayloadValidationError): + reconcile_case_parties(payload, imported, "illinois", imported.court_code) + + +@pytest.mark.django_db +def test_authoritative_payload_and_incomplete_import(imported): + payload = payload_for(imported) + payload["users"][0]["attorneys"] = ["substituted"] + reconcile_case_parties(payload, imported, "illinois", imported.court_code) + assert all(p["is_new"] is False and p["tyler_id"] for p in payload["users"] + payload["other_parties"]) + assert "attorneys" not in payload["users"][0] + imported.existing_case_snapshot["status"] = "partial" + imported.save() + with pytest.raises(PayloadValidationError, match="load its parties"): + reconcile_case_parties(payload, imported, "illinois", imported.court_code) + + +@pytest.mark.django_db +def test_unimported_resume_returns_to_confirmation(imported, client): + imported.existing_case_snapshot = {} + imported.save() + for step in ("parties", "payment", "case_review"): + response = client.get(reverse(step, kwargs={"jurisdiction": "illinois"})) + assert response.status_code == 302 + assert "/case-confirmation/" in response.url + + +@pytest.mark.django_db +def test_confirmation_preview_and_confirm_import_idempotently(imported, client, monkeypatch): + imported.parties.filter(source="court").delete() + imported.existing_case_snapshot = {} + imported.save() + snapshot = normalized() + snapshot.pop("confirmed") + monkeypatch.setattr("efile.services.existing_cases.fetch_case", lambda *args: copy.deepcopy(snapshot)) + url = reverse("case_confirmation", kwargs={"jurisdiction": "illinois"}) + response = client.get(url) + assert response.status_code == 200 + assert b"Already on the court case" in response.content + assert not imported.parties.filter(source="court").exists() + response = client.post(url, {"confirmed": "yes"}) + assert response.status_code == 302 + imported.refresh_from_db() + assert import_ready(imported) + ids = list(imported.parties.filter(source="court").values_list("pk", flat=True)) + response = client.post(url, {"confirmed": "yes"}) + assert response.status_code == 302 + assert list(imported.parties.filter(source="court").values_list("pk", flat=True)) == ids + + +@pytest.mark.django_db +def test_failed_confirmation_blocks_and_offers_retry(imported, client, monkeypatch): + imported.existing_case_snapshot = {} + imported.save() + + def unavailable(*args): + raise CaseImportError("Retry or sign in again.") + + monkeypatch.setattr("efile.services.existing_cases.fetch_case", unavailable) + response = client.post(reverse("case_confirmation", kwargs={"jurisdiction": "illinois"}), {"confirmed": "yes"}) + assert response.status_code == 422 + assert b"Retry loading the case" in response.content + imported.refresh_from_db() + assert imported.existing_case_snapshot["status"] == "failed" + assert not import_ready(imported) + + +@pytest.mark.integration +@pytest.mark.django_db(transaction=True) +def test_browser_lookup_confirmation_party_selection_review(imported, client, live_server, monkeypatch): + import os + import shutil + import subprocess + + from efile.models import FilingDocument + from efile.tests.helpers import reviewed_document + + app = Path(__file__).resolve().parents[2] + if not shutil.which("node") or not (app / "node_modules/@playwright/test").exists(): + pytest.skip("Install browser test dependencies to run the existing-case browser flow.") + snapshot = normalized() + snapshot.pop("confirmed") + imported.parties.filter(source="court").delete() + imported.previous_case_id = "" + imported.docket_number = "" + imported.existing_case_snapshot = {} + imported.selected_payment_account_id = "synthetic-payment" + imported.save() + reviewed_document( + draft=imported, + role=FilingDocument.Role.LEAD, + sort_order=0, + name="motion.pdf", + public_url="https://example.com/motion.pdf", + filing_type_code="synthetic-motion", + document_type_code="synthetic-nonconfidential", + document_type_confirmed=True, + filing_component_code="synthetic-lead", + ) + monkeypatch.setattr("efile.services.existing_cases.fetch_case", lambda *args: copy.deepcopy(snapshot)) + types = [{"code": p["party_type"], "name": p["party_type_name"], "required": False} for p in snapshot["parties"]] + monkeypatch.setattr("efile.views.parties.get_party_types", lambda draft: types) + monkeypatch.setattr("efile.views.parties.get_case_questions", lambda draft: []) + monkeypatch.setattr("efile.views.review.get_case_questions", lambda draft: []) + config = { + "base": live_server.url, + "session": client.session.session_key, + "draft": imported.pk, + "court": snapshot["court"], + "tracking": snapshot["tracking_id"], + "docket": snapshot["docket_number"], + "count": len(snapshot["parties"]), + } + result = subprocess.run( + ["node", str(app / "tests/existing-case-flow.js")], + env={**os.environ, "EXISTING_CASE_BROWSER_CONFIG": json.dumps(config)}, + capture_output=True, + text=True, + timeout=90, + check=False, + ) + assert result.returncode == 0, result.stdout + result.stderr + + +@pytest.mark.django_db +def test_explicit_new_party_stays_new_and_survives_reconfirmation(imported, client, monkeypatch): + monkeypatch.setattr( + "efile.views.parties.get_party_types", lambda draft: [{"code": "20641", "name": "Defendant", "required": False}] + ) + response = client.post(reverse("parties", kwargs={"jurisdiction": "illinois"}), {"action": "add"}) + assert response.status_code == 302 + added = imported.parties.get(source="added") + added.first_name, added.last_name, added.party_type = "New", "Person", "20641" + added.save() + apply_case(imported, normalized()) + added.refresh_from_db() + payload = payload_for(imported) + payload["other_parties"].append( + { + "_draft_party_id": added.pk, + "is_new": True, + "party_type": added.party_type, + "name": {"first": "New", "middle": "", "last": "Person", "suffix": ""}, + } + ) + reconcile_case_parties(payload, imported, "illinois", imported.court_code) + new_party = payload["other_parties"][-1] + assert new_party["is_new"] is True + assert "tyler_id" not in new_party + assert "_draft_party_id" not in new_party + + +def test_detail_local_id_never_replaces_the_search_uuid(): + snapshot = normalized("2024EV001752-raw.json") + assert snapshot["tracking_id"] != snapshot["court_local_case_id"] + assert snapshot["tracking_id"] == "8534ec52-0fa8-4e77-91ca-55af362742c3" + + +@pytest.mark.django_db +def test_missing_import_identity_is_partial_and_not_a_new_placeholder(imported): + snapshot = normalized() + snapshot["parties"][0]["external_party_id"] = "" + snapshot["status"] = "partial" + apply_case(imported, snapshot) + missing = imported.parties.get(source="court", external_party_id="") + assert not party_is_complete(missing) + count = imported.parties.count() + ensure_required_parties(imported, [{"code": "unknown", "name": "Unresolved", "required": True}]) + assert imported.parties.count() == count + assert not import_ready(imported) + + +@pytest.mark.django_db +def test_fetch_uses_selected_uuid_authenticated_headers_and_full_role_catalog(imported, monkeypatch): + from efile.services.existing_cases import fetch_case + + raw = json.loads((FIXTURES / "2025SC5-raw.json").read_text()) + types = json.loads((FIXTURES / "marion-party-types.json").read_text()) + calls = [] + + class Response: + def __init__(self, data): + self.data = data + + def raise_for_status(self): + pass + + def json(self): + return self.data + + def get(url, **kwargs): + calls.append((url, kwargs)) + return Response(types if url.endswith("/party_types") else raw) + + monkeypatch.setattr("efile.services.existing_cases.requests.get", get) + snapshot = fetch_case(imported, "test-token") + assert snapshot["status"] == "loaded" + assert snapshot["tracking_id"] == imported.previous_case_id + assert calls[0][0].endswith("/" + imported.previous_case_id) + assert calls[0][1]["headers"]["tyler-token-illinois"] == "test-token" + assert "/case_types/" not in calls[1][0] + assert snapshot["retrieved_at"] + + +def test_absent_roster_differs_from_successful_empty_roster(): + raw = json.loads((FIXTURES / "2025SC5-raw.json").read_text()) + snapshot = normalized() + augmentation = next(i["value"] for i in raw["value"]["rest"] if "caseParticipant" in i["value"]) + augmentation["caseParticipant"] = [] + assert ( + normalize_case(raw, court=snapshot["court"], tracking_id=snapshot["tracking_id"], party_types=[])["status"] + == "loaded" + ) + del augmentation["caseParticipant"] + with pytest.raises(CaseImportError, match="did not return a party roster"): + normalize_case(raw, court=snapshot["court"], tracking_id=snapshot["tracking_id"], party_types=[]) + + +@pytest.mark.parametrize("invalid", ["attorney_reference", "represented_reference", "party_id", "case_title"]) +def test_malformed_detail_is_an_actionable_import_error(invalid): + raw = json.loads((FIXTURES / "2025SC5-raw.json").read_text()) + snapshot = normalized() + augmentation = next(i["value"] for i in raw["value"]["rest"] if "caseParticipant" in i["value"]) + if invalid == "attorney_reference": + augmentation["caseOtherEntityAttorney"] = [{"roleOfPersonReference": "unreadable"}] + elif invalid == "represented_reference": + augmentation["caseOtherEntityAttorney"] = [{"caseRepresentedPartyReference": ["unreadable"]}] + elif invalid == "party_id": + entity = next( + p["value"]["entityRepresentation"]["value"] + for p in augmentation["caseParticipant"] + if entity_id(p["value"]["entityRepresentation"]["value"]) + ) + identification = next(p for p in entity["personOtherIdentification"] if entity_id({"id": p})) + identification["identificationID"] = {"value": "x" * 256} + else: + raw["value"]["caseTitleText"] = {"value": "x" * 501} + with pytest.raises(CaseImportError, match="Retry loading|too long to import"): + normalize_case( + raw, + court=snapshot["court"], + tracking_id=snapshot["tracking_id"], + party_types=json.loads((FIXTURES / "marion-party-types.json").read_text()), + ) + + +@pytest.mark.django_db +def test_identical_reimport_keeps_current_quote(imported): + from efile.services.fee_quotes import fee_quote_summary, record_fee_quote + + set_filing_parties(imported, [imported.parties.filter(source="court").first()]) + record_fee_quote(imported, "5.00", []) + apply_case(imported, normalized()) + assert fee_quote_summary(imported)["state"] == "current" + assert imported.quoted_fee_total == "5.00" + + +@pytest.mark.django_db +def test_account_link_does_not_merge_an_added_party_by_name(imported): + added = FilingParty.objects.create( + draft=imported, + role="other", + sort_order=100, + source="added", + source_case_id=imported.previous_case_id, + first_name="Account", + last_name="Contact", + ) + claim_party_as_filer(imported, imported.parties.filter(source="court").first()) + absorb_filer_duplicates(imported) + assert imported.parties.filter(pk=added.pk).exists() + claim_party_as_filer(imported, added) + added.refresh_from_db() + assert added.is_self and added.is_filing_party + assert imported.parties.get(role="filer").first_name == "Account" + + +@pytest.mark.django_db +def test_revisiting_people_preserves_selection_separately_from_account_link(imported, client, monkeypatch): + monkeypatch.setattr("efile.views.parties.get_party_types", lambda draft: []) + party, other = list(imported.parties.filter(source="court"))[:2] + filer = imported.parties.get(role="filer") + filer.first_name, filer.last_name = party.first_name, party.last_name + filer.save() + claim_party_as_filer(imported, party) + set_filing_parties(imported, [other]) + response = client.get(reverse("parties", kwargs={"jurisdiction": "illinois"})) + assert response.status_code == 200 + party.refresh_from_db() + other.refresh_from_db() + assert party.is_self and not party.is_filing_party + assert other.is_filing_party + + +@pytest.mark.django_db +def test_missing_required_role_requires_explicit_addition(imported, client, monkeypatch): + types = [{"code": "missing-required-role", "name": "Required role", "required": True}] + monkeypatch.setattr("efile.views.parties.get_party_types", lambda draft: types) + before = imported.parties.count() + ensure_required_parties(imported, types) + selected = imported.parties.filter(source="court").first() + url = reverse("parties", kwargs={"jurisdiction": "illinois"}) + response = client.post(url, {"action": "continue", "filing_for": [selected.pk]}, follow=True) + assert response.status_code == 200 + assert b"The court requires these roles" in response.content + assert imported.parties.count() == before diff --git a/efile_app/efile/tests/test_filing_plan_actions.py b/efile_app/efile/tests/test_filing_plan_actions.py index 0344ce39..b6eccb09 100644 --- a/efile_app/efile/tests/test_filing_plan_actions.py +++ b/efile_app/efile/tests/test_filing_plan_actions.py @@ -692,7 +692,7 @@ def test_a_case_the_plan_knows_is_not_searched_for_again(client, signed_in): @pytest.mark.django_db -def test_confirming_a_case_teaches_the_plan_where_to_file_next(client, signed_in): +def test_confirming_a_case_teaches_the_plan_where_to_file_next(client, signed_in, monkeypatch): plan = configured_plan(signed_in) signed_in.existing_case = ExistingCase.EXISTING signed_in.previous_case_id = "tracking-123" @@ -700,6 +700,12 @@ def test_confirming_a_case_teaches_the_plan_where_to_file_next(client, signed_in signed_in.case_title = "In re Ada Lovelace" signed_in.save() + from efile.tests.helpers import loaded_case_snapshot + + monkeypatch.setattr( + "efile.services.existing_cases.fetch_case", lambda draft, token: loaded_case_snapshot(draft, confirmed=False) + ) + client.post(reverse("case_confirmation", kwargs={"jurisdiction": "illinois"}), {"confirmed": "yes"}) plan.refresh_from_db() @@ -708,7 +714,7 @@ def test_confirming_a_case_teaches_the_plan_where_to_file_next(client, signed_in @pytest.mark.django_db -def test_saying_a_case_is_not_mine_unlinks_the_plan(client, signed_in): +def test_saying_a_case_is_not_mine_unlinks_the_plan(client, signed_in, monkeypatch): plan = configured_plan(signed_in) plan.case_tracking_id = "tracking-123" plan.docket_number = "2025MR000123" diff --git a/efile_app/efile/tests/test_review_submit_flow.py b/efile_app/efile/tests/test_review_submit_flow.py index 823fae92..b69ef210 100644 --- a/efile_app/efile/tests/test_review_submit_flow.py +++ b/efile_app/efile/tests/test_review_submit_flow.py @@ -200,6 +200,11 @@ def test_final_review_shows_a_found_case_as_the_courts_read_only_record(client, submission_draft.selected_payment_account_id = "pay-123" submission_draft.save() + from efile.tests.helpers import loaded_case_snapshot + + submission_draft.existing_case_snapshot = loaded_case_snapshot(submission_draft) + submission_draft.save(update_fields=["existing_case_snapshot"]) + content = client.get(reverse("case_review", kwargs={"jurisdiction": "illinois"})).content.decode() case_card = _case_card(content) diff --git a/efile_app/efile/views/case_confirmation.py b/efile_app/efile/views/case_confirmation.py index 804e126f..0048d1dc 100644 --- a/efile_app/efile/views/case_confirmation.py +++ b/efile_app/efile/views/case_confirmation.py @@ -3,8 +3,10 @@ from django.views.decorators.http import require_http_methods from efile.api.suffolk_api_views import get_tyler_token +from efile.models import FilingParty from efile.services.current_drafts import ensure_current_draft from efile.services.drafts import draft_snapshot, write_case_data +from efile.services.existing_cases import CaseImportError, confirm_case, import_ready, load_case from efile.services.filing_availability import draft_unavailable_message, unavailable_response from efile.services.filing_plans import link_case_to_plan, remember_case_for_plan from efile.workflow import ( @@ -36,8 +38,30 @@ def case_confirmation(request, jurisdiction): messages.error(request, "Find your court case before confirming it.") return redirect("case_lookup", jurisdiction=jurisdiction) + availability_message = draft_unavailable_message(draft) + if availability_message and request.method == "POST" and request.POST.get("confirmed") == "yes": + return unavailable_response(request, draft, availability_message) + + import_error = "" + if not availability_message and not (request.method == "POST" and request.POST.get("confirmed") != "yes"): + if not import_ready(draft): + try: + load_case(draft, get_tyler_token(request, jurisdiction), apply=False) + except CaseImportError as error: + import_error = str(error) + if draft.existing_case_snapshot.get("status") == "partial": + import_error = ( + " ".join(draft.existing_case_snapshot.get("problems", [])) + + " Retry loading the case or contact support." + ) + if request.method == "POST": - if request.POST.get("confirmed") == "yes": + if request.POST.get("confirmed") == "yes" and not import_error: + try: + confirm_case(draft) + except CaseImportError as error: + messages.error(request, str(error)) + return redirect("case_confirmation", jurisdiction=jurisdiction) if message := draft_unavailable_message(draft): return unavailable_response(request, draft, message) # The filer has just told us which court case this matter is. Keep @@ -49,6 +73,20 @@ def case_confirmation(request, jurisdiction): draft.save(update_fields=["current_step", "updated_at"]) return redirect(continue_url(draft, jurisdiction, return_to, WorkflowStepKey.DOCUMENT_CHECKLIST)) + if request.POST.get("confirmed") == "yes": + return render( + request, + "efile/case_confirmation.html", + { + "case": draft, + "import_error": import_error, + "is_logged_in": True, + "return_to": return_target(request), + **get_workflow_context(WorkflowStepKey.CASE_CONFIRMATION, jurisdiction, draft), + }, + status=422, + ) + # Saying "this is not my case" about the case the plan proposed means # the plan is pointing at the wrong one, so it stops pointing anywhere. if draft.plan is not None and draft.plan.case_tracking_id == draft.previous_case_id: @@ -73,7 +111,9 @@ def case_confirmation(request, jurisdiction): "is_logged_in": True, "filing_draft": draft_snapshot(draft), "case": draft, - "availability_message": draft_unavailable_message(draft), + "import_error": import_error, + "court_parties": [FilingParty(**party) for party in draft.existing_case_snapshot.get("parties", [])], + "availability_message": availability_message, "return_to": return_target(request), } context.update(get_workflow_context(WorkflowStepKey.CASE_CONFIRMATION, jurisdiction, draft)) diff --git a/efile_app/efile/views/parties.py b/efile_app/efile/views/parties.py index 42104628..d6578a60 100644 --- a/efile_app/efile/views/parties.py +++ b/efile_app/efile/views/parties.py @@ -11,6 +11,7 @@ from efile.party_sides import PARTY_SIDE_LABELS from efile.services.current_drafts import ensure_current_draft from efile.services.drafts import draft_snapshot +from efile.services.existing_cases import import_ready from efile.services.extracted_parties import party_display_name from efile.services.people import ( NOT_A_PARTY, @@ -37,6 +38,7 @@ ) from efile.workflow import ( RETURN_TO_REVIEW, + ExistingCase, WorkflowStepKey, continue_step, continue_url, @@ -75,6 +77,17 @@ def _chosen_filing_parties(request, draft): def _continue_from_parties(request, jurisdiction, draft, party_types, return_to): """Fill in the court's required parties, then move on or collect the gaps.""" + if draft.existing_case == ExistingCase.EXISTING: + covered = set(draft.parties.exclude(party_type="").values_list("party_type", flat=True)) + missing = [item["name"] for item in party_types if item["required"] and item["code"] not in covered] + if missing: + messages.error( + request, + "The court requires these roles: " + + ", ".join(missing) + + ". Use Add a new party if someone needs to join this case, or contact support about the court roster.", + ) + return redirect(_parties_url(jurisdiction, return_to)) ensure_required_parties(draft, party_types) incomplete = incomplete_parties(draft, party_types=party_types) if incomplete: @@ -119,6 +132,9 @@ def preview(role_code): if item["required"] and item["code"] not in held_by_others and item["code"] != role_code ] hints = [gettext("The court also needs a %(name)s in this case.") % {"name": name} for name in missing] + if missing and draft.existing_case == ExistingCase.EXISTING: + hints.append(gettext("Use Add a new party if someone needs to join this case, or contact support.")) + return {"label": gettext("Continue"), "hint": " ".join(hints)} if missing or others_incomplete or filer_incomplete: hints.append(gettext("Continue will ask for the details that are still missing.")) label = gettext("Continue to missing party details") @@ -143,6 +159,19 @@ def _save_filer_role(request, draft, filer, party_types): the answer is the same answer whichever button carried it. """ + if draft.existing_case == ExistingCase.EXISTING: + chosen = _chosen_filing_parties(request, draft) + if not chosen or any(p.source not in {"court", "added"} for p in chosen): + messages.error(request, "Choose the existing party you are filing for.") + return False + notice_email = request.POST.get("notice_email", "").strip() or filer.email + if not _is_email(notice_email): + messages.error(request, "Give an email address for notices about this case.") + return False + set_filing_parties(draft, chosen) + draft.notice_email = notice_email + draft.save(update_fields=["notice_email", "updated_at"]) + return True party_type_names = {item["code"]: item["name"] for item in party_types} filer_type = request.POST.get("filer_party_type", "").strip() if filer_type == NOT_A_PARTY: @@ -190,6 +219,9 @@ def parties(request, jurisdiction): current_step=WorkflowStepKey.PARTIES, workflow_version=2, ) + if draft.existing_case == ExistingCase.EXISTING and not import_ready(draft): + messages.error(request, "Confirm the court case and load its parties before continuing.") + return redirect("case_confirmation", jurisdiction=jurisdiction) filer = FilingParty.objects.filter(draft=draft, role="filer").first() if filer is None: return redirect("your_information", jurisdiction=jurisdiction) @@ -210,7 +242,12 @@ def parties(request, jurisdiction): # two rows agree on the name; when they do not, replacing one name with # another is a question, and it is put below rather than done quietly. marked_self = self_claimed_party(draft) - if marked_self is not None and party_can_be_the_filer(marked_self) and names_match(filer, marked_self): + if ( + marked_self is not None + and marked_self.source not in {"court", "added"} + and party_can_be_the_filer(marked_self) + and names_match(filer, marked_self) + ): claim_party_as_filer(draft, marked_self) filer.refresh_from_db() @@ -227,6 +264,8 @@ def parties(request, jurisdiction): party = FilingParty.objects.create( draft=draft, role="other", + source="added" if draft.existing_case == ExistingCase.EXISTING else "manual", + source_case_id=draft.previous_case_id if draft.existing_case == ExistingCase.EXISTING else "", sort_order=0 if last_order is None else last_order + 1, ) draft.current_step = WorkflowStepKey.PARTY_DETAILS @@ -246,7 +285,11 @@ def parties(request, jurisdiction): ) return redirect(f"{_parties_url(jurisdiction, return_to)}#your-role") name_choice = request.POST.get("name_choice", "") - if claim_replaces_a_name(filer, party) and name_choice not in {"mine", "theirs"}: + if ( + party.source not in {"court", "added"} + and claim_replaces_a_name(filer, party) + and name_choice not in {"mine", "theirs"} + ): # Replacing a differently-named party changes who the court is # told this case is about. Nobody should be able to do that by # clicking one button, so the screen asks first and this is @@ -258,7 +301,11 @@ def parties(request, jurisdiction): return redirect(_parties_url(jurisdiction, return_to)) claim_party_as_filer(draft, party, use_party_name=name_choice == "theirs") filer.refresh_from_db() - if filer.party_type: + if party.source in {"court", "added"}: + messages.success( + request, "You are linked to this party. Your account contact information stays separate." + ) + elif filer.party_type: messages.success( request, f"You are listed in this case as the {filer.party_type_name or filer.party_type}.", @@ -268,6 +315,9 @@ def parties(request, jurisdiction): return redirect(f"{_parties_url(jurisdiction, return_to)}#your-role") if action == "remove": party = get_object_or_404(FilingParty, pk=request.POST.get("party_id"), draft=draft, role="other") + if party.source == "court": + messages.error(request, "A party already on the court case cannot be removed.") + return redirect(_parties_url(jurisdiction, return_to)) party.delete() messages.success(request, "Party removed.") return redirect(_parties_url(jurisdiction, return_to)) @@ -290,7 +340,7 @@ def parties(request, jurisdiction): # Whether claiming this row is confirming who you are or replacing # somebody with a different name. The two are not the same action # and the screen does not call them the same thing. - "replaces_a_name": claim_replaces_a_name(filer, party), + "replaces_a_name": party.source not in {"court", "added"} and claim_replaces_a_name(filer, party), "claimable": party_can_be_the_filer(party), } for party in FilingParty.objects.filter(draft=draft) @@ -305,7 +355,11 @@ def parties(request, jurisdiction): # filing for someone else has answered it, and does not need telling again # every time they come back to this screen. marked_self = self_claimed_party(draft) - named_in_document = None if saved_filing_for else (marked_self or filer_name_match(draft)) + named_in_document = ( + None + if saved_filing_for or draft.existing_case == ExistingCase.EXISTING + else (marked_self or filer_name_match(draft)) + ) # Never alongside the people themselves: a filer who has said who is in # this case has answered a better version of this question already, and # being told what they are "likely" to be contradicts it. @@ -329,6 +383,10 @@ def parties(request, jurisdiction): continue_previews = _continue_previews(draft, filer, party_types, return_to) selected_role = attempted or (NOT_A_PARTY if filing_for_someone_else else filer.party_type) context = { + "existing_court_case": draft.existing_case == ExistingCase.EXISTING, + "caption_discrepancy": bool( + draft.existing_case_snapshot and draft.extracted_guesses and document_named_the_parties(draft) + ), "is_logged_in": True, "filing_draft": draft_snapshot(draft), "filer": filer, diff --git a/efile_app/efile/views/party_details.py b/efile_app/efile/views/party_details.py index 40f15ad3..84c88287 100644 --- a/efile_app/efile/views/party_details.py +++ b/efile_app/efile/views/party_details.py @@ -42,6 +42,9 @@ def party_details(request, jurisdiction): workflow_version=2, ) party = get_object_or_404(FilingParty, draft=draft, role="other", pk=request.GET.get("party")) + if party.source == "court": + messages.error(request, "Party details already on the court case cannot be changed here.") + return redirect("parties", jurisdiction=jurisdiction) filer_row = FilingParty.objects.filter(draft=draft, role="filer").first() party_types = get_party_types(draft) party_type_names = {item["code"]: item["name"] for item in party_types} diff --git a/efile_app/efile/views/review.py b/efile_app/efile/views/review.py index 92b9b293..6e9c5724 100644 --- a/efile_app/efile/views/review.py +++ b/efile_app/efile/views/review.py @@ -115,6 +115,10 @@ def case_review(request, jurisdiction): "fee_quote": fee_quote_summary(draft), "fee_inputs_token": fee_inputs_token(draft), "filer": filer, + "filer_court_party": next( + (party for party in other_parties if party.source in {"court", "added"} and party.is_self), None + ), + "existing_court_case": bool(draft.existing_case_snapshot), "parties": other_parties, # Who the filing is on behalf of, when that is not the filer. Worth # saying out loud on the last screen before submission: a filing sent diff --git a/efile_app/efile/views/session_api.py b/efile_app/efile/views/session_api.py index 3a7b536b..ae9feb5d 100644 --- a/efile_app/efile/views/session_api.py +++ b/efile_app/efile/views/session_api.py @@ -180,7 +180,7 @@ def forward_final_filing(request, data): # Same fixups the fee quote applied, so the filing matches the quote. try: - prepare_efile_payload(efile_data, jurisdiction_id, court_id) + prepare_efile_payload(efile_data, jurisdiction_id, court_id, draft=draft) except PayloadValidationError as error: return JsonResponse( { diff --git a/efile_app/js-tests/filing-payload.test.js b/efile_app/js-tests/filing-payload.test.js index 5eaa6a07..c0597758 100644 --- a/efile_app/js-tests/filing-payload.test.js +++ b/efile_app/js-tests/filing-payload.test.js @@ -668,4 +668,65 @@ test("new appeal payload keeps the originating case separate from the appellate const existing = handler.buildEFilingData(userData, caseData, {}, "pay-1"); assert.strictEqual(existing.lower_court_case, undefined); assert.strictEqual(existing.trial_court, undefined); +}); +test("existing court filing and other parties retain IDs without account duplicates", () => { + const handler = makeHandler(); + const courtParty = (id, filing) => ({ + id, + role: "other", + source: "court", + external_party_id: `court-${id}`, + party_type: filing ? "plaintiff" : "defendant", + first_name: "Court", + last_name: "Name", + is_filing_party: filing, + is_self: filing + }); + const payload = handler.buildEFilingData({ + fullName: "Account Contact", + email: "contact@example.com" + }, { + previous_case_id: "existing-case", + court: "kane", + case_type: "civil", + filing_parties: [{ + role: "filer", + first_name: "Account", + last_name: "Contact" + }, + courtParty(1, true), courtParty(2, false) + ] + }, { + files: { + lead: { + name: "motion.pdf", + url: "https://example.com/motion.pdf" + } + } + }, "payment"); + assert.equal(payload.users.length, 1); + assert.equal(payload.other_parties.length, 1); + assert.equal(payload.users[0].tyler_id, "court-1"); + assert.equal(payload.other_parties[0].tyler_id, "court-2"); + assert.equal(payload.users[0].is_new, false); + assert.equal(payload.other_parties[0].is_new, false); + assert.equal(payload.users[0].name.first, "Court"); + assert.deepEqual(payload.al_court_bundle[0].filing_parties, ["users[0]"]); +}); + +test("an imported party without an ID cannot be serialized as new", () => { + assert.throws(() => makeHandler().partyFromDraft({ + source: "court", + party_type: "plaintiff" + }), /no court ID/); +}); + +test("explicit new parties have no existing ID", () => { + const party = makeHandler().partyFromDraft({ + source: "manual", + first_name: "New", + last_name: "Person" + }); + assert.equal(party.is_new, true); + assert.equal(Object.hasOwn(party, "tyler_id"), false); }); \ No newline at end of file diff --git a/efile_app/tests/existing-case-flow.js b/efile_app/tests/existing-case-flow.js new file mode 100644 index 00000000..78f5d3d4 --- /dev/null +++ b/efile_app/tests/existing-case-flow.js @@ -0,0 +1,118 @@ +// Called by the Python live-server regression, with synthetic court responses. +const { + chromium, + expect +} = require('@playwright/test'); + +(async () => { + const config = JSON.parse(process.env.EXISTING_CASE_BROWSER_CONFIG); + const browser = await chromium.launch({ + headless: true + }); + try { + const context = await browser.newContext(); + await context.addCookies([{ + name: 'sessionid', + value: config.session, + url: config.base + }]); + const page = await context.newPage(); + page.setDefaultTimeout(10000); + const json = (route, body) => route.fulfill({ + contentType: 'application/json', + body: JSON.stringify(body) + }); + await page.route('**/api/dropdowns/court-selector/**', route => json(route, { + success: true, + data: { + available: false + } + })); + await page.route('**/api/dropdowns/courts/**', route => json(route, { + success: true, + data: [{ + value: config.court, + text: 'Sample court' + }] + })); + await page.route('**/api/filing-availability/**', route => json(route, { + success: true, + available: true + })); + await page.route('**/api/suffolk/lookup-case/**', route => json(route, { + success: true, + caseInfo: { + caseTrackingID: config.tracking, + caseDocketID: config.docket + } + })); + await page.route('**/api/payment-fees/**', route => json(route, { + success: false, + error: 'Synthetic browser test does not call live fees.' + })); + const url = step => `${config.base}/jurisdiction/illinois/${step}/?draft=${config.draft}`; + await page.goto(url('case-lookup')); + await page.getByLabel('Court', { + exact: true + }).selectOption(config.court); + await page.locator('#case-number').fill(config.docket); + await page.getByRole('button', { + name: 'Find my case' + }).click(); + await expect(page.getByRole('heading', { + name: 'Is this your court case?' + })).toBeVisible(); + await expect(page.getByRole('heading', { + name: 'Already on the court case' + })).toBeVisible(); + await page.getByRole('button', { + name: 'Yes, this is my case' + }).click(); + await page.waitForURL(/document-checklist/); + await page.goto(url('parties') + '&return_to=review'); + await expect(page.locator('.party-row').filter({ + hasText: 'Already on the court case' + })).toHaveCount(config.count); + await expect(page.getByRole('link', { + name: 'Edit', + exact: true + })).toHaveCount(0); + await expect(page.getByRole('button', { + name: 'Remove party' + })).toHaveCount(0); + await expect(page.getByRole('button', { + name: 'Add another person' + })).toHaveCount(0); + // Court-party selection remains native keyboard-accessible. + const selected = page.locator('input[name="filing_for"]').first(); + await selected.focus(); + await page.keyboard.press('Space'); + await expect(selected).toBeChecked(); + await page.getByRole('button', { + name: /^Continue/ + }).click(); + await page.waitForURL(/\/review\//); + const caseData = await page.locator('#case-data').textContent(); + const parties = JSON.parse(caseData).filing_parties.filter(p => p.source === 'court'); + expect(parties).toHaveLength(config.count); + expect(parties.filter(p => p.is_filing_party)).toHaveLength(1); + expect(parties.every(p => Boolean(p.external_party_id))).toBe(true); + await page.goto(url('parties') + '&return_to=review'); + await page.getByRole('button', { + name: 'This is me', + exact: true + }).first().click(); + await page.getByRole('button', { + name: /^Continue/ + }).click(); + await page.waitForURL(/\/review\//); + await expect(page.getByText(/Your account is linked to/)).toBeVisible(); + await expect(page.getByText(/This filing is on behalf of/)).toBeVisible(); + console.log('Existing-case browser flow passed: lookup, confirmation, read-only roster, keyboard selection, account link, review.'); + } finally { + await browser.close(); + } +})().catch(error => { + console.error(error); + process.exitCode = 1; +}); \ No newline at end of file From bda8b21573447a50a4173ae8ae9ca609913887f5 Mon Sep 17 00:00:00 2001 From: Quinten Steenhuis Date: Fri, 9 Oct 2026 08:53:16 -0400 Subject: [PATCH 2/2] Harden existing-case import and tidy the roster code Review fixes: - Only clear an import on drafts that have one, so new-case filing choices survive court and classification changes. - Unwrap case lineage entries and turn unexpected court record shapes into a retryable import error instead of a 500. - Normalize party countries to ISO codes rather than failing the import. - Skip attorney references to parties without a court ID. - Keep co-parties already chosen when the filer links themselves. - Send an added organization's name first in the browser payload, matching the server and person_type. - Reuse a recent unconfirmed preview on case confirmation instead of asking the court again on every reload; Retry still refetches. - Render the blocked confirmation with the court roster. - Keep the import gate in the middleware only. Cleanups: - FilingParty.CASE_ROSTER_SOURCES and on_case_roster replace string checks. - One lock-and-check helper for the case import, shared fee-quote and snapshot field lists, and a dict lookup in reconcile_case_parties. - Fetch the case and its role list concurrently. - linked_case_party and missing_required_party_types replace duplicated queries and role-coverage logic. Co-Authored-By: Claude Opus 5.5 --- efile_app/efile/middleware.py | 31 +-- efile_app/efile/models.py | 8 + efile_app/efile/services/drafts.py | 10 +- efile_app/efile/services/existing_cases.py | 253 ++++++++++-------- efile_app/efile/services/fee_quotes.py | 8 +- efile_app/efile/services/filing_path.py | 21 +- efile_app/efile/services/handoff.py | 3 +- efile_app/efile/services/people.py | 60 +++-- efile_app/efile/static/js/filing-payload.js | 4 +- .../efile/tests/test_case_confirmation.py | 2 +- efile_app/efile/tests/test_existing_cases.py | 176 +++++++++++- efile_app/efile/views/case_confirmation.py | 43 +-- efile_app/efile/views/parties.py | 32 +-- efile_app/efile/views/review.py | 4 +- efile_app/js-tests/filing-payload.test.js | 11 + 15 files changed, 440 insertions(+), 226 deletions(-) diff --git a/efile_app/efile/middleware.py b/efile_app/efile/middleware.py index f630359e..9b56d773 100644 --- a/efile_app/efile/middleware.py +++ b/efile_app/efile/middleware.py @@ -1,12 +1,19 @@ +from django.contrib import messages from django.contrib.auth import logout from django.http import JsonResponse -from django.shortcuts import render +from django.shortcuts import redirect, render from django.utils.deprecation import MiddlewareMixin from efile.models import FilingDraft -from efile.services.current_drafts import DraftIdentityError, resolve_explicit_draft +from efile.services.current_drafts import DraftIdentityError, get_current_draft, resolve_explicit_draft from efile.services.draft_urls import draft_url +from efile.services.existing_cases import import_ready from efile.utils.jurisdiction_stuff import get_jurisdiction_from_request +from efile.workflow import ExistingCase + +# Steps that read or file the court roster. An existing case reaches them only +# once its import is loaded and confirmed; this is the one place that says so. +NEEDS_CONFIRMED_CASE = {"parties", "party_details", "case_questions", "payment", "case_review"} class DraftIdentityMiddleware(MiddlewareMixin): @@ -21,22 +28,12 @@ def process_view(self, request, view_func, view_args, view_kwargs): if request.resolver_match.url_name == "filing_confirmation": statuses = (FilingDraft.Status.SUBMITTED,) try: - resolve_explicit_draft(request, jurisdiction=view_kwargs.get("jurisdiction"), statuses=statuses) - if request.resolver_match.url_name in { - "parties", - "party_details", - "case_questions", - "payment", - "case_review", - }: - from django.shortcuts import redirect - - from efile.services.current_drafts import get_current_draft - from efile.services.existing_cases import import_ready - from efile.workflow import ExistingCase - - draft = get_current_draft(request, jurisdiction=view_kwargs.get("jurisdiction")) + jurisdiction = view_kwargs.get("jurisdiction") + draft = resolve_explicit_draft(request, jurisdiction=jurisdiction, statuses=statuses) + if request.resolver_match.url_name in NEEDS_CONFIRMED_CASE: + draft = draft or get_current_draft(request, jurisdiction=jurisdiction) if draft and draft.existing_case == ExistingCase.EXISTING and not import_ready(draft): + messages.error(request, "Confirm the court case and load its parties before continuing.") return redirect("case_confirmation", jurisdiction=draft.jurisdiction) except DraftIdentityError as error: return self.process_exception(request, error) diff --git a/efile_app/efile/models.py b/efile_app/efile/models.py index 07ab5d24..4c6d5625 100644 --- a/efile_app/efile/models.py +++ b/efile_app/efile/models.py @@ -574,6 +574,10 @@ def __str__(self): class FilingParty(models.Model): """Person or organization associated with a filing draft.""" + # Rows on an existing court case's roster: "court" rows came from the + # court and cannot be edited; "added" rows join the case in this filing. + CASE_ROSTER_SOURCES = ("court", "added") + draft = models.ForeignKey(FilingDraft, on_delete=models.CASCADE, related_name="parties") role = models.CharField(max_length=50) sort_order = models.PositiveIntegerField(default=0) @@ -644,6 +648,10 @@ class Meta: ] verbose_name_plural = "Filing parties" + @property + def on_case_roster(self) -> bool: + return self.source in self.CASE_ROSTER_SOURCES + def __str__(self): display_name = " ".join(part for part in [self.first_name, self.middle_name, self.last_name] if part) return display_name or self.organization_name or f"{self.role} for draft #{self.draft_id}" diff --git a/efile_app/efile/services/drafts.py b/efile_app/efile/services/drafts.py index 45ab8103..3959a12e 100644 --- a/efile_app/efile/services/drafts.py +++ b/efile_app/efile/services/drafts.py @@ -208,8 +208,7 @@ def write_case_data( """ data = dict(case_data or {}) - FilingDraft.objects.select_for_update().get(pk=draft.pk) - draft.refresh_from_db() + draft.refresh_from_db(from_queryset=FilingDraft.objects.select_for_update()) old_identity = (draft.court_code, draft.previous_case_id, draft.existing_case) update_fields: list[str] = [] @@ -224,14 +223,15 @@ def write_case_data( setattr(draft, field, value) update_fields.append(field) - if old_identity != (draft.court_code, draft.previous_case_id, draft.existing_case): - from efile.services.existing_cases import clear_case_import + # existing_cases imports this module's draft statuses. + from efile.services.existing_cases import SNAPSHOT_CASE_FIELDS, clear_case_import + if old_identity != (draft.court_code, draft.previous_case_id, draft.existing_case): clear_case_import(draft) elif draft.existing_case_snapshot.get("status") == "loaded": snapshot = draft.existing_case_snapshot - for field in ("case_category_code", "case_type_code", "docket_number", "case_title"): + for field in SNAPSHOT_CASE_FIELDS: if getattr(draft, field) != snapshot[field]: setattr(draft, field, snapshot[field]) update_fields.append(field) diff --git a/efile_app/efile/services/existing_cases.py b/efile_app/efile/services/existing_cases.py index b3041098..d4999caf 100644 --- a/efile_app/efile/services/existing_cases.py +++ b/efile_app/efile/services/existing_cases.py @@ -6,6 +6,8 @@ """ import re +from concurrent.futures import ThreadPoolExecutor +from datetime import datetime, timedelta from urllib.parse import quote import requests @@ -15,7 +17,8 @@ from efile.models import FilingDraft, FilingParty from efile.party_sides import side_for_party_type_name -from efile.services.fee_quotes import fee_inputs_token, invalidate_fee_quote +from efile.services.drafts import ACTIVE_DRAFT_STATUSES +from efile.services.fee_quotes import FEE_QUOTE_FIELDS, fee_inputs_token, invalidate_fee_quote from efile.utils.proxy_connection import get_headers from efile.workflow import ExistingCase @@ -24,6 +27,33 @@ class CaseImportError(ValueError): pass +# Case fields the court's record decides; the draft mirrors them. +SNAPSHOT_CASE_FIELDS = ("docket_number", "case_title", "case_category_code", "case_type_code") +PARTY_FIELDS = ( + "first_name", + "middle_name", + "last_name", + "suffix", + "organization_name", + "email", + "phone", + "address_line_1", + "address_line_2", + "city", + "state", + "zip_code", + "country", +) +PARTY_FIELD_LIMITS = { + key: FilingParty._meta.get_field(key).max_length + for key in (*PARTY_FIELDS, "external_party_id", "party_type", "party_type_name") +} + + +def docket_key(value): + return re.sub(r"[^a-z0-9]", "", value.lower()) + + def unwrap(value): while isinstance(value, dict) and "value" in value: value = value["value"] @@ -49,6 +79,14 @@ def field(value, key): return next((text(item[key]) for item in objects(value) if item.get(key) is not None), "") +def country_code(value): + value = value.upper() + if value in {"USA", "UNITED STATES", "UNITED STATES OF AMERICA"}: + return "US" + # FilingParty.country holds an ISO code; anything else is not identity. + return value if re.fullmatch(r"[A-Z]{2}", value) else "" + + def entity_id(entity, category="CASEPARTYID"): ids = { text(item.get("identificationID")) @@ -82,7 +120,7 @@ def contacts(entity): "city": text(address.get("locationCityName")), "state": text(address.get("locationState")), "zip_code": text(address.get("locationPostalCode")), - "country": text(address.get("locationCountry")), + "country": country_code(text(address.get("locationCountry"))), } ) return result @@ -106,16 +144,15 @@ def entity_fields(entity): def normalize_case(raw, *, court, tracking_id, party_types, docket_number=""): case = unwrap(raw) lineage_ids = { - text(item.get("caseTrackingID")) + text(lineage.get("caseTrackingID")) for item in objects(case) if isinstance(item.get("caseLineageCase"), list) - for item in item["caseLineageCase"] + for lineage in map(unwrap, item["caseLineageCase"]) + if isinstance(lineage, dict) } if not isinstance(case, dict) or tracking_id not in {text(case.get("caseTrackingID")), *lineage_ids}: raise CaseImportError("The court returned a different case. Search for your case again.") - if docket_number and re.sub(r"[^a-z0-9]", "", text(case.get("caseDocketID")).lower()) != re.sub( - r"[^a-z0-9]", "", docket_number.lower() - ): + if docket_number and docket_key(text(case.get("caseDocketID"))) != docket_key(docket_number): raise CaseImportError("The court returned a different case number. Search for your case again.") returned_court = field( next((x for x in objects(case) if "caseCourt" in x), {}).get("caseCourt"), "identificationID" @@ -154,12 +191,10 @@ def normalize_case(raw, *, court, tracking_id, party_types, docket_number=""): entity = reference.get("ref") if not isinstance(entity, dict): continue - id_category = "CASEPARTYATTORNEYID" - attorney_id = entity_id(entity, id_category) - if not attorney_id: - id_category = "ATTORNEYID" - attorney_id = entity_id(entity, id_category) - if not attorney_id: + for id_category in ("CASEPARTYATTORNEYID", "ATTORNEYID"): + if attorney_id := entity_id(entity, id_category): + break + else: continue attorneys[attorney_id] = { "id": attorney_id, @@ -169,8 +204,9 @@ def normalize_case(raw, *, court, tracking_id, party_types, docket_number=""): } for reference in references: ref = reference.get("ref") - if isinstance(ref, dict): - associations.setdefault(entity_id(ref), []).append(attorney_id) + # An unidentified reference would match every party without an ID. + if isinstance(ref, dict) and (represented_id := entity_id(ref)): + associations.setdefault(represented_id, []).append(attorney_id) parties, problems, seen = [], [], set() for index, wrapped in enumerate(augmentation["caseParticipant"]): participant = unwrap(wrapped) @@ -186,28 +222,11 @@ def normalize_case(raw, *, court, tracking_id, party_types, docket_number=""): if party_id and party_id in seen: raise CaseImportError("The court returned conflicting party IDs. Retry loading the case.") seen.add(party_id) - values: dict[str, str] = { - key: "" - for key in ( - "first_name", - "middle_name", - "last_name", - "suffix", - "organization_name", - "email", - "phone", - "address_line_1", - "address_line_2", - "city", - "state", - "zip_code", - "country", - ) - } + values: dict[str, str] = {key: "" for key in PARTY_FIELDS} values.update(entity_fields(entity)) values.update(external_party_id=party_id, party_type=role, party_type_name=types.get(role, "")) for key, value in values.items(): - limit = FilingParty._meta.get_field(key).max_length + limit = PARTY_FIELD_LIMITS[key] if limit and len(value) > limit: raise CaseImportError( "The court returned party information that is too long to import. Contact support." @@ -216,15 +235,12 @@ def normalize_case(raw, *, court, tracking_id, party_types, docket_number=""): problems.append(f"Party {index + 1} has no court party ID.") if role not in types: problems.append(f"Party {index + 1} has an unrecognized court role ({role or 'missing'}).") - if not (values.get("organization_name") or (values.get("first_name") and values.get("last_name"))): + if not (values["organization_name"] or (values["first_name"] and values["last_name"])): problems.append(f"Party {index + 1} has an incomplete court name.") attorney_ids = associations.get(party_id, []) parties.append( { **values, - "external_party_id": party_id, - "party_type": role, - "party_type_name": types.get(role, ""), "representation": { "status": "represented" if attorney_ids else "unknown", "attorneys": [attorneys[i] for i in attorney_ids], @@ -263,8 +279,16 @@ def fetch_case(draft, token): f"{settings.EFSP_URL}/jurisdictions/{quote(draft.jurisdiction, safe='')}/cases/" f"courts/{quote(draft.court_code, safe='')}/cases/{quote(draft.previous_case_id, safe='')}" ) + codes_url = ( + f"{settings.EFSP_URL}/jurisdictions/{quote(draft.jurisdiction, safe='')}/codes/courts/" + f"{quote(draft.court_code, safe='')}/party_types" + ) headers = get_headers() headers[f"tyler-token-{draft.jurisdiction}"] = token + # The role list does not depend on the case, so fetch both at once. + pool = ThreadPoolExecutor(max_workers=1) + pending_codes = pool.submit(requests.get, codes_url, headers=headers, timeout=15) + pool.shutdown(wait=False) try: response = requests.get(url, headers=headers, timeout=30) response.raise_for_status() @@ -273,12 +297,7 @@ def fetch_case(draft, token): raise CaseImportError("We could not load the court's parties. Retry or sign in again.") from error # Classification and roles come from this response, not browser assertions. try: - codes = requests.get( - f"{settings.EFSP_URL}/jurisdictions/{draft.jurisdiction}/codes/courts/" - f"{quote(draft.court_code, safe='')}/party_types", - headers=headers, - timeout=15, - ) + codes = pending_codes.result() codes.raise_for_status() types = codes.json() if not isinstance(types, list): @@ -290,10 +309,13 @@ def fetch_case(draft, token): party_types=types, docket_number=draft.docket_number, ) + except CaseImportError: + raise except (requests.RequestException, ValueError) as error: - if isinstance(error, CaseImportError): - raise raise CaseImportError("We could not verify the court's party roles. Retry loading the case.") from error + except (AttributeError, TypeError, KeyError) as error: + # The court's record had a shape normalize_case did not expect. + raise CaseImportError("We could not read the court's case. Retry loading the case.") from error snapshot.update( source_environment=settings.EFSP_URL, jurisdiction=draft.jurisdiction, retrieved_at=timezone.now().isoformat() ) @@ -302,37 +324,50 @@ def fetch_case(draft, token): @transaction.atomic def clear_case_import(draft): - draft.parties.filter(source__in=["court", "added"]).delete() + """Forget an imported court roster. Returns whether there was one. + + New-case drafts are left alone: their filing-party and self choices + belong to the filer, not to an import. + """ + + roster = draft.parties.filter(source__in=FilingParty.CASE_ROSTER_SOURCES) + if not draft.existing_case_snapshot and not roster.exists(): + return False + roster.delete() draft.parties.update(is_filing_party=False, is_self=False) draft.existing_case_snapshot = {} invalidate_fee_quote(draft, save=False) - draft.save( - update_fields=[ - "existing_case_snapshot", - "quoted_fee_total", - "quoted_fee_breakdown", - "quoted_fee_fingerprint", - "updated_at", - ] - ) + draft.save(update_fields=["existing_case_snapshot", *FEE_QUOTE_FIELDS, "updated_at"]) + return True -@transaction.atomic -def apply_case(draft, snapshot): +def _lock_case(draft, court, tracking_id): + """Lock the draft, which must be editable and still point at this case.""" + current = FilingDraft.objects.select_for_update().get(pk=draft.pk) - if current.status not in {FilingDraft.Status.DRAFT, FilingDraft.Status.ERROR}: + if current.status not in ACTIVE_DRAFT_STATUSES: raise CaseImportError("This filing can no longer be edited.") if (current.court_code, current.previous_case_id, current.existing_case) != ( - snapshot["court"], - snapshot["tracking_id"], + court, + tracking_id, ExistingCase.EXISTING, ): raise CaseImportError("The selected case changed while loading. Reload your case.") + return current + + +@transaction.atomic +def apply_case(draft, snapshot): + _apply_locked(_lock_case(draft, snapshot["court"], snapshot["tracking_id"]), snapshot) + draft.refresh_from_db() + + +def _apply_locked(current, snapshot): if current.existing_case_snapshot.get("tracking_id") not in {None, snapshot["tracking_id"]}: clear_case_import(current) previous_fee_inputs = fee_inputs_token(current) # Extracted suggestions stay in extracted_guesses, not as duplicate parties. - current.parties.exclude(role="filer").exclude(source__in=["court", "added"]).delete() + current.parties.exclude(role="filer").exclude(source__in=FilingParty.CASE_ROSTER_SOURCES).delete() current.parties.filter(role="filer").update(party_type="", party_type_name="", is_filing_party=False) existing = {p.external_party_id: p for p in current.parties.filter(source="court") if p.external_party_id} keep = [] @@ -350,12 +385,11 @@ def apply_case(draft, snapshot): keep.append(party.pk) current.parties.filter(source="court").exclude(pk__in=keep).delete() current.existing_case_snapshot = snapshot - for key in ("docket_number", "case_title", "case_category_code", "case_type_code"): + for key in SNAPSHOT_CASE_FIELDS: setattr(current, key, snapshot[key]) if fee_inputs_token(current) != previous_fee_inputs: invalidate_fee_quote(current, save=False) current.save() - draft.refresh_from_db() def import_ready(draft): @@ -369,51 +403,56 @@ def import_ready(draft): ) -def load_case(draft, token, *, apply=True): +SNAPSHOT_REUSE_WINDOW = timedelta(minutes=15) + + +def reusable_snapshot(draft): + """An unconfirmed snapshot of this case recent enough to show or confirm.""" + + snapshot = draft.existing_case_snapshot or {} + if ( + snapshot.get("status") not in {"loaded", "partial"} + or snapshot.get("court") != draft.court_code + or snapshot.get("tracking_id") != draft.previous_case_id + or snapshot.get("source_environment") != settings.EFSP_URL + ): + return False + try: + retrieved_at = datetime.fromisoformat(snapshot.get("retrieved_at", "")) + except (TypeError, ValueError): + return False + return timezone.now() - retrieved_at < SNAPSHOT_REUSE_WINDOW + + +def load_case(draft, token): + """Fetch the court's case and store it as an unconfirmed preview.""" + try: snapshot = fetch_case(draft, token) - if apply: - apply_case(draft, snapshot) - else: - with transaction.atomic(): - current = FilingDraft.objects.select_for_update().get(pk=draft.pk) - if (current.court_code, current.previous_case_id, current.existing_case) != ( - snapshot["court"], - snapshot["tracking_id"], - ExistingCase.EXISTING, - ) or current.status not in {FilingDraft.Status.DRAFT, FilingDraft.Status.ERROR}: - raise CaseImportError("The selected case changed while loading. Reload your case.") - current.existing_case_snapshot = snapshot - current.save(update_fields=["existing_case_snapshot", "updated_at"]) - draft.refresh_from_db() - except CaseImportError: with transaction.atomic(): - current = FilingDraft.objects.select_for_update().get(pk=draft.pk) - if (current.court_code, current.previous_case_id, current.existing_case) == ( - draft.court_code, - draft.previous_case_id, - ExistingCase.EXISTING, - ) and current.status in {FilingDraft.Status.DRAFT, FilingDraft.Status.ERROR}: + current = _lock_case(draft, snapshot["court"], snapshot["tracking_id"]) + current.existing_case_snapshot = snapshot + current.save(update_fields=["existing_case_snapshot", "updated_at"]) + draft.refresh_from_db() + except CaseImportError: + try: + with transaction.atomic(): + current = _lock_case(draft, draft.court_code, draft.previous_case_id) current.existing_case_snapshot = { "status": "failed", "court": current.court_code, "tracking_id": current.previous_case_id, } invalidate_fee_quote(current, save=False) - current.save( - update_fields=[ - "existing_case_snapshot", - "quoted_fee_total", - "quoted_fee_breakdown", - "quoted_fee_fingerprint", - "updated_at", - ] - ) + current.save(update_fields=["existing_case_snapshot", *FEE_QUOTE_FIELDS, "updated_at"]) + except CaseImportError: + pass # The draft moved on; there is no failed load of it to record. draft.refresh_from_db() raise def reconcile_case_parties(payload, draft, jurisdiction, court): + # efsp_payload imports this module; import its error lazily. from efile.services.efsp_payload import PayloadValidationError existing = bool(payload.get("previous_case_id")) or (draft and draft.existing_case == ExistingCase.EXISTING) @@ -436,8 +475,11 @@ def reconcile_case_parties(payload, draft, jurisdiction, court): ("docket_number", "docket_number"), ): payload[wire] = getattr(draft, attr) - expected = {p.external_party_id: p for p in draft.parties.filter(source="court")} - expected.update({f"draft:{p.pk}": p for p in draft.parties.filter(source="added")}) + expected = { + p.external_party_id if p.source == "court" else f"draft:{p.pk}": p + for p in draft.parties.filter(source__in=FilingParty.CASE_ROSTER_SOURCES) + } + verified_parties = {p["external_party_id"]: p for p in draft.existing_case_snapshot["parties"]} selected = {key for key, p in expected.items() if p.is_filing_party} if not selected: raise PayloadValidationError("Choose the existing party you are filing for.") @@ -453,9 +495,7 @@ def reconcile_case_parties(payload, draft, jurisdiction, court): ) stored = expected[party_id] if stored.source == "court": - verified = next( - (p for p in draft.existing_case_snapshot["parties"] if p["external_party_id"] == party_id), None - ) + verified = verified_parties.get(party_id) if verified is None or any( getattr(stored, key) != verified.get(key, "") for key in ("party_type", "first_name", "middle_name", "last_name", "suffix", "organization_name") @@ -497,7 +537,7 @@ def reconcile_case_parties(payload, draft, jurisdiction, court): "city": stored.city, "state": stored.state, "zip": stored.zip_code, - "country": stored.country, + "country": stored.country or "US", } if collection == "users": party["is_form_filler"] = False @@ -512,13 +552,8 @@ def reconcile_case_parties(payload, draft, jurisdiction, court): @transaction.atomic def confirm_case(draft): - current = FilingDraft.objects.select_for_update().get(pk=draft.pk) - snapshot = current.existing_case_snapshot - if (current.court_code, current.previous_case_id, current.existing_case) != ( - draft.court_code, - draft.previous_case_id, - ExistingCase.EXISTING, - ) or snapshot.get("status") != "loaded": + current = _lock_case(draft, draft.court_code, draft.previous_case_id) + if current.existing_case_snapshot.get("status") != "loaded": raise CaseImportError("The selected case changed while loading. Reload your case.") - apply_case(current, {**snapshot, "confirmed": True}) + _apply_locked(current, {**current.existing_case_snapshot, "confirmed": True}) draft.refresh_from_db() diff --git a/efile_app/efile/services/fee_quotes.py b/efile_app/efile/services/fee_quotes.py index d77caffc..feed4257 100644 --- a/efile_app/efile/services/fee_quotes.py +++ b/efile_app/efile/services/fee_quotes.py @@ -58,7 +58,6 @@ def fee_inputs(draft: FilingDraft, *, payment_account_id: str | None = None) -> party.role, party.party_type, bool(party.is_filing_party), - bool(party.organization_name), party.external_party_id, party.source_case_id, json.dumps(party.representation, sort_keys=True), @@ -126,6 +125,9 @@ def fee_quote_is_usable(draft: FilingDraft) -> bool: return fee_quote_state(draft) in {FeeQuoteState.CURRENT, FeeQuoteState.WAIVED} +FEE_QUOTE_FIELDS = ("quoted_fee_total", "quoted_fee_breakdown", "quoted_fee_fingerprint") + + def invalidate_fee_quote(draft: FilingDraft, *, save: bool = True) -> None: """Forget the quote outright, for changes that make it meaningless.""" @@ -133,7 +135,7 @@ def invalidate_fee_quote(draft: FilingDraft, *, save: bool = True) -> None: draft.quoted_fee_breakdown = [] draft.quoted_fee_fingerprint = "" if save: - draft.save(update_fields=["quoted_fee_total", "quoted_fee_breakdown", "quoted_fee_fingerprint", "updated_at"]) + draft.save(update_fields=[*FEE_QUOTE_FIELDS, "updated_at"]) def quote_from_efsp_response(response: dict[str, Any]) -> tuple[str, list[dict[str, str]]] | None: @@ -192,7 +194,7 @@ def _store_quote(draft, total, breakdown, payment_account_id): draft.quoted_fee_total = total draft.quoted_fee_breakdown = breakdown draft.quoted_fee_fingerprint = fee_fingerprint(draft, payment_account_id=payment_account_id) - draft.save(update_fields=["quoted_fee_total", "quoted_fee_breakdown", "quoted_fee_fingerprint", "updated_at"]) + draft.save(update_fields=[*FEE_QUOTE_FIELDS, "updated_at"]) def fee_quote_summary(draft: FilingDraft) -> dict[str, Any]: diff --git a/efile_app/efile/services/filing_path.py b/efile_app/efile/services/filing_path.py index ec7d052a..2d329510 100644 --- a/efile_app/efile/services/filing_path.py +++ b/efile_app/efile/services/filing_path.py @@ -23,7 +23,8 @@ from django.db import transaction from efile.models import FilingDocument, FilingDraft, FilingParty, sync_primary_filing_type -from efile.services.fee_quotes import invalidate_fee_quote +from efile.services.existing_cases import clear_case_import +from efile.services.fee_quotes import FEE_QUOTE_FIELDS, invalidate_fee_quote from efile.workflow import ExistingCase, normalize_existing_case FILING_PATH_SOURCE_KEY = "_filing_path_source" @@ -96,13 +97,9 @@ def change_filing_path(draft: FilingDraft, new_path: str) -> FilingPathChange: update_fields = ["existing_case", "updated_at"] draft.existing_case = current - from efile.services.existing_cases import clear_case_import - - if draft.existing_case_snapshot or FilingParty.objects.filter(draft=draft, source="court").exists(): - had_quote = bool(draft.quoted_fee_total or draft.quoted_fee_breakdown or draft.quoted_fee_fingerprint) - clear_case_import(draft) - if had_quote: - change.cleared.append("fees") + had_quote = bool(draft.quoted_fee_total or draft.quoted_fee_breakdown or draft.quoted_fee_fingerprint) + if clear_case_import(draft) and had_quote: + change.cleared.append("fees") if current != ExistingCase.EXISTING and (draft.previous_case_id or draft.docket_number or draft.case_title): # A new case has no number or title until the court opens it, and a @@ -132,7 +129,7 @@ def change_filing_path(draft: FilingDraft, new_path: str) -> FilingPathChange: # The fingerprint would make it stale anyway; clearing it says outright # that the quote priced a different kind of filing. invalidate_fee_quote(draft, save=False) - update_fields += ["quoted_fee_total", "quoted_fee_breakdown", "quoted_fee_fingerprint"] + update_fields += FEE_QUOTE_FIELDS change.cleared.append("fees") draft.save(update_fields=update_fields) @@ -163,8 +160,6 @@ def clear_changed_classification(draft, court_code, case_category_code, case_typ requested_optional_services=[], filing_requires_amount_in_controversy=False, ) - from efile.services.existing_cases import clear_case_import - clear_case_import(draft) FilingParty.objects.filter(draft=draft).exclude(source="court").update(party_type="", party_type_name="") draft.case_subtype_code = "" @@ -184,9 +179,7 @@ def clear_changed_classification(draft, court_code, case_category_code, case_typ "previous_case_id", "case_title", "optional_services", - "quoted_fee_total", - "quoted_fee_breakdown", - "quoted_fee_fingerprint", + *FEE_QUOTE_FIELDS, "updated_at", ] ) diff --git a/efile_app/efile/services/handoff.py b/efile_app/efile/services/handoff.py index 387df2f7..099d8bfc 100644 --- a/efile_app/efile/services/handoff.py +++ b/efile_app/efile/services/handoff.py @@ -15,6 +15,7 @@ from efile.models import FilingDocument, FilingDraft, FilingMetadataEvent, FilingParty, InterviewHandoff from efile.party_sides import side_for_party_type_name from efile.services.document_checklists import normalize_name +from efile.services.existing_cases import import_ready from efile.services.filing_plans import _codes from efile.utils.config_loader import config_loader from efile.workflow import ExistingCase, WorkflowStepKey @@ -495,8 +496,6 @@ def need(path, value, message, view): if draft.existing_case == ExistingCase.EXISTING: need("previous_case_id", draft.previous_case_id, "Find and confirm the existing court case.", "case_lookup") if draft.previous_case_id: - from efile.services.existing_cases import import_ready - need( "_court_case_import", import_ready(draft), diff --git a/efile_app/efile/services/people.py b/efile_app/efile/services/people.py index c614b97a..481215f6 100644 --- a/efile_app/efile/services/people.py +++ b/efile_app/efile/services/people.py @@ -70,10 +70,13 @@ def filer_is_party(draft: FilingDraft) -> bool: """ filer = FilingParty.objects.filter(draft=draft, role="filer").first() - return ( - bool(filer and filer.party_type) - or FilingParty.objects.filter(draft=draft, source__in=["court", "added"], is_self=True).exists() - ) + return bool(filer and filer.party_type) or linked_case_party(draft) is not None + + +def linked_case_party(draft: FilingDraft) -> FilingParty | None: + """The court-roster party the filer said is them, if any.""" + + return FilingParty.objects.filter(draft=draft, source__in=FilingParty.CASE_ROSTER_SOURCES, is_self=True).first() def filing_parties(draft: FilingDraft) -> list[FilingParty]: @@ -198,7 +201,8 @@ def claim_replaces_a_name(filer: FilingParty | None, party: FilingParty | None) question to ask first. """ - if party is None: + if party is None or party.on_case_roster: + # Linking to a court-roster party never renames anyone. return False return bool(party_display_name(party)) and not names_match(filer, party) @@ -223,13 +227,16 @@ def claim_party_as_filer(draft: FilingDraft, party: FilingParty, *, use_party_na filer = FilingParty.objects.filter(draft=draft, role="filer").first() if filer is None or party is None: return - if party.source in {"court", "added"}: - FilingParty.objects.filter(draft=draft, source__in=["court", "added"]).update(is_self=False) + if party.on_case_roster: + roster = FilingParty.objects.filter(draft=draft, source__in=FilingParty.CASE_ROSTER_SOURCES) + roster.update(is_self=False) party.is_self = True party.save(update_fields=["is_self", "updated_at"]) filer.party_type = filer.party_type_name = "" filer.save(update_fields=["party_type", "party_type_name", "updated_at"]) - set_filing_parties(draft, [party]) + # Linking yourself adds you to who this filing is for; it does not + # drop co-parties the filer already chose. + set_filing_parties(draft, [*roster.filter(is_filing_party=True).exclude(pk=party.pk), party]) return name_fields: list[str] = [] if use_party_name and party_display_name(party): @@ -500,25 +507,21 @@ def absorb_filer_duplicates(draft: FilingDraft) -> str: put to them on the parties screen instead; see :func:`filer_name_match`. """ - if ( - not filer_is_party(draft) - or FilingParty.objects.filter(draft=draft, source__in=["court", "added"], is_self=True).exists() - ): + # A filer linked to a court-roster party is never merged by name. + filer = FilingParty.objects.filter(draft=draft, role="filer").first() + if not (filer and filer.party_type) or linked_case_party(draft) is not None: return side_named_for_filer(draft) duplicates = _filer_duplicates(draft) if not duplicates: return side_named_for_filer(draft) - filer = FilingParty.objects.filter(draft=draft, role="filer").first() - side = filer.party_side if filer is not None else "" - files_on_behalf = filer.is_filing_party if filer is not None else False + side = filer.party_side + files_on_behalf = filer.is_filing_party for party in duplicates: side = side or party.party_side or side_for_party_type_name(party.party_type_name) files_on_behalf = files_on_behalf or party.is_filing_party party.delete() - if filer is None: - return side updated = [] if side and not filer.party_side: filer.party_side = side @@ -531,28 +534,33 @@ def absorb_filer_duplicates(draft: FilingDraft) -> str: return side +def missing_required_party_types(draft: FilingDraft, party_types: list[dict[str, Any]]) -> list[dict[str, Any]]: + """The court's required roles that no party in the draft holds yet.""" + + covered = set(FilingParty.objects.filter(draft=draft).exclude(party_type="").values_list("party_type", flat=True)) + missing = {} + for party_type in party_types: + if party_type["required"] and party_type["code"] not in covered: + missing.setdefault(party_type["code"], party_type) + return list(missing.values()) + + def ensure_required_parties(draft: FilingDraft, party_types: list[dict[str, Any]]) -> None: if draft.existing_case_snapshot: # A verified roster cannot acquire new people through placeholders. # Existing-case additions require the filer's explicit Add action. return parties = FilingParty.objects.filter(draft=draft) - covered = set(parties.exclude(party_type="").values_list("party_type", flat=True)) last_order = parties.filter(role="other").order_by("-sort_order").values_list("sort_order", flat=True).first() next_order = 0 if last_order is None else last_order + 1 - for party_type in party_types: - code = party_type["code"] - if not party_type["required"] or code in covered: - continue + for offset, party_type in enumerate(missing_required_party_types(draft, party_types)): FilingParty.objects.create( draft=draft, role="other", - sort_order=next_order, - party_type=code, + sort_order=next_order + offset, + party_type=party_type["code"], party_type_name=party_type["name"], ) - covered.add(code) - next_order += 1 def needs_amount_in_controversy(draft: FilingDraft) -> bool: diff --git a/efile_app/efile/static/js/filing-payload.js b/efile_app/efile/static/js/filing-payload.js index c9a4672e..e54b7564 100644 --- a/efile_app/efile/static/js/filing-payload.js +++ b/efile_app/efile/static/js/filing-payload.js @@ -76,7 +76,9 @@ const FilingPayload = { person_type: "business" } : {}), name: { - first: party.first_name || party.organization_name || "", + // Matches person_type above: an organization name wins over + // a person's first name left over from before the switch. + first: party.organization_name || party.first_name || "", middle: party.middle_name || "", last: party.last_name || "", suffix: party.suffix || "" diff --git a/efile_app/efile/tests/test_case_confirmation.py b/efile_app/efile/tests/test_case_confirmation.py index f73976b0..39c6594c 100644 --- a/efile_app/efile/tests/test_case_confirmation.py +++ b/efile_app/efile/tests/test_case_confirmation.py @@ -83,7 +83,7 @@ def test_case_confirmation_accepts_case_and_converges_on_checklist(client, djang draft.save() monkeypatch.setattr( "efile.views.case_confirmation.load_case", - lambda draft, token, apply=False: apply_case(draft, {**snapshot, "confirmed": False}), + lambda draft, token: apply_case(draft, {**snapshot, "confirmed": False}), ) response = client.post( diff --git a/efile_app/efile/tests/test_existing_cases.py b/efile_app/efile/tests/test_existing_cases.py index b80aed12..bc1ac07f 100644 --- a/efile_app/efile/tests/test_existing_cases.py +++ b/efile_app/efile/tests/test_existing_cases.py @@ -453,9 +453,11 @@ def get(url, **kwargs): snapshot = fetch_case(imported, "test-token") assert snapshot["status"] == "loaded" assert snapshot["tracking_id"] == imported.previous_case_id - assert calls[0][0].endswith("/" + imported.previous_case_id) - assert calls[0][1]["headers"]["tyler-token-illinois"] == "test-token" - assert "/case_types/" not in calls[1][0] + # The two requests run concurrently, so find each by its URL. + case_call = next(call for call in calls if call[0].endswith("/" + imported.previous_case_id)) + codes_call = next(call for call in calls if call[0].endswith("/party_types")) + assert case_call[1]["headers"]["tyler-token-illinois"] == "test-token" + assert "/case_types/" not in codes_call[0] assert snapshot["retrieved_at"] @@ -561,3 +563,171 @@ def test_missing_required_role_requires_explicit_addition(imported, client, monk assert response.status_code == 200 assert b"The court requires these roles" in response.content assert imported.parties.count() == before + + +def _augmentation(raw): + return next(i["value"] for i in raw["value"]["rest"] if "caseParticipant" in i["value"]) + + +def _normalize(raw, snapshot): + return normalize_case( + raw, + court=snapshot["court"], + tracking_id=snapshot["tracking_id"], + party_types=json.loads((FIXTURES / "marion-party-types.json").read_text()), + ) + + +@pytest.mark.django_db +def test_classification_change_keeps_new_case_filing_choices(django_user_model): + from efile.services.filing_path import clear_changed_classification + + user = django_user_model.objects.create_user(username="new-case-owner", tyler_jurisdiction="illinois") + draft = FilingDraft.objects.create( + user=user, jurisdiction="illinois", existing_case=ExistingCase.NEW, court_code="old", case_type_code="1" + ) + filer = FilingParty.objects.create(draft=draft, role="filer", first_name="Account", last_name="Owner") + other = FilingParty.objects.create(draft=draft, role="other", first_name="Someone", last_name="Else") + set_filing_parties(draft, [other]) + filer.is_self = True + filer.save() + clear_changed_classification(draft, "new", "", "") + other.refresh_from_db() + filer.refresh_from_db() + assert other.is_filing_party + assert filer.is_self + + +def test_wrapped_and_unreadable_lineage_entries_still_match(): + raw = json.loads((FIXTURES / "2025SC5-raw.json").read_text()) + snapshot = normalized() + holder = next(i["value"] for i in raw["value"]["rest"] if "caseLineageCase" in i["value"]) + holder["caseLineageCase"] = ["unreadable", *({"value": entry} for entry in holder["caseLineageCase"])] + assert _normalize(raw, snapshot)["tracking_id"] == snapshot["tracking_id"] + + +@pytest.mark.parametrize(("country", "expected"), [("USA", "US"), ("us", "US"), ("United Kingdom", "")]) +def test_country_is_normalized_rather_than_failing_the_import(country, expected): + raw = json.loads((FIXTURES / "2025SC5-raw.json").read_text()) + snapshot = normalized() + + def visit(value): + if isinstance(value, dict): + if "locationCountry" in value: + value["locationCountry"] = {"value": country} + for item in value.values(): + visit(item) + elif isinstance(value, list): + for item in value: + visit(item) + + visit(_augmentation(raw)["caseParticipant"]) + countries = {p["country"] for p in _normalize(raw, snapshot)["parties"] if p["address_line_1"]} + assert countries == {expected} + + +def test_unidentified_attorney_reference_is_not_shared_by_parties_without_ids(): + raw = json.loads((FIXTURES / "2025SC5-raw.json").read_text()) + snapshot = normalized() + augmentation = _augmentation(raw) + unrepresented = {p["external_party_id"] for p in snapshot["parties"] if p["representation"]["status"] == "unknown"} + for participant in augmentation["caseParticipant"]: + entity = participant["value"]["entityRepresentation"]["value"] + if entity_id(entity) in unrepresented: + entity["personOtherIdentification"] = [] + augmentation["caseOtherEntityAttorney"][0]["caseRepresentedPartyReference"].append({"ref": {"personName": {}}}) + result = _normalize(raw, snapshot) + assert all(p["representation"]["status"] == "unknown" for p in result["parties"] if not p["external_party_id"]) + + +@pytest.mark.django_db +def test_linking_yourself_keeps_co_parties_already_chosen(imported): + mine, co_party = list(imported.parties.filter(source="court"))[:2] + set_filing_parties(imported, [mine, co_party]) + claim_party_as_filer(imported, mine) + mine.refresh_from_db() + co_party.refresh_from_db() + assert mine.is_self and mine.is_filing_party + assert co_party.is_filing_party + + +@pytest.mark.django_db +def test_added_organization_name_wins_over_a_stale_first_name(imported): + added = FilingParty.objects.create( + draft=imported, + role="other", + sort_order=100, + source="added", + source_case_id=imported.previous_case_id, + first_name="Stale", + organization_name="Example LLC", + party_type=imported.parties.filter(source="court").first().party_type, + ) + court = list(imported.parties.filter(source="court")) + set_filing_parties(imported, [court[0]]) + imported.refresh_from_db() + + def entry(party, **extra): + name = {"first": party.first_name, "middle": party.middle_name, "last": party.last_name, "suffix": party.suffix} + return {"name": name, "party_type": party.party_type, "tyler_id": party.external_party_id, "is_new": False} + + added_entry = { + "name": {"first": "Example LLC", "middle": "", "last": "", "suffix": ""}, + "party_type": added.party_type, + "is_new": True, + "_draft_party_id": added.pk, + } + payload = { + "previous_case_id": imported.previous_case_id, + "user_started_case": False, + "users": [entry(court[0])], + "other_parties": [*(entry(p) for p in court[1:]), added_entry], + } + reconcile_case_parties(payload, imported, "illinois", imported.court_code) + assert payload["other_parties"][-1]["name"]["first"] == "Example LLC" + + +@pytest.mark.django_db +def test_confirmation_reuses_a_recent_preview(imported, client, monkeypatch): + from django.utils import timezone + + imported.parties.filter(source="court").delete() + imported.existing_case_snapshot = {} + imported.save() + snapshot = normalized() + snapshot.pop("confirmed") + calls = [] + + def fetch(*args): + calls.append(args) + return {**copy.deepcopy(snapshot), "retrieved_at": timezone.now().isoformat()} + + monkeypatch.setattr("efile.services.existing_cases.fetch_case", fetch) + url = reverse("case_confirmation", kwargs={"jurisdiction": "illinois"}) + assert client.get(url).status_code == 200 + assert client.get(url).status_code == 200 + assert client.post(url, {"confirmed": "yes"}).status_code == 302 + assert len(calls) == 1 + imported.refresh_from_db() + assert import_ready(imported) + + +@pytest.mark.django_db +def test_blocked_confirmation_still_shows_the_court_roster(imported, client, monkeypatch): + from django.utils import timezone + + imported.existing_case_snapshot = {} + imported.save() + snapshot = normalized() + snapshot.pop("confirmed") + partial = { + **snapshot, + "status": "partial", + "problems": ["Party 1 has an unrecognized court role (X)."], + "retrieved_at": timezone.now().isoformat(), + } + monkeypatch.setattr("efile.services.existing_cases.fetch_case", lambda *args: copy.deepcopy(partial)) + response = client.post(reverse("case_confirmation", kwargs={"jurisdiction": "illinois"}), {"confirmed": "yes"}) + assert response.status_code == 422 + assert b"unrecognized court role" in response.content + assert b"Already on the court case" in response.content diff --git a/efile_app/efile/views/case_confirmation.py b/efile_app/efile/views/case_confirmation.py index 0048d1dc..4031dbd4 100644 --- a/efile_app/efile/views/case_confirmation.py +++ b/efile_app/efile/views/case_confirmation.py @@ -6,7 +6,13 @@ from efile.models import FilingParty from efile.services.current_drafts import ensure_current_draft from efile.services.drafts import draft_snapshot, write_case_data -from efile.services.existing_cases import CaseImportError, confirm_case, import_ready, load_case +from efile.services.existing_cases import ( + CaseImportError, + confirm_case, + import_ready, + load_case, + reusable_snapshot, +) from efile.services.filing_availability import draft_unavailable_message, unavailable_response from efile.services.filing_plans import link_case_to_plan, remember_case_for_plan from efile.workflow import ( @@ -39,14 +45,18 @@ def case_confirmation(request, jurisdiction): return redirect("case_lookup", jurisdiction=jurisdiction) availability_message = draft_unavailable_message(draft) - if availability_message and request.method == "POST" and request.POST.get("confirmed") == "yes": + confirming = request.method == "POST" and request.POST.get("confirmed") == "yes" + if availability_message and confirming: return unavailable_response(request, draft, availability_message) import_error = "" - if not availability_message and not (request.method == "POST" and request.POST.get("confirmed") != "yes"): - if not import_ready(draft): + if not availability_message and (request.method == "GET" or confirming): + # Reuse a recent preview rather than asking the court again on every + # reload; "Yes" on a partial or failed import is the Retry button. + retrying = confirming and draft.existing_case_snapshot.get("status") != "loaded" + if not import_ready(draft) and (retrying or not reusable_snapshot(draft)): try: - load_case(draft, get_tyler_token(request, jurisdiction), apply=False) + load_case(draft, get_tyler_token(request, jurisdiction)) except CaseImportError as error: import_error = str(error) if draft.existing_case_snapshot.get("status") == "partial": @@ -56,7 +66,7 @@ def case_confirmation(request, jurisdiction): ) if request.method == "POST": - if request.POST.get("confirmed") == "yes" and not import_error: + if confirming and not import_error: try: confirm_case(draft) except CaseImportError as error: @@ -73,19 +83,8 @@ def case_confirmation(request, jurisdiction): draft.save(update_fields=["current_step", "updated_at"]) return redirect(continue_url(draft, jurisdiction, return_to, WorkflowStepKey.DOCUMENT_CHECKLIST)) - if request.POST.get("confirmed") == "yes": - return render( - request, - "efile/case_confirmation.html", - { - "case": draft, - "import_error": import_error, - "is_logged_in": True, - "return_to": return_target(request), - **get_workflow_context(WorkflowStepKey.CASE_CONFIRMATION, jurisdiction, draft), - }, - status=422, - ) + if confirming: + return _render(request, jurisdiction, draft, import_error, availability_message, status=422) # Saying "this is not my case" about the case the plan proposed means # the plan is pointing at the wrong one, so it stops pointing anywhere. @@ -107,6 +106,10 @@ def case_confirmation(request, jurisdiction): ) return redirect(with_return_to(get_step_url(WorkflowStepKey.CASE_LOOKUP, jurisdiction), return_target(request))) + return _render(request, jurisdiction, draft, import_error, availability_message) + + +def _render(request, jurisdiction, draft, import_error, availability_message, status=200): context = { "is_logged_in": True, "filing_draft": draft_snapshot(draft), @@ -117,4 +120,4 @@ def case_confirmation(request, jurisdiction): "return_to": return_target(request), } context.update(get_workflow_context(WorkflowStepKey.CASE_CONFIRMATION, jurisdiction, draft)) - return render(request, "efile/case_confirmation.html", context) + return render(request, "efile/case_confirmation.html", context, status=status) diff --git a/efile_app/efile/views/parties.py b/efile_app/efile/views/parties.py index d6578a60..435cadbc 100644 --- a/efile_app/efile/views/parties.py +++ b/efile_app/efile/views/parties.py @@ -11,7 +11,6 @@ from efile.party_sides import PARTY_SIDE_LABELS from efile.services.current_drafts import ensure_current_draft from efile.services.drafts import draft_snapshot -from efile.services.existing_cases import import_ready from efile.services.extracted_parties import party_display_name from efile.services.people import ( NOT_A_PARTY, @@ -29,6 +28,7 @@ get_party_types, guess_filer_party_type, incomplete_parties, + missing_required_party_types, names_match, needs_amount_in_controversy, party_can_be_the_filer, @@ -78,8 +78,7 @@ def _continue_from_parties(request, jurisdiction, draft, party_types, return_to) """Fill in the court's required parties, then move on or collect the gaps.""" if draft.existing_case == ExistingCase.EXISTING: - covered = set(draft.parties.exclude(party_type="").values_list("party_type", flat=True)) - missing = [item["name"] for item in party_types if item["required"] and item["code"] not in covered] + missing = [item["name"] for item in missing_required_party_types(draft, party_types)] if missing: messages.error( request, @@ -161,7 +160,7 @@ def _save_filer_role(request, draft, filer, party_types): if draft.existing_case == ExistingCase.EXISTING: chosen = _chosen_filing_parties(request, draft) - if not chosen or any(p.source not in {"court", "added"} for p in chosen): + if not chosen or not all(p.on_case_roster for p in chosen): messages.error(request, "Choose the existing party you are filing for.") return False notice_email = request.POST.get("notice_email", "").strip() or filer.email @@ -219,9 +218,6 @@ def parties(request, jurisdiction): current_step=WorkflowStepKey.PARTIES, workflow_version=2, ) - if draft.existing_case == ExistingCase.EXISTING and not import_ready(draft): - messages.error(request, "Confirm the court case and load its parties before continuing.") - return redirect("case_confirmation", jurisdiction=jurisdiction) filer = FilingParty.objects.filter(draft=draft, role="filer").first() if filer is None: return redirect("your_information", jurisdiction=jurisdiction) @@ -242,12 +238,7 @@ def parties(request, jurisdiction): # two rows agree on the name; when they do not, replacing one name with # another is a question, and it is put below rather than done quietly. marked_self = self_claimed_party(draft) - if ( - marked_self is not None - and marked_self.source not in {"court", "added"} - and party_can_be_the_filer(marked_self) - and names_match(filer, marked_self) - ): + if marked_self is not None and party_can_be_the_filer(marked_self) and names_match(filer, marked_self): claim_party_as_filer(draft, marked_self) filer.refresh_from_db() @@ -261,11 +252,12 @@ def parties(request, jurisdiction): .values_list("sort_order", flat=True) .first() ) + joins_court_case = draft.existing_case == ExistingCase.EXISTING party = FilingParty.objects.create( draft=draft, role="other", - source="added" if draft.existing_case == ExistingCase.EXISTING else "manual", - source_case_id=draft.previous_case_id if draft.existing_case == ExistingCase.EXISTING else "", + source="added" if joins_court_case else "manual", + source_case_id=draft.previous_case_id if joins_court_case else "", sort_order=0 if last_order is None else last_order + 1, ) draft.current_step = WorkflowStepKey.PARTY_DETAILS @@ -285,11 +277,7 @@ def parties(request, jurisdiction): ) return redirect(f"{_parties_url(jurisdiction, return_to)}#your-role") name_choice = request.POST.get("name_choice", "") - if ( - party.source not in {"court", "added"} - and claim_replaces_a_name(filer, party) - and name_choice not in {"mine", "theirs"} - ): + if claim_replaces_a_name(filer, party) and name_choice not in {"mine", "theirs"}: # Replacing a differently-named party changes who the court is # told this case is about. Nobody should be able to do that by # clicking one button, so the screen asks first and this is @@ -301,7 +289,7 @@ def parties(request, jurisdiction): return redirect(_parties_url(jurisdiction, return_to)) claim_party_as_filer(draft, party, use_party_name=name_choice == "theirs") filer.refresh_from_db() - if party.source in {"court", "added"}: + if party.on_case_roster: messages.success( request, "You are linked to this party. Your account contact information stays separate." ) @@ -340,7 +328,7 @@ def parties(request, jurisdiction): # Whether claiming this row is confirming who you are or replacing # somebody with a different name. The two are not the same action # and the screen does not call them the same thing. - "replaces_a_name": party.source not in {"court", "added"} and claim_replaces_a_name(filer, party), + "replaces_a_name": claim_replaces_a_name(filer, party), "claimable": party_can_be_the_filer(party), } for party in FilingParty.objects.filter(draft=draft) diff --git a/efile_app/efile/views/review.py b/efile_app/efile/views/review.py index 6e9c5724..b793e4df 100644 --- a/efile_app/efile/views/review.py +++ b/efile_app/efile/views/review.py @@ -115,9 +115,7 @@ def case_review(request, jurisdiction): "fee_quote": fee_quote_summary(draft), "fee_inputs_token": fee_inputs_token(draft), "filer": filer, - "filer_court_party": next( - (party for party in other_parties if party.source in {"court", "added"} and party.is_self), None - ), + "filer_court_party": next((party for party in other_parties if party.on_case_roster and party.is_self), None), "existing_court_case": bool(draft.existing_case_snapshot), "parties": other_parties, # Who the filing is on behalf of, when that is not the filer. Worth diff --git a/efile_app/js-tests/filing-payload.test.js b/efile_app/js-tests/filing-payload.test.js index c0597758..5d642498 100644 --- a/efile_app/js-tests/filing-payload.test.js +++ b/efile_app/js-tests/filing-payload.test.js @@ -729,4 +729,15 @@ test("explicit new parties have no existing ID", () => { }); assert.equal(party.is_new, true); assert.equal(Object.hasOwn(party, "tyler_id"), false); +}); +test("an organization name wins over a stale first name", () => { + const party = makeHandler().partyFromDraft({ + source: "added", + id: 7, + first_name: "Stale", + organization_name: "Example LLC", + party_type: "defendant" + }); + assert.equal(party.person_type, "business"); + assert.equal(party.name.first, "Example LLC"); }); \ No newline at end of file