WFDASH-WEBAPP/docs/deliveries-n8n-workflow.md
SJCoetz 7a68cafe35 chore: add logos, opencode config and build assets
This commit includes the temporary Next.js build cache files, public brand
logos, and project configuration added in this change.
2026-07-27 08:21:20 +02:00

8.9 KiB
Raw Permalink Blame History

Deliveries — n8n Signed-Copy Workflow

Spec for the future n8n flow that closes out /deliveries items by uploading the scanned signed invoice/DN to PocketBase and flipping the order item to completed. Read this when you're ready to wire the workflow — every endpoint, payload shape, and field is defined below.

Status: NOT YET BUILT

This is a spec document, not a working integration. The page /deliveries ships with three tabs (To Load / Back from Driver / Checked In); the checked_in items sit there until n8n lands. The n8n workflow itself is owned by you, not by the app — this doc just defines the contract so the workflow can be built independently.

What it does

Once the office has typed the reference number on the signed paper (the checked_in tab in the app), the signed copy still needs to be:

  1. Scanned — by whatever scanner / phone-camera / IMAP inbox the office uses today.
  2. Uploaded to PocketBase on the right order_items row.
  3. Status flipped to completed so the office can stop looking at it.

The app exposes the PB schema fields for steps 2 + 3; n8n owns step 1.

API contract (everything the workflow needs)

Inputs the workflow must know

The workflow runs per signed copy, not per order. A single scan might cover 1 line or 20. To find the right order_items rows the workflow uses the reference number the operator typed at check-in time. The contract is:

workflow_in:
  reference: string  // e.g. "INV-12345" or "DN26071401"
  file: Buffer       // the scanned signed copy
  filename: string   // e.g. "INV-12345-signed.pdf"

1. Find the order_items

Hit the PocketBase list endpoint with the reference filter:

GET /api/collections/order_items/records?filter=delivery_reference%20%3D%20%22INV-12345%22&perPage=200

URL-decoded: filter=delivery_reference = "INV-12345"

