Dmitriy Kononov
Let’s talkContact

PROJECT

Group coffee orders for an office team

An internal service for pooled office coffee orders: wholesale catalogue, one supplier order, automatic cart filling, payments, receiving and handover.

Internal system

Requests → supplier order → receiving

Concept illustration of the workflow

The task

In an IT company's office, employees pool their coffee orders with a wholesale supplier: beans or ground coffee, plus tea and related goods. Requests used to go into a shared Google Sheet. The organiser then built the cart in the Tasty Coffee wholesale account by hand from those rows, worked out the amounts and checked the payments.

That process invites mistakes: the wrong grind or pack size, a missed row, an item the supplier no longer stocks. It was also hard to tell what stage a particular order had reached and whether it had been paid.

What I built

I designed and built the service in September and October 2026: the employee interface, the admin panel, the supplier integration, the database and the deployment. It runs in production on the studio's own server in Docker, behind Nginx with a Let's Encrypt TLS certificate, and I maintain it.

How it works

Placing an order

An employee starts typing the name of a coffee and picks the exact item from the suggestions. Once the catalogue is published, free text cannot be submitted. For coffee, choosing beans or ground is mandatory, followed by pack size and quantity. The form only offers combinations that can actually go into the supplier's cart, and when the wholesale API reports stock, it will not accept a larger quantity.

The form shows no prices. The amount appears on the order once the organiser has checked the real supplier cart. The order history shows the current stage and the next step. The site can be installed on a phone as a PWA.

One supplier order and an automatic cart

In the admin panel, requests collect in a queue. With one action the organiser combines them into a supplier batch, and the service fills the Tasty Coffee wholesale cart itself: it clears the cart and adds the saved lines. If the batch is edited afterwards, the cart can be refilled and no stale quantities are left behind. Any line that could not be transferred is flagged with a reason, and the organiser adds it by hand and ticks it off. Checkout and payment with the supplier stay manual: the integration never presses the final button.

Late requests can be added to a batch that has not been received yet. Until delivery is accepted, a batch can be corrected, exported again or cancelled entirely, which returns its requests to the queue.

Payments

The organiser fixes the amounts from the actual cart after the supplier's discount and opens the collection. Each employee sees their own amount and payment link, marks "I have paid" and can attach a screenshot. An order only counts as paid once the organiser has confirmed the money arrived. Verified payments can be confirmed one at a time or in a group of up to 100 orders. The service only allows the "Ordered" status after every payment has been checked and the order has really been placed with the supplier.

Receiving and handover

When the delivery arrives, the organiser accepts it line by line: received in full, partially, out of stock or substituted. If everything is there, a single confirmed action is enough. The service works out the result for each employee. Only fully received orders can be handed over; the rest go to the shortage queue.

The admin panel is organised around four queues: new requests, payment checks, handover and shortages. Orders and batches can be exported to CSV and PDF.

Moving off the old spreadsheet

While some colleagues were still using the Google Sheet, the switch had to be gradual. I built an import with a preview step: every row is checked for first and last name, an exact catalogue variant and a valid quantity, and any error blocks the import rather than substituting a similar product. Prices and payment marks from the sheet are not carried over. An import journal links each order to its source row number, so loading the sheet again does not create duplicates, and an edited row shows up as a conflict. A separate check compares imported requests with a cart assembled by hand without changing that cart.

Engineering choices

Catalogue and exact variants

The catalogue syncs with the Tasty Coffee wholesale API on demand or on a schedule of up to three runs a week. The server checks the schedule itself, so nobody needs to keep the admin panel open, and a failed run leaves the published catalogue in place. For every variant the database stores the exact mapping to the supplier's product, offer, grind and packaging. That key travels from the request to the cart line, so the service never has to guess a product by its name.

Security

All external traffic passes through Nginx and a Node.js gateway. The gateway strips any identity headers sent by the client and identifies the user itself from a signed HttpOnly cookie. Admin sessions last 8 hours, five failed logins from one address trigger a 15-minute lockout, and the password is stored only as a scrypt hash. Payment screenshots are kept in a private Yandex Object Storage bucket and are only reachable through the gateway. The wholesale account login lives in private server variables, and its token exists only in memory. Requests to the supplier and to Google Sheets go through internal proxies that are not exposed publicly.

Running on a small server

The application is written in React and TypeScript and runs on the Cloudflare Workers runtime with the D1 API on top of a local SQLite database in a Docker volume. The server is shared and has 2 GB of memory, so the containers have hard memory limits, and during a release the old instance stops before the new one starts. When a build needs more memory than the server can spare, the image is built elsewhere and uploaded ready to run.

The result

The service replaced the shared spreadsheet and hand-built carts. Requests are tied to exact catalogue variants from the start, which means fewer mistakes with grind, pack size and quantity, and both the employee and the organiser can see where an order and its payment stand. By my estimate, the service saves employees and administrators more than 100 hours of working time a month in total. This is an estimate, not the result of a separate measurement.

Discuss your workflow

If orders, payments or requests in your team still live in a spreadsheet and get copied by hand, tell me where most of the time goes. Discuss a similar challenge.

Related work: internal business systems, API integrations and spreadsheet automation. Further reading: when to move from spreadsheets to a system.

The same technologies, different tasks