Commit graph

774 commits

Author SHA1 Message Date
DEls
f06d545e02 fix(site-map): allow tab-separated survey files
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 08:52:19 +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
3770830e86 feat(site-map): progress, services, earthworks, signs, rain and accuracy panels
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 07:29:35 +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
f583814776 feat(site-map): services, earthworks, signs and rain layers; client save queue 2026-10-14 07:15:46 +02:00
DEls
220ca7bc45 feat(site-map): three.js scene engine, navigation and road layers 2026-10-14 07:12:32 +02:00
DEls
54a631c6ba feat(site-map): rain/ponding analysis as a pure module 2026-10-14 07:09:49 +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
226e3e6fcb feat(site-map): server helpers and view/edit permission check 2026-10-14 07:07:19 +02:00
DEls
dae2aeb044 feat(site-map): chainage, surface heights and BoQ quantities 2026-10-14 07:06:50 +02:00
DEls
300b1d2a07 feat(site-map): model upload script with progress-key parity 2026-10-14 07:06:13 +02:00
DEls
a5e5660893 feat(site-map): model types, progress keys and validation 2026-10-14 07:05:49 +02:00
DEls
c4da9d4a65 feat(site-map): PocketBase collections for models, progress and survey imports 2026-10-14 07:05:11 +02:00
DEls
d66c213712 feat(site-map): model pipeline with 20 m chainage sections 2026-10-14 07:04:34 +02:00
DEls
8dd01007bd docs(site-map): phase 1 spec and implementation plan 2026-10-14 07:02:23 +02:00
DEls
337b77be3f feat(management): refresh workbook plans and reconcile cashflow 2026-10-14 05:48:01 +02:00
DEls
f47e73044d fix(openmaus): use supported portable requirements 2026-10-12 22:57:22 +02:00
DEls
b2ce17814d feat(foreman): add checked receiving operator package 2026-10-12 22:48:46 +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
1fb65a9d9c chore(n8n): keep the stored PO-ingest callback in step with the published workflow
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 00:47:00 +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
ffe8901d15 Merge branch 'feat/financial-combined-chart' 2026-10-11 00:17:08 +02:00
DEls
6e3e9ec27c Merge branch 'fix/dashboard-figures' 2026-10-11 00:17:08 +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
c16fae3f73 chore: allow the Mega purchasing MCP server in local settings
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 00:03:38 +02:00
DEls
b9d6cdedb8 chore: add the dn-inbox-review skill under .agents
Identical to the copy in .claude/skills.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 00:03:37 +02:00
DEls
cca41d5bb1 chore(clock): keep the pre-recalc paid-hours backups
Row ids with the hours_worked values from before the 2026-10-09
paid-hours recalculation, so the run can be reversed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 00:03:35 +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
7719e4f36a feat(foreman): scheduler updates for the runner and mod hooks
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-10 23:52:12 +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
DEls
373ff2c1d3 feat(financial): show what was received and owed this month
"POs (total)" repeated the number already shown under Open PO value and
said nothing about deliveries, so the page had no figure for what came in
this month or what that leaves owed to suppliers.

Replaced with "Received this month", computed with the shared
deliveryLineValues helper -- the same series behind the Suppliers page's
"Payable this period" -- filtered to the current SAST month. Under the
app's rule that we only pay what we received this month (see
src/lib/supplier-period.ts), delivered-this-month and owed-this-month
are the same number, so one tile answers both and cannot drift from the
Suppliers page. The sub-line names how many suppliers that is; with none,
it says so rather than showing a bare R0. The tile links through to
Suppliers with the current site selection preserved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:10:43 +02:00
DEls
cd0d84a520 fix(financial): count real orders only in this month's PO spend
PO spend summed total_value of anything uploaded this month, including
failed and still-processing placeholder rows -- money that was never
committed. It also ignored orders raised through the MCP create_purchase_order
tool, which carry no date_uploaded at all and so never appeared in any
month.

The tile now sums real orders only, matching the rule already used for
open value, and falls back to `created` when there is no upload date.
Month membership is a YYYY-MM prefix against the SAST month key. The
sub-line says how many orders that was, so a plausible-looking total can
be sanity-checked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:10:01 +02:00
DEls
2c6a66182c fix(financial): subtract deliveries from open PO value
"Open PO value" summed total_value of every processed order ever and
never subtracted what had been delivered, so it grew without bound and
read as total committed rather than still owed. A R1 000 order with R400
delivered reported R1 000 open.

