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:
@@ -170,6 +170,11 @@ class ReceiptBody(It):
|
||||
note: str | None = Field(default=None, max_length=2000)
|
||||
|
||||
|
||||
class ShortApproveBody(It):
|
||||
sign: SignBody | None = None
|
||||
note: str | None = Field(default=None, max_length=2000)
|
||||
|
||||
|
||||
class WeldBody(It):
|
||||
page: int | None = Field(default=None, ge=1, le=100_000)
|
||||
fx: float | None = Field(default=None, ge=0, le=1, allow_inf_nan=False)
|
||||
@@ -880,6 +885,75 @@ def receive_short(
|
||||
return _read(d, pp, packet_id)
|
||||
|
||||
|
||||
def _not_approved(conn: Any, receipt_id: int) -> None:
|
||||
if conn.execute("SELECT 1 FROM short_approvals WHERE receipt_id = %s", (receipt_id,)).fetchone():
|
||||
raise ApiError(409, "already_approved", "This receipt is already approved.")
|
||||
|
||||
|
||||
@router.post("/{packet_id}/shorts/receipts/{receipt_id}/approve", dependencies=WRITE)
|
||||
def approve_short_receipt(
|
||||
packet_id: int,
|
||||
receipt_id: int,
|
||||
body: ShortApproveBody,
|
||||
request: Request,
|
||||
p: Principal = Depends(current_principal), # noqa: B008
|
||||
d: Db = Depends(get_db), # noqa: B008
|
||||
) -> dict[str, Any]:
|
||||
"""The department supervisor approves one received short (C7): the part and its heat number match what the
|
||||
warehouse issued. One approval per receipt; a second answers 409 already_approved."""
|
||||
note = clean_opt(body.note, LIMITS["note"])
|
||||
pp = _pp(d, p)
|
||||
conn = d.conn
|
||||
receipt = packets.one(
|
||||
conn,
|
||||
"SELECT r.*, b.item FROM short_receipts r JOIN bom_shorts s ON s.id = r.short_id"
|
||||
" JOIN bom_lines b ON b.id = s.bom_line_id WHERE r.id = %s AND r.packet_id = %s",
|
||||
(receipt_id, packet_id),
|
||||
)
|
||||
if receipt is None:
|
||||
raise ApiError(404, "not_found", "No such short receipt on this packet.")
|
||||
_not_approved(conn, receipt_id) # before the signature: a second approval costs no attempt
|
||||
content = {
|
||||
"action": str(A.SHORT_APPROVE),
|
||||
"packet_id": packet_id,
|
||||
"receipt_id": receipt_id,
|
||||
"item": receipt["item"],
|
||||
"qty": qty_text(receipt["qty"]),
|
||||
"heat": receipt["heat"],
|
||||
"note": note,
|
||||
}
|
||||
with packet_write(
|
||||
d,
|
||||
p,
|
||||
packet_id,
|
||||
A.SHORT_APPROVE,
|
||||
request=request,
|
||||
it_reason=body.it_reason,
|
||||
sign=body.sign,
|
||||
sign_content=content,
|
||||
policy_principal=pp,
|
||||
) as w:
|
||||
_not_approved(conn, receipt_id) # again under the packet lock: a racing approval waits, then sees the first
|
||||
conn.execute(
|
||||
"INSERT INTO short_approvals (receipt_id, short_id, packet_id, dept_code, note, signature_id, by)"
|
||||
" VALUES (%s, %s, %s, %s, %s, %s, %s)",
|
||||
(
|
||||
receipt_id,
|
||||
receipt["short_id"],
|
||||
packet_id,
|
||||
w.state.dept,
|
||||
note,
|
||||
w.signature.id if w.signature else None,
|
||||
w.user_id,
|
||||
),
|
||||
)
|
||||
w.changed("bom.short")
|
||||
w.detail.update({"item": receipt["item"], "receipt_id": receipt_id})
|
||||
parts = [f"{qty_text(receipt['qty'])} pc", receipt["heat"] and f"heat {receipt['heat']}", note]
|
||||
w.log(f"Short approved · item {receipt['item']}", " · ".join(x for x in parts if x))
|
||||
return _read(d, pp, packet_id)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- welds
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user