drawings: VISUAL's rev is the source of truth; attach it when M-Files holds it Effective or Retired
ci / frontend (pull_request) Successful in 43s
ci / server (pull_request) Successful in 11m3s
ci / image (pull_request) Skipped
ci / e2e (pull_request) Successful in 3m52s

When M-Files' Effective rev differs from the rev VISUAL calls, the drawing
job looks that rev up (MFilesKit DrawingLookup.revision). If M-Files has
it as Effective or Retired, the job copies it into drawing_revisions and
attaches it to the draft with no flag and no Clerical approval, so the
draft can auto-release. Past draft, already attached or with a hand
drawing: unchanged, no flag and no change order.

Any other status, or no document at that rev, still flags rev_mismatch
with a note saying why. Packet Station only ever attaches Effective or
Retired drawings. rev_approve stays for that case only.

This replaces the "Use rev V as VISUAL calls it" choice from #46: the
use=called path in rev-approve and its button are removed. A packet that
already kept the rev that way keeps it, since that rev is now used anyway.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-19 16:05:57 -06:00
co-authored by Claude Opus 5.5
parent c5bf48ac50
commit ca407a2db3
16 changed files with 292 additions and 250 deletions
+3 -43
View File
@@ -22,6 +22,7 @@ from feedhelpers import (
run_drawings,
run_feed,
set_feed,
set_revisions,
)
from pkthelpers import SIGN, seed_users, tiny_pdf, upload
from pshelpers import ORIGIN_HEADERS, app_conn, needs_pg, sign_in
@@ -176,6 +177,7 @@ def test_batches_and_changes(client: TestClient, fx: Settings) -> None:
def test_rev_approve(client: TestClient, fx: Settings) -> None:
set_revisions(fx.fixture_dir, "L900-0201", None) # M-Files has no rev 2 at all: the mismatch stays flagged
run_feed(fx, 1)
run_drawings(fx)
with app_conn(fx) as conn:
@@ -221,49 +223,6 @@ def test_rev_approve(client: TestClient, fx: Settings) -> None:
assert p["drawing_state"] == "attached" and "rev_mismatch" not in p["flags"]
def test_rev_approve_keeps_the_rev_visual_calls(client: TestClient, fx: Settings) -> None:
run_feed(fx, 1)
run_drawings(fx)
with app_conn(fx) as conn:
pid = packet_id(conn, gen.KEY_TWO_OPS) # VISUAL rev 2 (Retired in M-Files), M-Files rev 3
nf = packet_id(conn, gen.KEY_NOT_FOUND)
body = {"reason": "Engineering says build lot 1 to rev 2", "sign": SIGN, "use": "called"}
as_user(client, "clerk.leo")
r = client.post(f"/api/packets/{nf}/rev-approve", json=body)
assert r.status_code == 409 and r.json()["code"] == "nothing_staged", r.text
r = client.post(f"/api/packets/{pid}/rev-approve", json={**body, "use": "oldest"})
assert r.status_code == 422, r.text
r = client.post(f"/api/packets/{pid}/rev-approve", json=body)
assert r.status_code == 200, r.text
with app_conn(fx) as conn:
p = packet_row(conn, pid)
assert p["drawing_state"] == "attached" and "rev_mismatch" not in p["flags"]
ok = p["visual"]["rev_ok"]
assert (ok["rev"], ok["vis_rev"], ok["use"], ok["latest"], ok["controlled"]) == (
"2",
"2",
"called",
"3",
"Retired",
)
assert p["visual"]["rev_confirmed"] is True
pages = drawing_pages(conn, pid)
assert pages and all(pg[1] == "2" for pg in pages)
log = conn.execute(
"SELECT note FROM packet_log WHERE packet_id = %s AND action = 'Rev VISUAL calls kept by hand'", (pid,)
).fetchall()
assert len(log) == 1 and "Retired in M-Files" in log[0][0] and "M-Files has rev 3" in log[0][0]
# the drawing job leaves the kept rev alone while M-Files' Effective rev is still 3
with app_conn(fx) as conn:
conn.execute("UPDATE drawing_lookups SET checked_at = now() - interval '2 days', next_check_at = now()")
run_drawings(fx)
with app_conn(fx) as conn:
p = packet_row(conn, pid)
assert p["drawing_state"] == "attached" and "rev_mismatch" not in p["flags"]
assert p["visual"]["rev_confirmed"] is True and p["visual"].get("called") is None
assert all(pg[1] == "2" for pg in drawing_pages(conn, pid))
# ---------------------------------------------------------------- hand uploads
@@ -325,6 +284,7 @@ def _hand_bom(client: TestClient, pid: int, data: bytes, name: str, **form: str)
@pytest.mark.parametrize("kind", ["xlsx", "csv"])
def test_hand_bom(client: TestClient, fx: Settings, kind: str) -> None:
set_revisions(fx.fixture_dir, "L900-0201", None) # M-Files has no rev 2: the mismatch stays flagged
run_feed(fx, 1)
run_drawings(fx)
with app_conn(fx) as conn: