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]>
This commit is contained in:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user