AquaOps
Plant operations · gate to QC report
Fish meal & fish oil plants

One weighment. Every register updates itself.

A truck crosses the weighbridge once. AquaOps posts that number to inventory, opens the QC queue, and feeds the production batch — so the books cannot drift apart, because there is only one book.

8 operational modules 1 command to deploy 0 double entry
Weighbridge docket GE-050
VehicleTN 40 DK 3456
PartyBlue Ocean Marine
Gross26.20 MT
Tare6.00 MT
Net20.20 MT
posts to
InventoryRaw Fish (Intake) · stock card + ledger row +20.20 MT
Fish IntakeQC queue row opened against the entry awaiting grade
Productionavailable to Batch B-024 on Line A ready to issue
The problem
Every register below records some part of the same truck.

A plant keeps six books. None of them agree by Friday.

Each one is written by a different person, at a different hour, from a slip that has already been copied twice. Nothing is wrong on purpose — the numbers simply have no reason to match, and no one finds out until stock is counted.

01Gate registervehicle in / out
02Weighbridge slipgross · tare · net
03Intake logspecies · grade
04Stock bookreceipts · issues
05Production sheethourly readings
06Boiler logsteam · fuel burn
The chain
One entry, four consequences. Each step writes the next one's input.

Record it once, and the rest is bookkeeping the system does.

This is the whole idea. A weighment is not a note in a register — it is an event, and every downstream book is derived from it.

01

Gate Entry

The officer records vehicle, party, material and two weights. Net is arithmetic, not a judgement call. Completing the entry is the only manual act in the chain.

GE-050gross 26.20 − tare 6.00net 20.20 MT
02

Inventory

The entry posts itself as a receipt against the mapped material, converting units on the way in — diesel booked in MT lands in the stock card as litres. A ledger row records the balance the receipt left behind.

+20.20 MT Raw Fishbalance carried forward
03

Production

Starting a batch issues raw fish. Logging an hourly interval issues packaging — bag counts derive from the output weight, nobody types them. Readings outside the normal band raise an alert as they are saved.

−38.70 MT raw fish−23 HDPE bagsout-of-range alert
04

QC report

Grading is computed from the intervals and the boiler readings that belong to the batch's day — moisture and oil averaged, protein derived, the grade assigned against the spec. The report carries its own corrective actions.

moisture · oil · proteingrade A / B / C / Fcorrective actions
The inventory screen, with a strip of the latest gate receipts above the stock cards

Inventory, mid-shift. The strip along the top is not a summary someone typed — it is the last three gate entries, posting themselves as receipts.

The film
83 seconds, no sound needed.

Watch a shift move through it.

The truck at the gate, the receipt posting itself, the ledger row it leaves behind, the batch it feeds, and the report at the end of the day.

Every figure on screen is from the demonstration operating day the system seeds on first boot.

Audit trail
Every row is written inside the transaction that caused it.

Every gram carries its reason.

A stock figure on its own is a claim. A stock figure with the movement that produced it, the batch that caused it and the person who logged it is a record — and that is what a buyer's auditor asks for.

The stock movement ledger, showing date, material, source, type, quantity, running balance, linked record and operator

Stock movement ledger — the source column distinguishes a gate receipt from a production issue from a manual adjustment.

Balance after every move

The running balance is stored on the row, not recomputed later. Reading history never depends on replaying it.

Linked to its cause

Bags point at the batch. Coal points at the boiler session. A receipt points at the gate entry. Nothing floats.

Guardrails
Enforced where it counts — in the database, not in the form.

The rules hold even when two people are wrong at once.

Validation in a form is a suggestion. These are constraints the database itself refuses to break, so a second operator on a second screen cannot undo the first one's work.

Stock never goes negative

A deduction larger than the balance clamps at zero — and the ledger still records what was asked for, so the discrepancy is visible instead of silently absorbed.

One running batch per line

Starting a second batch on a line that is already running is rejected, not warned about.

partial unique index on (line) where status = 'Running'

Concurrent weighments cannot collide

Every movement locks the material row for the length of its transaction, so two simultaneous postings queue instead of overwriting one another.

select … for update, inside one transaction

Thresholds raise their own alerts

