TIP: You can type at any time to perform a new search.
Stock Orderpoint Safety Stock
stock_orderpoint_safety_stock · OCA/stock-logistics-orderpoint
- Repository
- OCA/stock-logistics-orderpoint · module folder · Try on Runboat
- Module version
- 1.0.1
- Category
- Inventory
- Folder size
- 2.05 MB
- License
- AGPL-3
- Application
- No
- Auto-installable
- No
- Website
- https://github.com/OCA/stock-logistics-orderpoint
- Last tracking update
- 2026-08-07 09:06:24
- Authors
- Camptocamp, Odoo Community Association (OCA)
- Maintainers
- Camptocamp, Odoo Community Association (OCA)
- Committers
- Iván Todorovich, Weblate, OCA-git-bot, oca-ci
- Odoo dependencies
- Python dependencies
- pyerf
- System dependencies
- None
- Required by
- None
- Description
This module enhances inventory management by introducing service-level–based, stochastic control using a continuous review replenishment system. Instead of relying solely on fixed min/max stock levels, the module estimates your typical (mean) and variable (variance) daily demand from historical data and factors in lead time. These calculations generate a statistically sound safety stock, which is included in your reorder threshold (the “min”). The “max” is set so your inventory will cover expected demand during the entire replenishment cycle. Now, you can set replenishment rules (orderpoints) based on the **Cycle Service Level (CSL)**, which reflects the probability of meeting all demand during a replenishment cycle without running out of stock. This provides a practical alternative to manually setting minimum and maximum stock thresholds. With CSL enabled, safety stock is automatically calculated using your historical daily sales data (both average and standard deviation), gathered over a period you define, plus the orderpoint’s lead time. The system then keeps your reordering rule's min and max levels up-to-date. Prefer manual control? You can always switch back to the “Manual” mode to specify min and max directly. ## Theory The backbone of this approach is the Cycle Service Level (CSL), a widely used supply chain metric: - **CSL Definition:** The chance that your inventory will fully cover demand during a restocking cycle. - **Example:** A CSL of 95% means that only 5% of cycles will risk a stockout. This system assumes demand is random (not fixed), so it uses statistical methods: - **Average daily demand** (μ) - **Standard deviation of daily demand** (σ) Because demand can fluctuate during the lead time, safety stock acts as a buffer to reduce the risk of running out of stock. **Safety stock formula:** `safety_stock = σ_L × z × g` Where: - **σ_L:** Standard deviation of demand over the lead time - **z:** Z-score for your desired CSL (e.g., 1.65 for 95% CSL) - **g:** Growth factor (optional, lets you add a margin) **Three zones are needed to define how min and max are derived:** - **Red zone = safety stock:** - This zone should never be touched. It acts as the buffer for unexpected variation. - Refer to the safety stock formula above. - **Yellow zone = expected demand during the lead time:** - This zone represents the expected stock consumption from the moment you click on replenish, until the moment you receive your purchase order. - Formula: `average daily demand × lead time in days` - **Green zone = expected demand during the cycle:** - Represents the stock consumption during the desired reordering cycle (the time between two replenishments) - Formula: `average daily demand × cycle days` Where: - **lead time:** The time it takes to receive the order. - **cycle days:** The desired number of days between orders. From these three zones, the min and max quantities are derived as follows: - **Minimum (min):** Red + Yellow - **Maximum (max):** Red + Yellow + Green **Why does it work?** - Odoo triggers replenishment whenever stock falls below the min, which should be enough to cover variance (safety stock) and lead time demand. - The max level is set high enough to cover all expected demand until the next restock, plus a buffer to cover the desired cycle days. This makes inventory management both more data-driven and easier to maintain. 
Code Analysis ⓘ
Views touched (6)
| XML ID | Name | Model | Type | Status |
|---|---|---|---|---|
res_config_settings_view_form |
res.config.settings | form | Inherits stock.res_config_settings_view_form | |
stock_reorder_report_search |
stock.warehouse.orderpoint | search | Inherits stock.stock_reorder_report_search | |
view_stock_cycle_service_level_list |
stock.cycle.service.level | list | New | |
view_stock_replenishment_info |
stock.replenishment.info | Inherits stock.view_stock_replenishment_info | ||
view_stock_warehouse_orderpoint_tree_editable |
stock.warehouse.orderpoint | tree | Inherits stock.view_warehouse_orderpoint_tree_editable | |
view_warehouse_orderpoint_form |
stock.warehouse.orderpoint | form | Inherits stock.view_warehouse_orderpoint_form |
HTTP endpoints (0)
No HTTP endpoints found for this module.
Models touched (6)
New fields (0)
No new fields.
Public methods (0)No public methods.
New fields (1)
-
demand_history_daysIntegerdefault=365help='The number of days in the past to use to compute the safety stock.'
No public methods.
New fields (1)
-
demand_history_daysIntegerreadonly=Falserelated='company_id.demand_history_days'
No public methods.
New fields (4)
-
cslFloatdigits=(2, 2)help='The probability that demand during the replenishment lead time is fully covered without a stockout.\nThis value defines how reliable the stock should be during each replenishment cycle. A higher target increases safety stock to reduce the risk of stockouts; a lower target reduces inventory at the cost of more frequent shortages.\nTypical values range from 0.90 to 0.99 (e.g., 0.95 = 95% of cycles without stockouts).'string='Cycle Service Level' -
orderpoint_countIntegercompute='_compute_orderpoint_count'string='Orderpoints Count' -
orderpoint_idsOne2many → stock.warehouse.orderpointstring='Orderpoints' args: 'stock.warehouse.orderpoint', 'cycle_service_level_id' -
z_scoreFloatcompute='_compute_z_score'digits=(16, 4)help='Statistical factor derived from the target cycle service level.\n\nThe z-score represents how many standard deviations of demand variability are covered by the safety stock.\nA higher z-score corresponds to a higher service level and results in more safety stock.'store=Truestring='Z-Score'
-
action_open_orderpoints(self)
New fields (10)
-
cycle_daysIntegerreadonly=Falserelated='orderpoint_id.cycle_days' -
cycle_service_level_idMany2onereadonly=Falserelated='orderpoint_id.cycle_service_level_id' -
demand_avg_qtyFloatrelated='orderpoint_id.demand_avg_qty' -
demand_history_daysIntegerrelated='orderpoint_id.demand_history_days' -
demand_lt_std_devFloatrelated='orderpoint_id.demand_lt_std_dev' -
demand_std_devFloatrelated='orderpoint_id.demand_std_dev' -
growth_factorFloatreadonly=Falserelated='orderpoint_id.growth_factor' -
safety_stockFloatrelated='orderpoint_id.safety_stock' -
safety_stock_methodSelectionreadonly=Falserelated='orderpoint_id.safety_stock_method' -
z_scoreFloatdepends=['orderpoint_id.cycle_service_level_id']related='orderpoint_id.z_score'
No public methods.
New fields (12)
-
cslFloatrelated='cycle_service_level_id.csl'string='Cycle Service Level Target' -
cycle_daysIntegerhelp='The desired number of days between orders.\nUsed to size the gap between the min and max quantities, to cover the expected demand during the desired reordering cycle.\n\nproduct_max_qty = product_min_qty + (demand_avg_qty * cycle_days)' -
cycle_service_level_idMany2one → stock.cycle.service.levelondelete='restrict'string='Cycle Service Level' args: 'stock.cycle.service.level' -
demand_avg_qtyFloatcompute='_compute_daily_demand'digits='Product Unit'help='The average daily outgoing quantity on this warehouse.'string='Average Daily Demand' -
demand_history_daysIntegerrelated='company_id.demand_history_days'string='Demand History Days' -
demand_lt_std_devFloatcompute='_compute_demand_lt_std_dev'digits='Product Unit'help='The standard deviation of the daily outgoing quantity on this warehouse over the lead time.'string='Standard Deviation of Daily Demand over Lead Time' -
demand_std_devFloatcompute='_compute_daily_demand'digits='Product Unit'help='The standard deviation of the daily outgoing quantity on this warehouse.'string='Standard Deviation of Daily Demand' -
growth_factorFloatdefault=0.0digits=(2, 2)help='A multiplier to apply to the safety stock and the resulting min and max quantities. Used to account for expected growth or decrease in demand.' -
safety_stockFloatcompute='_compute_safety_stock'digits='Product Unit'help='The safety stock is the amount of stock to keep on this warehouse to cover for the variability in demand during the lead time.\nIt is computed as the product of the standard deviation of the daily outgoing quantity over the lead time, the z-score (statistical factor derived from the target cycle service level) and the growth factor.\n\nsafety_stock = demand_lt_std_dev * z_score * (1.0 + growth_factor)' -
safety_stock_methodSelectiondefault='manual'help="The method to use to set the product's min and max quantities.\n* Manual: The product's min and max quantities are set manually.\n* Cycle Service Level: The product's min and max quantities are computed based on the target cycle service level and the growth factor.\n"selection=[('manual', 'Manual'), ('csl', 'Cycle Service Level')] -
safety_stock_update_dateDatetimereadonly=Truestring='Last modification date of Min and Max quantities from Safety Stock' -
z_scoreFloatrelated='cycle_service_level_id.z_score'
-
action_apply_safety_stock(self)Apply the safety stock to the orderpoint min and max quantities