Broo ERP Broo Café's ERPProduct · Full-stack · Integrations · AI · 2026

Portfolio / Traceability and costing ERP

From green coffee to the order, at real cost.

Broo Café buys green coffee from a farm, sends it to a partner roaster, packs whole beans, drip bags and cold brew, and sells mostly through WooCommerce, plus WhatsApp. We designed and built an ERP that follows every lot from the farm to the shipped order and turns that path into real cost, margin per order and customer relationships.

Client
Broo Café
Industry
Specialty coffee · Colombia
Product
Internal web application with five roles
Scope
Product, domain modelling, UX/UI, full-stack, integrations and AI
Stack
Next.js, React, TypeScript, Drizzle, Neon, Vercel
Year
2026
Broo ERP home screen: a navy sidebar grouped into Operación, Comercial, Análisis and Sistema; the month-to-date greeting and cards for net sales, contribution, estimated profit and ad spend, with the sales trend and the channel donut below.

01The challenge

A full coffee supply chain in a small brand.

Broo doesn't buy coffee ready to sell. It buys green coffee from a farm in Antioquia, has it roasted elsewhere, loses weight in the roast, buys naturally processed coffee already roasted, and packs several bag sizes, 15-sachet drip kits and 700 ml cold brew. Every step changes the cost of the coffee that ends up in a bag, and that cost lived across invoices, spreadsheets and the team's memory.

The business questions were specific: what each lot cost to produce, how much inventory is left, what each order leaves after coffee, packaging, the payment gateway fee and the shipping subsidy, and what customer acquisition budget is sustainable. All without mistaking revenue for profit, and with WooCommerce as the main channel.

What had to be coordinated

  • 01Green coffee with farm, variety, process and origin
  • 02Outsourced roasting and its weight loss
  • 03Recipes and production orders
  • 04Packaging per unit sold
  • 05WooCommerce, WhatsApp and event orders
  • 06Dispatch, tracking and labels
  • 07Customers and consent (Law 1581)
  • 08Order emails and campaigns
  • 09Expenses, ad spend and profitability

Our role

Domain and product modelling, architecture, visual system and interface, frontend and backend development, integrations (WooCommerce, Resend, Gemini, private file storage and n8n), automated tests, in-app user guides and deployment.

02The rule

The decision that shaped the whole system.

Inventory is a sum. Cost gets frozen.

There is no stock counter anyone can edit. Each lot's balance is the sum of movements that are never modified: a correction is an audited reversal, not an overwritten number. When an order ships, the cost of every lot that left —coffee and packaging included— is frozen on that line and stays put even if green coffee goes up tomorrow.

The same idea assigns an owner to each piece of data: WooCommerce owns the web order, and the ERP is the only owner of publishable stock. Stock is never imported from the store, so there are no sync loops.

Traceable to the farmReal cost, not estimatedOne source of stockAI proposes, people approve

03The life of a lot

From the farm to dispatch.

Every stage writes movements to the same ledger and links the new lot to the lots it consumed. That's why a bag that was sold can be traced back to the green coffee it came from, with the cost each input contributed.

  1. Farm

    Green coffee with origin
  2. Partner roaster

    Outsourced roasting
  3. Production

    Approved recipe · real cost
  4. Ledger

    Immutable movements
  5. WooCommerce

    Web orders
  6. Resend

    Order emails
  1. 01

    Buying from the farm

    Receiving creates a green coffee lot with farm, variety, process and origin, and a landed cost: price times exchange rate plus prorated extras.

  2. 02

    Outsourced roasting

    Green coffee leaves for the roaster and comes back roasted. The system works out the weight loss and the cost per usable kilo: green coffee, transport, roasting and extras, divided by what can actually be packed.

  3. 03

    Production

    An order on an approved recipe consumes lots first in, first out —first expired, first out for perishables—, records waste and costed labour time, and closes at real cost with a snapshot of planned against actual.

  4. 04

    Order

    It comes in from WooCommerce, from WhatsApp, as a manual sale or from an event or fair. Once payment is confirmed, stock is reserved; if a product is made to order, like cold brew, its production runs in a single step.

  5. 05

    Dispatch

    Stock is deducted exactly once and the cost of each lot, with its packaging, is frozen on the line. Carrier, tracking number and real shipping cost are recorded, and the 4×6 label prints.

  6. 06

    Emails and profitability

    The customer receives the order journey emails, and the order enters finance with its contribution: net revenue minus cost, gateway fee and shipping subsidy.

The figures in the screenshots come from the repository's test scenario: 10 kg of green coffee, 8.5 kg usable, 15% roast loss. They are invented data, not Broo's costs.

04Operations

Where the cost is built.

05The data model

Six decisions you don't see.

