WFDASH-WEBAPP/docs/pocketbase/raw_price_list.collection.json
SJCoetz 81c9f64158 feat(pricelist): notes field for AI instructions + file_name on upload
Operators can now type per-upload instructions for the AI extraction
step in the Pricelist upload form. The notes go into a new
`raw_price_list.notes` field that the n8n workflow reads and
injects into the LLM user message as per-upload context.

Common uses:
  - "Brand is JEI" → AI stamps every item with brand="JEI"
  - "Apply 30% markup" → Code node regexes this out for selling_price
  - "All prices in ZAR excl. VAT" → AI uses ZAR
  - "Page 4 has discount codes" → AI skips those rows

Also auto-populates `raw_price_list.file_name` (the original
browser-side filename) on upload — PB slugifies the upload to a
token stored inside supplier_file, so without this field the
operator saw the slugged version. Existing rows backfilled by the
admin script.

The `log_summary` field is now reserved for the AI's write-back
summary (set by the n8n workflow after extraction). It was
previously operator-editable on the form, which was wrong —
overwriting it clobbered the AI's output.

New migration: scripts/migrations/2026-06-30-add-pricelist-notes.ts
(idempotent, only adds `notes` if missing — `file_name` was
added manually via the PB admin UI before this commit).

doc/pricelist-extraction.md updated with the n8n LLM user message
shape, the markup regex, and the notes field's role in the pipeline.
2026-06-30 11:09:27 +02:00

148 lines
5.3 KiB
JSON

{
"_comment": "WFDASH — PocketBase collection for the Pricelist page. Holds raw supplier price-list files (PDF / CSV / text / image) before they're parsed into structured supplier pricing. Import via Settings → Import collections in the PB admin UI, or run scripts/migrations/2026-06-30-add-raw-price-list.ts to create it idempotently. The migration script also relaxes the auth rules to match the rest of the app (any authenticated user can CRUD) — the default for a fresh collection is superuser-only, which is too strict for the Pricelist page.",
"id": "pbc_raw_price_list_NEW",
"name": "raw_price_list",
"type": "base",
"listRule": "@request.auth.id != ''",
"viewRule": "@request.auth.id != ''",
"createRule": "@request.auth.id != ''",
"updateRule": "@request.auth.id != ''",
"deleteRule": "@request.auth.id != ''",
"fields": [
{
"autogeneratePattern": "[a-z0-9]{15}",
"hidden": false,
"id": "text_rpl_id",
"max": 15,
"min": 15,
"name": "id",
"pattern": "^[a-z0-9]+$",
"presentable": false,
"primaryKey": true,
"required": true,
"system": true,
"type": "text"
},
{
"hidden": false,
"id": "autodate_rpl_created",
"name": "created",
"onCreate": true,
"onUpdate": false,
"presentable": false,
"system": true,
"type": "autodate"
},
{
"hidden": false,
"id": "autodate_rpl_updated",
"name": "updated",
"onCreate": true,
"onUpdate": true,
"presentable": false,
"system": true,
"type": "autodate"
},
{
"_comment": "The actual file. Allowed types are whatever supplier pricelists come in as — PDFs, CSVs, plain text, and the common image formats. 50 MB cap matches quote_attachments.",
"hidden": false,
"id": "file_rpl_supplier_file",
"maxSelect": 1,
"maxSize": 52428800,
"mimeTypes": [
"application/pdf",
"text/csv",
"text/plain",
"image/png",
"image/jpeg",
"image/webp",
"image/gif",
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/octet-stream"
],
"name": "supplier_file",
"presentable": false,
"protected": false,
"required": false,
"system": false,
"thumbs": null,
"type": "file"
},
{
"_comment": "Original browser-side filename (e.g. \"Acme-Bolts-2026-Q2.pdf\"). PB slugifies the upload to a token stored inside supplier_file, so without this field the operator sees the slugged version in the table. Auto-populated by the WFDASH page on upload.",
"autogeneratePattern": "",
"hidden": false,
"id": "text_rpl_file_name",
"max": 0,
"min": 0,
"name": "file_name",
"pattern": "",
"presentable": true,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
},
{
"_comment": "Free-text operator notes passed to the AI extraction step as additional context. Examples: 'Brand is JEI. Apply 30% markup. All prices in ZAR excl. VAT. POA for any missing values.' Read by the n8n workflow's LLM user message and by the post-extraction Code node (which can regex out a 'markup: NN%' override).",
"autogeneratePattern": "",
"hidden": false,
"id": "text_rpl_notes",
"max": 0,
"min": 0,
"name": "notes",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
},
{
"_comment": "Relation to the suppliers collection. The live collection was created with this as a relation (not free text) so the supplier name is auto-rendered in the page's expand query — no client-side join needed. The picker on the upload form uses the existing SupplierSearch combobox.",
"cascadeDelete": false,
"collectionId": "pbc_3355664324",
"hidden": false,
"id": "relation_rpl_supplier_name",
"maxSelect": 1,
"minSelect": 0,
"name": "supplier_name",
"presentable": false,
"required": false,
"system": false,
"type": "relation"
},
{
"_comment": "Workflow status. Uploaded rows start as pending; the parser/AI bumps them through processing → completed, or → failed if it can't make sense of the file. Operator can override manually.",
"hidden": false,
"id": "select_rpl_status",
"maxSelect": 0,
"name": "status",
"presentable": false,
"required": false,
"system": false,
"type": "select",
"values": ["Pending", "Processing", "Completed", "Failed"]
},
{
"_comment": "Free-text log / summary. Whatever the operator wants to remember about the row — parser output, manual notes, \"called Acme, prices valid until Aug\", etc. The UI shows a truncated preview in the table and the full text in the detail dialog.",
"autogeneratePattern": "",
"hidden": false,
"id": "text_rpl_log_summary",
"max": 0,
"min": 0,
"name": "log_summary",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
}
],
"indexes": [
"CREATE INDEX `idx_rpl_supplier_name` ON `raw_price_list` (`supplier_name`)"
],
"system": false
}