Plan your party like an engineer. Configure drinks, cocktail recipes, and headcount β get a precise shopping list, profit margin, and break-even point in seconds.
BottleCount takes the guesswork out of organizing a ticketed party. You define who's coming, what they're drinking, and what it costs β the app calculates everything else: how many bottles to buy, how much you'll spend, when you break even, and how much profit you walk away with.
No spreadsheets. No magic numbers. Everything is driven by your cocktail menu.
The menu is organized into three category types:
- Spirits β 3-level structure: category β spirit β cocktail. For each spirit (e.g. Vodka), you configure which cocktails are served (e.g. 40% Vodka Lemon, 60% Vodka Redbull) and their recipes drive the mixer quantities automatically.
- Beer β 2-level: category β variety with a percentage split. No cocktail layer β served as-is. Example: 100% Beer 66cl, or 60% Lager / 40% Craft.
- Wine β 2-level: same as Beer. Example: 70% Red Wine / 30% White Wine.
This distinction means Beer and Wine skip the cocktail configuration entirely β you just set the split between varieties.
Alcohol intensity presets: πΏ Soft Β· πΉ Aperitivo Β· π Party Β· π₯ Hardcore β pure-alcohol target per person (25/50/75/100 ml).
- Auto-calculates bottle counts per ingredient based on ABV, bottle volume, and drink ratios
- Supports min/max price ranges per item (best deal vs. shelf price)
- All quantities scale automatically when headcount changes
- 10% safety buffer included by default
- Real-time profit range (min/max) based on price ranges
- Cost breakdown doughnut chart by category (Spirits, Wine, Beer, Mixers, Snacks, Fixed costs)
- KPI cards: Revenue, Spend, Fixed costs, Profit, Break-even guests
- Color-coded type badges throughout (amber for spirits, gold for beer, purple for wine, cyan for mixers)
All three pages are designed mobile-first:
- Sticky scrollable nav that never wraps or overflows
- Large touch targets (min 44px) on all inputs and buttons
- Shopping list uses flex-row cards instead of a table β no horizontal scroll
- KPI grid adapts: 2-col on mobile β 3-col on tablet β 5-col on desktop
- Collapsible sections (Shopping List, Cost Breakdown, Ingredients, Cocktails) to save screen space
- Ingredient cards with inline price editing β no page reload needed
- Full CRUD for ingredients and cocktails via the web UI
- Inline price editing with live save β click πΎ per row, no form submission
- Add cocktails with a recipe builder (ingredient + quantity + unit)
- Beer and Wine ingredients are separate from Spirits in the catalog; cocktail creation only allows Spirit-type bases
- Delete buttons work for both shared catalog entries (hidden per user) and personal additions
- Google OAuth sign-in β each user gets their own isolated settings and menu
- Unauthenticated users can browse but see a toast notification when attempting to save
store.pyabstraction layer: in DB mode (Supabase), changes are per-user; in local mode (noDATABASE_URL), falls back to JSON files
- All data persisted in Supabase PostgreSQL β no data loss on redeploy
store.pyacts as a clean abstraction layer β swap backend without touching business logic- Deployable in ~5 minutes on Railway or Render (free tier)
bottle-count/
β
βββ server.py # Flask app β all routes (pages + API + auth)
βββ core.py # Pure calculation logic (no I/O, fully testable)
βββ store.py # Data layer β Supabase or local JSON (swappable)
β
βββ templates/
β βββ base.html # Shared layout, nav, auth UI, JS helpers (toggleSection, apiPost, apiDelete)
β βββ dashboard.html # Settings, KPI cards, shopping list, doughnut chart
β βββ menu.html # Drink menu: macro split + per-category config
β βββ catalog.html # Ingredient & cocktail CRUD
β
βββ data/
β βββ catalog.json # Default ingredients + cocktail recipes (shared baseline)
β βββ settings.json # Default settings (used when DATABASE_URL is not set)
β
βββ requirements.txt
βββ Procfile # gunicorn entry point
βββ nixpacks.toml
Key design principle: core.py has zero I/O. It only takes plain Python dicts in and returns results β making it trivially testable and reusable.
- Python 3.11+
- A free Supabase account (optional β app works locally without it)
- A Google Cloud project with OAuth 2.0 credentials (optional for auth)
git clone https://github.com/YOUR_USERNAME/bottle-count.git
cd bottle-count
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
python server.pyOpen http://localhost:5000. Without DATABASE_URL and Google credentials, the app runs in local mode β data is stored in data/settings.json, no login required.
Create a .env file:
DATABASE_URL=postgresql://... # Supabase connection string
SECRET_KEY=your-flask-secret-key
GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secretFor Google OAuth, set the authorized redirect URI to:
https://your-domain.com/auth/callback
Run:
python server.py- Push the repo to GitHub
- Go to railway.app β New Project β Deploy from GitHub Repo
- Add environment variables in Railway's dashboard (
DATABASE_URL,SECRET_KEY,GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET) - Click Generate Domain β you're live
The core formula for each spirit bottle count:
Mixer quantities are derived directly from cocktail recipes β if Vodka Redbull uses 250ml of energy drink per serving, and there are 400 servings, the energy drink quantity is computed automatically.
Simpler formula β no cocktail recipe needed:
| Layer | Technology |
|---|---|
| Backend | Python 3.11 + Flask 3.x |
| Auth | Google OAuth via Authlib |
| Database | Supabase (PostgreSQL) or local JSON |
| Charts | Chart.js 4 |
| Styling | Tailwind CSS (CDN) + custom mobile-first CSS |
| Deploy | Railway / Render |
| Data layer | store.py abstraction (swappable) |
This project is licensed under the GNU Affero General Public License v3.0.
For commercial use without open-source obligations, contact the author.