MEGAINFRA-WEBAPP/scripts/pb-add-dn-batches.py

153 lines
7.1 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""
Creates the DN_BATCHES and DN_BATCH_NOTES collections, and adds the
batch back-references to DELIVERY_NOTES.
A batch is the uploaded file. Notes found inside it live in
DN_BATCH_NOTES as candidates until they pass every check — only then is
a DELIVERY_NOTES row created. That separation is the point: before it,
delivery_notes was both the inbox and the ledger, so a half-read
document sat inside the figures used to pay suppliers.
Idempotent — existing collections and fields are left alone.
"""
import sys
from pb_env import PB, http, superuser_token
def get_collection(token, name):
code, resp = http("GET", f"/api/collections/{name}", token=token)
return (resp if code == 200 else None)
def collection_id(token, name):
coll = get_collection(token, name)
if not coll:
sys.exit(f"collection {name} not found — cannot resolve its id")
return coll["id"]
def create_collection(token, payload):
existing = get_collection(token, payload["name"])
if existing:
print(f" • {payload['name']} already exists — skipping create")
return existing["id"]
code, resp = http("POST", "/api/collections", payload, token)
if code != 200:
sys.exit(f"CREATE {payload['name']} failed: {code} {resp}")
print(f" ✓ created {payload['name']}")
return resp["id"]
def add_fields(token, name, wanted):
coll = get_collection(token, name)
if not coll:
sys.exit(f"collection {name} not found")
fields = list(coll.get("fields", []))
existing = {f.get("name") for f in fields}
additions = [w for w in wanted if w["name"] not in existing]
if not additions:
print(f" • {name}: no new fields needed")
return
code, resp = http("PATCH", f"/api/collections/{coll['id']}", {"fields": fields + additions}, token)
if code != 200:
sys.exit(f"PATCH {name} failed: {code} {resp}")
for a in additions:
print(f" ✓ {name}: added {a['name']}")
def main():
token = superuser_token()
print(f"\n→ {PB}")
sites_id = collection_id(token, "sites")
pos_id = collection_id(token, "purchase_orders")
dns_id = collection_id(token, "delivery_notes")
batches_id = create_collection(token, {
"name": "DN_BATCHES",
"type": "base",
"fields": [
# PB system fields — required so `sort=created` / `sort=-created`
# queries work (the tick route and the inbox page both sort on
# these). PB manages their values internally once the field
# names match.
{"name": "created", "type": "date", "required": False, "onCreate": True},
{"name": "updated", "type": "date", "required": False, "onCreate": True, "onUpdate": True},
{"name": "file", "type": "file", "required": False, "maxSelect": 1, "maxSize": 20971520},
{"name": "file_hash", "type": "text", "required": False},
{"name": "site", "type": "relation", "required": True,
"collectionId": sites_id, "maxSelect": 1, "cascadeDelete": False},
{"name": "source", "type": "select", "required": False, "maxSelect": 1,
"values": ["web", "mobile", "email"]},
{"name": "uploaded_by", "type": "text", "required": False},
{"name": "status", "type": "select", "required": False, "maxSelect": 1,
"values": ["inbox", "analysing", "needs_attention", "processed"]},
{"name": "page_count", "type": "number", "required": False},
{"name": "notes_detected", "type": "number", "required": False},
{"name": "notes_promoted", "type": "number", "required": False},
{"name": "segmentation", "type": "json", "required": False, "maxSize": 2000000},
{"name": "checks", "type": "json", "required": False, "maxSize": 2000000},
{"name": "attempts", "type": "number", "required": False},
{"name": "claimed_at", "type": "date", "required": False},
{"name": "last_error", "type": "text", "required": False},
],
})
create_collection(token, {
"name": "DN_BATCH_NOTES",
"type": "base",
"fields": [
{"name": "created", "type": "date", "required": False, "onCreate": True},
{"name": "updated", "type": "date", "required": False, "onCreate": True, "onUpdate": True},
{"name": "batch", "type": "relation", "required": True,
"collectionId": batches_id, "maxSelect": 1, "cascadeDelete": True},
{"name": "note_index", "type": "number", "required": False},
{"name": "page_from", "type": "number", "required": False},
{"name": "page_to", "type": "number", "required": False},
{"name": "page_file", "type": "file", "required": False, "maxSelect": 1, "maxSize": 20971520},
{"name": "delivery_note_number", "type": "text", "required": False},
{"name": "po_number_raw", "type": "text", "required": False},
{"name": "po_number_normalised", "type": "text", "required": False},
{"name": "purchase_order", "type": "relation", "required": False,
"collectionId": pos_id, "maxSelect": 1, "cascadeDelete": False},
{"name": "gross_mass", "type": "number", "required": False},
{"name": "tare_mass", "type": "number", "required": False},
{"name": "nett_mass", "type": "number", "required": False},
{"name": "quantity", "type": "number", "required": False},
{"name": "unit", "type": "text", "required": False},
{"name": "items", "type": "json", "required": False, "maxSize": 2000000},
{"name": "checks", "type": "json", "required": False, "maxSize": 2000000},
{"name": "status", "type": "select", "required": False, "maxSelect": 1,
"values": ["candidate", "blocked", "promoted"]},
{"name": "delivery_note", "type": "relation", "required": False,
"collectionId": dns_id, "maxSelect": 1, "cascadeDelete": False},
],
})
add_fields(token, "delivery_notes", [
{"name": "batch", "type": "relation", "required": False,
"collectionId": batches_id, "maxSelect": 1, "cascadeDelete": False},
{"name": "page_from", "type": "number", "required": False},
{"name": "page_to", "type": "number", "required": False},
])
# Repair the standard system date fields on both new collections
# if they were created before this script knew to include them. The
# `create_collection` path only fires for from-scratch creates;
# add_fields is the path that heals an existing live collection.
add_fields(token, "dn_batches", [
{"name": "created", "type": "date", "required": False, "onCreate": True},
{"name": "updated", "type": "date", "required": False, "onCreate": True, "onUpdate": True},
])
add_fields(token, "dn_batch_notes", [
{"name": "created", "type": "date", "required": False, "onCreate": True},
{"name": "updated", "type": "date", "required": False, "onCreate": True, "onUpdate": True},
])
if __name__ == "__main__":
main()
print("\nDone.")