Gunny Kim · Manufacturing systems
I believe great manufacturing is built on reliable systems, not human memory.
After years of observing production floors and solving operational problems, I built this ERP around a simple philosophy.
Capture information once. Automate repetitive work. Preserve context. Build systems people can trust.

- screens, enquiry through to job cost
- 63
- functions of the business covered
- 23
- roles, from operator to accounts
- 10
- access rules the system enforces
- 120
- automated checks that those rules hold
- 1,148
- decisions recorded with their alternatives
- 52
Before this — built, run, and measured
Five of these ran on a real factory floor.
Before CompassERP I designed and built five shop-floor systems for a New Zealand cabinetry manufacturer — 20+ staff across two sites. They went into daily production use by the people they were built for, not a pilot beside the real process.
The figures beside this were measured from that use, rather than projected from what the systems were meant to do.
Locating materials
12–60×faster to find one rollTen seconds against two to ten minutes of walking between sites, across 1,020 tracked rolls.
Replenishment
−65%stock held, in aggregateEvery item given a reorder point, and a scan that raised the request. Per-item stock fell 24.6%.
Receiving and storage
96 slotsproject-mapped, across three racksWhich shelf a project's materials sat on, received in batches, with the job's paperwork attached to the bin.
Machine maintenance
7 of 7core machines, none left outTwo CNC routers, two edgebanders, two panel saws and the forklift, moved off reactive repair onto a photo-verified routine.
Production tracking
4 stagesone scan per job, end to endMachining, edgebanding, assembly and dispatch, each with a record of who did what and when.
from two of the five systems alone, while they ran. Built is not the same as used, and on a factory floor the second one is the hard part — a system people work around is a system that changed nothing.
January to July 2026, on one machine: 71 distinct faults, and 313.6 hours lost to the uncommon ones. The machine had been producing that log all along and nobody was reading it. All 71 of 71 codes were given a written description.
And then I hit the ceiling.
Five systems, and five separate sheets underneath them. A box of hinges received into one of them was invisible to the other two, so the same stock was counted three times and trusted none of them. I wrote up a merge — and writing it up was the lesson. What a factory needs is not five good tools. It is one record, and a tool that owns its own copy of the data cannot be merged into one later.
Everything below this line is that argument, built properly from the schema up.
The system
One record, from quote to delivery.
A cabinet factory runs on a quoting spreadsheet, a CAD file, a whiteboard, a supplier email thread and an accounting package that knows none of it — and every handover is retyped. I mapped that operation end to end and built the system that replaces it: 23 functions across 63 screens, quote through to delivered cost.
By connecting quoting, purchasing, production, inventory, delivery, and costing through a single integrated record, work flows naturally, information stays connected, and every handover happens with confidence.
The delivery date has to be derived, not chosen.
A date agreed because the customer asked for it is a promise nothing in the business was consulted about. Deriving it means the quote, the bill of materials, the stock ledger and the production board have to be the same record. That is the whole design.
What it looks like
Built for gloves and for spreadsheets.
Office users read forty-row tables all day; operators read at arm’s length under hard light with their hands full. Those are different problems, so the type scale sits at 14px for density while shop floor targets stay at 44px, and no status is ever carried by colour alone.
Items below their reorder point. Raise a purchase order.
Raised, not yet approved by somebody holding that right.
Promised before today and not fully received.
Receipts, issues and corrections posted to the ledger.
Shortages
Worst first| SKU | Item | Available | Reorder at | Unit |
|---|---|---|---|---|
| PB-18-WHT | Particleboard 18mm White Melamine 2400×1200 | 12 | 40 | SHT |
| EDG-22-WHT | Edge tape 22mm White ABS | 85 | 250 | M |
| HNG-CLIP-110 | Concealed hinge 110° clip-on, soft close | 46 | 200 | EA |
| MDF-16-RAW | MDF 16mm Raw 2400×1200 | 7 | 25 | SHT |
| RUN-BM-500 | Drawer runner blumotion 500mm full extension | 18 | 60 | PR |
Quote outcomes
618 quotesPurchase orders by status
357 raised- Fully received261
- Partially received41
- Sent to supplier29
- Approved14
- Awaiting approval12
All four panels are live components from the application, rendered with the demonstration dataset as at 5 August 2026 — 366 items, 61 customers, 20 suppliers, and a year of trading behind them.
How it works
Type it once. Everything after is derived.
Engineering is the pivot point of the whole system. Purchasing, scheduling, machine programs and costing all come out of what is decided there — so if that step is manual, everything after it is manual too.
One worked example carried the whole way through: a 1200 mm base cabinet, from the moment somebody types its width to the margin it earns. Every figure below is one the system returns, not an illustration.
- 01
Configuration
1200 mmwidth, typed onceThe only place anybody enters a dimension.
- 02
Bill of materials
8 linesresolved with their sizesA 1200 shelf becomes 1164 — it loses two carcass thicknesses.
- 03
Cut list
27 panelsfrom 3 cabinetsNeeds 6 sheets: 15.37 m² of panel against 2.88 m² a sheet.
- 04
Material demand
nettedagainst live stockWhat has to be bought, before it becomes an emergency.
- 05
Purchase order
2 rightsraise, and approveApproval is a separate right, and the database enforces it.
- 06
Work order
start / finishlabour off the clockTwo buttons stamp who started it and who finished it.
- 07
Cost and margin
read, not storedlive from the ledgerJob cost nobody assembled by hand.
- One specification, seven artefacts.Nobody retypes anything between them. That is the whole system.
What is in it
The whole operation, not a department of it.
Six areas, 23 functions and 63 screens, carrying one job from the first enquiry to what it finally cost. They share one vocabulary, and that is a working decision rather than a tidy one: a word that means two things on the floor becomes two fields in the system, and two fields that should agree eventually do not.
Commercial
Enquiries · Projects and jobs · Customers · Quotes · Sales orders
First contact through to a numbered project, and every job inside it running its own document chain.
Engineering
Product models · Configurations · Bills of materials · BOM import · Cut lists
Where the record begins. A model is a rule; a configuration is one instance; resolution is a parts list.
Procurement
Purchase orders · Receiving · Material demand · Suppliers
Approval is a separate right from raising, and the database is what enforces it.
Materials
Item master · Inventory · Edge tape rack · Stock movements · Stock counts
None of these screens edits a balance. Posting a movement is the only way stock changes.
Production
Live board · Calendar · Work orders · Quality · Maintenance
A column per work centre, labour taken off the clock, and defects logged against a cause rather than a count.
Business
Finance · Reports · People and roles
Actual job cost read live from the ledger and the floor, not stored and not assembled by hand.
How it holds up
The controls are in the system, not in the training.
An ERP is trusted with the numbers a business makes decisions on, so the guarantees have to sit somewhere nobody can step around them — not in a procedure a person is expected to remember, and not in a screen that hides a button.
These are the four that shape everything else.
Approval is a control, not a courtesy.
Raising a purchase order and approving one are separate rights, held by separate people. The system refuses the second to somebody who only holds the first — and refuses it in the data, so the answer is the same whether the request arrives from a screen, an import or a report. A hidden button is a convenience. It has never been a control.
Stock changes one way only.
No screen edits a balance. A quantity moves only by posting a movement to the ledger, which means every figure has a document behind it and a count can always be explained. This is the difference between an inventory number somebody trusts and one they check against the shelf before ordering.
The rules are checked, not asserted.
Each rule is tested where it is actually enforced rather than against a stand-in, and every one of the 26 database test files signs in as a real person holding real roles. So what the tests prove is what an operator, a buyer or an accounts clerk would meet on the day — not what a mock was told to say.
Every decision is written down.
Each one records the situation, what was chosen, what was rejected and what it costs. A system nobody can explain the shape of is a system nobody can safely change — and in an ERP the expensive failures are almost never the first build, they are the fifth change made by somebody who did not know why the third one was made.
What it runs on
Every choice here was made, not inherited.
5 runtime dependencies and 45,466 lines of TypeScript. Each dependency is one more thing that can break in the middle of a production run, so the standing policy is to prefer fewer and to be able to say why each one survived.
Next.js 16.2.12
Server components and server actions. Every form works without JavaScript.
React 19.2.4
Rendering only. No business rule and no query lives in a component.
Supabase — Postgres
Data, auth and 120 row-level security policies, in Sydney.
TypeScript
No JavaScript files, no escape hatches, and 45,466 lines of it.
Vercel
Push to main deploys. Database migrations go first, always.
pgTAP and Vitest
A rule is tested where it is enforced, never against a mock.
Considered and rejected
- A packaged ERPConfigured-to-order cabinet work does not fit generic manufacturing modules.
- A separate backend serviceAn extra deployment and an extra API boundary for no gain at this size.
- A component libraryDense operational tables usually mean fighting the library.
- NoSQLERP data is deeply relational. This is the case the relational model was built for.
Get in touch
Open to conversations about the work, or about what comes next.
Go and look at it.
A year of trading is already in there — 288 orders, 618 quotes, 357 purchase orders carried through approval and receiving, and 177 work orders across 708 operations. Nothing is a placeholder.
What you can see
Every quote, order, work order, machine, maintenance log, partner, item, stock balance and rack slot — every one of the 63 screens that the account's role reaches.
What it refuses
Every write, and supplier costs. The account holds viewer, so the database returns a policy violation rather than the screen hiding a button. Finance and Reports say so in words.