feat(backend): harden T5 — divergence comment + invariant tests
This commit is contained in:
@@ -233,6 +233,12 @@ class Remittance(Base):
|
|||||||
String(32), ForeignKey("batches.id", ondelete="CASCADE"), nullable=False
|
String(32), ForeignKey("batches.id", ondelete="CASCADE"), nullable=False
|
||||||
)
|
)
|
||||||
payer_claim_control_number: Mapped[str] = mapped_column(String(64), nullable=False)
|
payer_claim_control_number: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||||
|
# ORM policy: no ON DELETE (forward FK from Remittance → Claim). A claim
|
||||||
|
# is unlikely to be deleted in normal flow (audit trail); if it ever is,
|
||||||
|
# the application layer (T7 reconciliation) must clear this FK. The
|
||||||
|
# migration predates this rationale; amendment deferred to post-SQLite
|
||||||
|
# rollout. SQLite without `PRAGMA foreign_keys=ON` does not enforce,
|
||||||
|
# so the divergence is benign in this engine.
|
||||||
claim_id: Mapped[Optional[str]] = mapped_column(
|
claim_id: Mapped[Optional[str]] = mapped_column(
|
||||||
String(64), ForeignKey("claims.id"), nullable=True
|
String(64), ForeignKey("claims.id"), nullable=True
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from datetime import date, datetime, timezone
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from sqlalchemy.exc import IntegrityError
|
||||||
|
|
||||||
from cyclone import db
|
from cyclone import db
|
||||||
from cyclone.db import Base, Batch, CasAdjustment, Claim, ClaimState, Remittance
|
from cyclone.db import Base, Batch, CasAdjustment, Claim, ClaimState, Remittance
|
||||||
@@ -216,3 +217,79 @@ def test_cas_adjustment_aggregation():
|
|||||||
.where(CasAdjustment.remittance_id == "CLP-1")
|
.where(CasAdjustment.remittance_id == "CLP-1")
|
||||||
).scalar_one()
|
).scalar_one()
|
||||||
assert total == Decimal("62.00")
|
assert total == Decimal("62.00")
|
||||||
|
|
||||||
|
|
||||||
|
def test_remittance_raw_json_round_trips_dict():
|
||||||
|
"""JSONText on Remittance.raw_json round-trips a dict, not a string."""
|
||||||
|
with db.SessionLocal()() as s:
|
||||||
|
s.add(Batch(
|
||||||
|
id="b1", kind="835", input_filename="x",
|
||||||
|
parsed_at=datetime(2026, 6, 19, tzinfo=timezone.utc),
|
||||||
|
))
|
||||||
|
r = Remittance(
|
||||||
|
id="CLP-1", batch_id="b1", payer_claim_control_number="CLP-1",
|
||||||
|
status_code="1",
|
||||||
|
received_at=datetime(2026, 6, 19, tzinfo=timezone.utc),
|
||||||
|
raw_json={"x12_segments": ["CLP", "NM1", "SVC"], "version": "005010X221A1"},
|
||||||
|
)
|
||||||
|
s.add(r)
|
||||||
|
s.commit()
|
||||||
|
|
||||||
|
with db.SessionLocal()() as s:
|
||||||
|
loaded = s.get(Remittance, "CLP-1")
|
||||||
|
assert loaded.raw_json == {"x12_segments": ["CLP", "NM1", "SVC"], "version": "005010X221A1"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_remittance_unique_constraint_rejects_duplicate_pcn():
|
||||||
|
"""UNIQUE (batch_id, payer_claim_control_number) blocks duplicates."""
|
||||||
|
with db.SessionLocal()() as s:
|
||||||
|
s.add(Batch(
|
||||||
|
id="b1", kind="835", input_filename="x",
|
||||||
|
parsed_at=datetime(2026, 6, 19, tzinfo=timezone.utc),
|
||||||
|
))
|
||||||
|
s.add(Remittance(
|
||||||
|
id="CLP-1", batch_id="b1", payer_claim_control_number="SAME",
|
||||||
|
status_code="1",
|
||||||
|
received_at=datetime(2026, 6, 19, tzinfo=timezone.utc),
|
||||||
|
))
|
||||||
|
s.commit()
|
||||||
|
|
||||||
|
with db.SessionLocal()() as s:
|
||||||
|
s.add(Remittance(
|
||||||
|
id="CLP-2", batch_id="b1", payer_claim_control_number="SAME",
|
||||||
|
status_code="2",
|
||||||
|
received_at=datetime(2026, 6, 19, tzinfo=timezone.utc),
|
||||||
|
))
|
||||||
|
with pytest.raises(IntegrityError):
|
||||||
|
s.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def test_remittance_cascade_delete_drops_cas_adjustments():
|
||||||
|
"""Deleting a Remittance drops its CAS rows via ORM cascade."""
|
||||||
|
with db.SessionLocal()() as s:
|
||||||
|
s.add(Batch(
|
||||||
|
id="b1", kind="835", input_filename="x",
|
||||||
|
parsed_at=datetime(2026, 6, 19, tzinfo=timezone.utc),
|
||||||
|
))
|
||||||
|
r = Remittance(
|
||||||
|
id="CLP-1", batch_id="b1", payer_claim_control_number="CLP-1",
|
||||||
|
status_code="1", total_paid=Decimal("62.00"),
|
||||||
|
received_at=datetime(2026, 6, 19, tzinfo=timezone.utc),
|
||||||
|
)
|
||||||
|
s.add(r)
|
||||||
|
s.flush()
|
||||||
|
s.add(CasAdjustment(remittance_id="CLP-1", group_code="CO",
|
||||||
|
reason_code="45", amount=Decimal("30.00")))
|
||||||
|
s.commit()
|
||||||
|
|
||||||
|
with db.SessionLocal()() as s:
|
||||||
|
r = s.get(Remittance, "CLP-1")
|
||||||
|
s.delete(r)
|
||||||
|
s.commit()
|
||||||
|
|
||||||
|
with db.SessionLocal()() as s:
|
||||||
|
from sqlalchemy import select
|
||||||
|
cas = s.execute(
|
||||||
|
select(CasAdjustment).where(CasAdjustment.remittance_id == "CLP-1")
|
||||||
|
).all()
|
||||||
|
assert len(cas) == 0
|
||||||
|
|||||||
Reference in New Issue
Block a user