Commit graph

403 commits

Author SHA1 Message Date
DEls
1514d514b3 fix(dn): validate inbox 3 line edits
Co-Authored-By: MiniMax (Pinchy)
2026-10-26 19:30:59 +02:00
DEls
bc138bcae7 fix(dn): over-PO quantities need an explicit accept, never from the assistant
Co-Authored-By: MiniMax (Pinchy)
2026-10-26 19:27:29 +02:00
DEls
e7e8a2ff3d feat(dn): inbox 3 assistant edits lines by number, never posts
Co-Authored-By: MiniMax (Pinchy)
2026-10-26 19:20:10 +02:00
DEls
3ca3021d33 feat(dn): save inbox 3 line edits without posting
Co-Authored-By: MiniMax (Pinchy)
2026-10-26 19:09:58 +02:00
DEls
2dfe92b7d6 feat(dn): block slips linked to another supplier's PO
Co-Authored-By: MiniMax (Pinchy)
2026-10-26 16:00:57 +02:00
DEls
ddccdd8cfd feat(dn): confirm posts the ready lines and leaves the rest open
Co-Authored-By: MiniMax (Pinchy)
2026-10-26 15:46:52 +02:00
DEls
88a3a1e1be feat(workforce): keep the hourly rate off the workers roster
The Rate / hr column put every worker's wage on a wide table that is
glanceable, projected and exported. The rate already lives on the worker
detail page, where it is a deliberate lookup.

Drop the column from the roster and the rate from its CSV export — the
export is the wider accidental leak, since that file gets emailed and
opened on shared machines. Site managers can still see a worker's rate by
opening the worker, which stays deliberate access.
2026-10-26 01:10:08 +02:00
DEls
18fb7ef99d feat(surveying): Maps move to a Surveying section; Sites is opt-in for site managers
Surveying → Maps is open to every dashboard user, scoped to their sites.
Sites (contracts and profitability) shows for admins, and for site managers
only when "Can see Sites" is ticked on their account. Old map links redirect.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 13:31:53 +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
706d3bbd63 feat(workforce): payroll export, labour cost by site, all-sites totals in the site matrix
Adds an all-sites payroll (Excel/PDF/CSV, one row per worker, per-site split and
site wage cost), a period-selectable labour cost by site table on the Workforce
dashboard, and each worker's all-sites total plus site wage cost in the
timesheet matrix and its exports.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 07:00:48 +02:00
DEls
708e0b2742 feat(clock): a scan at another site moves workers and vehicles there automatically
A destination scan now ends the worker's previous site session at that moment
(transfer OUT, hours booked at the origin) and opens the new one, so nothing is
left for the office to review. Vehicle scans take a scanning site, move the unit
there and log the reading against it. Adds a script to close already-flagged moves.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 06:34:05 +02:00
DEls
1a67125b61 feat(site-map): Map 3 with ground survey, cut & fill, progress glow and service routes
New /dashboard/sites/[id]/map3: a 3D map forked from Map 2 (Map 1 and 2 untouched) with ground heat map, contours, cut & fill from the Chris survey, a longitudinal profile, yellow/green progress glow, a Services tab for stormwater/sewer/water routes, and status popups on earthworks strips and routes. Pure logic and tests in src/lib/site-map-v3.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 04:43:56 +02:00
DEls
2b411404da fix(management): drop 95% cost line from income chart; purple/green cumulative result
Remove the planned-cost line from the actual vs forecast income chart and colour the Turnover Overview cumulative result purple (planned) or green (actual).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 04:43:56 +02:00
DEls
07b12e98b4 feat(management): list every planned project on Retention
Awarded (not started) and non-calculated projects now appear on the
Retention table, labelled, blank until values are entered. Entered values
count towards totals and the company balance, and the retention save
endpoint accepts any project in the plan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 12:58:02 +02:00
DEls
cb4a1ce461 feat(site-map): Maps page with previews, double-click progress popup, Cedar Gate cut & fill
- Sites → Maps in the sidebar replaces the per-site Site map / Map 2 links.
  The Maps page shows each site's model as a plan preview (built sections
  solid) with % of road layerworks built and links to Site map and Map 2.
- Double-clicking a road section opens its progress tick-boxes over the map
  (Site map saves to the server; Map 2 stays a local scenario).
- Cedar Gate: 40 drawn cross-sections extracted from C3277-912-001 Rev A
  (extract_cedar_gate_xs.py), cut/fill limited to Cedar Gate Road, a Cut &
  fill workspace in Map 2 with a chainage chart, and pavement layers shown
  in their material colours.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 12:55:51 +02:00
DEls
0dfb129696 feat: update management comparisons and Cedar Gate site model
Add planned vs actual turnover dashboards, monthly actual costs, editable retention forecasts and actuals, starting retention, payouts and running balances. Preserve manual values across workbook imports and include retention backfill tooling.

