feat(sp25): add_*_ack publish ack_received / ta1_ack_received / two77ca_ack_received
This commit is contained in:
@@ -4,14 +4,47 @@ Each ACK type has its own ORM row (Ack / Ta1Ack / Two77caAck). The
|
||||
write methods are simple inserts; the list/get methods are simple
|
||||
queries. Fail-soft: persistence errors are logged but do not block
|
||||
parsing.
|
||||
|
||||
SP25: every write path publishes one event on the EventBus so the
|
||||
Acks page live-tail can subscribe — mirrors the existing
|
||||
``claim_written`` / ``remittance_written`` / ``activity_recorded``
|
||||
publish pattern (see ``cyclone.store.write``). Publish is best-effort:
|
||||
a failing subscriber MUST NOT roll back the persisted row.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from datetime import date
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from cyclone import db
|
||||
from cyclone.db import Ack
|
||||
|
||||
from . import utcnow
|
||||
from .ui import to_ui_ack, to_ui_ta1_ack, to_ui_two77ca_ack
|
||||
from .write import _sync_publish
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from cyclone.pubsub import EventBus
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _safe_publish(event_bus: "EventBus | None", kind: str, payload: dict) -> None:
|
||||
"""Best-effort publish wrapped in try/except.
|
||||
|
||||
Mirrors ``CycloneStore._publish_events_sync`` — never raises, never
|
||||
rolls back the persisted row. Falls back to skipping silently when
|
||||
the bus doesn't implement the private ``_sync_publish`` interface
|
||||
(e.g. test stubs).
|
||||
"""
|
||||
if event_bus is None:
|
||||
return
|
||||
try:
|
||||
_sync_publish(event_bus, kind, payload)
|
||||
except Exception: # noqa: BLE001
|
||||
log.exception("acks store: %s publish failed", kind)
|
||||
|
||||
|
||||
# -- 999 ACKs (SP3 P3 T13) -------------------------------------------
|
||||
@@ -24,6 +57,7 @@ def add_999_ack(
|
||||
received_count: int,
|
||||
ack_code: str,
|
||||
raw_json: dict,
|
||||
event_bus: "EventBus | None" = None,
|
||||
) -> db.Ack:
|
||||
"""Persist a 999 ACK row and return it.
|
||||
|
||||
@@ -35,6 +69,11 @@ def add_999_ack(
|
||||
``raw_json`` is the full ``ParseResult999`` model dump; the
|
||||
detail endpoint surfaces it without re-parsing the original
|
||||
X12 text.
|
||||
|
||||
SP25: when ``event_bus`` is provided, publishes one
|
||||
``ack_received`` event (with the full ``to_ui_ack`` row shape)
|
||||
after the row commits so the Acks page live-tail sees new 999s
|
||||
the moment they land.
|
||||
"""
|
||||
with db.SessionLocal()() as s:
|
||||
row = Ack(
|
||||
@@ -49,7 +88,14 @@ def add_999_ack(
|
||||
s.add(row)
|
||||
s.commit()
|
||||
s.refresh(row)
|
||||
return row
|
||||
row_id = row.id
|
||||
|
||||
if event_bus is not None:
|
||||
with db.SessionLocal()() as s:
|
||||
payload = to_ui_ack(s.get(Ack, row_id))
|
||||
_safe_publish(event_bus, "ack_received", payload)
|
||||
|
||||
return row
|
||||
|
||||
def list_acks() -> list[db.Ack]:
|
||||
"""Return every 999 ACK row, newest first (auto-increment id desc)."""
|
||||
@@ -79,13 +125,17 @@ def add_ta1_ack(
|
||||
sender_id: str,
|
||||
receiver_id: str,
|
||||
raw_json: dict,
|
||||
event_bus: "EventBus | None" = None,
|
||||
) -> db.Ta1Ack:
|
||||
"""Persist a TA1 (Interchange Acknowledgment) row and return it.
|
||||
|
||||
Mirrors :meth:`add_ack` for the lower-level envelope ack. The
|
||||
Mirrors :meth:`add_999_ack` for the lower-level envelope ack. The
|
||||
flat columns are promoted out of ``raw_json`` so the list
|
||||
endpoint can sort/filter without a JSON parse; the full
|
||||
``ParseResultTa1`` stays in ``raw_json`` for the detail endpoint.
|
||||
|
||||
SP25: when ``event_bus`` is provided, publishes one
|
||||
``ta1_ack_received`` event after the row commits.
|
||||
"""
|
||||
with db.SessionLocal()() as s:
|
||||
row = db.Ta1Ack(
|
||||
@@ -104,7 +154,14 @@ def add_ta1_ack(
|
||||
s.add(row)
|
||||
s.commit()
|
||||
s.refresh(row)
|
||||
return row
|
||||
row_id = row.id
|
||||
|
||||
if event_bus is not None:
|
||||
with db.SessionLocal()() as s:
|
||||
payload = to_ui_ta1_ack(s.get(db.Ta1Ack, row_id))
|
||||
_safe_publish(event_bus, "ta1_ack_received", payload)
|
||||
|
||||
return row
|
||||
|
||||
def list_ta1_acks() -> list[db.Ta1Ack]:
|
||||
"""Return every TA1 ACK row, newest first (auto-increment id desc).
|
||||
@@ -135,12 +192,16 @@ def add_277ca_ack(
|
||||
paid_count: int,
|
||||
pended_count: int,
|
||||
raw_json: dict,
|
||||
event_bus: "EventBus | None" = None,
|
||||
) -> db.Two77caAck:
|
||||
"""Persist a 277CA (Claim Acknowledgment) row and return it.
|
||||
|
||||
Mirrors :meth:`add_ack` but for the claim-level ack. The
|
||||
Mirrors :meth:`add_999_ack` but for the claim-level ack. The
|
||||
per-claim status detail stays in ``raw_json``; only the four
|
||||
counts are promoted so the list endpoint stays fast.
|
||||
|
||||
SP25: when ``event_bus`` is provided, publishes one
|
||||
``two77ca_ack_received`` event after the row commits.
|
||||
"""
|
||||
with db.SessionLocal()() as s:
|
||||
row = db.Two77caAck(
|
||||
@@ -156,7 +217,14 @@ def add_277ca_ack(
|
||||
s.add(row)
|
||||
s.commit()
|
||||
s.refresh(row)
|
||||
return row
|
||||
row_id = row.id
|
||||
|
||||
if event_bus is not None:
|
||||
with db.SessionLocal()() as s:
|
||||
payload = to_ui_two77ca_ack(s.get(db.Two77caAck, row_id))
|
||||
_safe_publish(event_bus, "two77ca_ack_received", payload)
|
||||
|
||||
return row
|
||||
|
||||
def list_277ca_acks() -> list[db.Two77caAck]:
|
||||
"""Return every 277CA ACK row, newest first (auto-increment id desc)."""
|
||||
|
||||
Reference in New Issue
Block a user