Files
spencermandClaude Opus 5.5 70a97398f1
ci / server (pull_request) Successful in 12m48s
ci / frontend (pull_request) Successful in 54s
ci / image (pull_request) Skipped
ci / e2e (pull_request) Successful in 4m38s
Build Seth's changes C1 to C11 (Gitea issues 1, 8, 18, 23, 24, 25, 27, 31, 32, 33, 36)
- C1 (issue 1): over target follows VISUAL ticket hours, not the timer.
- C2 (issue 24): a Q.C. reject always needs a reason; accept needs an NCR number after a reject without one.
- C3 (issue 25): an incomplete unit ITP blocks filing.
- C4 (issue 27): prints open the print dialog on the page, no new tab.
- C5 (issue 31): filing is blocked while VISUAL shows a work order open; the feed sets vclosed on VISUAL packets.
- C6 (issue 8): the data book leaves out the history pages.
- C7 (issue 18): the supervisor approves each received short; accept and filing wait for it.
- C8 (issue 23): per-department checklists, edited by the department supervisor or Admin, frozen at START.
- C9 (issue 32): Q.C. and Clerical may run the stamp check.
- C10 (issue 33): the PM screen and reports are for PS-ProjectMgmt and Admin only.
- C11 (issue 36): the Stall screen lists the packets assigned to the welder, at the supervisor's stall.

Migration 0011. Docs: PLAN (also issue 9 confirmed, issue 4 asked), POLICY, SECURITY, SCHEMA, FEED, README, CHANGELOG.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 16:36:49 -06:00

78 lines
3.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Change C2 (Gitea issue 24): a Q.C. reject always needs a detailed reason, and a packet rejected without an NCR
number cannot be accepted until the NCR number is given with the accept (QC_ACCEPT and QC_ACCEPT_AND_ROUTE).
The packet read says `ncr_required`; can() does not block on it (the NCR comes with the step); the server refuses
the step without it, 422 ncr_required. The accept stores the NCR on its inspection and in the log."""
from __future__ import annotations
from collections.abc import Iterator
import pytest
from fakedir import FakeDirectory
from pkthelpers import step
from pshelpers import needs_pg
from qchelpers import NCR, accept, at_qc, finish_inspection, reject, to_qc
from shophelpers import SIGN, Shop, fresh, last_log, ok, one, open_shop, refused
from packetstation.settings import Settings
pytestmark = needs_pg
REASON = "Weld 2 undercut too deep"
MSG = "Enter the NCR number of the reject first."
@pytest.fixture
def shop(pg_settings: Settings, fake_dir: FakeDirectory, dev_visual: None) -> Iterator[Shop]:
yield from open_shop(pg_settings, fake_dir)
def test_accept_after_a_reject_without_ncr_needs_one(shop: Shop) -> None:
pkt = at_qc(shop)
assert pkt["ncr_required"] is False
out = ok(reject(shop, pkt, REASON))
assert out["ncr_required"] is True and out["qc_last"]["ncr"] is None
pkt = finish_inspection(shop, to_qc(shop, fresh(shop("sup.wes"), pkt["id"])))
assert pkt["ncr_required"] is True
# can() does not block on it: the NCR is input with the step
assert {"qc_accept", "qc_accept_and_route"} <= set(pkt["can"]) and "qc_accept" not in pkt["blocked"]
refused(accept(shop, pkt), 422, "ncr_required", MSG)
refused(step(shop("qc.quinn"), pkt, "qc_accept_and_route", dept="09", sign=SIGN), 422, "ncr_required", MSG)
refused(step(shop("qc.quinn"), pkt, "qc_accept", routed_to="databook", ncr=" ", sign=SIGN), 422, "ncr_required")
refused(
step(shop("qc.quinn"), pkt, "qc_accept", routed_to="databook", ncr="NCR-26-1", sign=SIGN), 422, "invalid_ncr"
)
assert fresh(shop("qc.quinn"), pkt["id"])["status"] == "qc"
out = ok(step(shop("qc.quinn"), pkt, "qc_accept", routed_to="databook", ncr=" ncr-2026-0021 ", sign=SIGN))
assert out["status"] == "complete" and out["ncr_required"] is False
assert out["qc_last"]["result"] == "accepted" and out["qc_last"]["ncr"] == NCR
row = one(
shop.settings,
"SELECT ncr, result FROM qc_inspections WHERE packet_id = %s ORDER BY id DESC LIMIT 1",
(pkt["id"],),
)
assert tuple(row) == (NCR, "accepted")
log = last_log(shop("qc.quinn"), pkt["id"])
assert log["action"] == "Q.C. accepted → Clerical – Data Book"
assert log["note"] == f"Quinn QC [Q1] · 2 welds VT accepted · {NCR}", log
def test_accept_and_route_carries_the_ncr(shop: Shop) -> None:
pkt = at_qc(shop)
ok(reject(shop, pkt, REASON))
pkt = finish_inspection(shop, to_qc(shop, fresh(shop("sup.wes"), pkt["id"])))
out = ok(step(shop("qc.quinn"), pkt, "qc_accept_and_route", dept="09", ncr=NCR, sign=SIGN))
assert out["status"] == "ready" and out["ncr_required"] is False and out["qc_last"]["ncr"] == NCR
log = last_log(shop("qc.quinn"), pkt["id"])
assert log["note"] == f"Quinn QC [Q1] · from 03 Weld Parts · {NCR}", log
def test_a_reject_with_its_ncr_needs_none_at_accept(shop: Shop) -> None:
pkt = at_qc(shop)
out = ok(reject(shop, pkt, REASON, ncr=NCR))
assert out["ncr_required"] is False and out["return_note"] == f"{NCR} · {REASON}"
pkt = finish_inspection(shop, to_qc(shop, fresh(shop("sup.wes"), pkt["id"])))
out = ok(accept(shop, pkt))
assert out["status"] == "complete" and out["qc_last"]["ncr"] is None