89 tables across 10 versioned migrations, split by domain: identity, purchasing, inventory, production, sales, CRM, marketing, finance, AI and files. This is what makes the numbers add up.

01 · One ledger

Green coffee is just a lot.

A green coffee lot has no table of its own: it's a raw-material lot linked to farm, variety, process and origin. So there is a single movement ledger for everything, from green bean to bottle.

02 · Movements

Balance = sum.

Every movement carries an idempotency key and, if it corrects another one, a link to the reversal. Inventory never goes negative without an explicit permission.

03 · Genealogy

What came from what.

Each produced lot stores the lots it consumed, with quantity and contributed cost. Tracing a bag back to the farm means walking that relationship backwards.

04 · Frozen cost

A snapshot, not a live calculation.

Production stores planned against actual cost; dispatch allocates lots to each line with their cost and packaging. If green coffee goes up tomorrow, what sold yesterday doesn't change.

05 · Precision

Exact pesos and grams.

Money, quantities and unit costs use fixed decimals and decimal arithmetic, never floats. Base units are grams, millilitres and units, and mass is never converted to volume.

06 · Consent

Append only.

Every change in a customer's per-channel consent is a new row; the latest one is current. The history of what a customer agreed to, and when, is never lost.

06Sales and dispatch

What each order actually leaves.

07Channels and relationships

Every integration has one job.

WooCommerce

Orders without loops.

Incremental import of products, customers and orders, signed webhooks that are verified and deduplicated, and a per-order lock so a resent event never lands twice. Orders placed before go-live come in as history, without touching stock.

WhatsApp, manual and events

Direct sales, same costing.

Orders that don't go through the website are created in the panel with their channel, recipient and dispatch, and they deduct and cost exactly like a store order. WhatsApp opens with one click; nothing is sent automatically.

CRM · Law 1581

Consent per channel.

Customer profiles, dynamic segments recalculated daily, CSV exports limited to people who consented to that channel —protected against formula injection— and anonymisation for data-subject requests under Colombian law, keeping the orders.

Resend

Email within limits.

A batched queue with daily and monthly caps, priority for order emails, one-click unsubscribe and automatic suppression after bounces or complaints, driven by signed events.

Private storage

Invoices and evidence.

PDF or image attachments, up to 4 MB, on purchases, production, lots and orders. They download only with read permission on the record they belong to.

n8n

Look, don't touch.

A read-only view of workflows and their runs inside Marketing, to check the automation without leaving the panel.

08Finance

Revenue is not profit.

09Artificial intelligence

An analyst that reads, not one that writes.

The assistant uses Gemini with 7 fixed tools: each one is a parameterised, read-only SQL query that we wrote. The model chooses which one to call and with which dates; it never writes SQL and never changes data. If a model errors out or takes too long, the server falls back to the next one in a chain.

It also proposes drafts —an email, a WhatsApp message, a creative brief, a segment suggestion, a reply to a customer and a product description—, 6 kinds in all. None of them goes out on its own: each waits for someone with approval permission and, once approved, becomes an inactive template that a person decides to use.

The tools

  • 01Period KPIs
  • 02Profitability by SKU
  • 03Stock status
  • 04Campaign performance
  • 05Segment summary
  • 06Reorder and coverage suggestions
  • 07Recent production costs

No screenshot, on purpose

The screenshots in this case were taken with AI switched off. We don't show an assistant answer because we're not going to fake one.

10Roles and permissions

Five roles, checked on the server.

28 granular permissions —view, edit, approve recipes, authorise negative inventory, export, anonymise, approve AI drafts— spread across 5 roles. Every page and every server action requires its own: hiding a button is never the only barrier.

Administrator

The whole system.

Users, settings, integrations and the audit log. The only role that can anonymise a customer.

Operations

From green coffee to dispatch.

Purchasing, roasting, inventory, production and order dispatch.

Marketing

Customers and campaigns.

CRM, segments, templates, campaigns and consent-based exports.

Finance

Read everything, own the costs.

Read access to every module except the audit log, plus expenses, scenarios and editing the catalog and its prices.

Read-only

Look without changing.

View-only access for anyone who needs to see how the operation is doing.

11Security and privacy

Every action has an author.

Authentication is custom and has no public sign-up: database sessions with an opaque token stored as a hash, an httpOnly cookie, lockout after failed attempts, one-time access links that expire after 48 hours, and a forced password change whenever an administrator sets a temporary one.

Raw database errors never reach the screen: they're mapped to a message and a code. Webhooks are verified with constant-time comparison, relevant actions land in an audit log, and the AI tools return aggregated data, not customer records.

The model

  • 01Permissions on every page and action
  • 02Session token stored as a hash
  • 03Lockout by email and IP
  • 04One-time access links
  • 05Signed, deduplicated webhooks
  • 06Strict security headers
  • 07Formula-safe exports
  • 08Audit log of actions

