Build Seth's changes C1 to C11 (Gitea issues 1, 8, 18, 23, 24, 25, 27, 31, 32, 33, 36)
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

- 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 16:36:49 -06:00
co-authored by Claude Opus 5.5
parent 150c507a31
commit 70a97398f1
101 changed files with 3871 additions and 1098 deletions
+38 -61
View File
@@ -1,8 +1,10 @@
"""The manufacturing data book, page for page after the old databook.js.
Sections, in order: 1 Drawings, 2 Material traceability, 3 Welding record, 4 Q.C. inspection, 5 Q.C. and test
documents, 6 Material test reports, 7 Record history. Front matter (cover and index) is ours; drawings, documents
and MTRs are copied at their own size; the heat and weld maps are rasters at the sheet's size.
Sections, in order: 1 Drawings (with the change order record when the packet has change orders), 2 Material
traceability, 3 Welding record, 4 Q.C. inspection, 5 Q.C. and test documents (the packet's documents, NCRs included),
6 Material test reports. Front matter (cover and index) is ours; drawings, documents and MTRs are copied at their own
size; the heat and weld maps are rasters at the sheet's size. The packet history pages are left out (change C6, Gitea
issue 8): the book is the one copy and goes to the customer. History stays readable in Packet Station.
A part that cannot be read (a missing file, a file that is not a PDF or an image, a pypdf error) becomes a warning row
in the index and in `Built.warnings`. Only a failure to assemble the whole book raises PdfError.
@@ -65,7 +67,6 @@ SECTION_NAMES = (
"Q.C. inspection",
"Q.C. and test documents",
"Material test reports",
"Record history",
)
@@ -240,7 +241,6 @@ class Book:
self.s4_qc()
self.s5_documents()
self.s6_mtrs()
self.s7_history()
size = self.drawing_size(live or void)
front_n = 2
@@ -314,6 +314,8 @@ class Book:
pg,
void=maps.void_text(pg, self.tz),
)
if p.get("change_orders"):
self.gen(1, "Change order record", self.change_orders)
# ------------------------------------------------------------ section 2
@@ -545,9 +547,9 @@ class Book:
)
y += 18
y = d.section_label("Station checklist", y)
items = checklists.checklist_for(
weld, p.get("drawing"), p.get("rev"), p.get("wps"), checklists.load_templates(self.conn)
)
# change C8: the stage's checklist frozen at START, else its department's live list
templates = checklists.stage_templates(self.conn, rec.get("id"), str(code))
items = checklists.checklist_for(weld, p.get("drawing"), p.get("rev"), p.get("wps"), templates)
ck = rec.get("checklist") or {}
body = []
for it in items:
@@ -750,75 +752,50 @@ class Book:
if not lines:
self.warn(6, "No MTRs attached in Packet Station — add from the warehouse files")
# ------------------------------------------------------------ section 7
# ------------------------------------------------------------ change orders (section 1)
def s7_history(self) -> None:
self.gen(7, "Packet record history", self.history)
def history(self) -> Doc:
def change_orders(self) -> Doc:
"""The change orders on this packet: who issued each, what it changed and who verified it. The packet's
history pages are not in the book (change C6); they stay readable in Packet Station."""
p = self.p
d = Doc(("Record History", "Section 6 • Manufacturing data book"))
d = Doc(("Change Order Record", "Section 1 • Manufacturing data book"))
y = 128.0
d.tx(
"Every action taken on this packet in Packet Station, oldest first, with the person who took it.",
"The change orders issued on this packet, with the departments that verified each one.",
L,
y,
size=8.5,
color=GREY,
)
y += 18
cos = p.get("change_orders") or []
if cos:
d.lbl("Change orders", L, y)
y += 8
body = []
for co in cos:
verified = co.get("verified") or {}
req = co.get("required") or []
ver = "\n".join(
f"{self.dept_name(dp)}: "
+ (
f"{parts.name_of(self.users, verified[dp].get('by'))} {self.d(verified[dp].get('at'))}"
if dp in verified
else "PENDING"
)
for dp in req
)
body.append(
[
f"CO-{co.get('co_no')}" + (f"\n{co['ecn']}" if co.get("ecn") else ""),
f"{self.dt(co.get('issued_at'))}\n{parts.name_emp(self.users, co.get('issued_by'))}",
co_summary(co),
co.get("reason") or "",
ver or "No departments to verify",
]
)
y = d.table(
["CHANGE", "ISSUED", "DOCUMENTS", "REASON", "VERIFIED BY"],
body,
y,
{0: {"w": 56, "bold": True}, 1: {"w": 96}, 4: {"w": 130}},
size=7.4,
)
y += 16
y = d.ensure_space(y, 40)
d.lbl("Record history", L, y)
y += 8
body = []
for e in parts.log_rows(self.conn, int(p["id"])):
who = str(e.get("by_name") or "")
if who and e.get("by") is not None:
who += f" ({e.get('emp_no') or '?'})"
role = parts.role_text(e.get("role"))
for co in p.get("change_orders") or []:
verified = co.get("verified") or {}
req = co.get("required") or []
ver = "\n".join(
f"{self.dept_name(dp)}: "
+ (
f"{parts.name_of(self.users, verified[dp].get('by'))} {self.d(verified[dp].get('at'))}"
if dp in verified
else "PENDING"
)
for dp in req
)
body.append(
[self.dt(e.get("at")), e.get("action") or "", who + (f"\n{role}" if role else ""), e.get("note") or ""]
[
f"CO-{co.get('co_no')}" + (f"\n{co['ecn']}" if co.get("ecn") else ""),
f"{self.dt(co.get('issued_at'))}\n{parts.name_emp(self.users, co.get('issued_by'))}",
co_summary(co),
co.get("reason") or "",
ver or "No departments to verify",
]
)
d.table(
["DATE / TIME", "EVENT", "BY", "DETAIL"],
["CHANGE", "ISSUED", "DOCUMENTS", "REASON", "VERIFIED BY"],
body,
y,
{0: {"w": 92}, 1: {"w": 150, "bold": True}, 2: {"w": 96}},
size=7.6,
{0: {"w": 56, "bold": True}, 1: {"w": 96}, 4: {"w": 130}},
size=7.4,
)
return d