📈 Purchase planning
Updated
How much to buy from one supplier for the weeks ahead, read from what that supplier actually delivered and what the company actually sold. The process starts on the Purchase planning screen and ends with a draft PurchaseOrder you still have to confirm.
How the process works#
Nothing is stored between runs: the screen holds the parameters, the service reads the facts, and the only lasting result is an ordinary draft order.
Purchase planning screen
│ supplier + sales window + horizon
▼
PurchasePlanning service — reads receipts, sales, stock, reserve, open orders
▼
plan rows (one per item the supplier delivered)
│ "Create order"
▼
PurchaseOrder (Draft) ──"Confirm"──► Ordered ──"Receive"──► Received
Facts the plan reads, all of them over the window you set:
| Fact | Where it comes from |
|---|---|
| What this supplier delivered | PurchaseOrder in subtype Received, lines of the supplier |
| What everyone delivered | the same documents, every supplier — this gives the supplier's share |
| What was sold | SalesRealization in Issued or Shipped, minus SalesReturn in Posted |
| What is on hand | register Stock, every cell of the company |
| What is reserved | register ReservedStock |
| What is already coming | PurchaseOrder in subtype Ordered, arriving inside the horizon |
The item list is not the whole catalogue: an item appears only if this supplier delivered it inside the sales window. An item the supplier never sent you is not planned, even when it is out of stock.
Settings#
Every parameter lives on the screen itself, not in a dictionary: a plan is a question you ask once, not a configured rule. The screen is Replenishment → Purchase planning.
Supplier#
Where: model Replenishment → screen Purchase planning → field Supplier.
| Value | Meaning | What changes |
|---|---|---|
| a supplier | the plan is built for that supplier only | the item list is what the supplier delivered; demand is cut to the supplier's share |
| (empty) | refused | Calculate warns «Pick a supplier first» and lists nothing |
Default: empty. The screen cannot calculate without it.
Example. You buy enamel from two suppliers: 30 units came from A and 70 from B over the window. Sales were 100 units. In the plan for A the demand is 30, in the plan for B it is 70. Without that split both plans would ask for 100 and you would buy twice.
Sales history from / Sales history to#
Where: the same screen, fields Sales history from and Sales history to.
| Value | Meaning | What changes |
|---|---|---|
| a period | sales and receipts are read inside it | both the demand rate and the supplier's share |
| to earlier than from | refused | Calculate warns «Set the sales history period» |
Default: the last six months, ending today. Both ends are inclusive: a document dated on the to day counts.
Plan for (days)#
Where: field Plan for (days).
Default: 28 — four weeks, the horizon the warehouse asks for most often. Less than one day is refused.
What changes: the forecast is the sales rate times the selling days inside the horizon, not the calendar days inside it.
Selling days a week#
Where: field Selling days a week. Allowed values: 7, 6, 5. Anything else is refused with «selling days a week: 5, 6 or 7».
| Value | Meaning |
|---|---|
7 |
the company sells every day |
6 |
closed on Sunday |
5 |
closed on Saturday and Sunday |
Default: 7.
What changes: the same number is both the divisor of the window and the multiplier of the horizon, so a shop closed on weekends is not told to buy for days it does not trade.
Example. 100 units sold over a two-week window. At 7 that is 14 selling days, the rate is 7.14 a day, and a 30-day horizon with 30 selling days plans 214. At 5 the window has only 10 selling days, the rate is 10 a day, and the horizon has 22 selling days — the plan is 220. The rate went up and the horizon went down; the forecast barely moved, which is the point.
Safety stock (days)#
Where: field Safety stock (days). Any whole number from zero.
Default: 0.
What changes: the forecast is raised by the rate times those days before the stock is subtracted. Safety days are counted as calendar days of the rate, not as selling days.
Count open orders#
Where: switch Count open orders.
| Value | Meaning | What changes |
|---|---|---|
| on | quantities already ordered are treated as available | a PurchaseOrder in Ordered whose Expected receipt falls inside the horizon lowers the need |
| off | open orders are ignored | you plan as if nothing is coming |
Default: on. An order with an empty expected receipt counts as coming: an undated order is a promise, not a reason to buy twice.
Example. Need is 46. There is an order for 5 arriving next week and another for 100 arriving in 60 days with a 28-day horizon. The first lowers the need to 41; the second is ignored, because the stock will run out long before it lands.
Subtract reserve#
Where: switch Subtract reserve.
Default: on. With it on, quantities in ReservedStock are taken out of the available stock — they belong to customers already.
Round up to whole units#
Where: switch Round up to whole units.
Default: on. A need of 45.2 becomes 46. Rounding happens after stock and orders are subtracted, so it never turns a zero need into one unit.
Only items to buy#
Where: switch Only items to buy.
Default: on — the list holds only rows with a need above zero. Turn it off to see the whole supplier assortment with its zeros, which is what you want when you are checking why something was not planned.
Receiving cell and Legal entity#
Where: fields Receiving cell and Legal entity, below the plan.
They do not change a single number in the plan. They go into the draft order: the cell becomes the order's receiving location, the legal entity its company. Create order without a cell is refused.
Example with numbers#
Sales window: two weeks, seven selling days a week. Horizon: 28 days. One item, one supplier, everything delivered by them.
| Step | Input | Plan row after the step |
|---|---|---|
| 1. Sales over the window | 28 units sold | rate 2/day, forecast 56 |
| 2. Stock | 10 on hand | need 46 |
| 3. An order for 5 arrives next week | Count open orders on | need 41 |
| 4. 6 of the stock are reserved | Subtract reserve on | need 47 |
| 5. The customer returns 8 | return posted inside the window | sales 20, rate 1.43, forecast 40, stock 18, need 23 |
The last step is the one that surprises people: a return hits the plan twice. It puts the 8 units back on the shelf and lowers the measured demand, because the sale it reverses never really happened. The need falls from 47 to 23, not to 39.
A return larger than the sales of the window floors the demand at zero; it never becomes negative, so a heavy return month cannot produce a negative need that quietly cancels a real one.
What is posted#
Planning itself posts nothing — no register moves, no ledger entries, no stored report. The only document it writes is a draft:
| Document | Transition | Register | Sign | Ledger |
|---|---|---|---|---|
PurchaseOrder |
created in Draft |
— | — | — |
PurchaseOrder |
Draft → Ordered |
— | — | — |
PurchaseOrder |
Ordered → Received |
Stock, Payable |
+ | Dr inventory / Cr payables |
Prices in the draft come from the supplier's own price list through the purchase price resolver. An item with no price gets zero, and the buyer fills it with the Fill prices command on the order. A zero price is deliberate: a made-up price is worse than an empty one.
Remember that the subtype is moved by a command, not by picking it in the card: the draft the planner creates still has to be confirmed by a person.
Boundaries and edge cases#
- The plan is company-wide.
Stockis kept per cell, and sales do not know the cell, so stock in the plan is the sum over every cell. A per-store plan is not available. - An item the supplier never delivered inside the window never appears, however short it is.
- The supplier's share is capped at 1. A receipt corrected after the fact cannot push demand above the real sales.
- Disabled items are dropped from the result even when the arithmetic gives them a need.
- Create order refuses with «Tick at least one line with a quantity to order» when nothing is ticked, and warns when the supplier and the cell are not both set.
- When the supplier already has a draft order for the same receiving cell, the screen asks before adding a second one — an accidental double order is the most expensive mistake on this screen.
What's not there yet#
- No breakdown by store or cell: one plan covers the whole company.
- No seasonality and no trend. The forecast is a flat rate times the horizon; a December spike inside the window raises the rate for the quarter after it, and nothing warns you.
- No supplier lead time. Expected receipt on an open order is read, but the plan does not add the supplier's own delivery days to the horizon — put them into Safety stock (days) yourself.
- No minimum order quantity, no multiple of a pack, no supplier minimum order amount.
- Nothing is saved: closing the screen loses the parameters, and there is no stored «purchase requirement» document to approve.
- The plan does not look at ABC/XYZ classes: the two mechanisms answer different questions and do not share rules.
Where next#
- ABC/XYZ — the nightly policy that keeps a chosen number of days of stock, with the supplier taken from the last receipt
- Sales and purchasing — where the draft order goes from here
- Supplier contract — where the price the draft uses comes from