inventory-optimization について
Inventory Optimizationは、需要、リードタイム、コストを基に在庫管理と補充方針を検討するスキルです。発注量、安全在庫、発注点、SKU分類を扱い、在庫コストとサービス水準の均衡を支援します。
主な機能
- 01EOQ、発注点、安全在庫の計算
- 02連続レビュー・定期レビュー方針の設定
- 03ABC・XYZ分析によるSKU分類
- 04サービス水準と保有コストの分析
- 05複数SKUへの予算配分の最適化
活用シーン
- 01SKUごとの発注量と発注点を設定
- 02価値や需要変動に応じて在庫方針を分類
- 03在庫投資、回転率、欠品リスクを評価
- 04予算内で複数SKUの在庫を配分
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ kishorkukreja/ awesome-supply-chain --skill inventory-optimizationインストール後、Claude Code で/inventory-optimizationを入力して読み込まれたか確認します。
詳細
在庫最適化
あなたは在庫の最適化と管理の専門家です。目標は、在庫コストとサービスレベルのバランスを取り、顧客の需要を満たしながら総コストを最小限に抑える最適な在庫水準、発注点、在庫方針を決定することです。
初期評価
在庫を最適化する前に、次の点を把握してください。
-
ビジネスの背景
- どの製品/SKUの在庫を最適化する必要がありますか?
- 現在の在庫投資額と在庫回転率はどのくらいですか?
- 目標サービスレベル(充足率、欠品率)はどのくらいですか?
- 現在の課題は何ですか?(過剰在庫、不足、資金の固定化)
-
需要の特性
- 需要パターンはどのようなものですか?(安定、変動、季節性、断続的)
- 需要の変動性(変動係数)はどのくらいですか?
- 過去の販売データはありますか?(理想的には12~24か月分)
- 予測精度(MAPE)はどのくらいですか?
-
供給パラメーター
- サプライヤーからのリードタイムはどのくらいですか?
- リードタイムの変動性はどのくらいですか?
- 最小発注数量(MOQ)はいくつですか?
- 発注コストと制約はどのようなものですか?
-
コスト構造
- 製品の単価はいくらですか?
- 1回の発注あたりの発注/段取りコストはいくらですか?
- 在庫保管コスト率(年間)は何%ですか?
- 欠品/バックオーダーのコストはいくらですか?
在庫最適化のフレームワーク
主な在庫に関する意思決定
1. どれだけ発注するか?(発注量)
- 経済的発注量(EOQ)
- 定量発注
- ロット・フォー・ロット発注
- 数量割引の考慮
2. いつ発注するか?(発注点)
- 連続レビュー(s, Q)方針
- 定期レビュー(s, S)方針
- タイムフェーズ発注
- 動的安全在庫
3. 安全在庫をどれだけ持つか?
- サービスレベルの目標
- 需要の変動性
- リードタイムの変動性
- コストとサービスのトレードオフ
基本モデル
経済的発注量(EOQ)
基本的なEOQの公式:
EOQ = sqrt((2 * D * S) / H)
Where:
D = Annual demand (units)
S = Ordering cost per order ($)
H = Holding cost per unit per year ($)
総コスト:
TC = (D/Q) * S + (Q/2) * H + D * C
Where:
Q = Order quantity
C = Unit cost
Pythonでの実装:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
def eoq(annual_demand, order_cost, holding_cost_rate, unit_cost):
"""
Calculate Economic Order Quantity
Parameters:
- annual_demand: Annual demand in units
- order_cost: Cost per order ($)
- holding_cost_rate: Holding cost as % of unit cost (e.g., 0.25 for 25%)
- unit_cost: Cost per unit ($)
Returns:
- Dictionary with EOQ, total cost, orders per year, etc.
"""
# Holding cost per unit per year
holding_cost = unit_cost * holding_cost_rate
# EOQ formula
eoq_qty = np.sqrt((2 * annual_demand * order_cost) / holding_cost)
# Number of orders per year
orders_per_year = annual_demand / eoq_qty
# Total annual cost
ordering_cost_total = (annual_demand / eoq_qty) * order_cost
holding_cost_total = (eoq_qty / 2) * holding_cost
purchase_cost = annual_demand * unit_cost
total_cost = ordering_cost_total + holding_cost_total + purchase_cost
# Order interval (days)
order_interval = 365 / orders_per_year
return {
'EOQ': round(eoq_qty, 0),
'Orders_Per_Year': round(orders_per_year, 1),
'Order_Interval_Days': round(order_interval, 1),
'Total_Annual_Cost': round(total_cost, 2),
'Ordering_Cost': round(ordering_cost_total, 2),
'Holding_Cost': round(holding_cost_total, 2),
'Purchase_Cost': round(purchase_cost, 2)
}
# Example usage
result = eoq(
annual_demand=10000,
order_cost=100,
holding_cost_rate=0.25,
unit_cost=50
)
print(f"Optimal Order Quantity: {result['EOQ']} units")
print(f"Order {result['Orders_Per_Year']} times per year")
print(f"Order every {result['Order_Interval_Days']} days")
print(f"Total Annual Cost: ${result['Total_Annual_Cost']:,.2f}")
数量割引を考慮したEOQ:
def eoq_with_discounts(annual_demand, order_cost, holding_cost_rate, price_breaks):
"""
EOQ with all-units quantity discount
price_breaks: list of tuples [(qty, price), ...]
Example: [(0, 50), (500, 48), (1000, 45)]
"""
results = []
for qty_break, unit_price in price_breaks:
holding_cost = unit_price * holding_cost_rate
# Calculate EOQ at this price
eoq_qty = np.sqrt((2 * annual_demand * order_cost) / holding_cost)
# If EOQ < qty_break, use qty_break as order quantity
order_qty = max(eoq_qty, qty_break)
# Total cost at this quantity
ordering_cost = (annual_demand / order_qty) * order_cost
holding_cost_total = (order_qty / 2) * holding_cost
purchase_cost = annual_demand * unit_price
total_cost = ordering_cost + holding_cost_total + purchase_cost
results.append({
'Quantity_Break': qty_break,
'Unit_Price': unit_price,
'EOQ_at_Price': round(eoq_qty, 0),
'Order_Quantity': round(order_qty, 0),
'Total_Cost': round(total_cost, 2)
})
# Find minimum cost option
results_df = pd.DataFrame(results)
optimal = results_df.loc[results_df['Total_Cost'].idxmin()]
return results_df, optimal
# Example with quantity discounts
price_breaks = [
(0, 50), # 0-499: $50/unit
(500, 48), # 500-999: $48/unit
(1000, 45) # 1000+: $45/unit
]
results_df, optimal = eoq_with_discounts(
annual_demand=10000,
order_cost=100,
holding_cost_rate=0.25,
price_breaks=price_breaks
)
print(results_df)
print(f"\nOptimal: Order {optimal['Order_Quantity']} units at ${optimal['Unit_Price']}/unit")
安全在庫と発注点
安全在庫の計算
サービスレベル方式:
from scipy import stats
import numpy as np
def safety_stock(demand_std, lead_time_days, service_level=0.95):
"""
Calculate safety stock based on service level
Parameters:
- demand_std: Standard deviation of daily demand
- lead_time_days: Lead time in days
- service_level: Target service level (e.g., 0.95 for 95%)
Returns:
- Safety stock quantity
"""
# Z-score for desired service level
z = stats.norm.ppf(service_level)
# Safety stock = Z * std_dev during lead time
# Assuming demand is independent across days
std_during_lt = demand_std * np.sqrt(lead_time_days)
ss = z * std_during_lt
return round(ss, 0)
def safety_stock_variable_lt(demand_avg, demand_std, lead_time_avg,
lead_time_std, service_level=0.95):
"""
Safety stock with variable demand AND lead time
More comprehensive formula accounting for both sources of variability
"""
z = stats.norm.ppf(service_level)
# Combined variance formula
variance = (lead_time_avg * demand_std**2 +
demand_avg**2 * lead_time_std**2)
std_during_lt = np.sqrt(variance)
ss = z * std_during_lt
return round(ss, 0)
# Example: Basic safety stock
ss = safety_stock(
demand_std=50, # Daily demand std dev
lead_time_days=14, # 2 week lead time
service_level=0.95 # 95% service level
)
print(f"Safety Stock: {ss} units")
# Example: Variable lead time
ss_var = safety_stock_variable_lt(
demand_avg=100, # Avg daily demand
demand_std=50, # Std dev of daily demand
lead_time_avg=14, # Avg lead time (days)
lead_time_std=3, # Std dev of lead time (days)
service_level=0.95
)
print(f"Safety Stock (variable LT): {ss_var} units")
発注点(ROP)
公式:
ROP = (Average Daily Demand × Lead Time) + Safety Stock
Pythonでの実装:
def reorder_point(demand_avg_daily, lead_time_days, safety_stock):
"""
Calculate reorder point
ROP = Expected demand during lead time + Safety stock
"""
expected_demand_lt = demand_avg_daily * lead_time_days
rop = expected_demand_lt + safety_stock
return round(rop, 0)
def rop_with_review_period(demand_avg_daily, lead_time_days,
review_period_days, safety_stock):
"""
Reorder point with periodic review
Must cover lead time + review period
"""
total_period = lead_time_days + review_period_days
expected_demand = demand_avg_daily * total_period
rop = expected_demand + safety_stock
return round(rop, 0)
# Example
rop = reorder_point(
demand_avg_daily=100,
lead_time_days=14,
safety_stock=200
)
print(f"Reorder Point: {rop} units")
print(f"When inventory hits {rop}, place an order")
在庫方針モデル
(s, Q)連続レビュー方針
説明:
- 在庫を継続的に監視します
- 在庫ポジションがs(発注点)以下になったら、Q単位を発注します
- Qには通常、EOQを設定します
class ContinuousReviewPolicy:
"""(s, Q) continuous review inventory policy"""
def __init__(self, reorder_point, order_quantity):
self.s = reorder_point
self.Q = order_quantity
self.inventory_position = 0
self.on_hand = 0
self.on_order = 0
self.orders_placed = []
def check_and_order(self, current_on_hand, current_on_order):
"""Check if order should be placed"""
self.on_hand = current_on_hand
self.on_order = current_on_order
self.inventory_position = current_on_hand + current_on_order
if self.inventory_position <= self.s:
# Place order
order = {
'quantity': self.Q,
'inventory_position': self.inventory_position,
'on_hand': self.on_hand,
'on_order': self.on_order
}
self.orders_placed.append(order)
return True, self.Q
return False, 0
def simulate(self, demand_series, lead_time=14):
"""Simulate inventory policy over time"""
inventory = []
current_inv = self.Q # Start with one order quantity
on_order_queue = []
for day, demand in enumerate(demand_series):
# Receive orders (if any arrive today)
arrivals = [o for o in on_order_queue if o['arrival_day'] == day]
for arrival in arrivals:
current_inv += arrival['quantity']
on_order_queue.remove(arrival)
# Satisfy demand
current_inv = max(0, current_inv - demand)
# Check if order needed
current_on_order = sum(o['quantity'] for o in on_order_queue)
should_order, order_qty = self.check_and_order(current_inv, current_on_order)
if should_order:
on_order_queue.append({
'quantity': order_qty,
'order_day': day,
'arrival_day': day + lead_time
})
inventory.append({
'day': day,
'on_hand': current_inv,
'demand': demand,
'on_order': current_on_order,
'order_placed': should_order
})
return pd.DataFrame(inventory)
# Example simulation
np.random.seed(42)
demand_series = np.random.poisson(100, 365) # 365 days of demand
policy = ContinuousReviewPolicy(
reorder_point=1600,
order_quantity=1000
)
results = policy.simulate(demand_series, lead_time=14)
print(f"Average inventory: {results['on_hand'].mean():.0f} units")
print(f"Stockouts: {(results['on_hand'] == 0).sum()} days")
print(f"Orders placed: {results['order_placed'].sum()}")
(R, S)定期レビュー方針
説明:
- R期間ごと(例:毎週)に在庫を確認します
- S(補充目標水準)まで発注します
- 発注量は現在の在庫ポジションに応じて変わります
class PeriodicReviewPolicy:
"""(R, S) periodic review inventory policy"""
def __init__(self, review_period, order_up_to_level):
self.R = review_period
self.S = order_up_to_level
def calculate_order_qty(self, current_position):
"""Calculate order quantity to reach S"""
return max(0, self.S - current_position)
def simulate(self, demand_series, lead_time=14):
"""Simulate periodic review policy"""
inventory = []
current_inv = self.S # Start at order-up-to level
on_order_queue = []
for day, demand in enumerate(demand_series):
# Receive orders
arrivals = [o for o in on_order_queue if o['arrival_day'] == day]
for arrival in arrivals:
current_inv += arrival['quantity']
on_order_queue.remove(arrival)
# Satisfy demand
current_inv = max(0, current_inv - demand)
# Check if it's a review day
should_order = (day % self.R == 0)
order_qty = 0
if should_order:
current_on_order = sum(o['quantity'] for o in on_order_queue)
current_position = current_inv + current_on_order
order_qty = self.calculate_order_qty(current_position)
if order_qty > 0:
on_order_queue.append({
'quantity': order_qty,
'order_day': day,
'arrival_day': day + lead_time
})
inventory.append({
'day': day,
'on_hand': current_inv,
'demand': demand,
'order_qty': order_qty,
'review_day': should_order
})
return pd.DataFrame(inventory)
# Example
policy = PeriodicReviewPolicy(
review_period=7, # Weekly review
order_up_to_level=2100
)
results = policy.simulate(demand_series, lead_time=14)
print(f"Average inventory: {results['on_hand'].mean():.0f} units")
print(f"Total orders: {(results['order_qty'] > 0).sum()}")
ABC分析とセグメンテーション
ABC分類
手法:
- A品目:金額ベースで上位20%のSKU、売上の約80%
- B品目:次の30%のSKU、売上の約15%
- C品目:下位50%のSKU、売上の約5%
def abc_analysis(df, sku_col='sku', demand_col='annual_demand',
price_col='unit_price'):
"""
Perform ABC analysis on inventory
Parameters:
- df: DataFrame with SKU, demand, and price
- Returns: DataFrame with ABC classification
"""
# Calculate annual value
df = df.copy()
df['annual_value'] = df[demand_col] * df[price_col]
# Sort by value descending
df = df.sort_values('annual_value', ascending=False)
# Calculate cumulative percentage
total_value = df['annual_value'].sum()
df['cumulative_value'] = df['annual_value'].cumsum()
df['cumulative_pct'] = df['cumulative_value'] / total_value * 100
# Classify
def classify(pct):
if pct <= 80:
return 'A'
elif pct <= 95:
return 'B'
else:
return 'C'
df['abc_class'] = df['cumulative_pct'].apply(classify)
# Add ranking
df['rank'] = range(1, len(df) + 1)
return df
# Example usage
inventory_data = pd.DataFrame({
'sku': [f'SKU_{i}' for i in range(1, 101)],
'annual_demand': np.random.randint(100, 10000, 100),
'unit_price': np.random.uniform(10, 500, 100)
})
abc_result = abc_analysis(inventory_data)
# Summary by class
summary = abc_result.groupby('abc_class').agg({
'sku': 'count',
'annual_value': 'sum'
}).round(0)
summary['pct_skus'] = summary['sku'] / summary['sku'].sum() * 100
summary['pct_value'] = summary['annual_value'] / summary['annual_value'].sum() * 100
print(summary)
XYZ分析(需要の変動性)
分類:
- X:変動性が低い(CV < 0.5) - 予測可能
- Y:変動性が中程度(0.5 ≤ CV < 1.0) - 中程度
- Z:変動性が高い(CV ≥ 1.0) - 予測困難
def xyz_analysis(df, demand_history_col='demand_history'):
"""
Classify items by demand variability
demand_history_col should contain list/array of historical demand
"""
def classify_variability(demands):
if len(demands) < 2:
return 'Z' # Insufficient data
mean_demand = np.mean(demands)
std_demand = np.std(demands)
if mean_demand == 0:
return 'Z'
cv = std_demand / mean_demand # Coefficient of variation
if cv < 0.5:
return 'X'
elif cv < 1.0:
return 'Y'
else:
return 'Z'
df = df.copy()
df['xyz_class'] = df[demand_history_col].apply(classify_variability)
return df
# Combined ABC-XYZ matrix
def abc_xyz_matrix(df):
"""Create ABC-XYZ classification matrix"""
matrix = pd.crosstab(df['abc_class'], df['xyz_class'],
values=df['sku'], aggfunc='count')
return matrix
分類別の在庫方針
| クラス | サービスレベル | レビュー頻度 | 安全在庫 | 手法 |
|---|---|---|---|---|
| A-X | 99% | 毎日 | 高 | 連続レビュー、厳格な管理 |
| A-Y | 98% | 毎日 | 高 | 連続レビュー |
| A-Z | 95% | 毎週 | 非常に高 | 定期レビュー、高い安全在庫 |
| B-X | 97% | 毎週 | 中 | 定期または連続レビュー |
| B-Y | 95% | 毎週 | 中 | 定期レビュー |
| B-Z | 90% | 隔週 | 高 | 定期レビュー |
| C-X | 90% | 毎月 | 低 | 定期レビュー、簡易ルール |
| C-Y | 85% | 毎月 | 中 | 最小/最大ルール |
| C-Z | 80% | 必要に応じて | 低/なし | 受注時に発注するか、在庫を持たない |
高度な在庫最適化
サービスレベルとコストのトレードオフ
def service_level_analysis(demand_std, lead_time, unit_cost,
holding_cost_rate, stockout_cost):
"""
Analyze optimal service level balancing holding vs. stockout costs
"""
service_levels = np.arange(0.80, 0.995, 0.01)
results = []
holding_cost_per_unit = unit_cost * holding_cost_rate
for sl in service_levels:
# Safety stock required
z = stats.norm.ppf(sl)
std_during_lt = demand_std * np.sqrt(lead_time)
ss = z * std_during_lt
# Holding cost
holding_cost = ss * holding_cost_per_unit
# Expected stockout cost
# Simplified: (1 - service_level) * stockout_cost
expected_stockouts = (1 - sl) * stockout_cost
total_cost = holding_cost + expected_stockouts
results.append({
'service_level': sl,
'safety_stock': round(ss, 0),
'holding_cost': round(holding_cost, 2),
'stockout_cost': round(expected_stockouts, 2),
'total_cost': round(total_cost, 2)
})
results_df = pd.DataFrame(results)
# Find optimal
optimal_idx = results_df['total_cost'].idxmin()
optimal = results_df.iloc[optimal_idx]
return results_df, optimal
# Example
results_df, optimal = service_level_analysis(
demand_std=50,
lead_time=14,
unit_cost=100,
holding_cost_rate=0.25,
stockout_cost=5000 # Cost per stockout event
)
print(f"Optimal service level: {optimal['service_level']:.1%}")
print(f"Safety stock: {optimal['safety_stock']} units")
print(f"Total cost: ${optimal['total_cost']:,.2f}")
在庫回転率の最適化
在庫回転率の公式:
Inventory Turns = Cost of Goods Sold (COGS) / Average Inventory Value
def inventory_metrics(annual_cogs, avg_inventory_value, target_turns=None):
"""
Calculate inventory turnover metrics
"""
turns = annual_cogs / avg_inventory_value
days_on_hand = 365 / turns
metrics = {
'Inventory_Turns': round(turns, 2),
'Days_On_Hand': round(days_on_hand, 1),
'Avg_Inventory': avg_inventory_value
}
if target_turns:
target_inventory = annual_cogs / target_turns
reduction = avg_inventory_value - target_inventory
metrics['Target_Inventory'] = round(target_inventory, 0)
metrics['Inventory_Reduction'] = round(reduction, 0)
metrics['Cash_Freed'] = round(reduction, 0)
return metrics
# Example
metrics = inventory_metrics(
annual_cogs=50_000_000,
avg_inventory_value=10_000_000,
target_turns=8
)
print(f"Current turns: {metrics['Inventory_Turns']}")
print(f"Days on hand: {metrics['Days_On_Hand']}")
if 'Target_Inventory' in metrics:
print(f"Target inventory: ${metrics['Target_Inventory']:,.0f}")
print(f"Cash to be freed: ${metrics['Cash_Freed']:,.0f}")
複数SKUの最適化
from scipy.optimize import minimize
def optimize_multi_sku_inventory(skus_data, total_budget, target_service_level=0.95):
"""
Optimize inventory allocation across multiple SKUs with budget constraint
skus_data: DataFrame with columns ['sku', 'demand_avg', 'demand_std',
'lead_time', 'unit_cost', 'stockout_cost']
"""
n_skus = len(skus_data)
def objective(safety_stocks):
"""Minimize total cost (holding + expected stockouts)"""
total_cost = 0
for i, ss in enumerate(safety_stocks):
row = skus_data.iloc[i]
# Holding cost
holding_cost = ss * row['unit_cost'] * 0.25 # 25% holding rate
# Calculate achieved service level from safety stock
z = ss / (row['demand_std'] * np.sqrt(row['lead_time']))
service_level = stats.norm.cdf(z)
# Stockout cost
stockout_prob = 1 - service_level
expected_stockout = stockout_prob * row['stockout_cost']
total_cost += holding_cost + expected_stockout
return total_cost
def budget_constraint(safety_stocks):
"""Total inventory value must not exceed budget"""
total_value = sum(
ss * skus_data.iloc[i]['unit_cost']
for i, ss in enumerate(safety_stocks)
)
return total_budget - total_value
# Initial guess: equal allocation
x0 = np.full(n_skus, total_budget / (n_skus * skus_data['unit_cost'].mean()))
# Constraints
constraints = [
{'type': 'ineq', 'fun': budget_constraint}
]
# Bounds: non-negative safety stock
bounds = [(0, None) for _ in range(n_skus)]
# Optimize
result = minimize(objective, x0, method='SLSQP',
bounds=bounds, constraints=constraints)
# Extract results
optimal_ss = result.x
results_df = skus_data.copy()
results_df['optimal_safety_stock'] = optimal_ss
results_df['inventory_value'] = optimal_ss * results_df['unit_cost']
return results_df, result
# Example with multiple SKUs
skus_data = pd.DataFrame({
'sku': ['A', 'B', 'C', 'D'],
'demand_avg': [100, 200, 50, 150],
'demand_std': [20, 40, 25, 30],
'lead_time': [14, 7, 21, 14],
'unit_cost': [50, 30, 100, 75],
'stockout_cost': [500, 300, 1000, 600]
})
results_df, optimization = optimize_multi_sku_inventory(
skus_data,
total_budget=50000,
target_service_level=0.95
)
print(results_df[['sku', 'optimal_safety_stock', 'inventory_value']])
print(f"\nTotal inventory value: ${results_df['inventory_value'].sum():,.0f}")
ツールとライブラリ
Python ライブラリ
在庫最適化:
numpy:数値計算scipy:統計分布、最適化pandas:データ操作statsmodels:時系列分析
シミュレーション:
simpy:離散イベントシミュレーションciw:待ち行列ネットワークシミュレーション
最適化:
scipy.optimize:非線形最適化pulp:線形計画法pyomo:最適化モデリング
可視化:
matplotlib、seaborn:プロット作成plotly:インタラクティブなグラフ
商用ソフトウェア
在庫計画:
- SAP IBP:統合ビジネス計画
- Blue Yonder (JDA):在庫最適化
- Kinaxis RapidResponse:サプライチェーン計画
- Logility:在庫最適化
- o9 Solutions:デジタル計画プラットフォーム
特化型ツール:
- Inventory Planner:Eコマースの在庫
- Lokad:確率的予測と在庫
- NetSuite:在庫管理機能を備えたERP
- Fishbowl:在庫追跡・管理
よくある課題と解決策
課題:需要の変動性
問題:
- 需要の変動が大きいと、安全在庫の必要量が増加します
- 予測が困難です
解決策:
- ABC-XYZ分析でセグメント化し、セグメントごとに異なる方針を適用します
- 確率的予測を使用します(点推定ではなく分布を使用します)
- 需要平準化の戦略を検討します(プロモーション管理など)
- 予測頻度を上げます(月次ではなく週次にします)
- リアルタイムデータを活用した需要センシングを導入します
課題:リードタイムの長期化
問題:
- リードタイムが長くなるほど、必要な安全在庫が増加します
- 欠品や陳腐化のリスクが高まります
解決策:
- サプライヤーと連携してリードタイムを短縮します
- ベンダー管理在庫(VMI)を導入します
- 安全リードタイムの余裕を設定します
- 重要品目について複数調達先の確保を検討します
- 重要な補充には航空貨物を利用します
課題:過剰在庫
問題:
- 動きの遅い在庫や陳腐化した在庫が多すぎます
- 資金が拘束され、保管コストが発生します
解決策:
- ABC分析を導入し、C品目に重点を置きます
- 値下げ・在庫処分の戦略を実施します
- サプライヤーとの返品契約を結びます
- 需要予測を改善します
- 動きの遅い品目の発注量を減らします
- ロングテール品目にはドロップシッピングを検討します
課題:欠品とバックオーダー
問題:
- 在庫不足により、販売機会を逃します
- 顧客サービスが低下します
解決策:
- 安全在庫を増やします(コストとサービスのトレードオフを考慮します)
- 予測精度を改善します
- リードタイムの変動を抑えます
- 緊急配送の選択肢を導入します
- サプライチェーンの可視性を高めます
- 低販売量の品目には受注生産を検討します
課題:コストとサービスのバランス
問題:
- 在庫を最小化することと、サービスを最大化することの目的が競合します
解決策:
- 欠品コスト(販売機会の損失、ペナルティ)を定量化します
- 最適化を用いて最適なトレードオフを見つけます
- 在庫をセグメント化し、クラスごとに異なるサービスレベルを設定します
- サービスレベル契約(SLA)を導入します
- サービス提供コスト分析を使用します
課題:マルチエシェロンの複雑さ
問題:
- 複数の拠点(工場、DC、店舗)に在庫があります
- 全体を最適化することが困難です
解決策:
- multi-echelon-inventory スキルを参照してください
- ネットワーク最適化モデルを使用します
- 需要主導型の補充を導入します
- 計画を一元化します(ただし、在庫まで一元化する必要はありません)
- ポストポーンメント戦略を検討します
出力形式
在庫最適化レポート
エグゼクティブサマリー:
- 現在の在庫投資額と在庫回転率
- 推奨する在庫水準とポリシー
- 期待されるサービスレベルの向上
- 運転資本への影響
SKU レベルの推奨事項:
| SKU | ABC | XYZ | 現在の平均在庫 | 推奨平均在庫 | 安全在庫 | 発注点 | 発注量 | ポリシー | サービスレベル |
|---|---|---|---|---|---|---|---|---|---|
| SKU_001 | A | X | 500 | 450 | 200 | 1,600 | 1,000 | (s,Q) | 99% |
| SKU_002 | B | Y | 300 | 280 | 120 | 850 | 500 | (R,S) | 95% |
| SKU_003 | C | Z | 150 | 50 | 30 | 250 | Min/Max | 月次 | 85% |
財務への影響:
| 指標 | 現在 | 最適化後 | 改善 |
|---|---|---|---|
| 在庫総額 | $15M | $12M | -$3M (-20%) |
| 在庫回転率 | 4.5 | 6.0 | +1.5 (+33%) |
| 在庫日数 | 81 | 61 | -20日 |
| 充足率 | 92% | 97% | +5ポイント |
| 年間保管コスト | $3.75M | $3.0M | -$750K |
導入計画:
- フェーズ 1:A 品目(在庫価値の 80%)にポリシーを導入
- フェーズ 2:B 品目に展開
- フェーズ 3:C 品目の管理を簡素化
- 継続的な取り組み:実績に基づいて監視し、調整
質問事項
さらに詳しい情報が必要な場合:
- 最適化が必要な製品/SKU は何ですか?全体でいくつありますか?
- 現在の在庫投資額と在庫回転率はどのくらいですか?
- 目標とするサービスレベルはどのくらいですか?
- 需要パターンはどのようなものですか?(安定、変動、季節性、断続的)
- サプライヤーからのリードタイムはどのくらいですか?
- どのようなコストデータを利用できますか?(製品コスト、発注コスト、保管コスト率)
- この取り組みの目的は何ですか?(コスト削減、サービス向上、運転資本)
関連スキル
- economic-order-quantity:EOQ モデルの詳細
- demand-forecasting:在庫計画のための需要予測
- multi-echelon-inventory:ネットワーク在庫の最適化
- stochastic-inventory-models:確率論的アプローチ
- newsvendor-problem:単一期間の在庫意思決定
- supply-chain-analytics:KPI とパフォーマンス指標
- abc-analysis:分類とセグメンテーション(別のスキルとして存在する場合)
- warehouse-slotting-optimization:在庫配置の最適化
コメント
Markdown 対応