PB's ~ is substring (case-insensitive); = is exact. Use exact — the reference is meant to be exact, and a fuzzy match risks pulling in the wrong customer's rows. If the workflow has only a partial reference (e.g. the scanner OCR'd "INV-1234" with the last digit missing), it should fail loudly rather than guess.

Response shape (each item):

{
  "id": "abc123def456ghi",
  "delivery_reference": "INV-12345",
  "checked_in_at": "2026-07-20T11:30:00Z",
  "checked_in_by": "user_id_here",
  "item_status": "checked_in"
}

⚠️ Only items with item_status = "checked_in" should be auto-completed. An item that's been typo'd and lives at with_driver or earlier in the lifecycle should be left alone — the office might re-route it.

Optional pre-flight guard:

GET /api/collections/order_items/records?filter=delivery_reference%20%3D%20%22INV-12345%22%20%26%26%20item_status%20%3D%20%22checked_in%22

This way the workflow only operates on rows that are genuinely ready to complete.

2. Upload the signed copy

The signed copy goes onto order_items.order_item_attachements (multi-file field, maxSelect: 10, accepts any MIME). This is the existing field for "all attachments on this line" — same field that delivery-note PDFs, supplier POs, etc. live on. No new collection, no new field.

Per item, do a single PATCH with the file. PocketBase's REST endpoint for file uploads on an existing record is:

PATCH /api/collections/order_items/records/{id}
Content-Type: multipart/form-data

--boundary
Content-Disposition: form-data; name="order_item_attachements"; filename="INV-12345-signed.pdf"
Content-Type: application/pdf

<bytes>
--boundary--

Naming convention: use the reference number as the filename stem ({reference}-signed.pdf or {.ext} as the scanner produces). The retrieval side — see "How the office sees it back" below — relies on the filename pattern to find the signed copy on a given item. Don't append timestamps or random suffixes.

3. Flip the status to completed

Same record, same endpoint, JSON body this time:

PATCH /api/collections/order_items/records/{id}
Content-Type: application/json

{ "item_status": "completed" }

No body auth header is needed if the workflow uses the admin PB token (recommended — see "Auth" below).

4. Repeat for each item in the batch

Loop over the items from step 1 and do steps 2 + 3 per item. A single scan = one batch = N items = N×(PATCH file + PATCH status) calls. PB doesn't have a batch endpoint; the per-item round trip is fine for typical batch sizes (1–20).

If the workflow needs to be idempotent (resume after a crash): check item_status on the response from step 3. If it's already completed, skip. If it's still checked_in, the PATCH probably failed — log + retry once.

Auth

The workflow should use the PocketBase superuser token, not a per-user token. The PB schema's rule is @request.auth.id != '' on the order_items update, so any authenticated principal works, but the superuser path is the simplest because:

  • It doesn't expire while the workflow is running.
  • It doesn't need refresh handling.
  • The signed copy is an automated upload, not a user action — per-user audit doesn't add anything.

To grab the token, use the same flow as the rest of the back-office n8n workflows: a single "Bootstrap" HTTP node at the top of the workflow that hits POST /api/collections/_superusers/auth-with-password with the admin email + password from the n8n credential store, then stores token in a workflow variable. Every subsequent HTTP node reads Authorization: Bearer {{$token}}.

If your PB instance is on the v0.23+ rename (admin collection is _superusers not _admins), see docs/migration-audit.md for the endpoint confirmation.

How the office sees it back

Once the file is uploaded, the office has two ways to find it:

  1. Inside the existing order detail view (the one at /orders/[id]) — the page already reads order_item_attachements. The new file just shows up alongside the others.
  2. Inside the /deliveries "Checked In" tab — when v2 of the page ships (out of scope for v1), it'll show a "View signed copy" link per row that points at PB's file URL. The app's api/v1/files/[collection]/[recordId]/[filename] route is the canonical way to stream a PB file back through Next.js with auth.

Filename contract

The workflow must use a deterministic filename per item so the retrieval side can find it without scanning the whole attachment list:

{reference}-signed{ext}

Examples:

  • INV-12345-signed.pdf
  • INV-12345-signed.png
  • DN26071401-signed.pdf

If the scanner produces something else, the workflow should rename it to this pattern before the upload — not rely on whatever the device names it. The reference is the only stable key; everything else is mutable.

Failures to handle

The workflow should treat these as "log + retry" rather than "throw and stop the whole run":

  • 0 items found for the reference — typo'd reference or a paper that was never typed into the office. Don't create a new item; surface the unmatched reference to a "needs review" sink (a separate logs collection or a Slack message — your call).
  • HTTP 5xx on the PATCH — PB blip. Retry once with a 2s backoff.
  • item_status came back as cancelled — the office cancelled the item in the meantime. Skip the upload + status flip; surface to "needs review".

The workflow should treat these as hard failures that abort the run:

  • More than 50 items for one reference — almost certainly a data bug. Don't auto-upload 200 files based on a single scan.
  • Auth token expired mid-run — re-bootstrap from the start.

Suggested n8n shape

If you want a starting point:

  1. Trigger: depends on the scanner. Common patterns:
    • Watch a dedicated IMAP folder (signed copies get emailed from the scanner).
    • Watch a folder (e.g. /shared/scanner/ on a Synology).
    • A webhook the office hits from a phone app.
  2. OCR / extract reference: if the scan is an image, OCR it (Tesseract via a Execute Command node, or one of the hosted OCR services) to read the reference number from the paper. Cross-check against the manually-typed reference from the office — if they disagree, route to "needs review".
  3. HTTP Bootstrap: get the PB superuser token.
  4. HTTP Find items: list by reference (step 1 above).
  5. Loop per item: upload + flip (steps 2 + 3 above).
  6. Log + cleanup: move the source file out of the watch folder, log success/failure.

Out of scope for the app

These are deliberately not part of the /deliveries page v1 and don't need any app change to support:

  • The actual scan ingestion (n8n).
  • The OCR (n8n).
  • The signed-copy viewer in the page (a future "Completed" tab once there's enough data to be worth showing).
  • A "Completed" tab on the page. When the workflow lands and item_status = "completed" rows accumulate, we can add a 4th tab. The schema already supports it — the page just doesn't render it yet.