feat(sp7): persist ServiceLinePayment + link CAS rows on 835 ingest
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
"""SP7 — 835 ingest persists ServiceLinePayment rows + links CAS rows.
|
||||
|
||||
Spec §3.1, §3.3, T6.
|
||||
"""
|
||||
from datetime import date, datetime, timezone
|
||||
from decimal import Decimal
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from cyclone.db import (
|
||||
Batch,
|
||||
CasAdjustment,
|
||||
Remittance,
|
||||
ServiceLinePayment,
|
||||
SessionLocal,
|
||||
)
|
||||
from cyclone.parsers.models_835 import (
|
||||
ClaimAdjustment,
|
||||
ClaimPayment,
|
||||
ServicePayment,
|
||||
)
|
||||
from cyclone.store import _persist_835_remit, _remittance_835_row
|
||||
|
||||
|
||||
def _make_batch() -> str:
|
||||
bid = str(uuid.uuid4())
|
||||
with SessionLocal()() as s:
|
||||
s.add(Batch(
|
||||
id=bid, kind="835", input_filename="x.835",
|
||||
parsed_at=datetime.now(timezone.utc),
|
||||
totals_json={"total_claims": 1, "total_paid": "0"},
|
||||
validation_json={"passed": True, "warnings": [], "errors": []},
|
||||
raw_result_json={"_": "stub"},
|
||||
))
|
||||
s.commit()
|
||||
return bid
|
||||
|
||||
|
||||
def test_persist_835_creates_one_service_line_payment_per_svc_composite():
|
||||
bid = _make_batch()
|
||||
cp = ClaimPayment(
|
||||
payer_claim_control_number="PCN-SLP-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"),
|
||||
service_date=date(2026, 6, 1),
|
||||
adjustments=[ClaimAdjustment(group_code="CO", reason_code="45", amount=Decimal("20.00"))],
|
||||
),
|
||||
ServicePayment(
|
||||
line_number=2, procedure_qualifier="HC",
|
||||
procedure_code="99214", charge=Decimal("100.00"),
|
||||
payment=Decimal("70.00"),
|
||||
service_date=date(2026, 6, 1),
|
||||
adjustments=[],
|
||||
),
|
||||
],
|
||||
)
|
||||
with SessionLocal()() as s:
|
||||
remit = _remittance_835_row(cp, bid)
|
||||
s.add(remit)
|
||||
s.flush()
|
||||
_persist_835_remit(s, cp, remit.id)
|
||||
s.commit()
|
||||
|
||||
slps = list(
|
||||
s.execute(
|
||||
select(ServiceLinePayment).order_by(ServiceLinePayment.line_number)
|
||||
).scalars().all()
|
||||
)
|
||||
assert len(slps) == 2
|
||||
assert slps[0].procedure_code == "99213"
|
||||
assert slps[0].payment == Decimal("80.00")
|
||||
assert slps[1].procedure_code == "99214"
|
||||
assert slps[1].payment == Decimal("70.00")
|
||||
assert slps[0].remittance_id == remit.id
|
||||
|
||||
|
||||
def test_svc_level_cas_adjustments_link_to_service_line_payment():
|
||||
"""CAS rows that follow an SVC composite must point at the SLP."""
|
||||
bid = _make_batch()
|
||||
cp = ClaimPayment(
|
||||
payer_claim_control_number="PCN-SLP-2",
|
||||
status_code="1",
|
||||
total_charge=Decimal("100.00"),
|
||||
total_paid=Decimal("80.00"),
|
||||
service_payments=[
|
||||
ServicePayment(
|
||||
line_number=1, procedure_qualifier="HC",
|
||||
procedure_code="99213", charge=Decimal("100.00"),
|
||||
payment=Decimal("80.00"),
|
||||
adjustments=[ClaimAdjustment(group_code="CO", reason_code="45", amount=Decimal("20.00"))],
|
||||
),
|
||||
],
|
||||
)
|
||||
with SessionLocal()() as s:
|
||||
remit = _remittance_835_row(cp, bid)
|
||||
s.add(remit)
|
||||
s.flush()
|
||||
_persist_835_remit(s, cp, remit.id)
|
||||
s.commit()
|
||||
|
||||
cas = list(s.execute(select(CasAdjustment)).scalars().all())
|
||||
assert len(cas) == 1
|
||||
assert cas[0].service_line_payment_id is not None
|
||||
|
||||
slp = s.execute(
|
||||
select(ServiceLinePayment).where(
|
||||
ServiceLinePayment.id == cas[0].service_line_payment_id
|
||||
)
|
||||
).scalar_one()
|
||||
assert slp.procedure_code == "99213"
|
||||
|
||||
|
||||
def test_modifiers_persisted_as_json_array():
|
||||
bid = _make_batch()
|
||||
cp = ClaimPayment(
|
||||
payer_claim_control_number="PCN-SLP-3",
|
||||
status_code="1",
|
||||
total_charge=Decimal("100.00"),
|
||||
total_paid=Decimal("80.00"),
|
||||
service_payments=[
|
||||
ServicePayment(
|
||||
line_number=1, procedure_qualifier="HC",
|
||||
procedure_code="99213", modifiers=["25", "59"],
|
||||
charge=Decimal("100.00"), payment=Decimal("80.00"),
|
||||
adjustments=[],
|
||||
),
|
||||
],
|
||||
)
|
||||
with SessionLocal()() as s:
|
||||
remit = _remittance_835_row(cp, bid)
|
||||
s.add(remit)
|
||||
s.flush()
|
||||
_persist_835_remit(s, cp, remit.id)
|
||||
s.commit()
|
||||
|
||||
slp = s.execute(select(ServiceLinePayment)).scalar_one()
|
||||
import json as _json
|
||||
assert _json.loads(slp.modifiers_json) == ["25", "59"]
|
||||
|
||||
|
||||
def test_empty_service_payments_is_noop():
|
||||
"""A 835 with no SVC composites produces no ServiceLinePayment rows."""
|
||||
bid = _make_batch()
|
||||
cp = ClaimPayment(
|
||||
payer_claim_control_number="PCN-SLP-4",
|
||||
status_code="1",
|
||||
total_charge=Decimal("0.00"),
|
||||
total_paid=Decimal("0.00"),
|
||||
service_payments=[],
|
||||
)
|
||||
with SessionLocal()() as s:
|
||||
remit = _remittance_835_row(cp, bid)
|
||||
s.add(remit)
|
||||
s.flush()
|
||||
_persist_835_remit(s, cp, remit.id)
|
||||
s.commit()
|
||||
|
||||
slps = list(s.execute(select(ServiceLinePayment)).scalars().all())
|
||||
assert slps == []
|
||||
cas = list(s.execute(select(CasAdjustment)).scalars().all())
|
||||
assert cas == []
|
||||
Reference in New Issue
Block a user