MEGAINFRA-WEBAPP/scripts/pb-add-dn-alias-keys.py
DEls d4ac4bd6e4 fix(dn-pipeline): key aliases on what actually identifies a product
The alias table shipped keyed on material_code alone. Live data says
that cannot work:

  - 16 of 24 purchase orders carry ONE material_code across every line.
    MEGCPO9239 is "513095" sixteen times.
  - Code "580070" spans 24 different products; "513080" spans 16.

An alias keyed on it would have resolved to every line on the order and
been permanently ambiguous — for Builders, the supplier whose truncated
descriptions need aliases most. I verified every PO item HAD a
material_code and never checked whether it discriminated.

Also found, by reading the source scan: the truncation is the supplier's
printer. Builders' form prints descriptions in a fixed ~20-character
column that wraps twice then stops ("Grip Fibreglass Meas" / "uring Tape
Orange a"). Extraction was faithful; nothing upstream can recover it.
But every line also carries an article number and a barcode, which are
never clipped and which we were discarding.

So:
  - PageItem gains product_code, and the prompt asks for the Art No /
    SKU / Prod Code column plus an explicit instruction to transcribe a
    clipped description verbatim rather than guess its ending.
  - Aliases record slip_product_code and po_description_key as well.
    Lookup tries product code before description; resolution tries the
    PO line description, then material_code ONLY when it narrows to a
    single line on that order, then falls through to text matching.
  - The resolve-item route no longer rejects a PO line for having no
    material_code, which was based on the same wrong assumption.

dn_item_aliases had 0 rows, so there was nothing to migrate.

916 tests. Live regression unchanged: 25 matched, 3 ambiguous, 3 none,
zero wrong; 0 of 24 POs vulnerable to nonsense.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 22:47:55 +02:00

61 lines
2.2 KiB
Python

#!/usr/bin/env python3
"""
Adds slip_product_code and po_description_key to DN_ITEM_ALIASES.
The collection shipped keyed on material_code alone. Live data says that
cannot work: 16 of 24 purchase orders carry a SINGLE material_code across
every line (MEGCPO9239 is "513095" sixteen times) and code "580070"
spans 24 different products, so such an alias resolves to every line on
the order and is permanently ambiguous.
Two extra keys fix it:
slip_product_code - the supplier's article number or barcode from the
slip. Never truncated by the printer, unlike the
description, so it is the strongest lookup key.
po_description_key - norm() of the chosen purchase order line's own
description, which identifies a specific line.
Safe to run before any aliases exist (there were none at the time this
was written), and idempotent after.
"""
import sys
from pb_env import PB, http, superuser_token
WANTED = {
"dn_item_aliases": [
{"name": "slip_product_code", "type": "text", "required": False},
{"name": "po_description_key", "type": "text", "required": False},
],
}
def main():
token = superuser_token()
print(f"\n→ {PB}")
for name, wanted in WANTED.items():
code, coll = http("GET", f"/api/collections/{name}", token=token)
if code != 200:
sys.exit(f"GET {name} failed: {code} {coll}")
# Read the live field list and APPEND. PocketBase replaces the
# whole array on PATCH, so never build it from anything but a
# fresh read.
fields = list(coll.get("fields", []))
have = {f.get("name") for f in fields}
add = [w for w in wanted if w["name"] not in have]
if not add:
print(f" • {name}: nothing to add")
continue
code, resp = http(
"PATCH", f"/api/collections/{coll['id']}", {"fields": fields + add}, token
)
if code != 200:
sys.exit(f"PATCH {name} failed: {code} {resp}")
for a in add:
print(f" ✓ {name}: added {a['name']} (kept {len(fields)} existing)")
if __name__ == "__main__":
main()
print("\nDone.")