Phase 3: VISUAL feed and M-Files drawings in the worker
Migration 0004 (VISUAL and drawing caches, go-live ignored list, resource map, job tables, order-derived columns with provenance; work order key includes the operation sequence). The real worker on APScheduler with settings re-read each fire, pause, run now, advisory locks, job_runs and a heartbeat. The visual-feed job (five VisualKit reads, COPY and MERGE cache, suspect-batch guard, the apply in one transaction: drafts, change orders, retire-moved, work order gone, closed and cancelled observed, projects and units from order links, auto-release) and the fingerprint. The drawing-lookup job (queue, budget, download on version change, sha256 into assets, decision table, staging for rev approval, drawing-origin change orders). Clerical From VISUAL panel, flags, rev approval, hand uploads, change-order review; Admin Feed settings and resource map. Synthetic fixture generator and a fixture-driven worker in CI with a Playwright scenario. Docs updated. Verified: 3249 server tests and 13 Playwright scenarios on the docker-1 CI stack, 76 frontend tests, doctor at 0004; one feed and one drawing run against VISUAL DEV and the vault from the developer machine (counts in the changelog). Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
@@ -247,7 +247,7 @@ def test_v_open_cos(pg_settings: Settings) -> None:
|
||||
conn.execute("INSERT INTO co_required_depts (co_id, dept_code) VALUES (%s, 'WH'), (%s, '03')", (co, co))
|
||||
conn.execute(verify, (co, "WH", ids["user"], ids["sig"]))
|
||||
held = new_co(conn, pk, 2, "visual", "pending", hold=True)
|
||||
new_co(conn, pk, 3, "drawing", "pending", hold=False)
|
||||
new_co(conn, pk, 3, "manual", "pending", hold=False) # 0004: one pending feed CO, visual or drawing
|
||||
rejected = new_co(conn, pk, 4, "manual", "rejected", hold=True)
|
||||
conn.execute("INSERT INTO co_required_depts (co_id, dept_code) VALUES (%s, '03')", (rejected,))
|
||||
rows = conn.execute(
|
||||
@@ -276,6 +276,8 @@ def test_one_pending_visual_co_per_packet(pg_settings: Settings) -> None:
|
||||
new_co(conn, ids["packet"], 3, "visual", "approved")
|
||||
with pytest.raises(psycopg.errors.UniqueViolation):
|
||||
new_co(conn, ids["packet"], 4, "visual", "pending")
|
||||
with pytest.raises(psycopg.errors.UniqueViolation):
|
||||
new_co(conn, ids["packet"], 4, "drawing", "pending") # 0004: drawing-origin counts too
|
||||
with pytest.raises(psycopg.errors.UniqueViolation):
|
||||
new_co(conn, ids["packet"], 2, "manual", "approved")
|
||||
|
||||
@@ -367,3 +369,99 @@ def test_app_can_write_every_table_and_read_every_view(pg_settings: Settings) ->
|
||||
for priv in ("SELECT", "INSERT", "UPDATE", "DELETE"):
|
||||
allowed = one(conn, "SELECT has_table_privilege('ps_app', %s, %s)", (view, priv))
|
||||
assert allowed is (priv == "SELECT"), (view, priv)
|
||||
|
||||
|
||||
FEED_TABLES = [
|
||||
"visual_batches",
|
||||
"visual_rows",
|
||||
"visual_materials",
|
||||
"visual_orders",
|
||||
"visual_order_links",
|
||||
"visual_labor",
|
||||
"visual_row_changes",
|
||||
"visual_material_changes",
|
||||
"visual_ignored_wos",
|
||||
"visual_resource_map",
|
||||
"drawing_lookups",
|
||||
"job_requests",
|
||||
"job_runs",
|
||||
"job_schedule",
|
||||
"worker_heartbeat",
|
||||
]
|
||||
|
||||
|
||||
def test_feed_tables_and_app_grants(pg_settings: Settings) -> None:
|
||||
with db.connect_as("migrate", pg_settings, autocommit=True) as conn:
|
||||
names = [r[0] for r in conn.execute("SELECT name FROM schema_migrations ORDER BY name").fetchall()]
|
||||
assert "0004_feed.sql" in names
|
||||
with app_conn(pg_settings) as conn:
|
||||
for table in FEED_TABLES:
|
||||
for priv in ("SELECT", "INSERT", "UPDATE", "DELETE"):
|
||||
assert one(conn, "SELECT has_table_privilege('ps_app', %s, %s)", (table, priv)) is True, (table, priv)
|
||||
jobs = [r[0] for r in conn.execute("SELECT job FROM job_schedule ORDER BY job").fetchall()]
|
||||
assert jobs == ["drawing-lookup", "housekeeping", "visual-feed"]
|
||||
assert one(conn, "SELECT target FROM visual_resource_map WHERE resource = 'WELD-PART'") == "03"
|
||||
assert one(conn, "SELECT target FROM visual_resource_map WHERE resource = 'Electical'") == "14"
|
||||
assert one(conn, "SELECT count(*) FROM visual_resource_map WHERE confirmed") == 0
|
||||
with pytest.raises(psycopg.errors.CheckViolation):
|
||||
conn.execute("INSERT INTO visual_resource_map (resource, target) VALUES ('X', 'nowhere')")
|
||||
|
||||
|
||||
def test_feed_columns_are_text_ids(pg_settings: Settings) -> None:
|
||||
with app_conn(pg_settings) as conn:
|
||||
cols = dict(
|
||||
conn.execute(
|
||||
"SELECT table_name || '.' || column_name, data_type FROM information_schema.columns"
|
||||
" WHERE table_schema = 'public' AND column_name IN ('wo_base', 'lot', 'split', 'sub', 'seq', 'piece',"
|
||||
" 'part', 'cust_order_id', 'dwg')"
|
||||
).fetchall()
|
||||
)
|
||||
for name, kind in cols.items():
|
||||
col = name.split(".")[1]
|
||||
if col in ("seq", "piece"):
|
||||
assert kind == "integer", name
|
||||
else:
|
||||
assert kind == "text", name
|
||||
for t, c in (
|
||||
("packets", "visual_cancelled_at"),
|
||||
("packets", "wo_gone_at"),
|
||||
("packet_work_orders", "rev"),
|
||||
("projects", "order_ref"),
|
||||
("units", "customer_visual"),
|
||||
("units", "order_pin"),
|
||||
):
|
||||
assert (
|
||||
one(
|
||||
conn,
|
||||
"SELECT count(*) FROM information_schema.columns WHERE table_name = %s AND column_name = %s",
|
||||
(t, c),
|
||||
)
|
||||
== 1
|
||||
), (t, c)
|
||||
|
||||
|
||||
def test_two_operations_of_one_work_order_on_one_packet(pg_settings: Settings) -> None:
|
||||
"""Amendment 1: the key includes seq; hand rows (seq NULL) stay one per key."""
|
||||
with app_conn(pg_settings) as conn:
|
||||
ids = seed(conn)
|
||||
for seq in (10, 20):
|
||||
conn.execute(
|
||||
"INSERT INTO packet_work_orders (packet_id, wo_base, lot, split, sub, seq) "
|
||||
"VALUES (%s, 'L900-02', '1', '0', '0', %s)",
|
||||
(ids["packet"], seq),
|
||||
)
|
||||
with pytest.raises(psycopg.errors.UniqueViolation):
|
||||
conn.execute(
|
||||
"INSERT INTO packet_work_orders (packet_id, wo_base, lot, split, sub, seq) "
|
||||
"VALUES (%s, 'L900-02', '1', '0', '0', 20)",
|
||||
(ids["packet"],),
|
||||
)
|
||||
conn.execute(
|
||||
"INSERT INTO packet_work_orders (packet_id, wo_base, lot, split, sub) VALUES (%s, 'H1', '1', '0', '0')",
|
||||
(ids["packet"],),
|
||||
)
|
||||
with pytest.raises(psycopg.errors.UniqueViolation):
|
||||
conn.execute(
|
||||
"INSERT INTO packet_work_orders (packet_id, wo_base, lot, split, sub) VALUES (%s, 'H1', '1', '0', '0')",
|
||||
(ids["packet"],),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user