Duplication happens per NOTE, not per batch. The same stack of Vula invoices was scanned three times in overlapping groupings — 13, 16 and 14 pages with three different file hashes, so upload-time hash dedup could not see it. Each of those batches mixes notes that promoted, notes duplicating another batch, and invoices unique to it: vdn074468 alone carries four that exist nowhere else. So no batch-level action is correct. Dismissing one wholesale either discards unique invoices or zeroes a counter for notes already on the ledger. Adds POST /api/dn-batches/[id]/notes/[noteId]/dismiss, which stands one note down, refuses to touch a promoted one, and parks the batch only once nothing is outstanding. Also fixes the batch dismiss route, which wrote notes_promoted: 0 unconditionally. A batch that had promoted 8 of 13 notes would report nothing while 8 delivery notes sat in the figures people pay from — the same board-versus-ledger disagreement already fixed in run.ts. It now counts what actually reached the ledger and says so in the reason. 929 tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
54 lines
1.7 KiB
Python
54 lines
1.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Adds "dismissed" to DN_BATCH_NOTES.status.
|
|
|
|
Duplication happens per note, not per batch. The same stack of Vula
|
|
invoices was scanned three times in overlapping groupings (13, 16 and 14
|
|
pages, three different file hashes), so every one of those batches holds
|
|
a mix of promoted notes, duplicates of another batch, and invoices unique
|
|
to it. Dismissing a whole batch therefore either discards unique
|
|
invoices or zeroes a counter for notes already on the ledger.
|
|
|
|
"dismissed" lets a single duplicate note stand down, which is the only
|
|
safe granularity.
|
|
|
|
Idempotent.
|
|
"""
|
|
import sys
|
|
|
|
from pb_env import PB, http, superuser_token
|
|
|
|
NEW_VALUE = "dismissed"
|
|
|
|
|
|
def main():
|
|
token = superuser_token()
|
|
print(f"\n→ dn_batch_notes on {PB}")
|
|
|
|
code, coll = http("GET", "/api/collections/dn_batch_notes", token=token)
|
|
if code != 200:
|
|
sys.exit(f"GET failed: {code} {coll}")
|
|
|
|
# Read the live field list and modify in place. PocketBase replaces
|
|
# the whole array on PATCH, so never build it from anything but a
|
|
# fresh read.
|
|
fields = list(coll.get("fields", []))
|
|
status = next((f for f in fields if f.get("name") == "status"), None)
|
|
if not status:
|
|
sys.exit("no status field on dn_batch_notes")
|
|
|
|
values = list(status.get("values") or [])
|
|
if NEW_VALUE in values:
|
|
print(f" • status already allows '{NEW_VALUE}'")
|
|
return
|
|
|
|
status["values"] = values + [NEW_VALUE]
|
|
code, resp = http("PATCH", f"/api/collections/{coll['id']}", {"fields": fields}, token)
|
|
if code != 200:
|
|
sys.exit(f"PATCH failed: {code} {resp}")
|
|
print(f" ✓ status values now {status['values']} (kept {len(fields)} fields)")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
print("\nDone.")
|