Commit graph

890 commits

Author SHA1 Message Date
DEls
e722feaaa3 Merge branch 'codex/clock-scan-quality' 2026-11-06 13:03:55 +02:00
DEls
effa9236dd feat(delivery-notes): receipt redo, AI model settings and slip invoices
- Receipt redo: an admin can reverse a line-posted receipt and reopen the
  same document for fresh processing, keeping the audit history
  (API, MCP tools, document-page control, revisioned posting ids)
- Settings · AI: choose the models the DN pipeline uses, stored in the
  new app_settings collection (migration included), behind a settings cog
- Inbox 2/3: supplier-slip invoice uploads, slip-only suppliers, and
  links/Back that return to the inbox you came from

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 13:03:47 +02:00
DEls
64e302fa1f feat(plant): sync vehicle sites from Buildsite fuel logs
Adds the site-history sync script with its tests, and the 4 Nov
Buildsite run evidence (catalog, downloads, before-state, completion).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 13:03:47 +02:00
DEls
99d52e9760 feat(clock): scan plant on and off site, with weekend overtime
Routine hours now come from the fuel logger import, so the fleet scanner
only books machines onto and off sites:

- first scan books a machine in (opens a plant_site_stays row)
- a weekday second scan is the last scan and books it out
- a Saturday/Sunday scan asks: weekend overtime, or last scan
- a machine still on another site is scanned out there and in here
- an unknown QR can be added on site (name, type, supplier, hours)
- the site comes from GPS, or the scanner asks "What site is this?"

Machines get a hire supplier (dropdown or add new), and the vehicle
pages show a Site history card. Migration applied to live PocketBase.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 13:03:18 +02:00
DEls
ae5137aaad fix(po-email): book a PO against the site name, not the cost code
The project code on a PO's lines is a cost allocation, not a statement of
where the work is going. MEGCPO9550 is a Cedar Gate delivery -- its subject
says "(Cedar Gate)" and its email subject says "RE: Cedar Gate Paving &
Kerbs PO" -- yet four of its seven lines are billed to C26/08 (Castle Gate)
and three to C26/13 (Cedar Gate). Booking by code would have put a Cedar
Gate PO against Castle Gate, and that risk grows as sites are added.

The site is now read from the name written on the PO: its subject line
first, then its description, then the email subject. A PO naming two sites,
or none, is flagged for review instead of being booked against one of them.
The code is still read and kept on the filed row as po_cost_codes, because
the app's own finalisation step uses it as evidence.

MEGA does not always write a site name the way the app has it, so the name
is also matched without its instance qualifier ("Munyaka" for "Munyaka 2")
and through an explicit table of the spellings seen on real POs ("West Park"
for Wespark, MEGCPO9554; "Nellmpius" for Nellmapius Ext 25, MEGCPO9475).
Without those, POs naming a site would have stopped filing themselves.
2026-11-06 10:26:34 +02:00
DEls
4e5d13950a fix(po-email): book a PO against the site name, not the cost code
The project code on a PO's lines is a cost allocation, not a statement of
where the work is going. MEGCPO9550 is a Cedar Gate delivery -- its subject
says "(Cedar Gate)" and its email subject says "RE: Cedar Gate Paving &
Kerbs PO" -- yet four of its seven lines are billed to C26/08 (Castle Gate)
and three to C26/13 (Cedar Gate). Booking by code would have put a Cedar
Gate PO against Castle Gate, and that risk grows as sites are added.

The site is now read from the name written on the PO: its subject line
first, then its description, then the email subject. A PO naming two sites,
or none, is flagged for review instead of being booked against one of them.
The code is still read and kept on the filed row as po_cost_codes, because
the app's own finalisation step uses it as evidence.

