Grow ERP
An ERP for animal feed distributors: point of sale, e-invoicing, stock per branch, delivery routes and accounts.

A pet and animal feed distributor ran its business across spreadsheets, notebooks and separate programs. It sells at the counter and wholesale, from more than one branch, and every invoice has to go through AFIP/ARCA. Orders get approved, picked in the warehouse and delivered by truck. The owner is not a programmer, so the system also had to be something the business could run and keep alive on its own.
Three decisions that shaped it.
- 01
Server Actions as the business layer, checked per action
Why. The domain lives in 76 action files that pages call directly. Each one starts with protectAction and the list of roles allowed. Hiding a menu item is only visual; typing the URL by hand still hits the check. Nine roles, from owner to delivery driver, share one app.
Trade-off. There is no general REST API. The online store and uploads need their own routes with separate auth, and a new action without the check would go unprotected.
- 02
Customer debt is kept twice, on purpose
Why. The account ledger is the source of truth for a customer's total balance. Each invoice also keeps its own pending amount, so payments can be applied to specific invoices and debt can be aged.
Trade-off. Every money flow has to write both. If one is missed, screens disagree, so there is a reconciliation script that compares them.
- 03
One server the owner controls, plus a guide to run it
Why. The app, Postgres and Caddy run together with Docker Compose on a single VPS. A nine-chapter guide covers setup, backups, AFIP certificates and how to make changes with an AI coding assistant. A pre-push hook runs type checks, lint, tests and the build.
Trade-off. One server means no redundancy, and backups depend on a cron job someone has to check.




Grow ERP
An ERP for animal feed distributors: point of sale, e-invoicing, stock per branch, delivery routes and accounts.
- Role
- Full-stack development and maintenance, from the database schema to the server it runs on
- Stack
- Next.js 15 (App Router), React 19, TypeScript, Prisma + PostgreSQL, TanStack Table / Query, AFIP SDK, Docker Compose + Caddy, Vitest + Playwright

A pet and animal feed distributor ran its business across spreadsheets, notebooks and separate programs. It sells at the counter and wholesale, from more than one branch, and every invoice has to go through AFIP/ARCA. Orders get approved, picked in the warehouse and delivered by truck. The owner is not a programmer, so the system also had to be something the business could run and keep alive on its own.
Three decisions that shaped it.
Server Actions as the business layer, checked per action
Why. The domain lives in 76 action files that pages call directly. Each one starts with protectAction and the list of roles allowed. Hiding a menu item is only visual; typing the URL by hand still hits the check. Nine roles, from owner to delivery driver, share one app.
Trade-off. There is no general REST API. The online store and uploads need their own routes with separate auth, and a new action without the check would go unprotected.
Customer debt is kept twice, on purpose
Why. The account ledger is the source of truth for a customer's total balance. Each invoice also keeps its own pending amount, so payments can be applied to specific invoices and debt can be aged.
Trade-off. Every money flow has to write both. If one is missed, screens disagree, so there is a reconciliation script that compares them.
One server the owner controls, plus a guide to run it
Why. The app, Postgres and Caddy run together with Docker Compose on a single VPS. A nine-chapter guide covers setup, backups, AFIP certificates and how to make changes with an AI coding assistant. A pre-push hook runs type checks, lint, tests and the build.
Trade-off. One server means no redundancy, and backups depend on a cron job someone has to check.



