"""Shared helpers: drive the API exactly the way the frontend does.""" from httpx import AsyncClient def hdr(email: str) -> dict[str, str]: return {"X-Dev-User": email} async def lookup_ids(client: AsyncClient, email: str) -> tuple[int, int]: r = await client.get("/api/lookups", headers=hdr(email)) assert r.status_code == 200, r.text body = r.json() return body["departments"][0]["id"], body["deviation_categories"][0]["id"] async def user_id_by_email(client: AsyncClient, as_email: str, email: str, role: str) -> int: r = await client.get(f"/api/users?role={role}", headers=hdr(as_email)) assert r.status_code == 200, r.text for u in r.json(): if u["email"] == email: return u["id"] raise AssertionError(f"user {email} with role {role} not found") async def create_ncr(client: AsyncClient, team: dict, **overrides) -> dict: dept_id, cat_id = await lookup_ids(client, team["requester"]) dispo_id = await user_id_by_email( client, team["requester"], team["dispo"], "disposition_authority" ) payload = { "job_number": "J12345", "department_id": dept_id, "deviation_category_id": cat_id, "disposition_authority_id": dispo_id, "deviation_detail": "Bore diameter out of tolerance on 3 pieces.", } payload.update(overrides) r = await client.post("/api/ncrs", json=payload, headers=hdr(team["requester"])) assert r.status_code == 201, r.text return r.json()["ncr"] async def do_initial_disposition( client: AsyncClient, team: dict, ncr_id: int, *, secondary: bool = False, secondary_ids: list[int] | None = None, as_user: str | None = None, notes: str = "

Rework per instructions.

", ): body = { "qc_authority": "AS9100 8.7", "work_order": "WO-1001", "disposition_notes": notes, "secondary_review_needed": secondary, } if secondary_ids is not None: body["secondary_authority_ids"] = secondary_ids return await client.post( f"/api/ncrs/{ncr_id}/initial-disposition", json=body, headers=hdr(as_user or team["dispo"]), ) async def to_operations(client: AsyncClient, team: dict, ncr_id: int) -> dict: """Walk a fresh NCR straight to the Operations stage.""" r = await do_initial_disposition(client, team, ncr_id, secondary=False) assert r.status_code == 200, r.text return r.json()["ncr"] async def to_qc_inspection(client: AsyncClient, team: dict, ncr_id: int) -> dict: await to_operations(client, team, ncr_id) r = await client.post( f"/api/ncrs/{ncr_id}/operations-complete", headers=hdr(team["ops"]) ) assert r.status_code == 200, r.text return r.json()["ncr"] async def to_costing(client: AsyncClient, team: dict, ncr_id: int) -> dict: await to_qc_inspection(client, team, ncr_id) r = await client.post( f"/api/ncrs/{ncr_id}/inspection", json={"qc_approval": "yes", "inspection_notes": "All good.", "qc_closed": True}, headers=hdr(team["qc"]), ) assert r.status_code == 200, r.text return r.json()["ncr"] async def answer_capa_no(client: AsyncClient, team: dict, ncr_id: int) -> dict: """Answer the API Q1 corrective-action gate with 'No' so the NCR can close.""" r = await client.post( f"/api/ncrs/{ncr_id}/capa", json={ "corrective_action_required": False, "corrective_action_justification": "Isolated incident; contained by disposition.", }, headers=hdr(team["qc"]), ) assert r.status_code == 200, r.text return r.json()["ncr"] async def to_closed(client: AsyncClient, team: dict, ncr_id: int) -> dict: await to_costing(client, team, ncr_id) await answer_capa_no(client, team, ncr_id) r = await client.post( f"/api/ncrs/{ncr_id}/costing", json={ "labor_cost": "125.50", "material_cost": "60.00", "service_cost": "0", "other_cost": "14.50", }, headers=hdr(team["cost"]), ) assert r.status_code == 200, r.text return r.json()["ncr"]