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.
Built from real manufacturing experience, demonstrated with invented data — every customer, job and figure in the screens below was written for the demonstration and belongs to no real business.

- screens, enquiry through to job cost
- 74
- functions of the business covered
- 25
- roles, from operator to accounts
- 10
- access rules the system enforces
- 122
- automated checks that those rules hold
- 1,232
- decisions recorded with their alternatives
- 55
What is in it
The whole operation, not a department of it.
Six areas, 25 functions and 74 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 · Scheduling · Quality · Maintenance
A column per work centre, labour taken off the clock, and a backward pass from the promised date that says which job has run out of room.
Business
Finance · Reports · People and roles
Actual job cost read live from the ledger and the floor, not stored and not assembled by hand.
What it runs on
Every choice here was made, not inherited.
5 runtime dependencies and 49,264 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 122 row-level security policies, in Sydney.
TypeScript
No JavaScript files, no escape hatches, and 49,264 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 74 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.