Commit graph

15 commits

Author SHA1 Message Date
DEls
f72397259c feat(auth): add Office access and restrict Clock users 2026-10-07 14:22:57 +02:00
DEls
f5259d852f feat(auth): add PocketBase logins and site access controls 2026-09-28 23:51:47 +02:00
DEls
05c9b6b8f0 fix(dn): contain bulk batch lifecycle 2026-09-18 19:32:17 +02:00
DEls
00e82e51a5 fix(dn): close bulk upload consistency gaps 2026-09-18 19:28:56 +02:00
DEls
6e298990e4 fix(dn): harden bulk upload idempotency 2026-09-18 19:24:37 +02:00
DEls
e7e92fdae9 feat(dn): store bulk uploads as one batch 2026-09-18 19:19:05 +02:00
DEls
06311c6de2 fix(dn-batches): add uploaded_at so ordering works
PocketBase's created is empty on every dn_batches record, making the
board's -created sort a no-op and the tick's oldest-first meaningless.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 20:53:23 +02:00
DEls
a7c116c036 feat(delivery-notes): accept multi-file uploads (up to 50 per request)
Operators can now drop a folder of slips at once instead of uploading
them one at a time. Each file becomes its own inbox batch; the route
processes all of them in one request and returns per-file outcomes
(created / duplicate / error).

Backward compatible: single-file requests still get the legacy
response shape. New multi-file response shape:

  201 {
    results: Array<{ ok: true, batch_id, ... } |
    { ok: true, duplicate: true, ... } |
    { ok: false, error, filename }>,
    summary: { total, created, duplicates, errors }
  }

The UI input accepts multiple files; the result panel shows one row
per file with a colour-coded icon.

8 new tests cover: per-batch outcomes, >50 files rejected,
mixed MIME types, byte-identical duplicates within one request,
site lookup short-circuits before per-file work.
2026-09-17 18:46:51 +02:00
DEls
5e88ebe410 feat(delivery-notes): upload creates an inbox batch, not a delivery note 2026-09-17 14:31:22 +02:00
DEls
a3e4702159 refactor(schema): resolve project_code through site, not a stored copy
project_code was a text copy of the parent site's code, denormalised
onto 8 child collections that each already have a real site relation.
Written once at insert, never updated -- docs/data-model.md documents
the drift risk. On purchase_orders it never even held a real project
code: production had requisition numbers, site names, and the PO's own
number in that field.

Every read site now resolves the code through site/expand.site/siteById
instead, reusing each page's existing site map where one already
existed. Every create() call stops writing the column. The chat agent's
list_imports tool no longer reads data_import_batches -- it now reads
timesheets, fuel_sheets, plant_hours_sheets, purchase_orders and
delivery_notes filtered by processing_status, which was necessary before
the pool can be dropped (Task 20 would otherwise 404 the agent).

Also closed a Task 17 gap found while doing this: the agent tools' vehicle
and fuel-log handlers still resolved project code from the now-removed
vehicles.project_code column.

Wrote scripts/migrations/2026-09-15-drop-legacy-columns.py, the one
irreversible step in this whole restructure. Its guard against orphaning
fuel_logs rows checked the wrong condition -- "fuel_sheet is empty" --
which produced a false abort: 73 fuel_logs rows have no fuel_sheet
because they predate batch tracking entirely and never had a
batch_reference to begin with, so dropping data_import_batches costs
them nothing. Fixed the guard to check what it actually claims to check:
a row that HAD a batch_reference and was never relinked. Verified live
that no such row exists on any of the three affected collections.

Script run in dry-run only; not yet applied.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 16:01:14 +02:00
DEls
928ed26f7c refactor(delivery-notes): own the slip and the hash, drop the shadow batch
delivery_notes already creates its own row at upload time with the slip
attached, and n8n's OCR poller already reads delivery_notes directly by
processing_status=pending — it never looked at data_import_batches. The
batch row created alongside every upload was therefore a pure duplicate
write: same file, uploaded twice, read by nothing downstream. Move dedup
onto the DN row itself (it has file_hash from Task 1) and delete the
batch-tracking code paths in both the upload route and the OCR-result
webhook.

The plan's suggested dedup filter/sort used `created`, but this PB
instance has no usable created/updated autodate fields on delivery_notes
(sort=-created 400s here, as already noted for purchase_orders and
flagged by Task 6). Used date_received instead, matching the field the
rest of the app already sorts this collection by (src/lib/agent/load.ts).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 13:48:30 +02:00
DEls
78fa8da05c feat(delivery-notes): dedup by SHA-256 at upload time
Mirrors the existing /api/purchase-orders/upload pattern onto the
delivery-note path so byte-identical re-uploads short-circuit with
`200 + { duplicate: true, existing_dn_id, ... }` instead of
creating a second delivery_notes row.

Concretely:
- Hash the bytes with SHA-256 before any PB round-trip.
- Query `data_import_batches` for any processed batch with the same
  hash inside a 30-day window (the same window the PO route uses; the
  underlying `idx_batches_file_hash` index already covers it).
- On hit, return the existing DN + batch ids and skip creation. The
  n8n OCR pipeline is never invoked for a duplicate slip, so a
  double-clicking operator costs us nothing.
- On miss, stamp `file_hash` on the new batch row so subsequent
  re-uploads can dedup against it.
- Wire the response through `UploadDeliveryNoteButton` as a new
  'duplicate' panel showing the existing DN id plus a quick link to
  /dashboard/purchasing where the awaiting-PO inbox lives.

pb-types: adds file_hash?: string to DataImportBatchRecord so the
column the PB schema already has is reflected in the TS shape.
2026-09-07 14:49:33 +02:00
DEls
1606bd2cc7 feat(delivery-notes): own batch lifecycle in OCR pipeline
- upload: create data_import_batches row when DN is uploaded
  (batch_type=delivery_notes, processed_record_id=<dnId>)
- ocr-result: mark the batch processed on success, failed/dead on
  failure (uses nextBatchState from batch-state.ts for retries)
- new /api/delivery-notes/[id]/slip proxy route: lets n8n fetch the
  slip file via shared secret instead of embedding the PocketBase
  admin JWT in workflow headers (which was leaking in the workflow JSON)

Backfill: existing pending DN ekko6xkhynoekas needs a manually
created data_import_batches row until it's reprocessed.
2026-08-26 15:50:44 +02:00
DEls
8dbf393446 feat(delivery-notes): enhance delivery note upload and processing
- Updated the delivery note upload API to include site ID for better project association.
- Modified the delivery note schema to support additional fields such as processing status, OCR confidence, and received by name.
- Enhanced the UI components for uploading delivery notes to allow file drop functionality and improved user feedback during file selection.
- Refactored purchasing page to integrate delivery notes that require review, providing better visibility and management of pending items.
- Added new KPIs to track active purchase orders and fulfillment percentages, improving overall dashboard insights.
2026-08-26 15:33:30 +02:00
DEls
20a16a27f8 Enhance Dashboard and Sidebar Navigation
- Updated the Dashboard page to improve card styling with a warm-amber glow strip and adjusted icon presentation for better visual appeal.
- Expanded the Sidebar navigation by adding new entries for "Sites," "Workforce," "Plant & Equipment," "Fleet & Fuel," and "Purchasing," each with corresponding icons and descriptions to enhance user navigation and access to key features.
2026-08-19 01:54:16 +02:00