feat(backend): persist CasAdjustment rows from 835 SVC.adjustments

This commit is contained in:
Tyler
2026-06-19 23:07:48 -06:00
parent 6a65c2b750
commit cf2e52efb8
2 changed files with 136 additions and 1 deletions
+37 -1
View File
@@ -167,6 +167,12 @@ def _remittance_835_row(cp: ClaimPayment, batch_id: str) -> Remittance:
"""Build a Remittance ORM row from a ClaimPayment. NOT yet persisted."""
received_at = utcnow()
# Adjustment amount: sum the CAS rows for the first service line.
# NOTE: This is a best-effort placeholder used until the reconciliation
# pass (T10) overwrites it from the persisted CasAdjustment rows. The
# authoritative value comes from `reconcile.run()`, which sums
# ``CasAdjustment.amount`` per ``remittance_id`` and writes the result
# back to ``Remittance.adjustment_amount``. We keep this stub so the
# row has a sane value if reconciliation is disabled or fails.
adjustment = Decimal("0")
if cp.service_payments:
sp = cp.service_payments[0]
@@ -194,6 +200,27 @@ def _remittance_835_row(cp: ClaimPayment, batch_id: str) -> Remittance:
)
def _cas_adjustment_row(adj, remittance_id: str) -> "db.CasAdjustment":
"""Build a CasAdjustment ORM row from a ClaimAdjustment. NOT yet persisted.
One row per SVC-level CAS adjustment is persisted so the T10
reconcile aggregator can compute ``Remittance.adjustment_amount``
as ``SUM(CasAdjustment.amount) WHERE remittance_id = ...``.
``quantity`` is optional in the X12 CAS spec; we coerce to Decimal
only when present to keep the column NULL for the common no-QTY case.
"""
from cyclone.db import CasAdjustment
quantity = getattr(adj, "quantity", None)
return CasAdjustment(
remittance_id=remittance_id,
group_code=adj.group_code,
reason_code=adj.reason_code,
amount=Decimal(str(adj.amount)),
quantity=Decimal(str(quantity)) if quantity is not None else None,
)
# ---------------------------------------------------------------------------
# UI mappers: ORM rows → simpler UI types.
# ---------------------------------------------------------------------------
@@ -471,7 +498,16 @@ class CycloneStore:
cp.payer_claim_control_number, record.id,
)
continue
s.add(_remittance_835_row(cp, record.id))
remit_row = _remittance_835_row(cp, record.id)
s.add(remit_row)
# Flush so remit_row.id (FK target of cas_adjustments) is
# populated. SQLAlchemy assigns the PK on flush; without
# this the CasAdjustment inserts below would reference an
# unset id and violate the FK.
s.flush()
for svc in cp.service_payments:
for adj in svc.adjustments:
s.add(_cas_adjustment_row(adj, remit_row.id))
s.add(ActivityEvent(
ts=record.parsed_at,
kind="remit_received",