WFDASH-WEBAPP/docs/superpowers/specs/2026-10-30-delivery-pool-design.md
Dave 45529731a7 feat(delivery-pool): assign invoiced deliveries to drivers, close with the signed copy
New /delivery-pool page, a clone of the Collection Pool for deliveries
with no pending stage. Invoiced lines are grouped per company, one card
per customer invoice; a card expands to its lines. Assign a driver and
day per invoice (or bulk per company); uploading the signed copy adds it
to every line, marks the lines completed + delivered_at, and closes a
delivery note once none of its lines is still open.

- Driver lives in new order_items.delivery_driver / delivery_assigned_at
  (npm run pb:migrate:delivery-pool), never drv_id (collection driver).
- Pool starts at DELIVERY_POOL_START (2026-10-30); lines whose DN is
  already completed/cancelled (POS counter sales) are left out.
- Service + /api/v1/delivery-pool routes (browser session); the service
  503s until the migration has run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 09:55:35 +02:00

4.9 KiB

Delivery Pool — design

Date: 2026-10-30 · Status: draft for Dave's approval

Why

The current /deliveries page tracks paper (To Load → Back from Driver → Checked In) but never says which driver has a delivery or when it goes out, and the signed copy is left to an n8n job. The office wants deliveries to work like the Collection Pool: one list of everything that must go out, assign it to a driver for a day, and close it when the signed copy comes back.

What it is (Dave's words)

  • A clone of the Collection Pool for deliveries, without the pending stage — everything in the pool is ready to go.
  • Grouped per company, and under each company one card per invoice number. Expanding a card shows the lines on that invoice.
  • Assign a card to a driver; the driver delivers; when the paper comes back the office uploads the signed copy and every line on that invoice becomes completed.

Goals

  • New page /delivery-pool ("Delivery Pool" in the sidebar).
  • Company sections (collapsible, count + total value), invoice cards inside.
  • Card shows: invoice number, DN number(s) if any, order number, line count, value ex VAT, driver + date badge (Unassigned / Today / future date).
  • Card actions: Assign / Reschedule driver (same dialog as the Collection Pool), Unassign, Upload signed copy, open invoice PDF.
  • Bulk select cards in a company section → assign them all to one driver/date.
  • Filter box (company / invoice / DN / order / item text) and a driver filter ("All / Unassigned / ").

Non-goals

  • The driver app (WFDASHDRIVER-APP) — drivers do not see deliveries there yet. Assigning is office-side. Separate follow-up.
  • Map / nearby sort (the Collection Pool's supplier map). Not in v1.
  • The old /deliveries page and its with_driver / checked_in rows stay as they are until the new pool is trusted; then it is retired.

Anti-goals

  • Must not overwrite the collection driver (order_items.drv_id, drv_collected_at) — that history feeds Collection Confirm and stats.
  • Must not drop un-invoiced lines off the Invoicing page.
  • Must not flood the pool with months of already-delivered invoices.

Rules

What is in the pool — an order_items line where all hold:

  • item_status = "invoiced"
  • customer_invoice_number != "" (the customer's QB invoice — NOT order_item_details.invoice.number, which Collection Confirm also uses for the supplier invoice)
  • invoiced_date >= DELIVERY_POOL_START (constant, set to go-live day)
  • delivered_at = "" (not yet delivered)
  • not exclude_from_invoice
  • its delivery note (if any) is not completed / cancelled — this drops POS counter sales, which close their DN with a signed copy at the till.

Card key = customer_invoice_number, normalised (30352 and INV30352 are the same invoice; shown as INV30352).

Company = the order's company; for lines without an order (POS) the DN's company; else order_item_details.invoice.invoiceCompany; else "Unknown company".

Lines on a DN but not yet invoiced (item_status = "delivery_note") are not in the pool — the card is per invoice. They join the pool when they are invoiced. (My call, from "per invoice number" — say if a DN-only delivery must go out before invoicing.)

Data

Two new fields on order_items (migration script npm run pb:migrate:delivery-pool, manual live step):

  • delivery_driver — relation → users, single
  • delivery_assigned_at — date

Existing fields reused: delivered_at (stamped when the signed copy is uploaded; currently set on only 2 rows), item_status → completed.

Item status does not change on assign — the line stays invoiced until the signed copy is in. Both new fields go in nothing noisy, so the audit hook records who assigned what.

Server

src/lib/server/delivery-pool-service.ts:

  • listDeliveryPool() → { companies: [{ id, name, invoices: [{ invoiceNumber, dnNumbers, orderNumbers, driverId, assignedAt, lines[], total }] }] }
  • assignInvoice({ invoiceNumber, driverId, date }) — writes both fields on every pool line of that invoice; empty driverId = unassign.
  • completeInvoice({ invoiceNumber, file }) — adds the file to each line's order_item_attachements (with addAttachmentForm, ADD never replace), stores it as signed_copy on each linked DN that has none and sets that DN completed, stamps delivered_at, sets lines completed.

Routes (browser-session auth, like the other office pages):

  • GET /api/v1/delivery-pool
  • PATCH /api/v1/delivery-pool/{invoice}/assign
  • POST /api/v1/delivery-pool/{invoice}/signed-copy (multipart)

Page

src/app/(app)/delivery-pool/ — page.tsx, components.tsx, view-model.ts (grouping, badges, filters — pure + tested), reusing AssignDriverDialog and /api/v1/users/lookup from the Collection Pool.

Left to the builder

Styling details, empty states, toasts, test layout.