MEGA does not always write a site name the way the app has it, so the name
is also matched without its instance qualifier ("Munyaka" for "Munyaka 2")
and through an explicit table of the spellings seen on real POs ("West Park"
for Wespark, MEGCPO9554; "Nellmpius" for Nellmapius Ext 25, MEGCPO9475).
Without those, POs naming a site would have stopped filing themselves.
2026-11-06 10:25:34 +02:00
DEls
f24d170f6b docs: prohibit local production builds on the Mac Mini 2026-11-06 07:51:13 +02:00
DEls
87f966fb89 feat(clock): use still photos with required document scan review 2026-11-06 07:03:06 +02:00
DEls
2c97f36ff4 fix(po-email): read the site off the PO's own lines, not just its subject
The site was only looked for in the PO's Subject field and in the email
subject, so a PO that names its site in the Description -- or only in the
project code on its line items -- could not be placed and was flagged for
review. MEGCPO9547 ("Chamberlain PO", Chamberlain is the supplier) gave the
site three ways: Description "Tools (Nellmapius EXT25)" and C26/27 on
every line, while its email subject named nothing.

The site is now resolved from the document itself, in order of how reliable
each signal is: the project code on the PO's own lines, then the site named
in its Description, then its Subject, then the email subject. Only the
line-item block is scanned for project codes, so the "Head office" in a PO's
contact block cannot be mistaken for the HEAD site code, and lines naming
two different sites are left for a human, as MEGA's own finalisation step
treats them.

Also splits the document parsing out of parse_pdf so it can be tested, and
covers it with scripts/test_po_email_intake.py.
2026-11-05 11:15:32 +02:00
DEls
f3e0248546 Add slip invoice uploads and spend details
Add a manager-only endpoint for appending JPG, PNG, or WebP invoice photos to slip logs with file and photo-count limits. Expose invoice attachment controls in transaction sheets and add expandable monthly spend summaries to cash-float cards.
2026-11-03 13:10:53 +02:00
DEls
d055c7bb5d feat(cash-floats): office transactions with invoice drop, list layout, swapped spend columns
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 12:54:34 +02:00
DEls
821b988917 fix(users): no hydration mismatch on the Last active hover time
toLocaleString joins date and time differently in Node and the browser
("," vs "at"). Build "02 Nov 2026 23:29" from Intl parts in SAST, and let
the relative label differ if the clock crosses midnight between renders.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 23:37:50 +02:00
DEls
92050d2b01 style(users): full-width Users page like the other tables
The max-w-5xl cap was left from when the page was a small settings form.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 23:36:10 +02:00
DEls
22ae099f95 fix(users): pass last_active_at through to the Users list
The page maps each row field by field and left the new field out, so
everyone showed "Never" although the stamps were being written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 23:26:32 +02:00
DEls
de8aa83ef1 fix(po-email): a dry run must not consume a message
The state file was written before the documents were classified, so a --dry-run
marked every message as seen. A new PO seen first by a dry run would then be
skipped by every later real run -- never filed, never flagged for review.

Now a message is remembered only after it has been classified, and only on a real
run; the outcome (filed, already_filed, duplicate_number, needs_review) is stored
with it. A dry run leaves the state file untouched, and the report now says how
many messages were already seen so an idle hour is not mistaken for a failure.
2026-11-02 23:16:28 +02:00
DEls
607de20232 feat(sites): admins can correct a site's project code
The project code was locked once set, on the belief that changing it
would break joins. Nothing joins on it: every record links to the site by
id, and the PO email intake reads codes live. A mistyped code (Nellmapius
was 001 instead of C26/27) could only be fixed in the database.

In Edit site an admin double-clicks the code to unlock it. PATCH accepts
project_code from admins only, trims it, requires 1-64 characters, answers
409 when another site already uses it, and writes nothing when unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 22:59:30 +02:00
DEls
4a2e39be86 feat(purchase-orders): check a PO's site against its line cost codes
A PO's site was whatever the uploader or email picked, and process-batch
never re-checked it. Every MEGA PO line starts its cost code with the
project code ("C26/09, MC-SEW" is The Hills), which agreed with the filed
site on every live PO whose code is a site in the app.

