Retail order-to-cash is short: the customer pays the list price at checkout, the goods ship once, and the money arrives before the box does. Wholesale breaks every clause in that sentence. Price is negotiated, payment comes weeks later, the order ships in parts, and the most important decision in the whole chain is one retail never has to make: should these goods leave the building on credit?
This post is about the five places distribution differs, and what each one demands of the system underneath.
Difference one: price is a function, not a field
In wholesale, price is resolved from four inputs at the moment of order entry: which customer, which item, how many, and on what date. Customer A buys SKU-3390 at a contract price. Customer B buys it off the standard trade list with a 50-unit break. Both of those are correct prices for the same product on the same day.
The failure mode is storing a single discount percentage on the customer account. It works until the first customer negotiates a contract price on one item and standard terms on everything else, and then the percentage produces a wrong price on every other line. The workaround is manual override at order entry, which is how a distributor ends up with margin that cannot be explained at the end of the quarter, because the price on each line was a decision rather than a lookup.
A price-list model that holds up needs three things: a per-customer or per-group list, quantity breaks within a list, and effective dates so a price change can be scheduled rather than applied by hand on the morning it takes effect.
Difference two: terms mean the goods leave before the money arrives
Net 30 means you have financed your customer for a month. That is a credit decision, and it needs the same machinery any credit decision needs: a limit, an exposure calculation, and a rule about what happens when exposure exceeds the limit.
The exposure figure is where most implementations go wrong. Open invoices are the obvious component and they are not sufficient. Real exposure is:
| Component | Why it counts | Commonly omitted? |
|---|---|---|
| Open invoices, including not-yet-due | Money owed under agreed terms | No |
| Overdue invoices | Money owed and already late | No |
| Confirmed orders not yet shipped | Goods you have committed to hand over | Frequently |
| Shipped, not yet invoiced | Goods already gone, no document raised | Almost always |
| Unapplied credits and payments on account | Reduces exposure, so omitting it blocks good customers | Often |
Leaving out the middle two rows understates exposure by everything currently on the pick face and on a truck, which is precisely the stock about to leave without payment.
Difference three: the credit-hold decision has a correct location
A credit check at order entry is useful information. A credit check at order release is a control. Between those two moments the customer may place three more orders and let two invoices age past terms, and the entry-time approval is stale.
A workable design puts a decision at both points and records each one: a soft check at entry that warns the salesperson, and a hard check at release that stops the pick. The release check needs an override path with a named approver and a written reason, because credit holds are business decisions and a system that cannot express “ship it anyway, on my authority” gets bypassed by staff who stop entering orders in it.
The failure case: checked once, at the wrong moment
A distributor set a customer’s credit limit at 50,000 and checked it at order entry against open invoices only. Over one week the customer placed six orders. Each order passed, because at the moment of entry the open-invoice total was under the limit and none of the previous five orders had been invoiced yet.
By Friday, the customer held 74,000 of goods and had a 46,000 open-invoice balance. Nothing had malfunctioned. The check ran every time and gave the right answer to the wrong question, because confirmed-not-shipped and shipped-not-invoiced orders were invisible to it, and the only moment the check ran was the moment those figures were smallest. The customer entered administration eleven days later.
Two changes would have caught it: counting committed goods in exposure, and re-running the check at release rather than at entry.
Difference four: one order becomes several shipments and several invoices
Retail orders ship once. Distribution orders ship as stock allows. An order for 400 units may go out as 250 today and 150 when the container lands, and the correct document trail is two shipments and two invoices, each carrying the quantities actually dispatched.
The tempting shortcut is to invoice the order rather than the shipment. It produces a receivable for goods the customer does not have, a dispute when they reconcile against the delivery note, and a credit note to unpick it. In the meantime the aging report is wrong, because an invoice that should not exist yet is quietly getting older.
The structural requirement is that the invoice references the shipment, the shipment references order lines, and an order line tracks ordered quantity against shipped quantity independently. The same shape appears on the inbound side: a purchase-order line has to track what was ordered against what has actually arrived, so receiving in parts is normal rather than an exception someone handles by editing the order. In Oneop, receiving is partial-safe in exactly this sense, and receiving more than was ordered is rejected rather than silently clamped to the ordered quantity.
Difference five: the unfilled remainder is a backorder, not a note
When 150 of 400 units cannot ship, the remainder is a claim on stock that does not exist yet. Treated properly, a backorder carries a customer, a quantity, a promised date and a link to whatever inbound purchase order will satisfy it. Treated as a note in a comments field, it becomes a lost sale with extra steps, because when the container lands nothing connects the arriving units to the customer who has been waiting for them.
Backorders also change what “available” means. Availability for a new order is on-hand minus reserved, and the reservation invariant is what stops two orders claiming the same unit. Promising a date on a backorder additionally needs the expected receipt date of the inbound purchase order, which is a different question from current availability. Confusing the two is how a customer is promised Tuesday against stock that is on a ship. The availability half of this is the mechanism we set out in stock you can promise: ATP and why overselling happens.
Where the ledger meets the chain
Every step above eventually becomes an accounting entry, and the ones that go wrong are the ones posted by a person after the fact. Shipping goods should post the cost of those goods. Invoicing should post the receivable, the revenue and the tax as one transaction. Receiving a payment should apply against a specific invoice, not against a customer balance, or your aging report becomes a fiction.
In Oneop, invoicing a sales order posts the receivable, revenue and tax line atomically, shipping stock posts its cost with idempotent posting so a retry cannot double-post, and receivables and payables age across five buckets read from posted rows rather than from a running total something keeps up to date. Debits must equal credits, checked in the application and again by a PostgreSQL trigger that refuses to write an unbalanced entry, and closed periods stop accepting postings so a correction is a linked reversal rather than an edit to history. The finance module carrying all of that is opt-in and a workspace owner turns it on deliberately. The underlying discipline is worth understanding even if you never touch a ledger yourself, which is why we wrote double-entry accounting for founders.
What to check before choosing a system
The wholesale solution page states what Oneop covers for distributors and, in the same place, what it does not: no EDI, no B2B ordering portal, no automated receivables dunning, no automatic overdue status, a single deployment-wide ship-from address rather than one per warehouse, and tenant-maintained exchange rates. That list exists because a distributor needs to scope a migration, not be reassured.
Use the same standard on every vendor you shortlist. Ask how price is resolved, when the credit check runs, what exposure includes, whether an invoice belongs to a shipment or an order, and what a backorder is made of. Vague answers to those five questions predict manual work better than any feature grid.
The deeper reason these five behaviours are hard to bolt on is that each one spans modules: pricing is sales and finance, credit is finance and fulfilment, backorders are sales and purchasing. Systems that hold those in separate databases reconcile them afterwards, which is the tradeoff examined in the architecture behind CRM, finance and inventory in one platform. If you want to test the chain rather than read about it, the inventory and finance modules run one order from reservation to a posted journal entry on the free plan, and reporting reads the result from posted rows.