The three order statuses
A buyer phones your Innendienst and asks whether their order is done. There are three correct answers, and they can disagree with each other. This is the single concept that makes B2B order management different, and the one that causes the most confusion when people arrive from a consumer shop.
Why one status is not enough
In a B2C shop the order is simple enough to collapse into one line: paid, then packed, then shipped, then delivered. Payment happens first, everything ships at once, and a single word describes the whole thing.
B2B breaks that in three places at once:
Payment comes last. Kauf auf Rechnung (30 days net, sometimes 60) is still the default in German B2B. The goods leave before a cent arrives. An order that has to be paid before it can ship describes almost none of your business.
Deliveries are partial. You have 6 of the 10 fittings on the shelf and the rest arrives from the manufacturer on Thursday. The buyer needs the 6 now. One order, two deliveries, two delivery notes.
Approval is a stage of its own. The order may exist and be legitimate while still waiting for someone at the buyer's company, or yours, to release it.
Squeeze that into one status field and you get values like
partially_shipped_unpaid_awaiting_approval, which is three statuses spelled
as one word. Every combination has to be invented in advance, and the first
case nobody thought of has no value to put in the field.
The three dimensions
An order therefore carries three independent status fields, plus one flag.
| Dimension | Values | Who moves it |
|---|---|---|
status | pending → placed → in_fulfillment → completed, or cancelled | The lifecycle actions: placing, shipping, completing, cancelling |
payment_status | open, pending, authorized, paid, partially_paid, refunded, failed | The Payments app, from outside |
fulfillment_status | unfulfilled, partial, fulfilled | Nobody. It is derived from the position quantities |
on_hold | true / false, with a hold_reason | An operator, or a credit check |
The three are independent. Any lifecycle state can pair with any payment state and any fulfillment state, and most of the pairings are ordinary business rather than an error.
fulfillment_status is the odd one out: you never set it. It is read off the
positions. When every position's shipped quantity covers its ordered quantity,
the order is fulfilled; when some has shipped, partial; when nothing has,
unfulfilled. Cancelled quantities count as settled, which is why cancelling
the remaining 4 of 10 on a part-shipped order completes it rather than leaving
it open forever.
Reading a real order
Take a typical order: ten hydraulic fittings, article 4711-A, bought on
invoice by a customer with 30 days net. Six are in stock.
| What happens | status | payment_status | fulfillment_status |
|---|---|---|---|
| Buyer submits the cart | placed | open | unfulfilled |
| Your ERP acknowledges it | placed | open | unfulfilled |
| You ship 6 of 10 | in_fulfillment | open | partial |
| Invoice for the partial delivery is paid | in_fulfillment | partially_paid | partial |
| The remaining 4 arrive and ship | completed | partially_paid | fulfilled |
| The second invoice is paid | completed | paid | fulfilled |
Read row three again: in_fulfillment / open / partial. Confirmed,
unpaid, and half delivered, all at once, and entirely correct. There is no
single word for that row, which is the whole argument for three fields.
Look at row five as well. The order reaches completed while it is still not
paid. Whether that is right for you is a decision, and you make it in the
setting described under When an order closes below.
Which combinations are normal, and which are wrong
| Combination | Verdict |
|---|---|
placed / open / unfulfilled | Normal. A new invoice order. |
in_fulfillment / open / partial | Normal. The everyday B2B state. |
placed / paid / unfulfilled | Normal: prepayment, or a card payment captured up front. |
completed / open / fulfilled | Normal on invoice terms. Chase the payment, not the order. |
cancelled / paid / unfulfilled | Needs work. Money was taken for goods that will not ship; a refund is owed. |
in_fulfillment / failed / partial | Needs work. Goods left the building on a payment that did not go through. |
placed / anything / partial | Impossible. A shipment moves the lifecycle to in_fulfillment. |
The last row is the useful one for troubleshooting: if you ever see a
part-shipped order still sitting at placed, the shipment was not recorded
through the platform. Someone shipped from the warehouse system and never told
the order.
What moves each one
status is moved by the actions on the order detail page: Acknowledge,
Create shipment, Cancel positions, Cancel order, Complete.
payment_status is fed from outside. The Payments app owns the money: a
payment runs its own lifecycle (created → authorized → captured → refunded) and
reports the result onto the order. You can also set it by hand with Update
payment, which is what you do for a bank transfer that arrived on your
statement rather than through a payment provider. See
Payment methods.
fulfillment_status moves only when position quantities move, through a
shipment, a cancellation or a return.
on_hold is set with Put on hold and lifted with Release hold. What
it blocks is a tenant-wide decision: shipping only; shipping and cancellation,
which is the right setting while a credit check is running, because you do not
want the order cancelled out from under the check either; or nothing, which
makes the flag a visible warning and nothing more.
When an order closes
completed does not mean the same thing at every company, so it is configurable.
There are three positions:
| Setting | An order completes when | Choose it if |
|---|---|---|
| Full shipment | Every position has shipped | You treat delivery as the end of your responsibility. The default. |
| Full shipment and payment | Everything has shipped and payment_status is paid | You consider an unpaid order an open order, which is common where Innendienst chases payment |
| Manually | Never on its own; someone presses Complete | Your close is a judgement call, or your orders are services that never ship |
The manual option is also the honest end of an order that will never have a shipment at all: a service, an installation, a licence.
Where you see all three
On the order detail page the three appear as badges in the header, side by side, with the lifecycle drawn as a flow on the Overview tab. On Order Management › Orders they are three separate columns and three separate filters, plus a filter for On hold.
That is what makes a real worklist possible. "Orders on hold", "orders awaiting approval" and "open returns" are each one filter, and each is a different queue worked by a different person. A single status field would force all three into one list.
status alone. Revenue at risk is a
payment_status question, warehouse backlog is a fulfillment_status question,
and neither is answered by counting orders in in_fulfillment. A dashboard that
reads one field and calls it "order status" will be quietly wrong about both.Next
- The order lifecycle — what an order is made of.
- Partial deliveries and backorders — the fulfillment dimension in practice.
- Orders that get stuck — when the three disagree and nothing moves.