"""Shared SQL for the packet child rows: guarded row updates, the warehouse stage history, and the departments a packet touched (who verifies a change order). Every function here runs inside `packet_write`, so the packet row is already locked FOR UPDATE and child writes on the same packet serialise on it. """ from __future__ import annotations from collections.abc import Iterable, Mapping from typing import Any from psycopg import sql from psycopg.rows import dict_row from . import packets # Tables update_row may touch. The column names come from the handlers' fixed field lists, never from a request. TABLES = frozenset( {"bom_lines", "bom_shorts", "welds", "heat_tags", "packet_stages", "qc_inspections", "change_orders"} ) # Statuses past the warehouse: the packet has been in its department. IN_DEPT = frozenset({"ready", "assigned", "in_progress", "review", "qc", "complete", "filed"}) QC_STATUSES = frozenset({"qc", "complete", "filed"}) # Departments that never verify a change order: Clerical issues them, PM has no supervisor. NEVER_VERIFY = frozenset({"CL", "PM"}) def update_row( conn: Any, table: str, row_id: Any, values: Mapping[str, Any], *, bump: bool = True ) -> dict[str, Any] | None: """UPDATE one row by id with the given column values (and row_version + 1 when bump). Returns the new row.""" if table not in TABLES: raise ValueError(f"update_row: table {table!r} is not allowed") parts: list[sql.Composable] = [sql.SQL("{} = {}").format(sql.Identifier(k), sql.Placeholder()) for k in values] if bump: parts.append(sql.SQL("row_version = row_version + 1")) if not parts: raise ValueError("update_row: nothing to set") query = sql.SQL("UPDATE {} SET {} WHERE id = {} RETURNING *").format( sql.Identifier(table), sql.SQL(", ").join(parts), sql.Placeholder() ) with conn.cursor(row_factory=dict_row) as cur: r = cur.execute(query, (*values.values(), row_id)).fetchone() return dict(r) if r is not None else None def current_wh(conn: Any, packet_id: int) -> dict[str, Any] | None: return packets.one( conn, "SELECT *, stage::text AS stage FROM packet_wh WHERE packet_id = %s AND closed_at IS NULL", (packet_id,) ) def wh_move( conn: Any, packet_id: int, stage: str, by: Any, *, reason: str | None = None, note: str | None = None, auto: bool = False, ) -> None: """Close the current warehouse stage row and insert the new one.""" conn.execute("UPDATE packet_wh SET closed_at = now() WHERE packet_id = %s AND closed_at IS NULL", (packet_id,)) conn.execute( "INSERT INTO packet_wh (packet_id, stage, by, reason, note, auto) VALUES (%s, %s::wh_stage, %s, %s, %s, %s)", (packet_id, stage, by, reason, note, auto), ) def co_touched(conn: Any, row: Mapping[str, Any]) -> list[str]: """The departments a packet has touched, in the old coTouched() order, without Clerical and PM. WH once it left draft; its department once it reached the shop; every stage's department; QC once it reached Q.C. (status qc, complete or filed, a Q.C. history row, or any inspection). """ pid = row["id"] status = str(row["status"]) out: list[str] = [] def add(codes: Iterable[Any]) -> None: for c in codes: if c and str(c) not in out: out.append(str(c)) if status != "draft": add(["WH"]) if status in IN_DEPT: add([row["dept_code"]]) add(r[0] for r in conn.execute("SELECT dept_code FROM packet_stages WHERE packet_id = %s ORDER BY id", (pid,))) reached_qc = status in QC_STATUSES or bool( conn.execute( "SELECT EXISTS (SELECT 1 FROM qc_history WHERE packet_id = %s)" " OR EXISTS (SELECT 1 FROM qc_inspections WHERE packet_id = %s)", (pid, pid), ).fetchone()[0] ) if reached_qc: add(["QC"]) return [c for c in out if c not in NEVER_VERIFY] def active_depts(conn: Any, codes: Iterable[str]) -> set[str]: """The given codes that are active departments.""" want = sorted({str(c) for c in codes}) if not want: return set() return {r[0] for r in conn.execute("SELECT code FROM departments WHERE code = ANY(%s) AND active", (want,))}