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:
@@ -107,13 +107,15 @@ def set_feed_legs(
|
||||
observed: bool = True,
|
||||
) -> None:
|
||||
"""Simulate the feed: the packet becomes a VISUAL packet (source_kind visual, a visual_key), packet_visual_state
|
||||
holds the legs as the feed writes them ({key: "base/lot/split/sub", seq, st}) and visual_closed_at is set when
|
||||
`closed`. `legs` None writes no state row (the feed has not looked yet)."""
|
||||
holds the legs as the feed writes them ({key: "base/lot/split/sub", seq, st}), and when `closed` the feed's
|
||||
close is set: visual_closed_at, and vclosed_at with no vclosed_by (C5: the feed is the writer of vclosed on a
|
||||
VISUAL packet). `legs` None writes no state row (the feed has not looked yet)."""
|
||||
with app_conn(settings) as conn:
|
||||
conn.execute(
|
||||
"UPDATE packets SET source_kind = 'visual', visual_key = coalesce(visual_key, %s),"
|
||||
" visual_closed_at = CASE WHEN %s THEN now() END WHERE id = %s",
|
||||
(f"test-{packet_id}-{secrets.token_hex(3)}", closed, packet_id),
|
||||
" visual_closed_at = CASE WHEN %s THEN now() END, vclosed_at = CASE WHEN %s THEN now() END,"
|
||||
" vclosed_by = NULL WHERE id = %s",
|
||||
(f"test-{packet_id}-{secrets.token_hex(3)}", closed, closed, packet_id),
|
||||
)
|
||||
if legs is None:
|
||||
conn.execute("DELETE FROM packet_visual_state WHERE packet_id = %s", (packet_id,))
|
||||
@@ -250,7 +252,7 @@ def rich_complete(shop: Shop, *, itp_doc: bool = False, mtrs: bool = True) -> di
|
||||
pkt = submit(shop, finish_stage(shop, pkt, welds=2))
|
||||
pkt = ok(step(shop("sup.wes"), pkt, "send_to_qc", book=True, spool=True, location="Bay 2", sign=SIGN))
|
||||
pkt = finish_inspection(shop, pkt)
|
||||
pkt = ok(accept(shop, pkt, "databook"))
|
||||
pkt = accept_with_open_short(shop, pkt, 3)
|
||||
add_doc(shop, pkt)
|
||||
r = make_qc_doc(shop, unit_id=unit_id, type="ITP" if itp_doc else "WPS / PQR", title="Unit folder doc")
|
||||
assert r.status_code in (200, 201), r.text
|
||||
@@ -261,6 +263,34 @@ def rich_complete(shop: Shop, *, itp_doc: bool = False, mtrs: bool = True) -> di
|
||||
return out
|
||||
|
||||
|
||||
def approve_receipts(shop: Shop, pkt: dict[str, Any], by: str = "sup.wes") -> dict[str, Any]:
|
||||
"""The department supervisor approves every received short not yet approved (C7). Returns the packet read."""
|
||||
for sh in pkt.get("shorts") or []:
|
||||
for r in sh["receipts"]:
|
||||
if r["approval"] is None:
|
||||
url = f"/api/packets/{pkt['id']}/shorts/receipts/{r['id']}/approve"
|
||||
pkt = ok(shop(by).post(url, json={"sign": SIGN}))
|
||||
return pkt
|
||||
|
||||
|
||||
def accept_with_open_short(shop: Shop, pkt: dict[str, Any], item: int) -> dict[str, Any]:
|
||||
"""Accept to the data book while item `item` keeps its open short: a packet completed before change C7, which
|
||||
the data book and the trace CSV still show. The receipts are approved through the API; the open short is closed
|
||||
by SQL for the accept only (C7 refuses an accept with an open short) and opened again after it."""
|
||||
pkt = approve_receipts(shop, pkt)
|
||||
sid = line_of(pkt, item)["short"]["id"]
|
||||
with app_conn(shop.settings) as conn:
|
||||
conn.execute("UPDATE bom_shorts SET closed_at = now() WHERE id = %s", (sid,))
|
||||
pkt = ok(accept(shop, fresh_read(shop, pkt["id"]), "databook"))
|
||||
with app_conn(shop.settings) as conn:
|
||||
conn.execute("UPDATE bom_shorts SET closed_at = NULL WHERE id = %s", (sid,))
|
||||
return fresh_read(shop, pkt["id"])
|
||||
|
||||
|
||||
def fresh_read(shop: Shop, pid: int) -> dict[str, Any]:
|
||||
return ok(shop("clerk.clara").get(f"/api/packets/{pid}"))
|
||||
|
||||
|
||||
def upload_bytes_pdf() -> bytes:
|
||||
"""A one-page PDF with its own bytes (the store dedupes equal bytes)."""
|
||||
from qchelpers import unique_pdf
|
||||
|
||||
Reference in New Issue
Block a user