fix(api): regenerate raw_999_text in /api/acks/{id} detail response (SP3 P3 follow-up)
The P3 subagent shipped a Download button on /acks that fell back to 'no raw text' because the detail endpoint returned raw_json (the parsed model dump) but never the regenerated X12. Fix: detail endpoint now re-serializes raw_json via serialize_999 and returns it as raw_999_text, so the operator can actually download the 999 file. List endpoint unchanged (keeps payload small).
This commit is contained in:
@@ -971,6 +971,20 @@ def get_ack_endpoint(ack_id: int) -> dict:
|
|||||||
)
|
)
|
||||||
body = _ack_to_ui(row)
|
body = _ack_to_ui(row)
|
||||||
body["raw_json"] = row.raw_json
|
body["raw_json"] = row.raw_json
|
||||||
|
# Regenerate the X12 text from raw_json so the operator can download
|
||||||
|
# the actual 999 file. (SP3 P3 follow-up: list endpoint doesn't carry
|
||||||
|
# the regenerated text to keep payloads small; detail does.)
|
||||||
|
if row.raw_json:
|
||||||
|
try:
|
||||||
|
from cyclone.parsers.models_999 import ParseResult999
|
||||||
|
regenerated = ParseResult999.model_validate(row.raw_json)
|
||||||
|
icn = regenerated.envelope.control_number or "000000001"
|
||||||
|
body["raw_999_text"] = serialize_999(regenerated, interchange_control_number=icn)
|
||||||
|
except Exception as exc: # noqa: BLE001 — never 500 on a regen failure
|
||||||
|
log.warning("Could not regenerate 999 for ack %s: %s", ack_id, exc)
|
||||||
|
body["raw_999_text"] = None
|
||||||
|
else:
|
||||||
|
body["raw_999_text"] = None
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -108,3 +108,35 @@ def test_parse_999_rejected_fixture_surfaces_r_code(client: TestClient):
|
|||||||
assert body["ack"]["ack_code"] == "R"
|
assert body["ack"]["ack_code"] == "R"
|
||||||
assert body["ack"]["rejected_count"] == 1
|
assert body["ack"]["rejected_count"] == 1
|
||||||
assert body["ack"]["accepted_count"] == 0
|
assert body["ack"]["accepted_count"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_ack_detail_regenerates_x12_text(client: TestClient) -> None:
|
||||||
|
"""SP3 P3 follow-up: GET /api/acks/{id} must include `raw_999_text`
|
||||||
|
regenerated from raw_json so the operator can actually download the
|
||||||
|
999 file. The P3 subagent shipped `raw_999_text: None` as a stub."""
|
||||||
|
text = REJECTED.read_text()
|
||||||
|
post_resp = client.post(
|
||||||
|
"/api/parse-999",
|
||||||
|
files={"file": ("rejected_999.txt", text, "text/plain")},
|
||||||
|
headers={"Accept": "application/json"},
|
||||||
|
)
|
||||||
|
assert post_resp.status_code == 200
|
||||||
|
ack_id = post_resp.json()["ack"]["id"]
|
||||||
|
detail_resp = client.get(f"/api/acks/{ack_id}", headers={"Accept": "application/json"})
|
||||||
|
assert detail_resp.status_code == 200, detail_resp.text
|
||||||
|
body = detail_resp.json()
|
||||||
|
# raw_999_text must be present and non-empty X12.
|
||||||
|
assert "raw_999_text" in body
|
||||||
|
assert body["raw_999_text"] is not None
|
||||||
|
assert body["raw_999_text"].startswith("ISA*")
|
||||||
|
assert "AK1*" in body["raw_999_text"]
|
||||||
|
assert "AK9*" in body["raw_999_text"]
|
||||||
|
# IEA segment exists in output; serializer writes `IEA*1*<icn>~`.
|
||||||
|
assert "IEA*" in body["raw_999_text"]
|
||||||
|
assert body["raw_999_text"].rstrip().endswith("~")
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_ack_404_for_missing(client: TestClient) -> None:
|
||||||
|
"""GET /api/acks/{id} returns 404 for a missing id (not 500)."""
|
||||||
|
resp = client.get("/api/acks/9999", headers={"Accept": "application/json"})
|
||||||
|
assert resp.status_code == 404
|
||||||
|
|||||||
Reference in New Issue
Block a user