Commit graph

99 commits

Author SHA1 Message Date
DEls
7d755961f4 docs(dn): Inbox 3 build plan
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 13:28:24 +02:00
DEls
841947a161 docs(dn): Inbox 3 matching screen design
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 13:25:54 +02:00
DEls
9f0fce988e feat(cash-floats): petty-cash cards on users, one log, clock Slips tab
Each cash float card lives on its holder's user record (card number, last
4-6 digits of the physical card, low limit, assigned date). Every movement
goes in one cash_float_log collection: opening, top-up, slip, handover and
correction rows with signed amounts. Balances are always summed from the log.

- Management -> Cash Floats: cards table (lowest balance first), add, edit,
  top up, hand over, correct balance, retire, movements sheet with slip
  photos and CSV export. Admin-only.
- Settings -> Users: card section in the edit form and the balance per holder.
- Clock app: Slips tab, shown only to a user who holds a card. Photo, site,
  supplier and amount; the slip comes off that user's own card.
- Sidebar needs-attention mark when a card is under its limit.
- Slips count as site spend ("Cash float slips" on the site page).
- Migration scripts/migrations/2026-10-24-cash-floats.py (already applied to
  production on 2026-10-24).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 13:17:06 +02:00
DEls
a96f35366d feat(management): store planning data in PocketBase with editable income, add job and forecast vs actual chart
Move the Management planning snapshots into PocketBase collections
(plans, projects, per-year project plans, retention, edit log), loaded
with a bundled-JSON fallback. Admins can double-click an income cell to
enter forecast and actual, edit awarded jobs' planned amounts, change
status and costs, and add jobs, with revision checks and an audit trail.
Income chart now shows forecast and invoiced bars with not-yet-started
money stacked in red; Income is in the sidebar. Adds schema and import
scripts, and jsdom/Testing Library dev dependencies for UI tests.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 03:04:01 +02:00
DEls
0a7138f60c fix: harden workforce, plant and financials
Improve workforce scanning, site transfers and timesheet exports.
Audit plant imports, duplicate identities, rates and dashboard totals.
Repair delivery PO selection, periodic rechecks and receiving guards.
Align financial ledgers, supplier periods and mobile layouts.
Add the site map preview, shared theme controls and audit evidence.

Verify 3,455 application tests, six fuel export tests, TypeScript and
an optimized production build. Clean up a leaked test timer and keep
local worktrees and generated reports out of ESLint traversal.

Changed-file lint passes; repository-wide lint retains 33 existing
errors in unchanged files.
2026-10-19 13:18:31 +02:00
DEls
8dd01007bd docs(site-map): phase 1 spec and implementation plan 2026-10-14 07:02:23 +02:00
DEls
bab2ea7a43 feat(receiving): add Inbox 2 and strengthen duplicate checks 2026-10-12 20:20:27 +02:00
DEls
0113d1a1fa docs(dn): plan for deletion flags a person can act on
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:26:05 +02:00
DEls
529028b8d1 docs(dn): phase 1 plan — the delivery review page
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 19:59:39 +02:00
DEls
1e55fea2c2 docs(dn): explain-or-ask design (smarter in-app matching, line colours, rulebook)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 19:50:35 +02:00
DEls
d94d29f0a2 docs: Stage 3b plan (request_deletion, reassign_receipt, merge_suppliers, split_or_crop_page, background fragments)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 13:07:56 +02:00
DEls
35c18c32a3 docs: Stage 3a plan (stale preview refresh, MCP job drain, site question through regroup)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 12:42:40 +02:00
DEls
4a8edf1f7e docs(dn): site-decision implementation plan
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 06:57:29 +02:00
DEls
86abe6b1db docs(dn): stage 2 implementation plan
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 02:21:41 +02:00
DEls
62bd5af576 docs(dn): stage 1 implementation plan
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 02:01:47 +02:00
DEls
ab61182ced docs(dn): Avidan matching study and agent tools design
Study of 18 Avidan delivery documents matched by hand against their POs,
with root causes for why nearly every slip is flagged. Design for giving
agents full delivery-note tooling (everything except delete), staged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 01:55:21 +02:00
DEls
9c9a9d1866 docs: plan same-app remote purchasing MCP implementation 2026-10-05 01:22:08 +02:00
DEls
7ffe4eb94d docs: use existing app URL for remote MCP design 2026-10-05 01:16:34 +02:00
DEls
4970e61e70 docs: specify remote purchasing MCP service 2026-10-05 00:51:44 +02:00
DEls
ed69b517e8 Integrate agent work and add dashboard attention indicators
Remove repeated Plant, Financial and Workforce shortcut cards. Add site-scoped Plant and Financial sidebar attention status with automatic refresh.

