T8.4 - CR-012: the delivery location is the shared vocabulary plus fifty feet

Staging is not the pain; the last fifty feet are - the correct floor lay-down,
shark cage or conduit tree instead of material picked at will by whoever is
closest. The Kitting & MIMO section gains:

- Delivery Building / Floor / Sector: the SAME dependent pickers CR-004 built,
  through the same fillLocSelect (which learned an optional field-map instead
  of being copied), reading the same project location lists, storing PATHS.
  A parallel free-text location vocabulary is exactly what CR-004 removed;
  none was added.
- A free-text detail field for the specifics ("Shark cage 7, conduit tree C"),
  persisted as delivDetail.
- deliveryLoc, the composed display string (labels off the shared lists, then
  the detail after a dash) - which is what the CR-011 email already reads
  (kitting_body preferred deliveryLoc from day one, with mimoLoc as the
  pre-CR-012 fallback) and what the package printout now carries as its own
  Delivery Location row.

Verification (each probe run alone): kitting_check.py extended to 26/26 (the
delivery selects are asserted to offer the SAME option list as the CR-004
trio, values persist as paths, the printout carries the composed value);
kitting_notify_check 17/17 now asserting the mail carries CR-012's composed
value, not the fallback. Regression: locations_check 58/58.

Items: CR-012

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 12:11:05 -07:00
parent 829fa22236
commit b9d5f8ef92
5 changed files with 99 additions and 8 deletions

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Kitting: statuses, the owner, the filter — CR-009 (T8.1) + CR-010 (T8.2).
"""Kitting: statuses, owner, filter, delivery — CR-009/CR-010/CR-012 (T8.1/2/4).
The statuses become an explicit five (Not Started / Picking / Staged /
In Transit / Delivered) - fulfillment states, not free text. A value stored
@@ -25,6 +25,7 @@ import cdp # noqa: E40
from browser_check import seed, start_server, chk, _PASS, _FAIL # noqa: E402
from sections_check import set_sop # noqa: E402
from stepper_check import dismiss_dialogs # noqa: E402
from qa_gate_check import api # noqa: E402
KIT = ["Not Started", "Picking", "Staged", "In Transit", "Delivered"]
@@ -186,6 +187,50 @@ def main():
page.eval("!!document.querySelector('[aria-label=%s]')"
% json.dumps("Filter by warehouse owner")))
# ── 5. CR-012: the delivery location (T8.4) ──────────────────────────
print(chr(10) + "5. CR-012: delivery on the shared lists")
code, b1 = api(base, "/api/projects/projA/locations", tok["root"], "POST",
{"level": "building", "name": "B-100"})
code, f1 = api(base, "/api/projects/projA/locations", tok["root"], "POST",
{"level": "floor", "parent_id": b1["id"], "name": "Level 3"})
code, s1 = api(base, "/api/projects/projA/locations", tok["root"], "POST",
{"level": "sector", "parent_id": f1["id"], "name": "Sector East"})
page.goto(base + "/wp-creation-index.html?project=projA")
dismiss_dialogs(page)
wait_creator(page)
settle(1.8)
page.eval("window.alert=()=>{}; window.confirm=()=>false; window.prompt=()=>null;")
opts = json.loads(page.eval(
"JSON.stringify([...document.getElementById('wp_deliv_building').options].map(o=>o.value))"))
wp_opts = json.loads(page.eval(
"JSON.stringify([...document.getElementById('wp_building').options].map(o=>o.value))"))
chk("the delivery building select is fed by the SAME shared list as CR-004's",
opts == wp_opts and len(opts) > 1, ascii_((opts, wp_opts)))
page.eval("document.getElementById('wp_deliv_building').value=%s; onDeliveryLocChange('building')"
% json.dumps(b1["path"]))
settle(0.3)
page.eval("document.getElementById('wp_deliv_floor').value=%s; onDeliveryLocChange('floor')"
% json.dumps(f1["path"]))
settle(0.3)
page.eval("document.getElementById('wp_deliv_sector').value=%s; onDeliveryLocChange('sector')"
% json.dumps(s1["path"]))
page.eval("document.getElementById('wp_deliv_detail').value='Shark cage 7, conduit tree C'")
got = json.loads(page.eval("""JSON.stringify((() => { const p = collectPackage();
return {b: p.delivBuilding, f: p.delivFloor, s: p.delivSector,
d: p.delivDetail, disp: p.deliveryLoc}; })())"""))
chk("the picked values persist as PATHS off the shared lists",
got["b"] == b1["path"] and got["f"] == f1["path"] and got["s"] == s1["path"],
ascii_(got))
chk("...the detail field persists", got["d"] == "Shark cage 7, conduit tree C")
chk("...and the composed display carries labels AND the detail",
"B-100" in got["disp"] and "Shark cage 7" in got["disp"], ascii_(got["disp"]))
page.eval("document.getElementById('wp_subject').value='deliv print'")
page.eval("document.getElementById('wp_type').value='Conduit Install'")
doc = page.eval("(() => { renderPackage(collectPackage()); "
"return document.getElementById('pkg-doc').innerHTML; })()")
chk("the delivery location prints on the package output",
"Delivery Location" in doc and "Shark cage 7" in doc, ascii_(doc, 120))
js_errors = [e for e in page.js_errors() if "beforeunload" not in e]
chk("no JavaScript errors anywhere in this run", not js_errors,
ascii_(js_errors[:2]))

View File

@@ -38,7 +38,9 @@ def ascii_(v, n=300):
def save_wp(base, tok, kit_status, extra=None):
data = {"constraints": [{"name": "Materials", "status": "cleared", "comment": ""}],
"kitStatus": kit_status, "kitOwner": "Sue", "kitOwnerId": "user_sue",
"distributionIds": ["user_pat"], "mimoLoc": "Staging 04, dock B"}
"distributionIds": ["user_pat"], "mimoLoc": "Staging 04, dock B",
# CR-012 (T8.4): the composed delivery location wins over mimoLoc
"deliveryLoc": "B-100 / Level 3 / Sector East — Shark cage 7"}
data.update(extra or {})
return api(base, "/api/wps", tok, "POST", {
"id": "wpKN1", "project_id": "projA", "number": "KN-1",
@@ -108,7 +110,8 @@ def main():
chk("the mail says old status, new status and who",
"In Transit" in body and "Delivered" in body and "Root" in body,
ascii_(body, 260))
chk("...and the delivery location", "Staging 04, dock B" in body)
chk("...and the delivery location - CR-012's composed value, not the "
"mimoLoc fallback", "Shark cage 7" in body and "Staging 04" not in body)
chk("...and a deep link to THAT package, not the app root",
"/wp-creation-index.html?project=projA&wp=wpKN1" in body)
chk("...in the house convention (greeting + automated-message footer)",