The Order That Arrived by WhatsApp: Merging Web Baskets, Phone Calls, Standing Boxes and Restaurant Emails Into One Order Book
A local food co-op receives orders through four different channels, and only one of them is the storefront. Here is how to normalise phone calls, standing subscriptions and restaurant emails into a single order book before cut-off, without double-booking households or drifting producer stock.

Why a co-op's order book never comes from the storefront alone
Most local food co-ops receive orders through at least four channels: the shopper app basket, phone calls to the office, recurring standing boxes, and email or messaging orders from restaurants and other trade buyers. Only the first one produces a clean order record by itself. The other three arrive as human conversation.
At Plodovi.hr, a co-op of around 38 producers, this is not a transitional state that a better storefront will eliminate. The phone call exists because an older customer trusts a voice more than a checkout button. The standing box exists because a household does not want to shop weekly. The restaurant email exists because a chef writes their order while doing stock at 23:00 and will not open an app to do it. Through the Ajmo Lokalno pilot, HoReCa orders arrive with different quantities, different units and different price expectations than a household basket.
The mistake is treating each of these as a different kind of business. They are not. They are four different ways of saying the same sentence: this buyer wants this quantity of this producer's product for this fulfilment date. Everything after intake, picking, splitting, routing, payout, is identical. If the intake channel leaks into the downstream logic, you end up maintaining four half-broken order systems instead of one.
What a manual order record must actually contain
A phone or email order needs every field the storefront collects automatically, entered by hand: buyer identity, delivery or pickup point, fulfilment date and slot, each line item with producer, product, quantity and unit, agreed price, payment method and status, and a source tag recording where the order came from and who entered it. Miss one and it surfaces later as a pick error or an unpaid box.
The fields people forget are the boring ones. Unit is the most dangerous: a chef says "ten kilos of potatoes" and a household says "two bags", and if the admin types 10 against a product priced per bag, the producer gets a payout request for something they never declared. Price is the second: trade buyers often have an agreed list that differs from retail, so a manual order needs a price source, not just a number. Payment status is the third: a phone order is frequently pay-on-delivery, which means the driver needs to know and the reconciliation run needs to expect cash.
We also record who took the order and when. Not for blame. When a customer says "I asked for chard, not spinach", the only way to close that conversation in a minute is to look at the order note, see that it was entered by the office on Wednesday at 10:14 with a note saying chard was unavailable, and either fix it or explain it.
- Buyer: existing account, or a new one created at intake, never a free-text name
- Fulfilment: date, delivery point or pickup slot, address if home delivery
- Lines: producer, product, quantity, unit, price and price source (retail or trade list)
- Payment: method, status, and who collects it if it is cash on delivery
- Provenance: source tag (app, phone, standing, HoReCa email), entered-by user, timestamp
- Notes: substitutions agreed verbally, access instructions, packaging returns
The duplicate problem: one household, two orders
Double-booking happens when a household places a web basket and also phones the office, or when a standing box runs in the same week the customer ordered manually. The fix is to force every manual order onto an existing buyer account and to show the admin any open order for that buyer and fulfilment date before the entry can be saved.
The failure is quiet and expensive. Two orders for the same household in the same week means two boxes picked, two stops routed to the same door, and a refund conversation on delivery day. Worse, it consumes producer stock twice, so something else that could have been sold shows as unavailable. Before we tied manual entry to accounts, the office had no way to see the web basket at all. The order existed, it just was not in front of the person on the phone.
Two rules removed almost all of it. First, no manual order without a buyer record, so the search field is the account list and creating a new customer is a deliberate act rather than the default. Second, when an admin opens manual entry for a buyer with an open order for that cycle, the system offers to add lines to the existing order instead of creating a second one. Merge, do not stack. The remaining duplicates are the genuinely ambiguous ones, a household ordering a gift box for a relative at the same address, and those are resolved by a human looking at the two orders side by side.
A manual order still has to consume declared stock
Every order, no matter how it arrived, must decrement the producer's declared availability for that cycle. If phone and email orders bypass the availability ledger, the storefront keeps selling produce that is already committed and the declared numbers drift away from what can actually be picked.
This is the rule that people are most tempted to break, because it is inconvenient. A chef wants 12 kilos of tomatoes and only 9 are declared. The easy path is to type 12 anyway and sort it out later. The honest path is that the order line either takes the 9 available and flags a shortfall, or the admin contacts the producer and gets the declaration raised before the line is accepted. Our producers post availability weekly rather than running live inventory, which makes the ledger the single source of truth for the cycle and makes an override visible rather than silent.
Overrides still exist, because reality does. What changed is that an override is a recorded event with a reason and a named producer to confirm it, not an untracked number in a cell. When declared, ordered and delivered quantities are all held against the same product for the same cycle, the weekly variance report tells you which producers over-declare and which channel is generating the shortfalls. Usually it is the trade channel, because restaurant quantities are large enough to consume a small producer's entire week in one line.
What broke with standing orders, and the change we made
Standing subscriptions used to generate their orders on a schedule regardless of what producers had declared for that week. The result was orders containing produce nobody had available, discovered at picking. We changed generation to happen after the declaration window closes, and to resolve each line against actual availability before the order is confirmed.
The original design felt reasonable. A standing box is a promise, so create the order early and let the pick list deal with it. But a standing box in a short supply chain is a promise about a category, not a specific item. Nobody can promise fennel in week 34. When the generator wrote fixed lines on Sunday and producers declared on Tuesday, the mismatch surfaced on Thursday night in front of a volunteer holding a crate.
The sequence now runs in this order, and the customer-facing consequence is that a standing order is only final once availability exists to support it.
- Declaration window closes for the cycle, producer availability is locked
- Standing order templates generate draft orders against that availability
- Lines that cannot be filled are substituted per the customer's stored substitution preference, or dropped
- The customer receives the draft contents with a window to edit or skip before the order cut-off
- At cut-off, drafts confirm, stock is consumed, and the orders join the same book as app, phone and HoReCa orders
One order object with a source tag, not four pipelines
The architectural decision that mattered most was refusing to build channel-specific order pipelines. There is one order object. The channel is a field on it, not a separate table, workflow or codebase. Everything downstream, pick lists, producer splits, routing, invoicing and payouts, reads the same structure regardless of how the order arrived.
We started down the other road. A phone order looked different enough from a web order to justify its own handling, and a restaurant order looked different again with its trade prices, larger quantities and invoice-on-terms payment. Two channels in, we were writing the pick-list logic for the third time and the three versions had already diverged on rounding. Consolidating them meant accepting that some fields are only populated for some channels, a HoReCa order carries a company VAT number and a payment term, a phone order carries an entered-by user, a standing order carries a template reference. Nullable fields are a much smaller cost than parallel pipelines.
The source tag then earns its keep as reporting. You can see what share of revenue arrives outside the storefront, which is usually higher than anyone expects, which channels produce the most substitutions and shortfalls, and whether the app is actually absorbing phone customers over time or just adding new ones. That last number decides whether you invest in reducing manual intake or in making manual intake faster. For most co-ops we work with, the honest answer is the second one, which is why Plodie treats manual order entry as a first-class administrator workflow rather than an escape hatch.
Key Takeaways
- A co-op's order book comes from at least four channels: app baskets, phone calls, standing subscriptions and restaurant email orders. Only the first is self-documenting.
- Manual orders need every field the storefront captures automatically, plus a source tag, an entered-by user and a price source for trade buyers.
- Force manual entry onto existing buyer accounts and surface open orders for the same cycle, or you will pick, route and refund the same household twice.
- Manual and standing orders must consume declared producer availability, otherwise the storefront keeps selling produce that is already committed.
- Generate standing-box orders after the declaration window closes, not on a fixed schedule, and give the customer an edit window before cut-off.
- One order object with a channel field beats four channel-specific pipelines. Nullable fields are cheaper than divergent pick-list logic.
The stock ledger these rules depend on is described in detail in our declaration-based availability model, including the six rules that keep declared numbers close to delivered ones.
Frequently Asked Questions
How do you manage orders for a local food co-operative that still takes phone calls?
Treat the phone call as an intake method, not a separate order type. The person taking the call enters the order against an existing buyer account in the same system the storefront writes to, filling in fulfilment date, line items with units, agreed price and payment method, and tagging the source as phone. Everything downstream then behaves identically to a web order.
Should a food co-op accept orders over WhatsApp?
It is fine to accept them, as long as WhatsApp is only the message and not the record. Someone has to transcribe the message into a real order record with buyer, date, line items and units the same day, and the original message should be referenced in the order notes so any dispute can be checked. Leaving orders to live in a chat thread is where double-bookings and forgotten lines come from.
What is the difference between a retail basket and a HoReCa order in a co-op platform?
Structurally they are the same order object, but a HoReCa order usually carries trade prices from an agreed list, larger quantities in bulk units, a company VAT number, and payment on invoice terms rather than at checkout. It also often needs a delivery window tied to kitchen prep times. Those are extra fields on the order, not a separate system.
How do you stop a standing subscription box from ordering produce that is out of season?
Generate the order after producers have declared availability for the cycle rather than on a fixed schedule, then resolve each template line against what is actually available. Unfillable lines follow the customer's stored substitution preference or are dropped, and the customer sees the resulting draft contents with a window to edit or skip before cut-off.
What happens if two orders exist for the same household in the same week?
Merge them rather than fulfilling both. A good system flags any open order for the same buyer and fulfilment date when an admin starts a manual entry, and offers to add the new lines to the existing order. If both were already confirmed and picked, you are choosing between a refund on delivery day and a second van stop, which is exactly the cost the duplicate check is there to avoid.


