Splits the Running Costs group into six purpose-built pages, each with its own
collection and its own source data, plus a coordinate parser for purchase orders.
Insurance (R503 330/yr, 20 vehicles)
A premium can move every month or three, so it is stored as a RATE PERIOD in
insurance_rates and each month's cost is resolved against the rate in force
that month. cost_lines insurance rows are derived from those periods. An absent
rate is null, never 0 — nobody has entered the date, which is not the same as
being insured for free.
Financing (20 agreements, R428 585/mo)
Instalment from the sheet; start date, paid-off date and the settled toggle are
human-entered and preserved across re-seeds. Months-left and outstanding are
null until a date is entered, never R0 on a live debt. The two paid-off
agreements (WB02, DT01) show as settled and are excluded from the run-rate.
Purchases + Oil Stock
Uploads PO PDFs the way /income/invoices does. The parser reads columns by
x-position, because plain text extraction reorders them, and refuses any PO
whose lines do not sum to the sub-total. A cost code either names a machine
("SS-02" -> SS02) or a stock store ("MPLANT, ENGOIL") — never both, never
guessed. Oil POs move litres into oil_stock_movements instead of onto a machine;
stock is the sum of movements so the first usage is a subtraction from history.
Operators (19) and Mechanics (2)
Both are people with an employee number, separately reconcilable by the seed so
someone added in the app survives. Mechanics get a badge QR; a mechanic with no
employee number cannot be printed, because the code is what the QR encodes.
Cross-cutting
- The seed no longer deletes operators, mechanics or oil items: people are added
in the app, and those rows exist in no file.
- Re-pointing allocations that PocketBase clears because `assets` is rebuilt
with new ids each seed (operators, purchase lines).
- One QR encoder for screen and PDF, so a printed code cannot drift from the one
on screen. pdf-lib writes real vector PDFs; pdfjs-dist only reads them.
- Every figure is null-safe: unknown is never rendered as zero.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
54 lines
1.6 KiB
JSON
54 lines
1.6 KiB
JSON
{
|
|
"name": "mega-plant",
|
|
"version": "0.1.0",
|
|
"private": true,
|
|
"scripts": {
|
|
"dev": "next dev --webpack -H 0.0.0.0 -p 6007",
|
|
"build": "next build --webpack",
|
|
"start": "next start -H 0.0.0.0 -p 6007",
|
|
"lint": "next lint",
|
|
"typecheck": "tsc --noEmit",
|
|
"test": "node --import tsx --test lib/*.test.ts",
|
|
"setup:auth": "node --env-file=.env.local scripts/setup-auth.mjs",
|
|
"seed": "node scripts/seed.mjs"
|
|
},
|
|
"dependencies": {
|
|
"@ducanh2912/next-pwa": "^10.2.9",
|
|
"@radix-ui/react-dialog": "^1.1.4",
|
|
"@radix-ui/react-dropdown-menu": "^2.1.4",
|
|
"@radix-ui/react-label": "^2.1.1",
|
|
"@radix-ui/react-select": "^2.1.4",
|
|
"@radix-ui/react-slot": "^1.1.1",
|
|
"@radix-ui/react-tabs": "^1.1.2",
|
|
"@radix-ui/react-tooltip": "^1.1.6",
|
|
"@types/leaflet": "^1.9.22",
|
|
"class-variance-authority": "^0.7.1",
|
|
"clsx": "^2.1.1",
|
|
"leaflet": "^1.9.4",
|
|
"lucide-react": "^0.469.0",
|
|
"next": "^16",
|
|
"pdf-lib": "^1.17.1",
|
|
"pdfjs-dist": "^4.10.38",
|
|
"pocketbase": "^0.27.3",
|
|
"qrcode": "^1.5.4",
|
|
"react": "^19.0.0",
|
|
"react-dom": "^19.0.0",
|
|
"react-leaflet": "^5.0.0",
|
|
"react-leaflet-cluster": "^4.1.3",
|
|
"recharts": "^2.15.0",
|
|
"tailwind-merge": "^2.6.0",
|
|
"xlsx": "^0.18.5"
|
|
},
|
|
"devDependencies": {
|
|
"@tailwindcss/postcss": "^4.0.0",
|
|
"@types/leaflet.markercluster": "^1.5.6",
|
|
"@types/node": "^22.10.0",
|
|
"@types/qrcode": "^1.5.6",
|
|
"@types/react": "^19.0.0",
|
|
"@types/react-dom": "^19.0.0",
|
|
"postcss": "^8.4.49",
|
|
"tailwindcss": "^4.0.0",
|
|
"tsx": "^4.19.2",
|
|
"typescript": "^5.7.2"
|
|
}
|
|
}
|