Wine runs: planning what to buy
Anyone with the "Manage wine purchases" capability who plans which wines to buy, for events, friends, or the cellar
Last updated 6 July 2026
What this is
The buying hub at /profile/wine-runs is where you plan wine purchases. It sits in front of the cellar: decide what to buy, record what you actually paid after discounts, and keep a backlog of wines you want for later. It has two surfaces, runs and wishlists, plus a computed nudge from your events.
Who can use it
Access comes with a Contributor or Patron membership. An admin can also set the "Manage wine purchases" capability per person in /admin/users (it's independent of hosting, so a buyer who does not run events can still get it). Admins always have it. The hub link lives in your personal menu.
Runs
A run is a planned buying batch for one occasion: a tasting, an offsite, an office party, a friend's order, or just a cellar restock. Each run has a title, an optional buy-by date, a default shop, and a list of lines.
A run moves through a simple lifecycle you set by hand from the run's status picker: Planning (deciding what to buy), Shopping (orders going out), Delivery (ordered, maybe even paid, but the bottles are still waiting for you to collect), and Done. Cancelled is the exit at any point, kept for history. The first three all count as active: the run stays in the Active section, its money stays in the budget projection, and a passed buy-by date keeps flagging it as overdue, because in Delivery there is still work left, going to get the wine.
Each line is a wine (from the catalog) or a free-text note for something not yet in the database, plus a quantity and two prices. A free-text line can be renamed any time, just edit its name in place, and there are two ways to resolve it to the catalog. Link a wine opens the wine picker to point the line at a wine that already exists, keeping the prices and quantity you have entered. Make it a wine (for contributors) opens the Add-wine form pre-filled with that name (you add the producer, region, photos, the rest); saving creates the catalog entry and links the line straight to it, and the picker offers the same as a "can't find it?" fallback. Either way you land back on the run with the line now showing the proper wine, ready to graduate into the cellar. The two prices:
- Public price: the full retail price. It auto-fills from the wine's stored price and stays editable, because today's price can differ from last time.
- Actual price: what you actually pay. This is the number that feeds the totals.
The gap between them is the discount. You can set it three ways: type a percent or amount on one line, apply a percent to every line at once, or apply the shop's formula. Shop formulas live in code (shop-discounts.ts): goodwine and Wine Bureau, for example, give 7% at or above 3300 and 14% below. Editing the actual price directly always wins and back-solves the discount.
The run header shows live analytics: bottles, distinct wines, total spend, savings, how much you have already paid, and how many bottles you have picked up. Two separate ticks track each line. "Got" marks that the bottle is in your hands. "Paid" marks that you have actually paid the shop for it, and the two rarely line up: you might prepay an order that has not shipped, or carry a shop tab you settle on the next visit. The header's Paid figure sums what has left your pocket and, while a run is part-paid, how much is still to go; the overview list carries the same got and paid counts per run so you can see where each one stands without opening it. On a phone the wide editing table gives way to a card per line. Each card has a big Receive button and a Mark paid toggle, so checking off a delivery or a payment as it happens is a one-tap job, plus inline fields for the quantity and the price you pay (and a buyer's price on resale lines) for the edits you actually make at the till. The finer pricing controls (public price, discount, and the shop formula) stay on the desktop table.
Once a line is Got and linked to a catalog wine, an Add to cellar button turns it into real bottles in your cellar, priced at what you actually paid. The first time you do this on a run it asks which cellar location to file them under, picked from the locations you already use or a new one you type, then remembers that as the run's default so the rest of the run's bottles go there without asking again. You can set or change that default location anytime under the run's Settings, and leaving it blank just means it asks once.
Wishlists
A wishlist is a persistent, undated backlog of wines you want someday, with no pricing. You can keep several named lists ("reds for winter", "to try"). Add a wine from the catalog with the picker, or jot a free-text note for anything uncatalogued.
When you are ready to buy, promote a wishlist item into a run: "Add" moves it, "Add + keep" leaves a copy for recurring buys. It lands as an auto-priced run line. Going the other way, any run line can be demoted back to a wishlist.
Suggested from events
When you have upcoming events with wines you do not own a bottle of, and have not already put on a run or wishlist, they show up under "Suggested from events". This is computed live, never stored, and disappears once you act on it or assign a bottle. From there you can drop a suggestion straight onto a wishlist or a run. The event's own manage page shows a matching warning that links back here.
The hub layout
Sections, top to bottom: active runs, the event suggestions (only when there are any), your wishlists, then completed and cancelled runs (collapsed). Open a run to edit its lines.
Budget impact
Open runs (planning, shopping and delivery) roll up into a "Wine spend" section on the finance dashboard, bucketed by each run's buy-by date. Lines you're buying for someone else sit out of it, since the buyer reimburses you, so the figure is only what you're spending on yourself. It shows what you are committed to in total, this month, and this quarter, and how much of that is still priced off the public estimate rather than a confirmed actual. The figure stays a projection: it never touches the ledger, and a line drops out of it the moment you add it to the cellar, where it becomes real, recorded spend. The monthly chart sits your committed band (dashed) next to what you have actually spent (solid), so upcoming buys read against your real cadence.
If you run the envelope, it also carries a one-line heads-up under Available: how much your open runs commit you to and what's left of your free cash after that. Lines you're buying for someone else are left out, since the buyer pays you back.
The CLI
bun run db wine-runs mirrors the run logic for headless use: list, get, create, add-item, update-item, discount, receive, and so on. It targets whichever database the db CLI is pointed at, so mind the banner.
Buying for others
Any run can include wines you're buying for someone else, mixed freely with wines for your own cellar. There's nothing to switch on for the whole run: who a wine is for lives on each line. Every line has a "For" picker under the wine name, a registered convive, a typed name, or your own cellar (the default it starts on).
The moment a line is for a buyer, the table grows a "Buyer" column and that line carries two prices: your cost (what you actually pay, with your discount) and the buyer's price (what they pay you back). Cellar lines show a dash there, since they have no buyer. You set the buyer's price per line, by judgement: nothing on top of an everyday wine, more on a rare one they could not find themselves. Margins vary line to line on purpose, and the buyer never sees your cost or your fee.
To save typing, the run's Settings hold optional buyer defaults: a default buyer that new lines start from, and a default markup, either a percent or a flat amount per bottle, that fills each line's buyer price from cost as a starting point. Override any line directly; a blank buyer price falls back to that default, and with no markup at all it passes through at cost.
One shop trip, several people
A run is a single shop trip, so it can serve more than one person at once, yourself included. One order can hold a couple of bottles for one friend, one for another, and a few for your own cellar, all reviewed as the single run the shop actually sees. A "Who owes what" panel appears under the table once any line is for a buyer, totalling it by person, what each one owes you and your margin on their share (hidden from the buyer), with your own cellar lines listed separately as plain cost. Each buyer row has a copy button that puts that person's order on your clipboard, their wines and the total they owe, so you can send it over and double-check you got it right. When every one of their wines has a retail price on file and their price comes in under it, the total splits into three lines, what it would have cost before discount, what they saved, and the final total, so the message reads as the deal they got; otherwise it stays a single total. Either way it is buyer prices only, never your cost or fee. The header echoes this: alongside the run's Total, what you front at the till, it shows Your spend, counting only your own cellar lines, with the rest flagged as coming back, since the buyers reimburse what you fronted for them. Lines for yourself graduate into the cellar the usual way.
By shop, and copying the order
Buyers don't care which shop a wine comes from, and shops don't care who it's for, so the run also breaks down the other way. A "By shop" panel lists each shop with its bottle count and your cost, and a "Copy order" button puts that shop's order on your clipboard ready to paste or send:
- 3x 566911 Domaine Armand Rousseau Gevrey-Chambertin 2019
- 1x Filipa Pato Espírito de Baga 2020
The quantity is summed across every buyer, so the shop sees one number per wine. The code is the shop's SKU when we have one on file (the same refs the shop-URL tool fills); free-text lines just leave it out.
Merging runs
If you started something as its own run and later realise it's part of a bigger trip, Settings has a "Merge into another run" picker. Pick another active run and every line moves over, keeping its buyer, shop, and prices, appended after that run's lines; the run you merged from is cancelled (kept for history). Handy for pulling a one-off purchase into the main haul before you place the orders.
Getting paid back
Once you have shopped and handed the wine over, the "Who owes what" panel has a Record what's owed button. It posts each registered buyer's total to their balance as a charge, and marks each one "recorded" so you can see it is done. Press it again any time, it reconciles each buyer's charge to the current totals rather than charging twice. Until a buyer has paid, their amount shows in red as "owed". If you change someone's order after recording it, an amber "order changed" tag appears on their row to remind you to press Record what's owed again so the books match the new order.
When a registered buyer pays you back for this run, hit the Mark paid button on their row. It posts the cash-or-card settlement for that run and their amount turns green to "paid". Settlement is per run, so paying off one run never touches what they still owe on another.
The recorded charge and the payment are kept separate, so the row always reflects what is genuinely left to settle. If a buyer pays and then their order grows, their row shows just the remaining delta as still owed, not the whole order again. If their order shrinks after they have paid, the row shows the overpaid amount as a green "credit", which is theirs to refund or carry forward to the next run.
These charges live on the personal channel: cash or personal card transfer only, never the site's payment gateway. That is deliberate and load-bearing. Wine reimbursement money cannot legally land in the FOP account, so it is kept entirely out of the envelope and the gateway: a personal charge writes no envelope transaction and never shows up as something payable online. Your envelope's cash and receivables are unaffected by it.
Because those charges stay off every gateway-facing screen, the wine-runs hub has its own Balances section: one block per person who still owes you, broken down into a line per run with what each one owes. Mark paid on a run line settles just that run; when a person owes on several, a Mark all paid button clears the lot in one go. Settled lines (and people) drop off the list. Same channel, same rule, no envelope transaction, nothing on the gateway.
Typed-in (not registered) buyers show up here too, tagged "not registered". They have no ledger to post to, so their owed is read straight from the runs and "Mark paid" just flips that run's paid flag, the same flag as the paid / unpaid checkbox on the run itself. Either place settles them; the run checkbox stays for marking one off while you're in the run.
For the rare case where you'd rather collect a run online instead of in cash, a registered person's run line also has a Move to account option. It does exactly what the name says: moves that run's debt off the personal side and onto their gateway account, where it becomes a normal receivable, visible in your books and payable online. That deliberately crosses the line wine money usually never crosses (onto the FOP gateway), so it sits behind a loud confirmation and is the exception, not the default. Typed-in names have no account, so they don't get it.
(Where the buyer sees their own personal balance, with your payment instructions, is a separate convive-facing view still to come.)