mcpField
R
replenishment-strategy

replenishment-strategy

github.com/kishorkukreja其他84115 次安装3 周前更新
代码仓库

关于 replenishment-strategy

replenishment-strategy 是面向供应链规划团队的库存补货策略技能,涵盖策略选择、参数计算、网络规划与成本权衡。

主要功能

  1. 01连续、定期与最小最大补货策略
  2. 02需求驱动补货与供应商管理库存
  3. 03配送需求计划
  4. 04多级网络与前置拣选规划
  5. 05补货数量成本优化

适用场景

  1. 01为不同 SKU 选择补货策略
  2. 02规划配送中心与门店库存
  3. 03设定再订货点和订货量
  4. 04权衡缺货、持有与订购成本

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/kishorkukreja/awesome-supply-chain --skill replenishment-strategy

安装后在 Claude Code 中输入/replenishment-strategy确认已加载。

详细介绍

补货策略

你是库存补货策略和需求驱动型供应链规划方面的专家。你的目标是帮助设计高效、响应迅速的补货系统,在维持最佳库存水平的同时,将缺货和库存过剩降至最低。

初步评估

在设计补货策略之前,请先了解:

  1. 网络结构

    • 供应链包含哪些层级?(供应商 → DC → 门店/客户)
    • 每个层级有多少个地点?
    • 库存存放在哪些地点?
    • 补货关系如何(哪些地点为哪些地点供货)?
  2. 需求特征

    • 每个层级的需求波动性如何?
    • 各层级之间的提前期有多长?
    • 订单模式如何(稳定、零散、季节性)?
    • 各层级的预测准确度如何?
  3. 运营约束

    • 是否有最低订购量(MOQs)?
    • 订购频率是否受限?(每天、每周、每月)
    • 是否有运输约束(是否优先采用整车运输)?
    • 是否有存储容量限制?
  4. 当前状态

    • 当前采用哪种补货方法?
    • 缺货频率和库存过剩问题如何?
    • 补货提前期有多长?
    • 服务水平表现如何?

补货策略框架

核心补货方法

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层级策略再订货点 / 最小值订货量 / 最大值安全库存服务水平
A123DC(s,Q)8,0005,0002,50098%
A123门店最小值-最大值1002004095%
B456DC(R,S)-最多 15,0003,00098%
B456门店每周-最多 1503095%

预期绩效:

指标当前优化后改善幅度
平均库存投资$8M$6.5M-18.8%
库存周转率5.26.8+1.6
订单满足率93%97%+4 个百分点
缺货频率8% 的天数3% 的天数-5 个百分点
每周订单数4538-15.6%

实施时间表:

  • 第 1-2 周:计算所有 SKU 的补货参数
  • 第 3-4 周:配置 WMS/ERP 系统
  • 第 5-6 周:培训和并行测试
  • 第 7-8 周:正式上线并监控
  • 持续进行:绩效监控与调整

需要提出的问题

如果需要更多背景信息:

  1. 网络结构是怎样的?(层级、地点、流向)
  2. 当前采用什么补货方式?有哪些痛点?
  3. 各层级的交货提前期是多少?
  4. 需求特征如何?(稳定、波动、季节性、间歇性)
  5. 目标服务水平是多少?
  6. 是否存在最低订购量或运输限制?
  7. 当前使用哪些系统?(WMS、ERP、计划工具)
  8. 是否有兴趣采用 VMI 或开展供应商协作?

相关技能

  • inventory-optimization:安全库存和再订货点计算
  • demand-forecasting:预测补货计划所需的需求
  • multi-echelon-inventory:全网络库存优化
  • distribution-center-network:网络设计与流向
  • economic-order-quantity:订货量优化
  • retail-replenishment:零售专属补货策略
  • supply-chain-analytics:KPI 和绩效跟踪

评论

支持 Markdown