Back / Programming

Ordering pipeline with one checkbox

Marko Petrović / Aug 19, 2026

A supplier ordering pipeline in Superhuman Docs (formerly Coda): six order states, no status column — one human-set checkbox, everything else derived.

{
"stack": ["Superhuman Docs"]
}

"Naručivanje" is the system I built for ordering parts from foreign suppliers: an order gets created, filled with items from a parts catalogue, sent to the supplier, and then tracked through arrival and payment until it lands in the archive. It runs in Superhuman Docs (formerly Coda), and a colleague has been using it for years to handle foreign-partner orders — low frequency, but real work with real money attached.

On the surface it's a classic state machine. An order is created, then ordered, then maybe partially arrived, possibly unpaid and eventually archived — a defined set of states with allowed transitions between them. Each state has its own page in the doc, and you'd reasonably assume there's a status column underneath with those values in it.

There isn't. The schema has no status column at all. The only lifecycle field a human ever touches is a single checkbox called Naručeno? ("ordered"). Everything else the doc knows about an order's state, it derives. The state machine is real; the implementation is probably not the usual one.

## The shape of the doc

The doc is 17 pages split the way all my Superhuman systems are split: a visible dashboard side and a hidden Setup section holding the data. The dashboard has one page per state — create, awaiting, ordered, partially arrived, unpaid, archive — plus a page for the parts catalogue. The user navigates states by navigating pages.

Picture form Narucivanje doc
Hidden pages on the left side (grayed out) that represent "backend" of this doc

Four tables carry the whole system. Orders and the items on them are the working pair; the ident catalogue (348 parts, each with a supplier code and a classification) and a deliberately minimal supplier table (one column: the name) are the reference spine everything points at. Each ident also carries an ERP ID, which quietly makes the catalogue a bridge to the accounting system — the same part is addressable on both sides.

One honest structural quirk: the base orders table physically lives on a frontend page, and the _DB_Nalozi table in the hidden backend is actually an unfiltered view of it — the mirror image of the convention I use everywhere else, where tables live hidden and pages get filtered views. It works identically in practice, but it's counterintuitive — and it was forced by a platform limitation at the time. Notifications always link back to wherever the base table lives, and if that's a hidden page (as it usually is in my setup), following a notification drops the user into the hidden backend — confusing, and not the experience I want anyone to have. There was no way to point the notification at the filtered view where the user is supposed to land. So for some tables I built it in reverse: the base table sits on the frontend page with its filters applied, and the hidden Setup section gets a view of it with every column shown and every filter disabled, used for setup and debugging.

## Three fields, six states

An order's lifecycle state is the product of three fields that don't know about each other.

The first is Naručeno?, the checkbox. Checking it is the only explicit state transition in the system: this order has been sent to the supplier. Everything before that is "awaiting"; everything after is downstream bookkeeping.

The second is Status Delova — parts status — and nobody sets it. It's computed by comparing what was ordered against what's arrived, summed across the order's items:

SwitchIf(
  [_S_Razlika - Sum] = [_S_Količina - Sum], "❌ Nije Stiglo",
  [_S_Razlika - Sum] < [_S_Količina - Sum] AND [_S_Razlika - Sum] > 0, "🛑 Delimično",
  [_S_Razlika - Sum] = 0, "✅ Stiglo"
)

_S_Razlika is per-item remaining quantity (ordered minus arrived), so the order-level status is: nothing arrived, something arrived, everything arrived. The conditions are ordered so that an order with no items yet — where both sums are zero — falls into "not arrived" rather than "complete", which is the right default for a row that's still being built.

The third is _S_Plaćanje, the payment field — not paid, partially paid, paid — which only ever moves through a button.

The state pages are filtered views over combinations of these three, and the dashboard counters spell the combinations out explicitly. Awaiting is Naručeno? = false. Ordered is Naručeno? = true and nothing arrived. Partially arrived is Naručeno? = true and the parts status says partial. Unpaid is Naručeno? = true and payment isn't "Plaćeno".

Dashboard with the per-state counters

### Partial delivery is arithmetic, not a state

Partial delivery is always a tricky state for order trackers, and there's a standing question of whether it even belongs in order tracking or should be handled separately. Here, each item carries Količina (ordered) and Stiglo (arrived), and the remaining quantity is just subtraction. Receiving three of five pumps doesn't require anyone to declare anything; the order is now partially arrived, because the sums say so.