Include accumulated delivery processing, purchase order recovery, supplier reconciliation, plant data and user management changes and audit records.
2026-10-04 21:53:32 +02:00
DEls
f5259d852f feat(auth): add PocketBase logins and site access controls 2026-09-28 23:51:47 +02:00
DEls
fecc885d3e docs(ask): agent actions
docs/modules.md §7.1: threads, memory, files, free queries/virtual
datasets, the propose/confirm/apply/undo gateway, both PDF export paths
(manual answer export and Ask-made reports via make_pdf_report),
images, and change requests — with the actor/x-ask-user model and the
three new PocketBase collections.

docs/data-model.md §4.17: field-by-field ask_threads/ask_messages/
ask_log reference, cross-linked from modules.md via explicit anchors.

docs/api-reference.md §12/§12b: /api/chat's new body shape and its
thread/action/chart/image/pdf SSE events, plus every new agent-actions
route (picker, threads, attachments, confirm-card actions, memories,
change requests) and their auth-matrix entries; the pb-files proxy
entry now notes its ask_messages admin-token path.

README.md: the intro, the modules table (adds Change requests), and
the API surface summary now describe an agent that acts, not just a
read-only chat.

Also marks Tasks 1–12 and 9b's implementation checkbox done in the plan
doc (docs/superpowers/plans/2026-09-26-ask-agent-actions.md) — the
remaining unchecked box is 9b's live check, skipped because
ask_threads/ask_messages/ask_log aren't applied on the target
PocketBase yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 21:32:53 +02:00
DEls
ba75c65795 feat(ask): download Ask-made PDF reports
Added mid-plan (docs/superpowers/plans/2026-09-26-ask-agent-actions.md,
new Task 9b — image generation from Task 9 is untouched): the user can
ask for any custom report and get a finished PDF, e.g. "give me a PDF
on fuel at The Hills for September", not just a manual export of
whatever is on screen (Task 8).

make_pdf_report({ title, subtitle?, filename? }) is the model's own
"this answer is a finished report" signal, called last once it has
already built the report (headings, narrative, KPI tables quoting tool
display strings, charts via make_chart). extractPdfMeta splits the
meta out the same way extractCharts/extractActionCard already do: the
model gets an ack, the browser gets the full meta via a new `event:
pdf`. The assistant's ask_messages row stores it in the existing
`parsed` json field, so reopening the thread still shows the report
cover and Download button — no schema change.

chat-view.tsx renders a report cover (wordmark, title, subtitle, date,
"Prepared by Ask for {name}") and a prominent "Download PDF · {title}"
button reusing Task 8's answerToPdf (same page-slicing, so a chart is
never cut across pages), and auto-downloads once per turn via
nextPdfTurnToDownload — a pure decision function, unit-tested directly
since this codebase's component tests use renderToStaticMarkup rather
than interactive DOM testing.

Live check skipped: needs a thread, which needs ask_threads (not yet
applied). Covered by unit tests (tool meta, the route's pdf event and
persistence, and the once-per-turn auto-download decision) instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 21:25:00 +02:00
DEls
7299f81c63 docs(ask): agent actions plan
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 17:42:46 +02:00
DEls
16652bb36c fix(auth): remove the ?demo=1 PIN bypass
The query string skipped the PIN gate on any URL. The PIN is now the
only way in. Docs updated; the login plan notes there is no fixed
cutover date while the app is in on-site testing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 17:37:20 +02:00
DEls
e83a0d302c fix: overview counts, wrong-unit fuel limits, meter guard, en-ZA numbers, vehicle merge, docs
- Overview/Plant tiles and pages share one vehicle bucket rule (blank unit_type = fleet)
- Plant landing equipment figures were always 0; hour diffs now reject 0 baselines and >24h/day jumps
- km/L limits on plant (and L/h on vehicles) report unit-mismatch instead of false alerts
- formatPercent/formatDecimal replace display toFixed (en-ZA)
- POST /api/vehicles/[id]/merge + Merge duplicate dialog (dry run first)
- Attention links go to equipment vs vehicles by unit type; PO upload view link fixed
- Conditional hook fixed in DN document view; ESLint flat config (next lint removed in 16)
- Docs: current modules/routes/env vars; login + Ask reports plans

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 22:04:08 +02:00
DEls
5cdfaa3128 feat(clock): review missed scan-outs without changing phone flow 2026-09-25 07:27:12 +02:00
DEls
e68e2d5dab docs(clock): plan daily shifts and exception review 2026-09-25 01:56:35 +02:00
DEls
d694dde501 docs(clock): design daily shifts and missed scan review 2026-09-25 01:50:25 +02:00
DEls
da756f5224 feat(worker-qr): real PDF + CSV batch export
Replace the off-screen CSS grid + window.print() in the worker QR
batch dialog with two on-click exports:

  - Download PDF — A4 portrait, 2x5 grid of CR80 (85x54mm) cards per
    page, brand orange header, generated with pdf-lib (dynamically
    imported, same pattern as src/lib/photo-pdf.ts).
  - Download CSV — UTF-8 BOM, columns employee_no, name, position,
    worker_type, project_code, qr_payload via Papa.unparse.