After OCR, process-batch now reads those codes:
- all lines name the filed site -> confirmed
- all lines name one other site, nothing booked yet -> PO moves there and
  waiting deliveries are queued at the new site
- mixed projects, an unknown code, or receipts already booked -> flagged
  in raw_ocr_data.site_check, filed site kept
The check is never fatal: if the site list can't be read the PO is still
filed on the uploaded site.

Nellmapius Ext 25's code was corrected from 001 to C26/27 in the data;
the intake runbook is updated to match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 22:59:30 +02:00
DEls
c0b3f5fd13 feat(users): Last active column to spot people not using the app
users.last_active_at (migration applied) is stamped at sign-in and by the
proxy in the background at most every 15 minutes per person; a failed
stamp never blocks a request. The Users list shows "Today" / "5 days ago" /
"3 months ago" / "Never", amber after a quiet week and red after an idle
month, with a count of active accounts idle 30+ days.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 22:54:07 +02:00
DEls
e6a8f97767 feat(po-email): hourly mailbox check that files new POs into MEGA
scripts/po_email_intake.py reads ai@megainfra.co.za through Mail.app, pulls the PO
documents out of unseen messages, reads the PO number, site and supplier from the
document, checks for duplicates by file hash and by PO number, and files what is new
into the same purchase_orders pending queue the browser upload uses so the existing
MEGA - Purchase Order Ingest workflow finalises it. Anything it cannot place is
flagged for review instead of being filed against a guessed site.

A scheduled task runs it every hour (7 * * * *, Africa/Johannesburg).
2026-11-02 22:37:12 +02:00
DEls
15b9618122 feat(users): site managers can correct their clock users' name and email
Site managers get an Edit action on the clock users they can see; the
dialog shows only Name and Email. PATCH /api/admin/users/[id] is now
reachable by managers (proxy-path), and the route limits them to name
and email on clock users wholly within their sites (others read as 404).
Delete, access, mail and card actions stay admin-only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 22:20:28 +02:00
DEls
3c51c68fa7 feat(users): admins can correct a user's email
The Email field in Edit user was locked and PATCH ignored it, so a typo
(e.g. "megainfra.coza") could only be fixed by deleting the account.
Admins can now edit it; the route trims, lower-cases and validates it,
only writes it when it changed, and answers 409 when another account
already uses the address.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 22:17:17 +02:00
DEls
136c318fa7 feat(auth): 6-character password minimum, set in one place
PASSWORD_MIN_LENGTH (session-policy.ts) now drives every check: the
change-password and set-password screens and routes, admin add/edit,
and the welcome email copy. Previously 10 for self-chosen and 8 for
admin-set. PocketBase's users password field min is already 6.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 22:01:06 +02:00
DEls
dcb9a5d098 fix(ui): true accent orange on light sidebar; reorder user tabs
Sidebar icons and sub-menu lines took text-primary's darkened ink, which
turns the default orange brown in light mode. They now use the accent
itself in light mode (labels stay dark for reading); dark mode keeps the ink.

Users tabs now run All, Admin, Site agents, Office, Clock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 21:53:59 +02:00
DEls
8acc27466e feat(users): welcome and password-reset emails
Admins can email a new user a welcome with a one-time link to choose
their password, and send a reset link from the users list. The sign-in
page gets "Forgot password?". Mail goes out through Resend; the emails
show the person's sign-in address.

- password_links collection (migration applied): only the SHA-256 of each
  token is stored; invites last 7 days, resets 1 hour, one use each
- /set-password and /forgot-password pages and their /api/auth routes
- forgot-password never reveals whether an account exists, throttles
  repeat mails, and builds links only from APP_URL