Crossing a minimum downward creates the alert as part of the movement — there is no sweep job to miss it, and no one has to notice.

Modules
Access is per module. An operator opens the plant and sees their station.

Eight modules over one database.

Not eight systems that sync. One schema, with eight ways into it — which is why the gate officer's entry is already in the supervisor's stock card.

Dashboard

Today against yesterday, every line's status, boiler uptime, low stock, a seven-day output chart and the day's activity.

Supervisor · Admin

Gate Entry

Weighbridge capture, material routing, and a printable ticket for the driver.

Gate officer

Fish Intake

Species, grade and allocation to batches, with photos and documents attached to the landing.

Supervisor · QC

Production

Batches per line, hourly intervals, and the packaging and raw-material issues they cause.

Line operator

Boiler & Utilities

Steam and pressure readings, fuel burn, chemical dosing and blowdown logs.

Boiler operator

Inventory

Stock cards and the movement ledger, with manual adjustments kept distinguishable.

Stores · Supervisor

QC / QA

Computed grading against spec, with corrective actions attached to the report.

QC

Reports

Batch and shift reporting, printable and exportable to CSV, with the numbers traced back to their movements.

Supervisor · Admin
The boiler module showing hourly steam readings, fuel burn and dosing logs

Boiler & Utilities — hourly readings; fuel burn issues itself from stock.

The QC module showing computed batch grading against specification

QC / QA — grading computed from the batch's own intervals and readings.

Deployment
Runs on the plant's own server. No cloud dependency required.

One command on the box in the office.

Three containers — Postgres, the API, and nginx serving the built front end. Migrations apply themselves at boot behind an advisory lock, so several API replicas can start at the same time without racing each other.

plant-server
$ docker compose up -d
 db   postgres 16
 api  fastify
 web  nginx

# the plant grew — add API capacity, same schema
$ docker compose up -d --scale api=4

Stateless API

Sessions live in Postgres and are revocable, so any replica can serve any request.

Replica-safe boot

One advisory lock decides which replica runs the migrations; the others wait and carry on.

Round-robin front door

nginx re-resolves the API service, so replicas added later are used without a restart.

LayerWhat it runs on
Front endReact 18 · Vite 6 · Tailwind 4 · React Router 7 · Recharts
APINode · Fastify 5 · TypeScript · Zod · JWT with revocable sessions
DataPostgreSQL 16 · Drizzle ORM · plain-SQL migrations
DeliveryMulti-stage Docker builds · nginx · docker compose
Verification7 integration suites · 40 tests against a real Postgres · browser smoke test in CI
OperationsPrometheus metrics · OpenAPI 3 + Swagger UI · structured audit trail · CSV on every list and report
Procurement
What the IT questionnaire asks, answered in the product.

Configured to your plant. Built to pass the IT questionnaire.

Nothing customer-specific is compiled in. Lines, gate materials, stock routing, every alert threshold and every role are rows an administrator edits. Around them sits the plumbing a buyer's IT team will ask about.

Your lines, your materials

One line or twelve. Any gate material, routed into any stock item with its unit conversion. Limits that grade QC and raise live alerts.

Roles that bite

Admin, Supervisor, Operator plus per-user modules, enforced in the API. Suspend a user and their session ends on the next request.

Audit trail

Who did what, from where, to which record, with the diff, written in the same transaction as the change. Exportable.

Passwords done properly

bcrypt, complexity policy, expiry, forced first-login change, e-mail reset links, idle timeouts, rate-limited sign-in.

Open data

CSV from every list and report, a full JSON export, and a documented REST API with Swagger UI for ERP and Tally integration.

Your instance

One isolated stack per plant, hosted in your region or on a server in the office. Health probes, metrics and backups included.

First boot
An empty database seeds itself through the same services the app uses.

It arrives with a day already running.

On an empty database AquaOps writes a demonstration operating day — gate entries, a batch mid-run, boiler readings, the stock they consumed — through the real service layer rather than raw inserts. If the seed completes, the business logic worked; the first boot is also the first test.

9

materials tracked, from raw fish to filter cloth

3

roles — admin, supervisor, operator — gated per module

40

integration tests run against a real database, not mocks

Seeding is skipped once the database has data, and can be switched off entirely for a production install.