The single-card print on the worker detail page still uses
window.print() — one card has no pagination problem, only the batch
view did.

Spec §4.4 updated to match the new output mechanism and explain the
mobile failure mode that prompted the change.

New tests (8) cover the CSV builder (header order, qr_payload ==
employee_no per spec §4.1, RFC 4180 quoting, empty input) and the PDF
builder (empty input throws, valid %PDF output, 11 workers paginate
onto 2 pages, fake PNG injection so layout is testable without qrcode).
2026-09-20 20:43:10 +02:00
DEls
7ab55703b4 docs: plan hybrid delivery note ingestion 2026-09-18 18:43:52 +02:00
DEls
fb8ff612e8 docs: design hybrid delivery note ingestion 2026-09-18 18:34:57 +02:00
DEls
d02feee57f feat(dn-batches): one batch per tick by default
A tick is fully sequential: one vision call per page, then each note's
checks and promotion in turn. Claiming three batches of a 13-page scan
can approach the route's 300s ceiling, and a tick that times out leaves
its batch to be reclaimed and re-read, paying for those pages twice.

One batch per tick also makes n8n's execution log directly readable —
one run, one document, one outcome. DN_TICK_BATCH_SIZE still overrides
it once the page-count distribution is known.

34 batches at a 1-minute schedule is ~34 minutes rather than ~12.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 22:54:32 +02:00
DEls
c32b41506e docs(delivery-notes): record the Task 3 fixture files the plan missed
Making mass_unit required breaks typecheck in three test files the task's
file list did not name. Anyone re-running Task 3 in isolation hits the
same failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 21:04:39 +02:00
DEls
b1ce964f39 docs(delivery-notes): add the inbox corrections implementation plan
Twelve tasks against the nine defects the first production run exposed.

Task 6 is the core: a three-outcome PO item matcher (matched / ambiguous /
none) with a dimension gate, where the set of integers >= 100 must match
exactly. Two naive relaxations tested during design both produced wrong
matches -- U PIPES-600 resolving to S&S Pipes-1050, Kabelflex to uPVC Pipe
-- and a wrong match applies the wrong rate to money. The task ships a
regression test asserting no known input returns a wrong match, and the
plan tells the implementer not to move the thresholds without extending
that table first.

Task 7 adds the alias table, keyed on material_code so a human decision
carries onto the next purchase order. That is what makes wording like
"Kabelflex Pipe & Coupling" -> "160mm Sleeves Black" tractable at all.

Task 11 deliberately narrows the original page-accounting guarantee: a
stray page becomes a dismissable open question instead of failing the
batch, because batch 5qzcv6tvgb6cqks promoted 3 of 3 notes correctly and
still read as a failure over a tax invoice cover sheet. The guarantee that
every detected note must promote is unchanged.

Task 12 re-runs all 34 batches and audits every ledger link, so a matcher
regression cannot land silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 20:49:49 +02:00
DEls
1fcd5f0e8e docs(delivery-notes): spec the inbox corrections after the first real run
Ran the pipeline over 34 real documents: 7 processed, 14 blocked, 13 never
picked up. 13 of the 14 blocks were our own bugs.

Root causes, measured from dn_batches.checks:
- 5 batches are tax invoices or collection notes, which carry no delivery
  note number. The pipeline was built assuming every document has one.
- 4 blocked on product_matched; the matcher requires the PO line's first
  token to be 3+ chars, so "IJ Pipes 450x2.5 100D" never matches
  "IJ PIPES-450 X 2.5(100D)".
- 2 on weighbridge_arithmetic comparing nett in kg against quantity in tons.
- 2 on a single stray page condemning notes that promoted correctly.