Add Cedar Gate model extraction and reports, measured service routes and ground surfaces, plus site-map section totals and quantity refinements.
2026-10-20 12:23:44 +02:00
DEls
db53a82809 feat(management): rework Turnover Overview layout, KPI band and colours
Show invoiced, forecast and awarded split in the turnover KPI; pin the
cash result column and add per-month invoiced/forecast lines; stack the
side panels in a column with a year comparison chart so both columns end
level; reword the income-less-cost chart labels. KPI band follows the
app greys in dark mode and a light grey in light mode, with all figures
neutral except the accent-coloured turnover.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 03:40:58 +02:00
DEls
d9cf20dd02 feat(management): income KPI tiles match chart colours, align table, purple forecast line
Income tab shows Actual, Forecast, Planned and a combined total with
colour stripes matching the chart; month columns right-align with their
headers; the forecast line under an actual uses the forecast purple;
the red stack falls back to the forecast bar when a month has no
invoiced figure yet.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 03:19:02 +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
a738b890bc fix(auth): keep named user sessions for 60 days 2026-10-19 20:04:14 +02:00
DEls
f6f441d1d7 fix(deliveries): put delivery note KPIs above the duplicate receipt notice
The delivery notes page showed the "Possible duplicate receipts need office
review" notice before the KPI strip, so the numbers the office acts on sat
below the thing it needs to act on. The notice now sits directly under the
KPIs and directly above the search and table.

The KPI strip moves out of the client-side table view into its own server
component, so the page owns the order of the three blocks. That also removes
a double padding bug: the page and the view each applied p-4 sm:p-6, which
indented the KPI and table column 32px while the notice sat at 16px.

The figures are unchanged - they always described the full site-scoped set
of delivery notes, not the filtered rows.

A page test locks the new order (KPI < notice < search < table) and fails
if the blocks are rearranged.
2026-10-19 17:22:07 +02:00
DEls
0857e56cb3 fix(workforce): let site managers add and rename employers and brokers
The employer and broker API routes were never named in the path
classifier, so they fell through to its admin-only default and the
proxy rejected a site manager with a bare {"error":"Not allowed"}
before the route handler ran. The handler's own site scoping was
always correct — it just never got the chance to apply.

- classify /api/employers as dashboard and map it to the workforce
  module, mirroring /api/employees
- let the site dropdown be chosen when adding an employer; only a
  rename freezes the site, which preserves assignment history
- cover the rename path, which had no route test, and the proxy
  gate for the site-manager role
2026-10-19 16:33:41 +02:00
DEls
8ef3c8f780 fix(deliveries): unblock new scans and match duplicate Drift supplier POs
- Tick runs new scans before batches requeued for PO rechecks; old
  uploaded_at dates kept rechecks ahead of today's scans for hours.
- Supplier records with the same normalised name and no conflicting
  registration/VAT count as one company, so PO9480 (held by the second
  Drift Laezonia record) no longer fails as a supplier conflict.
- Inbox shows a queued scan as waiting rather than being read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:27:52 +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
c3566a1f02 Merge origin/main with delivery receiving fixes 2026-10-15 20:26:32 +02:00
DEls
69b941e198 fix(deliveries): preserve saved line decisions and correct receiving units 2026-10-15 20:25:04 +02:00
DEls
e01bb31c8c feat(ui): simplify dashboard table controls 2026-10-15 19:57:09 +02:00
DEls
757861c293 feat(workforce): simplify toolbar and add workers 2026-10-15 16:17:12 +02:00
DEls
bb2d15a39b feat(workforce): add employer and broker management 2026-10-15 15:58:06 +02:00
DEls
0d8e4b5566 Allow manual worker times to override recorded scans 2026-10-15 09:11:55 +02:00
DEls
d21303f3af Prefill clock correction times from recorded daily scans 2026-10-14 16:19:10 +02:00
DEls
921e906d55 Fix corrected arrival datetime in clock correction form 2026-10-14 14:24:30 +02:00
DEls
ab20d681b2 fix(site-map): review fixes — batches of 50, survey CSV locales, save recovery
- write PocketBase batches in chunks of at most 50 (production limit)
- survey import: refuse files with no rows, write peg events before the import record
- read en-ZA Excel CSVs (decimal comma), quoted fields, CR line endings; no crash without pegs
- save queue: reset on model reload, drop 400-refused items instead of blocking,
  keep queued values on screen when a server state arrives, flush on leaving the page

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 08:56:05 +02:00
DEls
0b04613ae1 feat(site-map): Site map entry on the site page and in the sidebar
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 07:31:24 +02:00
DEls
de727e7129 feat(site-map): map page with layers panel, navigation pad and saving 2026-10-14 07:19:54 +02:00
DEls
70c0784b18 feat(site-map): surveyor CSV import marks matched pegs checked 2026-10-14 07:08:32 +02:00
DEls
a2db3a2b13 feat(site-map): model and progress API routes 2026-10-14 07:07:57 +02:00
DEls
337b77be3f feat(management): refresh workbook plans and reconcile cashflow 2026-10-14 05:48:01 +02:00
DEls
bab2ea7a43 feat(receiving): add Inbox 2 and strengthen duplicate checks 2026-10-12 20:20:27 +02:00
DEls
68f3e1eea6 feat(scanner): crop and review delivery and PO photos 2026-10-12 20:20:05 +02:00
DEls
c7c48fde10 fix(financial): keep each month's order and delivery bars side by side
The bars were capped at 36px but centred in half-month slots, so on a
wide screen a month's pair drifted ~65px apart. A fixed 32px bar with a
3px gap keeps them together (recharts still shrinks them if space runs
out).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 04:12:31 +02:00
DEls
c55676bff0 fix(po): photographed POs get their order date from the PO Creation Date
Text PDFs already get it from pdftotext; photos and scans have no text,
so 4 of 5 undated POs were photos that do print a PO Creation Date.
- n8n PO reader (stored patch): asked for order_date = 'PO Creation
  Date' (day/month/year), never Required By or Printed; the callback
  passes it on.