- env: RESEND_API_KEY, EMAIL_FROM, EMAIL_REPLY_TO, APP_URL (docs/setup/email.md)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 21:39:38 +02:00
DEls
5a44273abe Merge origin/main (plant equipment commit) into local main
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-02 17:06:37 +02:00
DEls
7db223bd54 Add Buildsite site mappings and import snapshot
Adds confirmed Halfway Gardens and Montana site ID mappings and a dated post-import database snapshot for the Hills Cedar fuel-log import.
2026-11-02 16:33:09 +02:00
DEls
ba26656630 feat(appearance): wider two-column dialog and accent-coloured sidebar
The Appearance dialog lays out in two columns from tablet width up
(mode and accent on the left, sidebar on the right) instead of one
long narrow strip; phones keep one column.

Sidebar icon and line colour becomes a three-way choice: Plain (as
before), Accent (follows the theme colour) or Per section. The old
on/off setting still reads as Per section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 16:30:18 +02:00
DEls
e42f901188 feat(sidebar): layout, folding and section colours in Appearance
The Appearance dialog gains sidebar settings, saved in this browser:
- Layout on a computer: Full (as before), Icons only, or Hidden behind
  the menu button. Full also gets a hover button to collapse to icons.
- Pages under each section: Always open (as before), Fold, or Current
  only. The section you're in always stays open.
- Colour each section (off by default).

The defaults are the original sidebar; phones always get the full menu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 16:14:46 +02:00
DEls
6e090c082a feat(users): search, role tabs and one status pill on the users list
Search matches name, email, role and assigned site. Role tabs (All, Site
manager, Admin, Clock user, plus Office user when present) show counts
that follow the search. Each user gets a single status: Pending (red,
still on the temporary password), Active (green) or Inactive (grey),
with a matching left edge on cards and rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 16:06:33 +02:00
DEls
361c4cf1ed feat(sites): list view with net sparklines and sort
Adds a Cards/List toggle to Sites. List rows follow the plant app's
income list: a smooth 12-month income-minus-spend area sparkline,
a net pill, and an expandable month-by-month breakdown. Sites can be
sorted by contract value (default) or name; both choices are
remembered per browser.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 15:55:09 +02:00
DEls
dd6e90bed3 feat: show the build commit as a faint corner mark
Declares ARG NEXT_PUBLIC_APP_COMMIT, supplied by Ship, and renders it as a
nearly transparent, click-through stamp. Renders nothing when unset.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 01:27:22 +02:00
DEls
665bb215f1 feat(plant): inactive equipment and no-rate-needed override; brighter worker week strip
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 01:15:52 +02:00
DEls
9ccfcd1714 chore(reports): add BuildSite fuel-log import runs for 2026-10-26 and 2026-10-28
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 00:58:00 +02:00
DEls
cfcfbc6056 Preserve arrival date in correction dialog
When a scan is corrected from clock-in to clock-out, retain the scan date and clear only the arrival time so users can enter the actual arrival without re-entering the date.
2026-10-30 00:51:30 +02:00
DEls
9ff13b3ce6 Track cash float spend since top-up
Replace the last-slip field with cumulative spending since the latest top-up, falling back to the opening when no top-up exists. Update the management table to display the new amount and cover the calculation with server and UI tests.
2026-10-30 00:05:47 +02:00
DEls
be890df613 r 2026-10-29 23:44:09 +02:00
DEls
6ba4f5be6c feat(workforce): allow any effective date when editing a worker's employer/broker
Backdating before the latest assignment and future dates were refused, so a
wrong start date could never be corrected. Corrections still append a revision.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 23:38:38 +02:00
DEls
e9edc1988e feat(plant): sort equipment by rate then hours this month
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-29 10:54:41 +02:00
DEls
824271cdd0 docs(agents): record the PO duplicate check and filed-label rules 2026-10-28 23:18:24 +02:00
DEls
844918da0d feat(po-email): pre-flight duplicate check and filed labels
Checks for a duplicate before filing anything: the same document at the same site is
archived, and a PO number already in the system is tagged PO Duplicate for a human
instead of being filed twice. Successfully filed mail is tagged PO Ingested and moved
out of the inbox; PO Needs Review and PO Duplicate stay in the inbox for review and are
skipped on later runs.
2026-10-28 23:17:52 +02:00
DEls
de85bf897e feat(po-email): native Outlook node build + Mail.app read route
Rebuilds the PO email intake on n8n's native Microsoft Outlook node so the mailbox
can be connected with a single sign-in instead of an Azure app registration, and
documents reading the same mailbox from Mail.app via AppleScript for one-off
backfills. Records the real-world site-matching gap: the site often appears only
inside the PDF, not in the email.
2026-10-28 23:04:16 +02:00
DEls
2c2d493e12 feat(plant): actual vs bill charts, attention card under KPIs, sidebar mark for missing rates
- Plant overview: monthly Actual vs Bill hours and Total vs Bill value across
  all equipment, using the same rules as the equipment detail tiles
  (billed-day logic moved to lib/metrics/plant-billing).
