Build Seth's changes C1 to C11 (Gitea issues 1, 8, 18, 23, 24, 25, 27, 31, 32, 33, 36)
ci / frontend (pull_request) Successful in 57s
ci / server (pull_request) Successful in 12m33s
ci / image (pull_request) Skipped
ci / e2e (pull_request) Successful in 5m4s

- 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]>
This commit is contained in:
2026-10-30 13:33:51 -06:00
co-authored by Claude Opus 5.5
parent 1ae6819289
commit 1cd6688247
101 changed files with 3871 additions and 1098 deletions
+45 -32
View File
@@ -1,7 +1,7 @@
"""Phase 7 close and file through the API: the VISUAL close and the filing as every role, the feed's open work
orders (the note suffix, the 409 and the confirm), the data book filed with the seal, the reopen that keeps the
broken seal and its data book, the refile with a second seal, a failed data book build that files nothing, and a
hand packet with no feed state.
orders (a block on both routes since C5; the feed closes a VISUAL packet), the data book filed with the seal, the
reopen that keeps the broken seal and its data book, the refile with a second seal, a failed data book build that
files nothing, and a hand packet with no feed state.
The feed state is written by SQL in the shape feed/apply.py writes it. Nothing here reads VISUAL or M-Files."""
@@ -132,6 +132,7 @@ def test_vclosed_and_file_refused_outside_complete(shop: Shop) -> None:
def test_shipping_route_files_without_the_visual_close(shop: Shop) -> None:
"""A hand packet on the Shipping route: no feed state, no Clerical close."""
pkt = at_complete(shop, route="shipping")
r = shop("clerk.clara").post(f"/api/packets/{pkt['id']}/vclosed", json={"closed": True})
assert r.status_code in (403, 409) and r.json()["code"] in ("wrong_status", "precondition"), r.text
@@ -140,10 +141,18 @@ def test_shipping_route_files_without_the_visual_close(shop: Shop) -> None:
assert out["seal"]["databook"]["pages"] > 0
# ---------------------------------------------------------------- the feed's open work orders
# ---------------------------------------------------------------- the feed's open work orders (C5)
MSG_FEED_CLOSES = "The VISUAL feed marks this job closed when it sees every work order closed."
MSG_FEED_WAIT = "Filing waits until the VISUAL feed sees the job closed."
def test_vclose_check_and_the_open_wo_note(shop: Shop) -> None:
def open_msg(wos: str) -> str:
return f"VISUAL still shows work orders open on this packet: {wos}. Filing waits until VISUAL closes them."
def test_visual_packet_open_in_visual_cannot_be_closed_or_filed(shop: Shop) -> None:
"""C5: Clerical cannot mark a VISUAL packet closed; the open work orders block the file, and say why."""
pkt = at_complete(shop)
pid = pkt["id"]
set_feed_legs(shop.settings, pid, [leg("L670001", st="C"), leg("L670002", st="R")])
@@ -151,54 +160,58 @@ def test_vclose_check_and_the_open_wo_note(shop: Shop) -> None:
vc = view["vclose_check"]
assert vc["open_wos"] == ["L670002/001"] and vc["closed_at"] is None and vc["observed_at"] is not None
assert vc["agrees"] is True # neither closed
pkt = vclose(shop, view)
assert pkt["vclose_check"]["agrees"] is False
row = find_log(shop("clerk.clara"), pid, "Job closed in VISUAL")
assert row["note"] == "L670001, L670002 · VISUAL still shows open: L670002/001", row
assert "set_vclosed" not in view["can"] and "file" not in view["can"]
assert view["blocked"]["file"] == [open_msg("L670002/001")]
refused(shop("clerk.clara").post(f"/api/packets/{pid}/vclosed", json={"closed": True}), 403, "wrong_status")
refused(file_packet(shop, view), 403, "precondition", open_msg("L670002/001"))
def test_vclose_check_closed_by_the_feed(shop: Shop) -> None:
pkt = at_complete(shop)
pid = pkt["id"]
set_feed_legs(shop.settings, pid, [leg("L670001", st="C"), leg("L670002", st="C")], closed=True)
vc = fresh(shop("clerk.clara"), pid)["vclose_check"]
assert vc["open_wos"] == [] and vc["closed_at"] is not None and vc["agrees"] is False
pkt = vclose(shop, fresh(shop("clerk.clara"), pid))
assert pkt["vclose_check"]["agrees"] is True
assert find_log(shop("clerk.clara"), pid, "Job closed in VISUAL")["note"] == "L670001, L670002"
out = ok(file_packet(shop, pkt))
assert out["seal"]["open_wos"] is None
view = fresh(shop("clerk.clara"), pid)
vc = view["vclose_check"]
assert vc["open_wos"] == [] and vc["closed_at"] is not None and vc["agrees"] is True
assert view["vclosed"]["at"] is not None and view["vclosed"]["by"] is None # the feed, not a person
assert "file" in view["can"] and "file" not in view["blocked"]
out = ok(file_packet(shop, view))
assert out["status"] == "filed" and out["seal"]["open_wos"] is None
def test_vclose_check_before_the_feed_looked(shop: Shop) -> None:
pkt = at_complete(shop)
set_feed_legs(shop.settings, pkt["id"], None)
vc = fresh(shop("clerk.clara"), pkt["id"])["vclose_check"]
view = fresh(shop("clerk.clara"), pkt["id"])
vc = view["vclose_check"]
assert vc is not None and vc["observed_at"] is None and vc["open_wos"] == []
assert view["blocked"]["file"] == [MSG_FEED_WAIT]
def test_file_with_open_work_orders_needs_the_confirm(shop: Shop) -> None:
pkt = at_complete(shop)
@pytest.mark.parametrize("route", ["databook", "shipping"])
def test_file_blocked_while_visual_shows_work_orders_open(shop: Shop, route: str) -> None:
"""C5: a block on both routes, not a warning. The old confirm_open_wos is ignored. A close set before C5
(vclosed by hand on a VISUAL packet) does not pass while the feed shows work orders open."""
pkt = at_complete(shop, route=route)
pid = pkt["id"]
set_feed_legs(shop.settings, pid, [leg("L670001", st="R"), leg("L670002", lot="7A", st="R")])
pkt = vclose(shop, fresh(shop("clerk.clara"), pid))
with_hand_close = route == "databook"
if with_hand_close:
q1(shop.settings, "UPDATE packets SET vclosed_at = now() WHERE id = %s RETURNING id", (pid,))
pkt = fresh(shop("clerk.clara"), pid)
sigs = q1(shop.settings, "SELECT count(*) FROM signatures")[0]
body = refused(file_packet(shop, pkt), 409, "open_work_orders")
assert body["work_orders"] == ["L670001/001", "L670002/7A"]
assert body["detail"] == (
"VISUAL still shows 2 work orders open on this packet: L670001/001, L670002/7A. File anyway?"
)
msg = open_msg("L670001/001, L670002/7A")
refused(file_packet(shop, pkt), 403, "precondition", msg)
refused(file_packet(shop, pkt, confirm_open_wos=True), 403, "precondition", msg)
# nothing written, no signature taken
assert q1(shop.settings, "SELECT count(*) FROM signatures")[0] == sigs
assert q1(shop.settings, "SELECT count(*) FROM packet_seals WHERE packet_id = %s", (pid,))[0] == 0
assert fresh(shop("clerk.clara"), pid)["status"] == "complete"
refused(file_packet(shop, pkt, confirm_open_wos=False), 409, "open_work_orders")
out = ok(file_packet(shop, pkt, confirm_open_wos=True, note="Customer pickup today"))
assert out["status"] == "filed" and out["seal"]["open_wos"] == ["L670001/001", "L670002/7A"]
row = q1(shop.settings, "SELECT open_wos FROM packet_seals WHERE packet_id = %s", (pid,))
assert row[0] == ["L670001/001", "L670002/7A"]
note = find_log(shop("clerk.clara"), pid, "Filed in the data book")["note"]
assert note == "Customer pickup today · Filed with work orders open in VISUAL: L670001/001, L670002/7A", note
# the feed sees VISUAL closed: the packet files, on either route
set_feed_legs(shop.settings, pid, [leg("L670001", st="C"), leg("L670002", lot="7A", st="X")], closed=True)
out = ok(file_packet(shop, fresh(shop("clerk.clara"), pid), confirm_open_wos=True, note="Customer pickup today"))
assert out["status"] == "filed" and out["seal"]["open_wos"] is None
assert find_log(shop("clerk.clara"), pid, "Filed in the data book")["note"] == "Customer pickup today"
def test_cancelled_legs_do_not_count_as_open(shop: Shop) -> None: