"""SP7 — integration test: reconcile.run() persists LineReconciliation rows. Spec §4.4. Architecture note: 837 service lines are stored inside ``Claim.raw_json["service_lines"]`` keyed by ``line_number`` (1-based). The ``LineReconciliation`` table therefore stores ``claim_service_line_number`` (int), not a FK to a separate service_lines table. """ from datetime import date, datetime, timezone from decimal import Decimal import json import uuid from sqlalchemy import select from cyclone.db import ( Batch, Claim, LineReconciliation, SessionLocal, ) from cyclone.reconcile import run from cyclone.store import _persist_835_remit, _remittance_835_row from cyclone.parsers.models_835 import ClaimPayment, ServicePayment def _setup_db() -> tuple: """Build a minimal 837 claim + 835 remit that share PCN. Returns (claim_id, batch_id).""" bid = str(uuid.uuid4()) claim_id = "CLM-T1" service_lines_json = [ { "line_number": 1, "procedure": {"qualifier": "HC", "code": "99213", "modifiers": []}, "charge": "100.00", "unit_type": "UN", "units": "1", "place_of_service": "11", "service_date": "2026-06-01", "provider_reference": None, }, { "line_number": 2, "procedure": {"qualifier": "HC", "code": "99214", "modifiers": []}, "charge": "100.00", "unit_type": "UN", "units": "1", "place_of_service": "11", "service_date": "2026-06-01", "provider_reference": None, }, ] with SessionLocal()() as s: # 1. Batch s.add(Batch( id=bid, kind="835", input_filename="x.835", parsed_at=datetime.now(timezone.utc), totals_json={"total_claims": 1, "total_paid": "150"}, validation_json={"passed": True, "warnings": [], "errors": []}, raw_result_json={"_": "stub"}, )) # 2. Claim with two service lines (in raw_json) s.add(Claim( id=claim_id, patient_control_number="PCN-1", charge_amount=Decimal("200.00"), provider_npi="1234567890", payer_id="SKCO0", service_date_from=date(2026, 6, 1), service_date_to=date(2026, 6, 1), state="submitted", batch_id=bid, raw_json={"service_lines": service_lines_json}, )) # 3. Remit with two SVC composites — line 1 matches 99213, line 2 # is 90837 (no claim match). cp = ClaimPayment( payer_claim_control_number="PCN-1", status_code="1", total_charge=Decimal("200.00"), total_paid=Decimal("150.00"), service_payments=[ ServicePayment( line_number=1, procedure_qualifier="HC", procedure_code="99213", charge=Decimal("100.00"), payment=Decimal("80.00"), units=Decimal("1"), service_date=date(2026, 6, 1), ), ServicePayment( line_number=2, procedure_qualifier="HC", procedure_code="90837", charge=Decimal("100.00"), payment=Decimal("70.00"), units=Decimal("1"), service_date=date(2026, 6, 1), ), ], ) remit = _remittance_835_row(cp, bid) s.add(remit) s.flush() _persist_835_remit(s, cp, remit.id) s.commit() return claim_id, bid def test_run_persists_line_reconciliation_rows(): claim_id, bid = _setup_db() with SessionLocal()() as s: result = run(s, bid) s.commit() assert result.matched == 1 lrs = list( s.execute( select(LineReconciliation).where(LineReconciliation.claim_id == claim_id) ).scalars().all() ) # Expect 3 rows: 99213 matched, 99214 unmatched_837_only, 90837 unmatched_835_only assert len(lrs) == 3 by_status = {lr.status for lr in lrs} assert by_status == {"matched", "unmatched_837_only", "unmatched_835_only"}