| REPOSITORY | |
|---|---|
| REPOSITORY | OCA/stock-logistics-orderpoint |
| GIT | |
| GIT | https://github.com/OCA/stock-logistics-orderpoint.git |
| GIT FOLDER | |
| GIT FOLDER | https://github.com/OCA/stock-logistics-orderpoint/tree/19.0/stock_orderpoint_safety_stock |
| VERSION | |
| VERSION | 1.0.1 |
| CATEGORY | |
| CATEGORY | Inventory |
| LICENSE | |
| LICENSE | AGPL-3 |
| APPLICATION | |
| APPLICATION | No |
| AUTO-INSTALLABLE | |
| AUTO-INSTALLABLE | No |
| AUTHORS | |
| AUTHORS | Odoo Community Association (OCA), Camptocamp |
| MAINTAINERS | |
| MAINTAINERS | Odoo Community Association (OCA), Camptocamp |
| COMMITTERS | |
| COMMITTERS | Weblate, OCA-git-bot, Iván Todorovich, oca-ci |
| WEBSITE | |
| WEBSITE | https://github.com/OCA/stock-logistics-orderpoint |
| LAST TRACKING UPDATE | |
| LAST TRACKING UPDATE | 2026-07-06 19:40:49 |
| ODOO DEPENDENCIES | |
| ODOO DEPENDENCIES |
odoo/odoo: - stock - product - base - base_setup - web - bus - web_tour - html_editor - uom - barcodes_gs1_nomenclature - barcodes - digest - portal - http_routing - auth_signup - resource |
| PYTHON DEPENDENCIES | |
| PYTHON DEPENDENCIES |
pyerf |
| SYSTEM DEPENDENCIES | |
| SYSTEM DEPENDENCIES | Not have |
| DESCRIPTION | |
| 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.  |
| XML ID | Name | Model | Type | Status |
|---|---|---|---|---|
res_config_settings_view_form |
res.config.settings | xpath | Inherits stock.res_config_settings_view_form | |
stock_reorder_report_search |
stock.warehouse.orderpoint | field | 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 | xpath | Inherits stock.view_stock_replenishment_info | |
view_stock_warehouse_orderpoint_tree_editable |
stock.warehouse.orderpoint | field | Inherits stock.view_warehouse_orderpoint_tree_editable | |
view_warehouse_orderpoint_form |
stock.warehouse.orderpoint | field | Inherits stock.view_warehouse_orderpoint_form |
No new fields.
Public methods (0)No public methods.
demand_history_days
Integer
default=365
help='The number of days in the past to use to compute the safety stock.'
No public methods.
demand_history_days
Integer
readonly=False
related='company_id.demand_history_days'
No public methods.
csl
Float
digits=(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_count
Integer
compute='_compute_orderpoint_count'
string='Orderpoints Count'
orderpoint_ids
One2many → stock.warehouse.orderpoint
string='Orderpoints'
args: 'stock.warehouse.orderpoint', 'cycle_service_level_id'
z_score
Float
compute='_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=True
string='Z-Score'
action_open_orderpoints(self)
cycle_days
Integer
readonly=False
related='orderpoint_id.cycle_days'
cycle_service_level_id
Many2one
readonly=False
related='orderpoint_id.cycle_service_level_id'
demand_avg_qty
Float
related='orderpoint_id.demand_avg_qty'
demand_history_days
Integer
related='orderpoint_id.demand_history_days'
demand_lt_std_dev
Float
related='orderpoint_id.demand_lt_std_dev'
demand_std_dev
Float
related='orderpoint_id.demand_std_dev'
growth_factor
Float
readonly=False
related='orderpoint_id.growth_factor'
safety_stock
Float
related='orderpoint_id.safety_stock'
safety_stock_method
Selection
readonly=False
related='orderpoint_id.safety_stock_method'
z_score
Float
depends=['orderpoint_id.cycle_service_level_id']
related='orderpoint_id.z_score'
No public methods.
csl
Float
related='cycle_service_level_id.csl'
string='Cycle Service Level Target'
cycle_days
Integer
help='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_id
Many2one → stock.cycle.service.level
ondelete='restrict'
string='Cycle Service Level'
args: 'stock.cycle.service.level'
demand_avg_qty
Float
compute='_compute_daily_demand'
digits='Product Unit'
help='The average daily outgoing quantity on this warehouse.'
string='Average Daily Demand'
demand_history_days
Integer
related='company_id.demand_history_days'
string='Demand History Days'
demand_lt_std_dev
Float
compute='_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_dev
Float
compute='_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_factor
Float
default=0.0
digits=(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_stock
Float
compute='_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_method
Selection
default='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_date
Datetime
readonly=True
string='Last modification date of Min and Max quantities from Safety Stock'
z_score
Float
related='cycle_service_level_id.z_score'
action_apply_safety_stock(self)