本文目前仅提供英文版本。翻译即将推出。
属于我们的Supply Chain & Procurement系列
阅读完整指南The three numbers that control any replenishment system are: EOQ (economic order quantity — how much to order), safety stock (the buffer against demand and lead-time variability), and the reorder point (the stock level that triggers a new order). The formulas are: EOQ = sqrt(2DS / H), safety stock = z × demand standard deviation × sqrt(lead time), and reorder point = average demand × lead time + safety stock. Get these three right per SKU and you order the right quantity at the right moment; get them wrong and you oscillate between stockouts and dead capital. This guide works each formula with real numbers, gives you the full parameter and data-model table, the service-level z-score trade-off table, and the exact mapping onto Odoo reordering rules.
Key Takeaways
- EOQ = sqrt(2DS / H) balances ordering cost against holding cost — it answers "how much per order", not "when to order"
- The reorder point answers "when": ROP = average daily demand × lead time in days + safety stock
- Safety stock is driven by a z-score you choose: 90% service level uses z = 1.28, 95% uses z = 1.65, 99% uses z = 2.33 — each step up costs disproportionately more stock
- When lead time itself varies, use the combined formula that includes lead-time standard deviation — supplier unreliability often dominates demand noise
- In Odoo, the reordering rule's Min Quantity is your reorder point and Max Quantity should be roughly ROP + EOQ — setting Min = Max is the single most common configuration mistake
- Holding cost H must be annual cost per unit (typically 15–30% of unit cost), and D and H must use the same time basis or EOQ comes out meaningless
- Parameters decay: demand, lead times, and costs drift, so recompute quarterly per A-class SKU at minimum
- Formulas assume reasonably normal, stable demand — for lumpy, seasonal, or new products, use forecast-driven replenishment instead of static rules
The Three Questions Every Replenishment Policy Answers
A continuous-review replenishment policy (the kind Odoo's reordering rules implement) answers three independent questions:
| Question | Concept | Formula (plain text) |
|---|---|---|
| How much should each order be? | Economic Order Quantity (EOQ) | EOQ = sqrt(2 × D × S / H) |
| How much buffer do I hold against uncertainty? | Safety stock (SS) | SS = z × sigma_d × sqrt(LT) |
| At what stock level do I reorder? | Reorder point (ROP) | ROP = d_avg × LT + SS |
They are independent levers. EOQ is pure cost optimization and has nothing to do with uncertainty. Safety stock is pure uncertainty management and has nothing to do with ordering cost. The reorder point ties timing together. Practitioners who blur them — for example, inflating order quantities "to be safe" — pay for buffer twice and still stock out, because extra order quantity does not protect the window between hitting the reorder point and the goods arriving.
Parameters and Data Model
Before any formula, you need these inputs per SKU. This table doubles as the data model if you are building a calculation layer or spreadsheet on top of your ERP:
| Parameter | Symbol | Definition | Typical Source | Unit |
|---|---|---|---|---|
| Annual demand | D | Units sold or consumed per year | Sales/consumption history (12 months) | units/year |
| Average daily demand | d_avg | D ÷ selling days | Same, divided by ~250–365 days | units/day |
| Demand standard deviation | sigma_d | Std dev of daily (or weekly) demand | Computed from demand history at the same bucket size as LT | units/day |
| Lead time | LT | Days from order placement to stock available | Purchase order receipt history, not the supplier's promise | days |
| Lead-time standard deviation | sigma_LT | Std dev of actual lead times | Same PO history | days |
| Ordering cost | S | Fixed cost per purchase order: admin time, receiving, inspection, freight fixed portion | Costing exercise; commonly 25–150 USD per order | currency/order |
| Holding cost | H | Annual cost to hold one unit: capital, warehouse space, insurance, obsolescence, shrinkage | Usually 15–30% of unit cost per year | currency/unit/year |
| Unit cost | c | Purchase cost per unit | Supplier pricelist / ERP cost field | currency/unit |
| Service level | SL | Probability of not stocking out during a replenishment cycle | Management decision per ABC class | percent |
| z-score | z | Standard-normal value matching the service level | Lookup table below | dimensionless |
Two data-hygiene rules that wreck more calculations than any formula error:
- Match time buckets. If your lead time is in days, sigma_d must be the standard deviation of daily demand. Computing sigma from monthly buckets and plugging it into a formula with daily lead time inflates safety stock by roughly sqrt(30), more than five-fold.
- Use actual lead times, not promised ones. Pull receipt dates minus order dates from your PO history. Suppliers' quoted lead times are marketing; your sigma_LT lives in the gap.
EOQ: The Formula and a Worked Example
EOQ minimizes the total of annual ordering cost (more orders = more fixed cost) and annual holding cost (bigger orders = more average inventory):
EOQ = sqrt(2 × D × S / H)
Total annual cost at EOQ = (D / EOQ) × S + (EOQ / 2) × H
Worked example. A distributor sells a power adapter:
- D = 12,000 units/year
- S = 50 USD per order
- Unit cost c = 12 USD; holding rate 20%/year, so H = 0.20 × 12 = 2.40 USD/unit/year
EOQ = sqrt(2 × 12,000 × 50 / 2.40)
= sqrt(1,200,000 / 2.40)
= sqrt(500,000)
≈ 707 units
Sanity check the economics:
- Orders per year = 12,000 / 707 ≈ 17 orders
- Annual ordering cost = 17 × 50 = 850 USD
- Average cycle stock = 707 / 2 ≈ 354 units; holding cost = 354 × 2.40 ≈ 849 USD
Ordering cost ≈ holding cost — that equality is the signature of a correct EOQ. In practice you would round 707 to a supplier pack size or container quantity (say 700 or 720); the total-cost curve is flat near the optimum, so rounding within ±20% costs almost nothing. That flatness is also why agonizing over precise S and H values is wasted effort — get them roughly right and move on.
Where EOQ misleads: quantity-break pricing (compare total cost at each break point against EOQ), perishables and short-lifecycle goods (cap order quantity at sellable-before-expiry), and supplier minimum order quantities that exceed EOQ (the MOQ wins; your model should flag the excess holding cost it causes).
Safety Stock: The z-Score Decision
Safety stock protects you during the exposure window — the lead time between triggering a reorder and receiving it. The base formula, when demand varies but lead time is stable:
SS = z × sigma_d × sqrt(LT)
Worked example. Same adapter:
- d_avg = 12,000 / 300 selling days = 40 units/day
- sigma_d = 12 units/day (computed from daily sales history)
- LT = 7 days
- Target service level 95%, so z = 1.65
SS = 1.65 × 12 × sqrt(7)
= 1.65 × 12 × 2.646
≈ 52.4 → round to 53 units
When Lead Time Also Varies
If your supplier's actual lead time swings (and pull the PO history before assuming it does not), use the combined formula:
SS = z × sqrt( LT × sigma_d² + d_avg² × sigma_LT² )
Continuing the example with sigma_LT = 2 days:
SS = 1.65 × sqrt( 7 × 144 + 1,600 × 4 )
= 1.65 × sqrt( 1,008 + 6,400 )
= 1.65 × sqrt( 7,408 )
= 1.65 × 86.1
≈ 142 units
Read those two results side by side: 53 units against 142 units. The lead-time variability term (6,400) dwarfs the demand variability term (1,008). A supplier who is two days erratic costs you nearly three times the buffer of your own demand noise. This is the quantitative case for supplier reliability programs — shaving sigma_LT from 2 days to 1 day cuts safety stock from 142 to about 97 units, on this one SKU alone.
The Service-Level Trade-Off Table
The z-score is a management decision dressed as a statistic. Each step toward 100% service costs disproportionately more, because z grows faster as you push into the tail:
| Service Level | z-Score | SS in Example (units) | Index vs. 90% |
|---|---|---|---|
| 90% | 1.28 | 41 | 100 |
| 95% | 1.65 | 53 | 129 |
| 97.5% | 1.96 | 62 | 153 |
| 98% | 2.05 | 65 | 160 |
| 99% | 2.33 | 74 | 181 |
| 99.9% | 3.09 | 98 | 240 |
(SS column uses the simple formula with sigma_d = 12, LT = 7.)
Going from 90% to 99.9% service nearly two-and-a-half times your buffer — on every SKU you apply it to. This is why ABC segmentation matters: target 98–99% on A-class items where a stockout costs real revenue or a production stop, 95% on B-class, and 90% (or less, or none) on C-class long-tail items. We cover the segmentation method in depth in our guide to demand forecasting, ABC analysis, and safety stock.
You can sanity-check your own numbers against ours with the free safety stock calculator.
Reorder Point: Putting Timing Together
ROP = d_avg × LT + SS
Worked example, using the combined-variability safety stock:
ROP = 40 × 7 + 142
= 280 + 142
= 422 units
When forecasted available stock touches 422 units, order EOQ ≈ 707 units. Average inventory over time will be roughly SS + EOQ/2 = 142 + 354 ≈ 496 units, worth about 5,950 USD at cost — a number you can now defend line by line in front of a CFO, which is the real point of doing this formally.
One subtlety: the trigger should compare against forecasted available stock (on hand − reservations + incoming receipts), not raw on-hand. If 800 units are already on a confirmed inbound PO, you do not want to reorder at 422 on hand. Odoo's reordering engine handles this correctly out of the box, which brings us to setup. The reorder point calculator implements the same forecast-aware logic if you want to test scenarios first.
Configuring Odoo Reordering Rules with These Values
Odoo's reordering rules (Inventory → Operations → Replenishment, or via the product form) implement a min/max continuous-review policy. The mapping from the formulas:
| Odoo Field | Set It To | From Our Example |
|---|---|---|
| Min Quantity | Reorder point (ROP) | 422 |
| Max Quantity | ROP + EOQ (approximately) | 422 + 707 = 1,129 |
| Multiple Quantity | Supplier pack/carton size (0 if none) | e.g., 10 |
| Location | The warehouse/location the rule governs | Main warehouse stock |
| Route / Preferred route | Buy, Manufacture, or Resupply | Buy |
| Vendor lead time (on the supplier pricelist line) | Your actual average LT | 7 days |
How the engine behaves: the scheduler computes forecasted quantity (on hand − outgoing reservations + confirmed incoming) at the rule's location. When forecast falls below Min, Odoo generates a draft RFQ (or manufacturing order) for the quantity needed to reach Max, rounded up to the Multiple. Because the trigger uses forecasted stock, inbound POs correctly suppress duplicate orders.
Settings that interact with your math — set them deliberately:
- Vendor lead time lives on the supplier pricelist line of the product. Odoo uses it to schedule the order date backward from the needed date. Keep it equal to the LT you used in the ROP formula, or the timing logic and your buffer math diverge.
- Security Lead Time for Purchase (Inventory settings) moves every order earlier by a fixed number of days. It is a crude, days-based hedge. If you computed safety stock properly with sigma_LT, leave security lead time at zero — using both double-counts lead-time risk across your entire catalog.
- Days to Purchase (how long your own purchasing department takes to convert an RFQ to a confirmed PO) adds to the effective lead time. If RFQs sit two days awaiting approval, either include those 2 days in LT or set this field — once, not both.
- Visibility Days lets the rule consider demand beyond the lead-time horizon (useful when long-dated sale orders should pull replenishment early). Use sparingly; it effectively inflates d_avg × LT.
- Trigger: Auto vs. Manual — Auto lets the scheduler create draft RFQs; Manual only flags suggestions in the Replenishment screen. Start A-class items on Auto, long-tail on Manual until you trust the parameters.
- Scheduler frequency — the stock scheduler runs nightly by default. High-velocity operations can run it more often (it is a scheduled action), but nightly is right for most.
A worked Odoo configuration for our adapter SKU:
Product: ADAPT-65W
Reordering rule: Min = 422, Max = 1,129, Multiple = 10
Supplier pricelist: Delivery lead time = 7 days
Route: Buy
Trigger: Auto
Result: When forecasted stock dips below 422, Odoo drafts an
RFQ for ~710 units (rounded to multiple of 10),
scheduled so goods arrive before stock hits zero
in the average case, with 142 units of buffer
absorbing the bad cases.
For the broader warehouse-configuration context around these rules — locations, routes, putaway, cycle counting — see our Odoo inventory management best practices guide.
Common Mistakes (Field-Audit Edition)
These are the errors we actually find when auditing live Odoo databases:
- Min = Max. The rule fires tiny top-up orders constantly — dozens of small POs, no cycle-stock economics at all. Max must exceed Min by approximately one EOQ.
- Annual demand with monthly sigma. Mixed time bases produce buffers that are wildly wrong in either direction. Recompute sigma at the bucket size matching your lead-time unit.
- Holding cost as a per-order or flat number. H is annual, per unit. Using unit cost itself as H (a surprisingly common spreadsheet slip) understates EOQ badly because it overstates holding cost by 3–7×.
- Promised lead times in the pricelist. Supplier says 7, history says 11 with a sigma of 3. Everything downstream of that lie is fiction.
- Security lead time AND statistical safety stock together. Double-counted buffer across the whole catalog — typically 10–20% excess inventory that nobody can explain.
- One service level for everything. 99% on the C-class long tail is dead capital; 90% on your hero SKU is lost revenue. Segment first.
- Set-and-forget parameters. Demand drifts, suppliers change, freight costs move. Recompute A-class quarterly, B-class semi-annually, C-class annually — and always after a supplier change.
- Static rules on non-stationary demand. Strong seasonality, promotions, or new-product ramps violate the formulas' stable-demand assumption. For those SKUs, use forecast-driven replenishment (or at minimum, seasonal parameter sets) rather than a single year-round Min/Max.
Frequently Asked Questions
What is the difference between reorder point and minimum stock in Odoo?
They are the same number wearing different names. Odoo's reordering rule "Min Quantity" is where you encode your computed reorder point: average demand over lead time plus safety stock. When forecasted available stock falls below Min, Odoo proposes replenishment up to Max. The mistake to avoid is treating Min as "the lowest stock I would like to see" picked by gut feel — derive it from d_avg × LT + SS, or the rule will trigger systematically too early (excess stock) or too late (stockouts).
How do I calculate safety stock when both demand and lead time vary?
Use the combined formula: SS = z × sqrt(LT × sigma_d² + d_avg² × sigma_LT²), where sigma_d is the standard deviation of daily demand and sigma_LT is the standard deviation of lead time in days. The second term — your supplier's unreliability multiplied by your demand rate — frequently dominates the first. In our worked example, adding a 2-day lead-time sigma nearly tripled safety stock from 53 to 142 units, which is why measuring actual receipt history (not supplier promises) is the highest-value data fix in most replenishment projects.
What z-score should I use for a 95% or 99% service level?
Use 1.28 for 90%, 1.65 for 95%, 1.96 for 97.5%, 2.33 for 99%, and 3.09 for 99.9%. These are standard-normal values: a 95% cycle service level means you accept a stockout in roughly 1 in 20 replenishment cycles. Because z grows steeply in the tail, each service-level step costs disproportionately more inventory — moving from 95% to 99% adds about 41% more safety stock. Set service levels per ABC class rather than globally, reserving 98–99% for items where a stockout stops production or loses an irreplaceable sale.
Should Odoo's Max Quantity be the EOQ?
Not the EOQ itself — set Max ≈ Min + EOQ. Odoo replenishes the gap between forecasted stock and Max, so with Min at your reorder point of 422 and Max at 1,129, each triggered order is approximately one EOQ of 707 units (rounded to your pack multiple). If you set Max equal to EOQ alone and EOQ is below your reorder point, the rule produces undersized orders or behaves erratically. And never set Max equal to Min: that degenerates the policy into continuous micro-ordering with none of the EOQ cost savings.
How often should I recalculate EOQ, safety stock, and reorder points?
Quarterly for A-class SKUs, semi-annually for B-class, annually for C-class — and immediately after structural changes: a supplier switch, a freight-cost step change, a demand-pattern shift (new sales channel, lost key customer), or a measured change in lead-time behavior. The inputs decay at different speeds: demand statistics drift fastest, holding-cost rates slowest. Automating the recompute from your ERP's sales and PO-receipt history (then reviewing exceptions instead of editing every rule by hand) is the difference between a policy and a one-off project.
Do these formulas work for seasonal or slow-moving products?
With caveats. The formulas assume demand is reasonably stable and approximately normal. For seasonal items, compute separate parameter sets per season (or drive replenishment from a forecast instead of a static rule). For slow movers with intermittent, lumpy demand — many zero-demand days punctuated by spikes — the normal-distribution assumption breaks down and the z-score buffer becomes unreliable; consider Poisson-based models, a simple Min/Max set from order-cycle logic, or make-to-order. Our demand forecasting and ABC analysis guide covers choosing the right method per demand pattern.
作者
ECOSIRE TeamTechnical Writing
The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.
相关文章
BMF Programmablaufplan Lohnsteuer 2026:实施德国官方工资税计算(XML、API、Odoo)
BMF Programmablaufplan Lohnsteuer 2026 开发人员指南:PAP 是什么、XML 伪代码格式、官方测试服务以及到 Odoo 工资单的映射。
2026 年 CRM 系统的成本是多少? 40 多个实施的实际定价
来自 40 多个实施的真实 CRM 定价:每个用户的许可成本、实施费用、隐藏成本以及 Odoo、HubSpot、Salesforce 等的 3 年 TCO。
eMAG Odoo 集成:将罗马尼亚最大的市场连接到您的 ERP(订单、库存、e-Factura)
将 eMAG Marketplace 连接到 Odoo ERP:报价和订单同步、AWB 运输、退货、库存和价格更新,以及卖家的罗马尼亚 e-Factura 合规性。
更多来自Supply Chain & Procurement
Odoo 19 库存:上架、移除和补货深入探讨
掌握 Odoo 19 库存:新的上架策略、FEFO/LIFO/FIFO 移除逻辑、动态补货、多步骤路线和条形码流。
用于供应链优化的人工智能:可见性、预测和自动化
利用人工智能改变供应链运营:需求感知、供应商风险评分、路线优化、仓库自动化和中断预测。 2026年指南。
如何编写 ERP RFP:免费模板和评估标准
使用我们的免费模板、强制性要求清单、供应商评分方法、演示脚本和参考检查指南编写有效的 ERP RFP。
用于需求规划的机器学习:准确预测库存需求
实施基于 ML 的需求规划,以 85-95% 的准确度预测库存需求。时间序列预测、季节性模式和 Odoo 集成指南。
Odoo 采购:完整自动化指南 2026
掌握 Odoo 19 采购与询价、供应商管理、三向匹配、到岸成本和再订购规则。全自动化指南。
Power BI 供应链仪表板:可见性和绩效跟踪
构建 Power BI 供应链仪表板,跟踪库存周转、供应商交货时间、订单履行、需求与供应、物流成本和仓库利用率。