- process-batch accepts extracted.order_date (YYYY-MM-DD); the PDF text
  wins when both exist.
- create_purchase_order / update_purchase_order / move_to_purchase_orders
  take order_date, so agents record it too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 00:45:14 +02:00
DEls
a41653ce87 fix(dn): date deliveries by the slip and orders by the PO's order date
The reader always read the slip date (and receiver, site name, customer
VAT, supplier registration), but dn_batch_notes never had those fields,
so they were dropped on save and every receipt was dated the moment it
was posted. The Financial dashboard dated orders by upload date, so 24
of 41 POs (R7.08M) sat in no month and deliveries piled up in the month
they were booked: 'R1.1M ordered, R2.4M delivered'.

- scripts/pb-add-dn-dates.py: adds the five header fields,
  delivery_notes.posted_at and purchase_orders.order_date (applied).
- scripts/dn-backfill-dates.py: filled 270 documents from the stored
  readings, moved 192 receipts to their slip date (booking time kept in
  posted_at) and read 36 PO order dates from the PDFs (applied; replaced
  values in docs/audits/2026-10-11-dates-backfill-backup.json).
- Posting refuses a slip without a delivery date instead of using today;
  receipts record posted_at. The review page shows the date and locks
  Post with a plain message when it is missing.
- PO intake reads 'PO Creation Date' from the PDF text (pdftotext).
- Dashboard tile and monthly chart date orders by order_date only; an
  order without one is undated, never put on its upload month.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 00:35:00 +02:00
DEls
f26d79a89c feat(financial): one chart of PO value against deliveries per month
Orders and deliveries now share a grouped bar chart. Orders with no date
(most of The Hills' value: date_uploaded is empty and the `created`
fallback does not exist on purchase_orders) cannot be placed on a month,
so the chart now totals them in a note instead of dropping them silently.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 00:13:38 +02:00
DEls
db91aa23b7 feat(financial): charts of PO value and delivery note value per month
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-10 23:57:46 +02:00
DEls
36dff2ff09 fix(financial): open Suppliers on the period the tile linked to
The "Received this month" tile linked to the Suppliers page, which
always opened on "All", so the page showed a different number than the
tile. Add a validated parseInitialPeriod helper, pass the parsed
period through the page as initialPeriod, and have the tile link carry
?period=thisMonth.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:24:35 +02:00
DEls
84816fa466 fix(financial): say "supplier" when one supplier is owed
The "Received this month" sub-line always said "suppliers", so a single
supplier read as "Owed to 1 suppliers". Use the singular when the count
is 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:24:12 +02:00
DEls
9fbfba891a fix(financial): count PO month spend on the SAST work date
date_uploaded and created are UTC timestamps written with
toISOString(), but monthKey is the South African month. Comparing the
raw YYYY-MM prefix put an order uploaded at 00:30 SAST on 1 November
into October. Resolve the SAST work date first, then compare months,
and skip values that cannot be parsed.

Also reword two workforce comments that referred to a worker as "he".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:24:04 +02:00
DEls
9a084abb72 test(workforce): spell out the non-breaking space the tiles contain
Intl renders en-ZA currency and numbers with U+00A0, so the tile helpers
normalise it before asserting. Written as a literal that byte is
invisible in the source and easy to "fix" into a plain space by accident.
An explicit escape says what it means.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:15:05 +02:00
DEls
0987dcc33a test(workforce): update the missing-rate overview for this-month figures
The overview test asserted the previous all-time sub-line wording and
that employee_rates was read with a site filter, both of which the cost
workforce commits changed on purpose.

Pins the clock so "this month" is a fixed window, and gives the unpriced
worker an October entry so the new month-scoped warning is actually
exercised rather than skipped. Added a case asserting rate history is
read unfiltered, which is what lets a worker who moved sites keep the
rate he was paid at.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:14:41 +02:00