BrewCalc — Methodology

Coffee Cost Tracker · lab.johlem.net/coffee

Purpose

BrewCalc tracks shared coffee costs in a workplace setting. When colleagues share a coffee machine and take turns buying beans, it becomes difficult to track who owes what. BrewCalc solves this by recording coffee bag purchases and individual drink consumption, then computing a fair cost split every time a bag is finished.

The tool calculates a per-drink cost (bag cost divided by the drinks served from that bag), multiplies by each person's consumption, and offsets against what they paid for bags. The result is a simple balance: credit (overpaid) or debt (underpaid). Settlements close automatically when a bag is emptied — either manually or when a new bag is opened.

Architecture

BrewCalc uses the default lab.johlem.net stack:

Roles

Data Model

TablePurposeKey Fields
groupsWorkspace per baristaname, created_at
usersLogin accountsusername, pin_hash, role, group_id, person_id
peopleCoffee drinkers (group members)group_id, name, emoji
coffee_bagsBag purchasesbuyer_id, total_cost, name, is_empty, emptied_at
drink_logsIndividual drinks (attributed to a bag)person_id, bag_id, created_at
settlementsClosed bag settlementsgroup_id, bag_id, mode='bag', closed
creditsManual balance adjustments / cash transfersperson_id, amount, note
bag_ratingsPer-person bag ratings + tasting notesbag_id, person_id, stars, note
bag_suggestions / bag_suggestion_votesProposals for the next bag + upvotesname, status, picked_bag_id
audit_logsAppend-only activity log for baristasactor, action, summary, created_at

Settlement Algorithm (bag-based)

A settlement is generated each time a bag is closed. The algorithm scopes to that bag's lifetime and to drinks attributed to it.

  1. The bag's total_cost is divided by the drinks whose bag_id equals the bag — so cost-per-drink stays stable even across month boundaries.
  2. Per person: cost share = drinks_from_bag × cpd, paid = bags bought in the window, credits = credits booked in the window.
  3. Balance = cost share − paid − credits. Positive = owes money, negative = is owed money.
  4. When a new bag is logged the server automatically closes any previously-active bags, creating a per-bag settlement for each. Exactly one bag is active at a time.
  5. Dashboard shows a greedy creditor/debtor match as a settlement preview. Baristas can "Mark paid" on any transfer, which books offsetting credits on both sides.

Cups-per-bag Yield & ETA

Each emptied bag produces a yield sample: cups = COUNT(drink_logs WHERE bag_id = b.id). The server averages this per brand, falling back to group-wide average. For the active bag the dashboard shows:

Security

Security limitations tracked for a public launch are documented in PHASE2.md at the repo root.

Privacy

BrewCalc stores only the minimum data necessary: usernames (derived from group name), names, emoji avatars, drink counts, bag costs, and optional tasting notes. No email addresses are collected at this phase.

Data Management

BrewCalc supports full JSON export and import via the Data tab:

Limitations

Stack