Critically, two candidate relaxations of the matcher were tested against the
real descriptions and BOTH produced wrong matches (U PIPES-600 to S&S
Pipes-1050; Kabelflex to uPVC Pipe). A wrong match applies the wrong rate to
money, so the fix is a three-outcome matcher -- matched / ambiguous / none --
with a dimension gate, plus a learned alias table for wording that can never
match textually. The specified algorithm was verified against all 23 failing
descriptions with zero wrong matches.

Also found: dn_batches.created is never populated, so board ordering is
arbitrary; and the tick has never claimed a batch, so the n8n schedule is
not running.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 20:41:20 +02:00
DEls
dd0ac47375 docs(delivery-notes): add the phase 2-3 inbox pipeline implementation plan
Seventeen tasks covering the batch collections, the pure segmentation and
check modules, page rendering via poppler, per-page extraction, promotion,
the orchestrator, the tick route, the Kanban board and the batch detail view
with note correction.

The design point: pages are read independently and grouped into notes by the
delivery note numbers printed on them, so a note spanning three pages is one
note and a page belonging to no note blocks the batch. segmentPages throws
rather than return a segmentation that loses a page, and runBatch cannot
reach "processed" unless notes_promoted equals notes_detected -- the check
whose absence let two notes vanish from scan_20260915 while the pipeline
reported success at 95% confidence.

Task 16 step 7 is the regression test for exactly that: force a page read to
fail and confirm the batch lands in needs_attention rather than processed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:51:03 +02:00
DEls
4fd50f79a2 docs(delivery-notes): add the phase 1 unit correctness implementation plan
Eight TDD tasks covering the units module, the row builder that removes the
matched-id off-by-one, the PocketBase schema additions, the route rewiring,
dual-unit display, the backfill migration for the 57 existing rows, and the
operational steps to stop n8n minting orphan records and recover the two
lost notes from scan_20260915.

Also corrects the spec to name the migration as Python, matching the
existing scripts/ convention.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 11:51:21 +02:00
DEls
e7df31f7e1 docs(delivery-notes): spec the inbox pipeline, unit fix and Kanban board
Diagnoses why multi-note delivery slips lose data and specifies phases 1-3
of the fix.

Root cause: the n8n workflow's "Match items to PO" Code node runs in
runOnceForAllItems mode and returns a single item, so the workflow makes
exactly two callbacks regardless of how many notes a document holds. A
4-note scan produced 2 promoted notes and 2 empty records in an invisible
status, while reporting auto-verified at 95% confidence.

Also found: delivery notes are weighbridge tickets in kg while PO lines are
priced per ton, and nothing converts. po-fulfilment and delivery-note-value
therefore overstate received quantity 1000x across all 57 existing rows.

Phase 1 fixes the unit model and migrates existing data. Phase 2 moves
ingestion into a staged inbox with a deterministic validation gate, so
nothing reaches delivery_notes unless every page is accounted for. Phase 3
renders that state machine as a Kanban board.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 11:00:46 +02:00
DEls
e3fefd5419 docs(clock): add implementation plan for MEGA Clock timeclock PWA
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 00:30:54 +02:00
DEls
c30b11135f docs(clock): add design spec for standalone MEGA Clock timeclock PWA
Scans a worker's existing QR badge to clock them in/out, writing
directly into timesheet_entries as a "Direct (QR)" entry — the
timekeeping app foreshadowed in the workers-timesheets-qr spec.
2026-09-11 23:54:39 +02:00
DEls
fe55a5d7df docs(site-scope): correct stale line refs, record tasks 1-5 as done
Re-verified every file:line against the current tree. Three were wrong:
the Overview ranges in task 6 (and it listed two sites.items uses where
there are four), the hamburger line in task 5, and a claim that
plant/page.tsx still had perPage: 5000 caps — it does not.