- Attention Needed moves under the KPI cards and now holds the missing hire
  rate, readings-to-review and fuel price notices.
- Sidebar Plant mark also counts equipment with no hire rate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 22:43:56 +02:00
DEls
e7c8c90e32 feat(po-email): n8n workflow + runbook to auto-file POs from the MEGA mailbox
Polls ai@megainfra.co.za through Microsoft Graph, downloads the PO attachment and
writes it into the same purchase_orders pending queue the browser upload uses, so
the existing MEGA - Purchase Order Ingest workflow OCRs and finalises it. The
source email is archived once filed; POs that cannot be matched to a site are
tagged PO Needs Review instead of being filed against the wrong site.
2026-10-28 16:07:29 +02:00
DEls
8e1cbc8b75 feat(site-map): Munyaka 2 stormwater and village network, Progress section highlight, layer switches
Munyaka model: 27 stormwater structures and 26 pipes from the issued structure table
(MH/KI/outlet by CAD block), water node points, and the information-stage village network
(services, ponds, boundary, lots, hydrants) aligned from the SDP Rev C PDF.

Map 3: selected Progress section is outlined and its row highlighted; Show-on-map gains
ground surface, contours, kerbs, markings, toes, reserve, signs, studs and HOLD switches;
the ground fades when underground services are ticked; pipes and manholes are chunkier.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 12:13:38 +02:00
DEls
d5d226e022 fix(clock): scanner picks the site from GPS on its own; correction dialog no longer blocks saving
The person scanner now reads the phone location when it opens and when the app
returns to the front, and selects the nearest site unless one was picked by hand.
The Fix missing clock time button stays available, accepts 5:00 style times and
leaves out-of-range times for the server to explain.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 10:46:11 +02:00
DEls
ff48709fee chore(deps): sync package-lock with the pinned unpdf version
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 06:50:14 +02:00
DEls
2537a0e570 feat(cash-floats): multi-page slip photos, no cost code, red low rows
- Clock slip form: no cost code (a slip is booked to its site). Each photo
  is previewed, with "Add another page" for the invoice behind a bank slip,
  up to 5 pages in order; extra pages can be removed.
- Slip API takes 1-5 photos; the photo route serves ?page=N and is now
  holder-or-admin only (the shared-login rule was left over).
- Movements sheet shows every page; CSV drops the cost code column.
- Cash Floats table: a card under its limit gets a red row, and the card
  number opens its movements.
- Sidebar: the low-card mark shows on Cash Floats only, not on Management.
- Migration: slip_photo holds up to 5 files (applied to production
  2026-10-28).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 06:39:23 +02:00
DEls
76d450aa1d chore: site certificates, site spend and cedar-gate model updates
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-27 12:23:34 +02:00
DEls
1c8bf45664 feat(plant): period filter, weekly KPI breakdowns, bill hours and same-meter review flag
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-27 12:23:34 +02:00
DEls
bb94dccfce fix(sites): split certificate chart into certified invoice and retention
Retention is about a tenth of the certified invoice, so on one shared
scale its bars were barely visible and paired bars sat off their month
labels. Each figure now gets its own chart and scale.

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