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

262 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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):
```json
{
"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.