feat(sp27): dedup ack ID helpers — one copy in handlers/_ack_id.py

This commit is contained in:
Nora
2026-06-29 11:00:37 -06:00
parent 79fa30d018
commit 4592bca372
2 changed files with 130 additions and 45 deletions
+18 -45
View File
@@ -665,41 +665,21 @@ async def parse_835_endpoint(
# 999 ACK (Implementation Acknowledgment)
# --------------------------------------------------------------------------- #
def _ack_count_summary(result) -> tuple[int, int, int, str]:
"""Aggregate (received, accepted, rejected, ack_code) from a ParseResult999.
Counts are derived from the set-level ``IK5`` responses (one per
AK2 in the 999), not the functional-group ``AK9``. Gainwell's
MFT ships AK9 segments that contradict the per-set IK5
(e.g. ``AK9*A*1*1*1`` with ``IK5*A``), so trusting AK9's
rejected count would over-report rejections. The set-level
IK5 is the authoritative per-claim accept/reject signal.
"""
sets = result.set_responses
received = len(sets)
accepted = sum(1 for s in sets if s.set_accept_reject.code == "A")
rejected = received - accepted
if rejected == 0:
code = "A"
elif accepted == 0:
code = "R"
else:
code = "P"
return (received, accepted, rejected, code)
def _ack_synthetic_source_batch_id(interchange_control_number: str) -> str:
"""Return a synthetic batches.id for a received 999 with no source batch.
The acks.source_batch_id FK requires a row in batches; for received
999s we synthesize an id of the form ``999-<ISA13>``. The synthetic
row is NOT created in batches — the FK enforcement is a no-op in
SQLite without ``PRAGMA foreign_keys=ON`` (the project default for
tests). The dashboard never surfaces these synthetic ids; they exist
solely to satisfy the ORM contract.
"""
return f"999-{(interchange_control_number or '').strip() or '000000001'}"
# SP27 Task 6: ack ID helpers were deduped. Both api.py and scheduler.py
# used to define these locally. Canonical copies now live in
# ``cyclone.handlers._ack_id`` (set up in Task 1). The aliased imports
# below keep the existing callsites (``_ack_count_summary(result)``,
# ``_ack_synthetic_source_batch_id(icn)``, ``_277ca_synthetic_source_batch_id(icn)``)
# working unchanged.
from cyclone.handlers._ack_id import (
ack_count_summary as _ack_count_summary,
)
from cyclone.handlers._ack_id import (
ack_synthetic_source_batch_id as _ack_synthetic_source_batch_id,
)
from cyclone.handlers._ack_id import (
two77ca_synthetic_source_batch_id as _277ca_synthetic_source_batch_id,
)
@app.post("/api/parse-999", dependencies=[Depends(matrix_gate)])
@@ -913,16 +893,9 @@ async def parse_ta1_endpoint(
# --------------------------------------------------------------------------- #
def _277ca_synthetic_source_batch_id(interchange_control_number: str) -> str:
"""Return a synthetic ``batches.id`` for a received 277CA with no source batch.
Mirrors :func:`_ack_synthetic_source_batch_id`. The 277CA row's
``source_batch_id`` FK requires a row in batches; for received
277CAs we synthesize an id of the form ``277CA-<ISA13>``. The row
is NOT created in batches — same FK-is-no-op convention as the 999
path.
"""
return f"277CA-{(interchange_control_number or '').strip() or '000000001'}"
# (The _277ca_synthetic_source_batch_id helper was moved to
# cyclone.handlers._ack_id in SP27 Task 6; this alias import at the
# top of the file binds the name for any inline callsites below.)
@app.post("/api/parse-277ca", dependencies=[Depends(matrix_gate)])