When a shipment shows up complete, there's a shortcut: a "Sve Stiglo" button on the order that updates every item and sets arrived equal to ordered. The common case is one click; the messy case is editing the actual items that arrived short. Both converge on the same derived status.

Order menu overview

### Payment doesn't care about boxes

Payment is tracked on its own axis. The Plaćeno? button is one-way only — each press advances Nije → Delimično → Plaćeno (not paid → partially paid → paid). An order can be fully arrived and unpaid, or fully paid while parts are still missing; both are normal states of affairs when ordering from foreign suppliers.

This is why the "Nije Plaćeno" page overlaps the fulfilment pages instead of following them in sequence. It looks like a sixth sequential state, but it's actually a separate, parallel track. An order leaves that page when it's paid, no matter the status of the shipment itself.

## Transitions are guards, not code

The platform has no state machine primitive and no transition hooks, so the enforcement lives in the one place it can: button disableIf formulas. Illegal transitions aren't rejected — they're unclickable.

Checking Naručeno? freezes the order's contents: the "Dodaj Stavku" button that adds items disables the moment the order is marked as ordered. Deleting items is closed off by a different mechanism — page locking, a native platform feature where you lock a page and choose, to a degree, which interactions stay allowed on it. Locking is coarse and leaves a lot to be desired, but it covers the path the disabled button can't. Between the two, what you sent to the supplier is what the record says you sent — no quietly adding or removing items afterwards. In the other direction, the payment button and the "Sve Stiglo" (everything arrived) button are both disabled until the order is ordered — you can't pay for or receive an order that was never sent.

Which raises a fair question: why is the one human-driven transition a checkbox — freely uncheckable — instead of a button that disables itself once clicked, like everything else here? It could have been. It stayed a checkbox because the person using the system asked for the way back. An order to a foreign manufacturer isn't final the moment you send it: the supplier might reply ten days later, some items turn out unavailable or months away, codes change, products get retired, quantities shift in the meantime. The correction workflow is exactly uncheck → adjust → check again — and unchecking isn't quiet, because the order visibly drops back to awaiting, its items unlock, and the counters move. A self-disabling button with some special unlock path for corrections would have been the same thing with more complexity. The checkbox is the escape hatch, on purpose.

So that's the entire transition system: one checkbox, two derived fields, a handful of disable conditions, and a lock. It's less rigorous than a real state machine — the state that should be irreversible is deliberately not — but for a system with a handful of trusted users, making the wrong actions unclickable and leaving one honest way back covers the cases that actually happen.

## The catalogue spine

Orders don't contain free-text part names. Every item on an order points at an ident in the catalogue, and every ident points at a supplier — so the analytics on the dashboard (orders per supplier, total value per supplier, most-ordered parts) fall out of the relations without any extra bookkeeping.

The catalogue page also carries a small detail I still like: a live formula at the top listing duplicate supplier codes, straight from Duplicates() over the code column. It's a data-quality check sitting in the UI where the person entering idents will actually see it, instead of a cleanup script that runs after the damage is done.

the Identi page with the duplicate-code check (shows here if there are duplicates -> Duple šifre dobavljača: [ ] )

## What v0.9 means

The doc's subtitle says v0.9, and that's accurate rather than modest. Some of the scar tissue, honestly listed:

- The arrival date is derived from Modified() on the computed status column — if the status says complete, the timestamp of its last change is treated as the arrival date. It's clever, it works, and it's fragile in a way I wouldn't repeat: the date is an artifact of recalculation, not a recorded fact.

- One status formula compares a number against the string "0". Coda's coercion makes it work. It's still a string.

- Two dead columns are still in the schema — an empty Sum() and an unused support field — and a support table for statuses exists that nothing references. Leftovers from an earlier idea of how state would work, before the derived approach won.

- My own notes page in the doc lists what's missing: per-order sequential item numbering (item IDs are currently global), and proper ordered-to-arrived time tracking.

There are a few other quirks and small improvements that could be made, but this is an internal tool used by one person — plus a backup when he's away — and none of it causes real problems in practice, so it was left as it is. Hence v0.9. The doc was forked from an earlier system rather than started blank, and it's still in use today. For a 17-page document with one checkbox doing all the lifecycle work, it has needed remarkably little of me since it shipped — which is roughly the definition of done I care about, v0.9 or not.