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>
61 lines
2.2 KiB
Python
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.")
|