关于 replenishment-strategy
replenishment-strategy 是面向供应链规划团队的库存补货策略技能,涵盖策略选择、参数计算、网络规划与成本权衡。
主要功能
- 01连续、定期与最小最大补货策略
- 02需求驱动补货与供应商管理库存
- 03配送需求计划
- 04多级网络与前置拣选规划
- 05补货数量成本优化
适用场景
- 01为不同 SKU 选择补货策略
- 02规划配送中心与门店库存
- 03设定再订货点和订货量
- 04权衡缺货、持有与订购成本
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ kishorkukreja/ awesome-supply-chain --skill replenishment-strategy安装后在 Claude Code 中输入/replenishment-strategy确认已加载。
详细介绍
补货策略
你是库存补货策略和需求驱动型供应链规划方面的专家。你的目标是帮助设计高效、响应迅速的补货系统,在维持最佳库存水平的同时,将缺货和库存过剩降至最低。
初步评估
在设计补货策略之前,请先了解:
-
网络结构
- 供应链包含哪些层级?(供应商 → DC → 门店/客户)
- 每个层级有多少个地点?
- 库存存放在哪些地点?
- 补货关系如何(哪些地点为哪些地点供货)?
-
需求特征
- 每个层级的需求波动性如何?
- 各层级之间的提前期有多长?
- 订单模式如何(稳定、零散、季节性)?
- 各层级的预测准确度如何?
-
运营约束
- 是否有最低订购量(MOQs)?
- 订购频率是否受限?(每天、每周、每月)
- 是否有运输约束(是否优先采用整车运输)?
- 是否有存储容量限制?
-
当前状态
- 当前采用哪种补货方法?
- 缺货频率和库存过剩问题如何?
- 补货提前期有多长?
- 服务水平表现如何?
补货策略框架
核心补货方法
1. 连续评审(s,Q)
- 持续监控库存
- 库存达到再订货点 s 时,订购固定数量 Q
- 适用于:高价值商品、自动化系统
2. 定期评审(R,S)
- 每隔 R 个周期评审一次库存
- 补货至 S 水平
- 适用于:向同一供应商采购多种商品、协调补货
3. Min-Max(s,S)
- 库存 ≤ 最小值(s)时,补货至最大值(S)
- 结合连续评审与定期评审
- 适用于:零售业、简单系统
4. 需求驱动补货(DDR)
- 基于实际消耗/销售
- 采用拉动式,根据需求快速响应
- 适用于:需求多变、提前期短
5. 供应商管理库存(VMI)
- 由供应商管理库存水平
- 由供应商负责补货
- 适用于:供应商关系稳固、寄售模式
补货策略模型
连续评审(s,Q)策略
参数:
- s(再订货点):何时订货
- Q(订货量):订购多少(通常为 EOQ)
import numpy as np
from scipy import stats
import pandas as pd
def calculate_reorder_point(demand_avg_daily, lead_time_days,
demand_std_daily, service_level=0.95):
"""
Calculate reorder point for continuous review
Parameters:
- demand_avg_daily: Average daily demand
- lead_time_days: Lead time in days
- demand_std_daily: Standard deviation of daily demand
- service_level: Target service level (e.g., 0.95)
Returns:
- Reorder point
"""
# Expected demand during lead time
expected_demand = demand_avg_daily * lead_time_days
# Safety stock
z = stats.norm.ppf(service_level)
std_during_lt = demand_std_daily * np.sqrt(lead_time_days)
safety_stock = z * std_during_lt
# Reorder point
rop = expected_demand + safety_stock
return {
'reorder_point': round(rop, 0),
'expected_demand_lt': round(expected_demand, 0),
'safety_stock': round(safety_stock, 0),
'service_level': service_level
}
# Example
rop_params = calculate_reorder_point(
demand_avg_daily=100,
lead_time_days=14,
demand_std_daily=20,
service_level=0.95
)
print(f"Reorder Point: {rop_params['reorder_point']} units")
print(f" Expected demand during LT: {rop_params['expected_demand_lt']}")
print(f" Safety stock: {rop_params['safety_stock']}")
模拟:
def simulate_continuous_review(demand_series, lead_time, reorder_point,
order_quantity, initial_inventory):
"""
Simulate continuous review (s, Q) policy
Parameters:
- demand_series: Array of daily demand
- lead_time: Lead time in days
- reorder_point: Reorder point (s)
- order_quantity: Order quantity (Q)
- initial_inventory: Starting inventory
Returns:
- Simulation results DataFrame
"""
results = []
inventory = initial_inventory
on_order = [] # Queue of orders in transit
for day, demand in enumerate(demand_series):
# Check for order arrivals
arrived_orders = [o for o in on_order if o['arrival_day'] == day]
for order in arrived_orders:
inventory += order['quantity']
on_order.remove(order)
# Satisfy demand
actual_demand = min(demand, inventory)
stockout = max(0, demand - inventory)
inventory = max(0, inventory - demand)
# Check if need to order
inventory_position = inventory + sum(o['quantity'] for o in on_order)
order_placed = False
if inventory_position <= reorder_point:
on_order.append({
'quantity': order_quantity,
'order_day': day,
'arrival_day': day + lead_time
})
order_placed = True
results.append({
'day': day,
'demand': demand,
'inventory_on_hand': inventory,
'inventory_position': inventory_position,
'stockout': stockout,
'order_placed': order_placed
})
return pd.DataFrame(results)
# Example simulation
np.random.seed(42)
demand = np.random.poisson(100, 365) # 365 days
sim_results = simulate_continuous_review(
demand_series=demand,
lead_time=14,
reorder_point=1600,
order_quantity=1000,
initial_inventory=2000
)
# Performance metrics
print(f"Average inventory: {sim_results['inventory_on_hand'].mean():.0f}")
print(f"Stockout days: {(sim_results['stockout'] > 0).sum()}")
print(f"Service level: {(1 - (sim_results['stockout'] > 0).sum() / len(sim_results)):.1%}")
print(f"Orders placed: {sim_results['order_placed'].sum()}")
定期评审(R,S)策略
参数:
- R(评审周期):多久评审一次(例如每周)
- S(补货至水平):目标库存水平
def calculate_order_up_to_level(demand_avg_daily, review_period_days,
lead_time_days, demand_std_daily,
service_level=0.95):
"""
Calculate order-up-to level for periodic review
Must cover demand during review period + lead time
"""
# Total coverage period
coverage_period = review_period_days + lead_time_days
# Expected demand during coverage period
expected_demand = demand_avg_daily * coverage_period
# Safety stock for coverage period
z = stats.norm.ppf(service_level)
std_during_period = demand_std_daily * np.sqrt(coverage_period)
safety_stock = z * std_during_period
# Order-up-to level
order_up_to = expected_demand + safety_stock
return {
'order_up_to_level': round(order_up_to, 0),
'expected_demand': round(expected_demand, 0),
'safety_stock': round(safety_stock, 0),
'coverage_period_days': coverage_period,
'service_level': service_level
}
# Example
periodic_params = calculate_order_up_to_level(
demand_avg_daily=100,
review_period_days=7, # Weekly review
lead_time_days=14,
demand_std_daily=20,
service_level=0.95
)
print(f"Order-up-to Level (S): {periodic_params['order_up_to_level']} units")
print(f"Coverage period: {periodic_params['coverage_period_days']} days")
模拟:
def simulate_periodic_review(demand_series, review_period, order_up_to_level,
lead_time, initial_inventory):
"""
Simulate periodic review (R, S) policy
"""
results = []
inventory = initial_inventory
on_order = []
for day, demand in enumerate(demand_series):
# Check for arrivals
arrived_orders = [o for o in on_order if o['arrival_day'] == day]
for order in arrived_orders:
inventory += order['quantity']
on_order.remove(order)
# Satisfy demand
actual_demand = min(demand, inventory)
stockout = max(0, demand - inventory)
inventory = max(0, inventory - demand)
# Check if review day
is_review_day = (day % review_period == 0)
order_qty = 0
if is_review_day:
inventory_position = inventory + sum(o['quantity'] for o in on_order)
order_qty = max(0, order_up_to_level - inventory_position)
if order_qty > 0:
on_order.append({
'quantity': order_qty,
'order_day': day,
'arrival_day': day + lead_time
})
results.append({
'day': day,
'demand': demand,
'inventory_on_hand': inventory,
'is_review_day': is_review_day,
'order_quantity': order_qty,
'stockout': stockout
})
return pd.DataFrame(results)
# Example
sim_results = simulate_periodic_review(
demand_series=demand,
review_period=7,
order_up_to_level=2200,
lead_time=14,
initial_inventory=2000
)
print(f"Average inventory: {sim_results['inventory_on_hand'].mean():.0f}")
print(f"Stockout days: {(sim_results['stockout'] > 0).sum()}")
print(f"Orders placed: {(sim_results['order_quantity'] > 0).sum()}")
Min-Max 补货
简单且应用广泛:
- Min:触发点(类似再订货点)
- Max:目标水平(补货至此水平)
def calculate_min_max(demand_avg_daily, lead_time_days, review_frequency_days,
demand_std_daily, service_level=0.95):
"""
Calculate min and max levels
Min = reorder point
Max = enough to cover lead time + review period + safety stock
"""
# Min level (reorder point)
expected_demand_lt = demand_avg_daily * lead_time_days
z = stats.norm.ppf(service_level)
std_during_lt = demand_std_daily * np.sqrt(lead_time_days)
safety_stock = z * std_during_lt
min_level = expected_demand_lt + safety_stock
# Max level (order-up-to level)
coverage_period = lead_time_days + review_frequency_days
expected_demand_coverage = demand_avg_daily * coverage_period
std_during_coverage = demand_std_daily * np.sqrt(coverage_period)
safety_stock_max = z * std_during_coverage
max_level = expected_demand_coverage + safety_stock_max
return {
'min_level': round(min_level, 0),
'max_level': round(max_level, 0),
'order_quantity_typical': round(max_level - min_level, 0),
'safety_stock': round(safety_stock, 0)
}
# Example
min_max = calculate_min_max(
demand_avg_daily=100,
lead_time_days=14,
review_frequency_days=7,
demand_std_daily=20,
service_level=0.95
)
print(f"Min Level: {min_max['min_level']} units")
print(f"Max Level: {min_max['max_level']} units")
print(f"Typical order quantity: {min_max['order_quantity_typical']} units")
配送需求计划(DRP)
DRP 概念:
- 针对多级网络进行分时段规划
- 由中央规划部门向下推送
- 预测未来库存状况
- 提前规划补货订单
DRP 计算
def drp_calculation(forecast, on_hand, scheduled_receipts, lead_time,
safety_stock, order_quantity):
"""
Distribution Requirements Planning calculation
Parameters:
- forecast: Array of forecasted demand by period
- on_hand: Starting on-hand inventory
- scheduled_receipts: Dict {period: quantity} of scheduled orders
- lead_time: Lead time in periods
- safety_stock: Safety stock target
- order_quantity: Standard order quantity
Returns:
- DRP table
"""
periods = len(forecast)
drp_table = []
current_on_hand = on_hand
for period in range(periods):
# Scheduled receipt this period
receipt = scheduled_receipts.get(period, 0)
# Projected available before order
proj_available_before = current_on_hand + receipt - forecast[period]
# Check if order needed
if proj_available_before < safety_stock:
planned_order = order_quantity
proj_available_after = proj_available_before + planned_order
else:
planned_order = 0
proj_available_after = proj_available_before
drp_table.append({
'period': period + 1,
'forecast': forecast[period],
'scheduled_receipt': receipt,
'proj_available_before': round(proj_available_before, 0),
'planned_order': planned_order,
'proj_available_after': round(proj_available_after, 0)
})
current_on_hand = proj_available_after
# Schedule planned order to arrive after lead time
if planned_order > 0 and period + lead_time < periods:
if period + lead_time not in scheduled_receipts:
scheduled_receipts[period + lead_time] = 0
scheduled_receipts[period + lead_time] += planned_order
return pd.DataFrame(drp_table)
# Example
forecast = [100, 110, 105, 120, 115, 125, 130, 135, 140, 120, 115, 110]
scheduled_receipts = {0: 500} # Order arriving in period 0
drp_table = drp_calculation(
forecast=forecast,
on_hand=200,
scheduled_receipts=scheduled_receipts,
lead_time=2,
safety_stock=150,
order_quantity=500
)
print(drp_table)
需求驱动补货
拉动式补货
特征:
- 根据实际消耗/销售触发补货
- 响应需求信号
- 将牛鞭效应降至最低
- 提前期较短时效果最佳
def demand_driven_replenishment(sales_data, replenishment_frequency='daily',
coverage_days=14, safety_stock_days=7):
"""
Calculate demand-driven replenishment quantities
Parameters:
- sales_data: DataFrame with daily sales history
- replenishment_frequency: 'daily', 'weekly', 'monthly'
- coverage_days: Days of supply to maintain
- safety_stock_days: Additional safety stock days
Returns:
- Replenishment recommendations
"""
# Calculate average daily rate (ADR)
lookback_days = 30
recent_sales = sales_data[-lookback_days:]
adr = recent_sales['sales'].mean()
# Calculate target inventory
target_inventory = adr * (coverage_days + safety_stock_days)
# Current inventory position
current_inventory = sales_data.iloc[-1]['inventory']
# Replenishment quantity
replen_qty = max(0, target_inventory - current_inventory)
# Adjust for replenishment frequency
if replenishment_frequency == 'weekly':
replen_qty = max(0, target_inventory - current_inventory)
elif replenishment_frequency == 'monthly':
# Ensure full month coverage
target_inventory = adr * 30
replen_qty = max(0, target_inventory - current_inventory)
return {
'average_daily_rate': round(adr, 1),
'target_inventory': round(target_inventory, 0),
'current_inventory': current_inventory,
'replenishment_quantity': round(replen_qty, 0),
'days_of_supply_current': round(current_inventory / adr, 1) if adr > 0 else 0
}
# Example
sales_history = pd.DataFrame({
'date': pd.date_range('2024-01-01', periods=60),
'sales': np.random.poisson(100, 60),
'inventory': 1500 - np.random.poisson(100, 60).cumsum()
})
replen = demand_driven_replenishment(
sales_data=sales_history,
replenishment_frequency='weekly',
coverage_days=14,
safety_stock_days=7
)
print(f"Average daily rate: {replen['average_daily_rate']}")
print(f"Current days of supply: {replen['days_of_supply_current']}")
print(f"Replenishment needed: {replen['replenishment_quantity']} units")
多级补货
两级模型(DC → 门店)
注意事项:
- DC 从供应商处补货
- 门店从 DC 补货
- 在各级之间平衡库存投入
- 为每个层级设定服务水平目标
def two_echelon_replenishment(dc_params, store_params, num_stores):
"""
Calculate replenishment parameters for two-echelon system
Parameters:
- dc_params: Dict with DC demand and lead time from supplier
- store_params: Dict with store demand and lead time from DC
- num_stores: Number of stores supplied by DC
Returns:
- Replenishment parameters for DC and stores
"""
# DC level
# Aggregate demand from all stores
dc_demand_avg = store_params['demand_avg'] * num_stores
dc_demand_std = store_params['demand_std'] * np.sqrt(num_stores) # Square root law
# DC reorder point (from supplier)
z_dc = stats.norm.ppf(dc_params['service_level'])
dc_lt = dc_params['lead_time_from_supplier']
dc_rop = (dc_demand_avg * dc_lt +
z_dc * dc_demand_std * np.sqrt(dc_lt))
# DC order quantity (EOQ or other method)
dc_order_qty = dc_params.get('order_quantity', dc_demand_avg * 7) # Default: 1 week
# Store level
# Each store maintains its own inventory
z_store = stats.norm.ppf(store_params['service_level'])
store_lt = store_params['lead_time_from_dc']
store_rop = (store_params['demand_avg'] * store_lt +
z_store * store_params['demand_std'] * np.sqrt(store_lt))
store_order_qty = store_params.get('order_quantity',
store_params['demand_avg'] * 3) # Default: 3 days
return {
'dc': {
'demand_avg': round(dc_demand_avg, 0),
'reorder_point': round(dc_rop, 0),
'order_quantity': round(dc_order_qty, 0),
'safety_stock': round(z_dc * dc_demand_std * np.sqrt(dc_lt), 0)
},
'store': {
'demand_avg': store_params['demand_avg'],
'reorder_point': round(store_rop, 0),
'order_quantity': round(store_order_qty, 0),
'safety_stock': round(z_store * store_params['demand_std'] * np.sqrt(store_lt), 0)
},
'total_safety_stock': round(
z_dc * dc_demand_std * np.sqrt(dc_lt) +
num_stores * z_store * store_params['demand_std'] * np.sqrt(store_lt), 0
)
}
# Example
dc_params = {
'lead_time_from_supplier': 14,
'service_level': 0.98
}
store_params = {
'demand_avg': 10,
'demand_std': 3,
'lead_time_from_dc': 2,
'service_level': 0.95
}
replen_params = two_echelon_replenishment(dc_params, store_params, num_stores=100)
print("DC Replenishment:")
print(f" Demand (from all stores): {replen_params['dc']['demand_avg']}")
print(f" Reorder point: {replen_params['dc']['reorder_point']}")
print(f" Order quantity: {replen_params['dc']['order_quantity']}")
print("\nStore Replenishment (each):")
print(f" Reorder point: {replen_params['store']['reorder_point']}")
print(f" Order quantity: {replen_params['store']['order_quantity']}")
print(f"\nTotal network safety stock: {replen_params['total_safety_stock']}")
前置拣选区补货
仓库场景:
- 储备区(整托盘)
- 前置拣选位(整箱/单件拣选)
- 从储备区为前置拣选区补货
前置拣选区容量规划与补货
def forward_pick_replenishment(sku_velocity_daily, pick_location_capacity,
replenishment_frequency='nightly',
target_days_supply=2):
"""
Determine forward pick location size and replenishment
Parameters:
- sku_velocity_daily: Daily pick volume (eaches)
- pick_location_capacity: Max capacity of forward location
- replenishment_frequency: 'nightly', 'shift', 'continuous'
- target_days_supply: Days of supply to maintain in forward location
Returns:
- Forward pick configuration
"""
# Minimum forward pick size (cover until next replenishment)
if replenishment_frequency == 'nightly':
min_capacity_needed = sku_velocity_daily * 1.2 # 20% buffer
elif replenishment_frequency == 'shift':
min_capacity_needed = sku_velocity_daily * 0.5 * 1.2 # Half day
else: # continuous
min_capacity_needed = sku_velocity_daily * 0.25 # 6 hours
# Target forward pick quantity
target_qty = min(sku_velocity_daily * target_days_supply, pick_location_capacity)
# Replenishment trigger point
replen_trigger = sku_velocity_daily * 0.5 # When half-day supply remains
# Replenishment quantity
replen_qty = target_qty - replen_trigger
return {
'velocity_daily': sku_velocity_daily,
'min_capacity_needed': round(min_capacity_needed, 0),
'recommended_capacity': round(target_qty, 0),
'replenishment_trigger': round(replen_trigger, 0),
'replenishment_quantity': round(replen_qty, 0),
'replenishments_per_day': round(sku_velocity_daily / replen_qty, 1)
}
# Example
forward_pick = forward_pick_replenishment(
sku_velocity_daily=200,
pick_location_capacity=500,
replenishment_frequency='nightly',
target_days_supply=2
)
print(f"Forward pick capacity needed: {forward_pick['recommended_capacity']}")
print(f"Replenishment trigger: {forward_pick['replenishment_trigger']}")
print(f"Replenishment quantity: {forward_pick['replenishment_quantity']}")
print(f"Replenishments per day: {forward_pick['replenishments_per_day']}")
供应商管理库存(VMI)
VMI 模式:
- 供应商可查看客户库存
- 供应商负责制定补货决策
- 客户共享 POS/消耗数据
- 通常采用寄售模式(按消耗付费)
VMI 的优势与实施
优势:
- 减少缺货(供应商主动管理)
- 降低库存(供应商在多个客户之间优化库存)
- 减轻管理负担(无需创建 PO)
- 改进供应商的计划
实施要求:
def vmi_data_sharing_requirements():
"""
Define data sharing requirements for VMI
"""
requirements = {
'customer_provides': [
'Current inventory levels (on-hand)',
'Point-of-sale (POS) data or consumption',
'Scheduled receipts (orders in transit)',
'Promotional calendar',
'Min-max levels or service level targets',
'Storage capacity constraints'
],
'supplier_provides': [
'Replenishment recommendations',
'Inventory visibility dashboard',
'Shipment notifications',
'Performance reports (fill rate, inventory turns)'
],
'technology_needs': [
'EDI or API integration',
'Vendor portal for data access',
'Real-time or daily data feeds',
'Exception alerts (stockout risk, overstock)'
]
}
return requirements
# Example VMI replenishment calculation (supplier-side)
def vmi_replenishment_calculation(current_inventory, daily_consumption,
lead_time_days, target_service_level=0.95,
min_order_qty=100):
"""
Supplier calculates replenishment for VMI customer
"""
# Historical consumption (use rolling average)
avg_consumption = daily_consumption
# Calculate target inventory (order-up-to level)
coverage_days = lead_time_days + 7 # Lead time + 1 week buffer
target_inventory = avg_consumption * coverage_days
# Replenishment quantity
replen_qty = max(0, target_inventory - current_inventory)
# Apply min order quantity
if replen_qty > 0 and replen_qty < min_order_qty:
replen_qty = min_order_qty
days_of_supply = current_inventory / avg_consumption if avg_consumption > 0 else 999
return {
'current_inventory': current_inventory,
'days_of_supply': round(days_of_supply, 1),
'target_inventory': round(target_inventory, 0),
'replenishment_quantity': round(replen_qty, 0),
'action': 'Ship order' if replen_qty >= min_order_qty else 'No action needed'
}
# Example
vmi_replen = vmi_replenishment_calculation(
current_inventory=450,
daily_consumption=50,
lead_time_days=7,
target_service_level=0.95,
min_order_qty=100
)
print(f"Current inventory: {vmi_replen['current_inventory']}")
print(f"Days of supply: {vmi_replen['days_of_supply']}")
print(f"Replenishment needed: {vmi_replen['replenishment_quantity']}")
print(f"Action: {vmi_replen['action']}")
补货优化
基于成本的优化
权衡因素:
- 订购成本(设置、运输)
- 持有成本(库存持有)
- 缺货成本(销售损失、加急配送)
from scipy.optimize import minimize_scalar
def optimize_replenishment_quantity(demand_annual, order_cost, holding_cost_rate,
unit_cost, stockout_cost_per_unit):
"""
Optimize order quantity considering ordering, holding, and stockout costs
Based on EOQ with stockout costs
"""
def total_cost(order_qty):
"""Calculate total annual cost for given order quantity"""
# Ordering cost
orders_per_year = demand_annual / order_qty
annual_ordering_cost = orders_per_year * order_cost
# Holding cost
avg_inventory = order_qty / 2
annual_holding_cost = avg_inventory * unit_cost * holding_cost_rate
# Simplified stockout cost (inversely related to order qty)
# More frequent orders = less stockout risk
annual_stockout_cost = stockout_cost_per_unit * (1000 / order_qty)
return annual_ordering_cost + annual_holding_cost + annual_stockout_cost
# Optimize
result = minimize_scalar(total_cost, bounds=(50, 5000), method='bounded')
optimal_qty = result.x
optimal_cost = result.fun
# Calculate components at optimal
orders_per_year = demand_annual / optimal_qty
ordering_cost = orders_per_year * order_cost
holding_cost = (optimal_qty / 2) * unit_cost * holding_cost_rate
stockout_cost = stockout_cost_per_unit * (1000 / optimal_qty)
return {
'optimal_order_quantity': round(optimal_qty, 0),
'orders_per_year': round(orders_per_year, 1),
'total_annual_cost': round(optimal_cost, 2),
'ordering_cost': round(ordering_cost, 2),
'holding_cost': round(holding_cost, 2),
'stockout_cost': round(stockout_cost, 2)
}
# Example
optimal = optimize_replenishment_quantity(
demand_annual=10000,
order_cost=100,
holding_cost_rate=0.25,
unit_cost=50,
stockout_cost_per_unit=500
)
print(f"Optimal order quantity: {optimal['optimal_order_quantity']}")
print(f"Orders per year: {optimal['orders_per_year']}")
print(f"Total annual cost: ${optimal['total_annual_cost']:,.2f}")
工具与库
软件解决方案
供应链计划:
- SAP IBP:集成业务计划与 DRP
- Blue Yonder (JDA):补货计划
- Oracle ASCP:高级供应链计划
- Kinaxis RapidResponse:需求驱动计划
- o9 Solutions:AI 驱动的计划
具备补货功能的 WMS:
- Manhattan Associates:高级补货逻辑
- Blue Yonder WMS:集成补货
- SAP EWM:扩展仓库管理
零售专用:
- Oracle Retail:门店补货优化
- Blue Yonder Luminate:零售计划
- Relex Solutions:自动补货
Python 库
# Commonly used libraries
import numpy as np # Numerical computations
import pandas as pd # Data manipulation
from scipy import stats # Statistical distributions
from scipy.optimize import minimize # Optimization
import matplotlib.pyplot as plt # Visualization
常见挑战与解决方案
挑战:牛鞭效应
问题:
- 需求波动沿供应链逐级放大
- 零售端的微小变化会导致供应商端的大幅波动
- 库存过剩和缺货
解决方案:
- 共享 POS/消费数据(而非订单)
- 实施需求驱动补货
- 缩短提前期
- 稳定订购模式(避免批量订购)
- VMI 或协同计划
挑战:提前期长
问题:
- 需要大量安全库存
- 对需求变化的响应能力差
- 库存投资更高
解决方案:
- 与供应商合作缩短提前期
- 关键商品采用空运
- 按 SKU 优化安全库存
- 选择本地供应商或近岸采购
- 延迟策略
挑战:需求不规律
问题:
- 需求间歇或零星发生
- 传统公式效果不佳
- 缺货风险高或库存过剩
解决方案:
- 对间歇性需求使用 Croston 方法或类似方法
- 考虑不备库存(按需订购)
- 汇总需求(产品系列)
- 使用较高的安全库存系数
- 定期检查并调整
挑战:多级网络的复杂性
问题:
- 多个层级均有库存(DC、门店等)
- 难以进行全局优化
- 各层级分别优化导致整体次优
解决方案:
- 使用多级优化模型
- 集中计划(而非仅进行局部优化)
- 共享各层级的可视性
- 采用 DRP 或需求驱动方法
- 考虑库存池化/整合
挑战:运输成本的平衡
问题:
- 小批量、高频次订购 = 运输成本高
- 大批量、低频次订购 = 持有成本高
- 库存与运输之间存在矛盾
解决方案:
- 经济订货量会同时考虑两者
- 优化整车装载(将多个 SKU 合并订购)
- 通过循环取货整合配送
- 通过越库作业拆分大宗货物
- 使用 3PL 共享运输资源
挑战:预测不准确
问题:
- 补货依赖预测
- 预测误差会导致缺货或库存过剩
- 难以制定计划
解决方案:
- 改用需求驱动方式(使用实际数据,而非预测)
- 提高预测准确性(参见 demand-forecasting)
- 增加安全库存,以缓冲预测误差
- 缩短计划周期
- 构建敏捷供应链(快速响应)
输出格式
补货策略文档
执行摘要:
- 推荐的补货方法
- 预期库存减少幅度
- 服务水平提升情况
- 实施要求
按层级划分的补货策略:
供应商 → DC:
- 策略:连续评审(s,Q)
- 再订货点:12,000 件
- 订货量:10,000 件(整车)
- 交货提前期:14 天
- 目标服务水平:98%
- 每月订单数:约 9 单
DC → 门店:
- 策略:定期评审(每周)
- 订货至目标水平:500 件(每家门店平均)
- 评审频率:每周
- 交货提前期:2 天
- 目标服务水平:95%
SKU 级参数:
| SKU | 层级 | 策略 | 再订货点 / 最小值 | 订货量 / 最大值 | 安全库存 | 服务水平 |
|---|---|---|---|---|---|---|
| A123 | DC | (s,Q) | 8,000 | 5,000 | 2,500 | 98% |
| A123 | 门店 | 最小值-最大值 | 100 | 200 | 40 | 95% |
| B456 | DC | (R,S) | - | 最多 15,000 | 3,000 | 98% |
| B456 | 门店 | 每周 | - | 最多 150 | 30 | 95% |
预期绩效:
| 指标 | 当前 | 优化后 | 改善幅度 |
|---|---|---|---|
| 平均库存投资 | $8M | $6.5M | -18.8% |
| 库存周转率 | 5.2 | 6.8 | +1.6 |
| 订单满足率 | 93% | 97% | +4 个百分点 |
| 缺货频率 | 8% 的天数 | 3% 的天数 | -5 个百分点 |
| 每周订单数 | 45 | 38 | -15.6% |
实施时间表:
- 第 1-2 周:计算所有 SKU 的补货参数
- 第 3-4 周:配置 WMS/ERP 系统
- 第 5-6 周:培训和并行测试
- 第 7-8 周:正式上线并监控
- 持续进行:绩效监控与调整
需要提出的问题
如果需要更多背景信息:
- 网络结构是怎样的?(层级、地点、流向)
- 当前采用什么补货方式?有哪些痛点?
- 各层级的交货提前期是多少?
- 需求特征如何?(稳定、波动、季节性、间歇性)
- 目标服务水平是多少?
- 是否存在最低订购量或运输限制?
- 当前使用哪些系统?(WMS、ERP、计划工具)
- 是否有兴趣采用 VMI 或开展供应商协作?
相关技能
- inventory-optimization:安全库存和再订货点计算
- demand-forecasting:预测补货计划所需的需求
- multi-echelon-inventory:全网络库存优化
- distribution-center-network:网络设计与流向
- economic-order-quantity:订货量优化
- retail-replenishment:零售专属补货策略
- supply-chain-analytics:KPI 和绩效跟踪
评论
支持 Markdown