Also records that tasks 1-5 have landed, so the next worker starts at 6,
and keeps the scope-aware Sites sub-label that was implemented in place
of the deletion this plan originally specified.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 23:05:10 +02:00
DEls
58b47eafee docs(project-calendar): add the project month/week plan; fix a stale doc reference
The migration's docstring pointed at a spec file that was never
written for this feature (the design was worked out via conversation,
not a separate spec doc) — points at the plan instead.
2026-09-11 22:35:41 +02:00
DEls
85692dedec docs(site-scope): anchor dropdown removal on names, not line numbers
A concurrent project Month/Week feature is landing in the same view
files and has already shifted purchasing-view.tsx by ~10 lines, adding
a periodFilter beside projectFilter. The removal steps now match on
identifiers and snippets, and the plan header says to treat every
file:line as a hint.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 22:30:08 +02:00
DEls
3cd7382115 docs(site-scope): add the implementation plan
16 TDD tasks: pure scope helpers, the getSiteScope seam, the topbar
switcher, then per-module server-side filtering. Task 15 records why
the chat agent's default-site change is deferred out of this plan.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 22:28:10 +02:00
DEls
d570352b76 docs(site-scope): spec the global site scope switcher
Replaces the four independent per-table project-code dropdowns with a
single global scope carried in the URL as ?site=, resolved server-side,
and applied as a PocketBase site= filter. Defines the getSiteScope()
seam so the separate PocketBase users work plugs in without touching
the 23 Topbar call sites.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 22:20:05 +02:00
DEls
8e72acc8a5 chore(assets): update application icons and logo with new designs 2026-09-11 20:54:44 +02:00
DEls
d5e3b2ce2b docs(spec): Workers/Timesheets split and worker QR badges
Workers becomes a lean roster (name, employee number, position, type,
site, rate) with filters unchanged. Everything currently cluttering the
list row -- total hours, cost, the 8-week sparkline, per-employee entry
history -- moves to a new worker detail page that doesn't exist yet
(today's "detail" is a dialog). Same pattern already used for Suppliers
and Vehicles/Equipment this session: lean list, detail page carries
depth.

Timesheets keeps its flat entries table but gains a genuinely separate
upload-status section -- the timesheets document collection already has
this data, it's just currently squeezed into a per-row badge instead of
being its own thing. The duplicated Import-timesheets button (currently
on both pages) moves here only.

QR badges: encodes employee_no directly, no new PocketBase field --
every worker already has a valid code the moment they exist, with no
generation step. Explicitly a deliberate tradeoff (guessable numbers,
no revocation) the user chose for now, pending seeing how the separate
timekeeping PWA wants identity handled -- moving to a stored opaque
token later is additive, not a rework. Card layout confirmed via the
visual companion (centered, QR-forward). Both single-worker and
batch-sheet print confirmed in scope. Rendering via the qrcode npm
package + the browser's own print dialog (@media print), not a PDF
library -- CSS handles real-world card dimensions more reliably than
fighting a PDF engine's coordinate system.

The timekeeping PWA itself is explicitly out of scope -- a separate
project this spec only needs to stay compatible with.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 19:04:34 +02:00
DEls
dc85da3f36 docs(plan): suppliers implementation plan
13 tasks across 6 phases: additive schema, pure matching/ledger
functions (TDD, tested in isolation), the one irreversible migration
(guarded, dry-run default), OCR wiring, API routes, and UI.

Grounded in the actual codebase rather than the spec's prose: the
existing "unknown supplier" inline-editor on the PO detail page
(SupplierEditor + POST /api/purchase-orders/[id]/supplier) is upgraded
in place rather than duplicated, following the same principle applied
to the sidebar restructure's delivery-note review queue. Every code
sample is written against files actually read during planning, not
guessed -- site-form-dialog.tsx, the /api/sites routes, matchSite,
po-fulfilment.ts, and the live purchase_orders.supplier data.

Self-review caught a real gap before committing: pb-bootstrap-schema.py
and pb-types.ts were named in the file-structure table but no task
actually touched them -- exactly the foot-gun docs/data-model.md
section 7 warns about. Added as Task 1 steps 5-8.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 18:08:30 +02:00
DEls
c73a2aad6c docs(spec): correct the suppliers migration's expected merge count
19 -> ~16 was wrong. Ran the actual normalizeSupplierName() (trim +
whitespace-collapse + uppercase) against the three example pairs cited
as evidence: only the case-variant pair (Builders Warehouse) actually
collapses. The Ikhasi pair keeps a stray space after a hyphen that
differs between variants; the Global Precast pair differs by the
"(Pty) Ltd" suffix, not case or whitespace. Neither collapses under
exact-normalized matching.

This is not a defect -- it's the conservative-matching principle in
section 3 working as designed. The correct expectation against
production is 19 raw strings -> 18 groups, one merge. Both pairs that
don't collapse are exactly the kind of ambiguous case the spec already
says should stay separate for a human to resolve, not the kind that
should be forced together by loosening the normalizer.

Caught while grounding the implementation plan in real data, before
writing a single line of the plan itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 18:02:18 +02:00