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:
@@ -0,0 +1,64 @@
|
||||
import { fireEvent, screen, within } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { OrderRef, VisualOrder } from "../../api/types";
|
||||
import { DEPARTMENTS, makeMe, renderApp } from "../../test/auth";
|
||||
import { callsTo, mockFetch } from "../../test/fetchMock";
|
||||
import { makeProject, makeUnit } from "../../test/packets";
|
||||
|
||||
const ORDERS: VisualOrder[] = [
|
||||
{
|
||||
cust_order_id: "CO-100",
|
||||
status: "F",
|
||||
customer_name: "Test Co",
|
||||
customer_po_ref: "PO-1",
|
||||
order_date: "2026-09-01",
|
||||
desired_ship_date: "2026-12-01",
|
||||
promise_date: null,
|
||||
},
|
||||
{
|
||||
cust_order_id: "CO-101",
|
||||
status: "F",
|
||||
customer_name: "Test Co",
|
||||
customer_po_ref: "PO-2",
|
||||
order_date: "2026-09-15",
|
||||
desired_ship_date: null,
|
||||
promise_date: "2026-12-20",
|
||||
},
|
||||
];
|
||||
|
||||
const REF: OrderRef = { ...ORDERS[0]!, via: "project_open", chosen_at: "2026-10-16T08:00:00Z" };
|
||||
|
||||
describe("ProjectPage orders", () => {
|
||||
it("lists the VISUAL orders, marks the one in use, and pins another with a reason", async () => {
|
||||
const project = makeProject({ units: [makeUnit()], orders: ORDERS, order_ref: REF, order_pin: null });
|
||||
const fetch = mockFetch({
|
||||
"/api/auth/me": { body: makeMe() },
|
||||
"/api/departments": { body: DEPARTMENTS },
|
||||
"/api/projects/7": { body: project },
|
||||
"PATCH /api/projects/7": { body: { ...project, order_pin: "CO-101", version: 2 } },
|
||||
});
|
||||
renderApp("/clerical/projects/7");
|
||||
const table = await screen.findByRole("table", { name: "Orders in VISUAL" });
|
||||
expect(screen.getByText("first open order", { exact: false })).toBeInTheDocument();
|
||||
expect(within(table).getByText("used by the feed")).toBeInTheDocument();
|
||||
fireEvent.click(within(table).getByRole("button", { name: "Pin order CO-101" }));
|
||||
const ask = await screen.findByRole("dialog", { name: "Pin order CO-101" });
|
||||
fireEvent.change(within(ask).getByLabelText("Reason"), { target: { value: "Second order covers this unit" } });
|
||||
fireEvent.click(within(ask).getByRole("button", { name: "Pin" }));
|
||||
expect(await screen.findByText("Order CO-101 pinned")).toBeInTheDocument();
|
||||
expect(callsTo(fetch, "PATCH", "/api/projects/7")).toEqual([
|
||||
{ set: { order_pin: "CO-101", order_pin_reason: "Second order covers this unit" }, base: { order_pin: null } },
|
||||
]);
|
||||
});
|
||||
|
||||
it("shows no orders section for a project without VISUAL orders", async () => {
|
||||
mockFetch({
|
||||
"/api/auth/me": { body: makeMe() },
|
||||
"/api/departments": { body: DEPARTMENTS },
|
||||
"/api/projects/7": { body: makeProject({ units: [makeUnit()] }) },
|
||||
});
|
||||
renderApp("/clerical/projects/7");
|
||||
await screen.findByRole("heading", { name: "Units" });
|
||||
expect(screen.queryByRole("heading", { name: "Orders in VISUAL" })).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user