It now comes from computeSupplierLedger -- the same computation the
Suppliers page uses -- over the items of real in-scope POs and the
delivery lines belonging to them (by booking snapshot, or by parent PO
item for older unbooked receipts). Clamped per PO, so an over-delivered
order cannot net off another order's open balance and understate what is
still owed. The sub-line counts orders still actually open rather than
repeating the same number as a "total POs" figure elsewhere on the page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:09:13 +02:00
DEls
938b1784dd refactor(suppliers): share the delivery-line value computation
The Suppliers page built its per-delivery-line value series inline to
feed the "Payable this period" KPI. Extracted it to deliveryLineValues
so the Financial landing can show the same figure for a single month
without re-deriving the rules in a second place, where they could
quietly diverge.

Behaviour is unchanged: booking snapshot net wins, else qty_received x
the PO line's rate; receipt documents only; lines against POs outside the
page's scope are dropped; lines with no date_received are skipped. The
returned rows now also carry the PO id so callers can attribute a total
to a supplier.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:06:13 +02:00
DEls
8108640f87 fix(plant): count only fleet litres and spend on the Vehicles page
fuel_logs is site-scoped, so it holds equipment fill-ups as well as
fleet ones, and the "Total litres pumped" / "Total fuel spend" tiles —
labelled as fleet — were summing both. The same diesel was counted here
and again on Plant.

Fleet-ness now comes from the log's EXPANDED vehicle (the query already
expands "vehicle") rather than from membership of the site-filtered
vehicles list, so a truck that has since moved to another site still
counts at the site where it was actually fuelled. Unclassified equipment
counts for neither bucket. The flagged count follows the same rule.

Extracted into a pure helper next to the page so it is testable without
standing up the PocketBase reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:04:50 +02:00
DEls
a3e042cd76 fix(workforce): keep a worker's hours at the site he worked them
Timesheet entries are stamped with the site they were worked at and are
never joined to the worker's current home site, but both the cost loop
and employee_rates were filtered by employee.site. A worker who moved
from A to B therefore lost his A hours when viewing A (he was no longer
on A's roster) and never had them counted when viewing B — the hours
only reappeared under "All sites".

Hours and cost now iterate the in-scope timesheet entries and price each
employee through an unfiltered lookup, so they follow the site the work
happened at. An employee id missing from that map keeps its hours but
counts as unpriced. The roster stays site-filtered and still drives the
figures that are about who works here now: headcount, permanent/casual,
idle 30d, missing rates and the inactive filter on clock events.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:04:05 +02:00
DEls
3dfa28f0c6 fix(workforce): show this-month cost and hours instead of all-time
The workforce KPI row showed total cost (all-time) and hours this week,
with nothing about the current month — the two figures a cost-to-date
conversation actually starts from.

Cost (this month) sums hours x resolveEmployeeRate over entries whose
date falls in the current SAST calendar month (YYYY-MM prefix, so
PocketBase's "2026-10-01 00:00:00.000Z" needs no parsing). It still
prices through the dated rate history, and its sub-line falls back to
the all-time total so the longer view is not lost. Hours (this month)
is the matching hours figure, with the week alongside it as sub-line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:03:27 +02:00
DEls
4e7583256d feat(dn): Set aside list with Restore, next to Completed
Set-aside delivery documents were kept but shown nowhere, so they looked
lost (e.g. The Waste Group slip T025511, set aside 'Waiting for
invoice'). The inbox now has a Set aside button: every set-aside
document with why (in plain words) and when, a link to its page, and a
Restore button that puts it back in the inbox through the same restore
the agents use, recorded with who and why.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 22:18:50 +02:00
DEls
6f21803573 feat(dn): deletion flags open as Needs attention cards at the top of the inbox
Co-Authored-By: MiniMax Code <noreply@minimax.io>
2026-10-09 21:57:20 +02:00
DEls
84da8eceac feat(dn): a page to see and decide a deletion flag
Co-Authored-By: MiniMax Code <noreply@minimax.io>
2026-10-09 21:56:07 +02:00
DEls
7c1a7f74fc feat(dn): route to delete or keep a flagged record
Co-Authored-By: MiniMax Code <noreply@minimax.io>
2026-10-09 21:54:14 +02:00
DEls
77dbdeb160 feat(dn): rules for deciding deletion flags (delete with a saved copy, keep, never evidence)
Co-Authored-By: MiniMax Code <noreply@minimax.io>
2026-10-09 21:52:53 +02:00