12Visual system

The store's brand, inside the panel.

Broo Orange

Primary action · Active state · Focus

HEX
#FF6A00
RGB
255 · 106 · 0
Orange Ink

Orange text on light backgrounds

HEX
#C24F00
RGB
194 · 79 · 0
Navy

Sidebar · Headings · Charts

HEX
#062149
RGB
6 · 33 · 73
Copper

“ERP” tag on navy

HEX
#C8915F
RGB
200 · 145 · 95
Cream

Application background

HEX
#FAF9F8
RGB
250 · 249 · 248
Sand

Secondary surfaces · Hover

HEX
#F5F3F1
RGB
245 · 243 · 241

13The way in

An internal panel with a brand voice.

14The principles

The language of the operation, not of software.

01 · Typography

Mattone to headline, Kanit to read.

The panel uses broocafe.co's display face for headings and figures, and Kanit for text, tables and forms, with small-caps labels and tabular numbers. On this page the headlines are set in Unbounded: Mattone's licence isn't confirmed for use outside the panel.

02 · Nothing is assumed

No data? It says so.

Costs, recipes, taxes and shelf life are settings the team enters; the system invents none of them. A figure with no baseline reads “sin base”, and an empty simulator field stays empty.

03 · Help in context

A guide for this screen.

Every screen has its own guide, one button or the “?” key away, and a help centre collects the walkthroughs by module, from first setup to the weekly review.

15Architecture

Business rules you can test.

Domain services take the database as a parameter and run without Next.js. That way tests execute the same SQL as production against an embedded Postgres, with no Docker.

  1. Next.js 16

    App Router · Server Actions
  2. Services

    Rules · permissions · audit
  3. Neon Postgres

    Drizzle · transactions
  4. Job queue

    Outbox · retries
  5. Gemini

    Read-only
  6. Vercel

    Deployment · storage
  1. 01

    The interface

    Next.js 16 with the App Router, React 19, TypeScript and Tailwind CSS 4. Mutations go through Server Actions that check the permission first.

  2. 02

    The domain

    One service per module validates, calculates with exact decimals and writes in a transaction, locking the rows that could be overwritten.

  3. 03

    The database

    Drizzle on Neon, with connections that support interactive transactions. Locally and in tests, PGlite: the same Postgres, embedded.

  4. 04

    Async work

    A Postgres-backed queue with exponential backoff, recovery of stuck jobs and a final state for whatever couldn't be processed. Integrations and email go through it.

  5. 05

    Quality

    More than 160 automated tests against real Postgres and 4 end-to-end flows on a production build. On every change, CI checks migrations, lint, types, tests and the build, with no secrets.

Resend sends the email and Gemini answers the assistant; both are called from the server only. Keys live in the deployment environment, never in the browser.

16Evidence of depth

Technical scope, not a sales result.

Figures counted on the repository. They describe the size of what was built, not sales, hours saved or adoption, which will be measured once the system has been running for a while.

28
permissions across 5 roles, checked on the server
89
tables across 10 versioned migrations
160+
automated tests against real Postgres
4
end-to-end flows on a production build
7
read-only AI tools
~8
days from first commit to the complete system

17Where it stands

Built, tested and in initial operation.

The first commit is from 12 October 2026; by the 19th the system had every module, hardening for day-to-day operation, end-to-end tests and user guides. Purchasing, roasting, production, inventory, sales, labels, CRM, the customer journey, finance and the assistant are built and covered by tests.

What is still off is off on purpose. Pushing stock to WooCommerce waits until physical inventory is reconciled; the n8n trigger every ten minutes is inactive and, in the meantime, a daily fallback runs. Relationship automations —welcome, repeat purchase and win-back— ship inactive until the owner turns them on, and WhatsApp marketing is hidden.

Real costs, the cold brew recipe and the labour rate are still being entered, so profitability figures will fill in with operation. There is no point of sale and no electronic invoicing: they weren't in scope.

Planned or switched off

  • 01Pushing stock to WooCommerce
  • 02n8n trigger every 10 minutes
  • 03Relationship automations
  • 04WhatsApp marketing
  • 05Loading real costs and recipes
  • 06VAT and e-invoicing decision

18The result

What changes when cost becomes visible.

Knowing what each order leaves.

Broo now has a foundation for answering with data what used to be estimated: which farm the coffee in a bag came from, what it cost to roast and pack, and what was left of the order after coffee, packaging, fees and shipping.

Everything else is decided on top of that: how much to spend on ads, what to restock and which customers to write to, always with a person approving what goes out.

TraceableCostedAudited

beleafdesign

Do you know what each order leaves you, or only what you bill?

We design custom systems that work with what you already use —your store, your email, your suppliers— and turn the operation into numbers you can check. Let's look at how your business runs today.

Let's talk about your project