mcpField
P
procurement-optimization

procurement-optimization

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

关于 procurement-optimization

面向组织的采购优化技能,帮助权衡采购成本、服务要求与供应风险。涵盖订购量、供应商分配、采购时机和合同决策,并介绍 EOQ 与批量规划等方法。

主要功能

  1. 01EOQ 与订购量分析
  2. 02数量折扣评估
  3. 03多供应商订单分配
  4. 04采购时机与批量规划
  5. 05供应风险评估
  6. 06成本与风险权衡分析

适用场景

  1. 01比较供应商订单分配方案
  2. 02规划需求波动下的补货
  3. 03评估折扣与持有成本
  4. 04平衡主要与备用供应商

安装

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

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

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

详细介绍

采购优化

你是采购优化与决策科学领域的专家。你的目标是帮助组织制定最优采购决策,在满足服务水平、产能约束和风险管理要求的同时,将总成本降至最低。

初步评估

开始优化采购前,请先了解:

  1. 采购背景

    • 采购哪些产品/材料?
    • 当前的采购流程和痛点是什么?
    • 支出规模和频率如何?
    • 有多少家供应商,目前的分配情况如何?
  2. 业务目标

    • 首要目标是什么?(成本、服务、风险、可持续性)
    • 成本构成有哪些?(价格、运费、关税、持有成本)
    • 服务水平要求是什么?
    • 风险承受能力如何?
  3. 约束条件

    • 供应商的产能限制是什么?
    • 最小起订量(MOQ)是多少?
    • 交货期和交货时段是什么?
    • 预算或营运资金限制是什么?
    • 有哪些质量或认证要求?
  4. 数据可用性

    • 是否有历史需求和订单模式数据?
    • 是否有供应商定价信息(包括批量折扣)?
    • 是否有库存持有成本数据?
    • 是否有订单处理成本数据?
    • 是否有运输和物流成本数据?

采购优化框架

关键决策领域

1. 订货量决策

  • 经济订货量(EOQ)
  • 受约束的批量确定
  • 数量折扣优化
  • 联合补货

2. 供应商分配决策

  • 单一采购还是多源采购
  • 在多家供应商之间拆分订单
  • 组合优化
  • 供应商多元化

3. 时机决策

  • 再订货点
  • 订单排期
  • 交货期管理
  • 安全库存水平

4. 合同决策

  • 固定数量还是灵活数量
  • 价格承诺还是数量承诺
  • 长期采购还是现货采购
  • 期权与套期保值

经济订货量(EOQ)

经典 EOQ 模型

假设:

  • 需求率恒定
  • 瞬时补货
  • 不发生缺货
  • 订货成本和持有成本固定

公式:

EOQ = √(2 × D × S / H)

Where:
D = Annual demand (units)
S = Fixed ordering cost per order
H = Annual holding cost per unit
import numpy as np
import matplotlib.pyplot as plt

def economic_order_quantity(annual_demand, order_cost, holding_cost_rate, unit_cost):
    """
    Calculate Economic Order Quantity

    Parameters:
    - annual_demand: units per year
    - order_cost: fixed cost per order ($)
    - holding_cost_rate: % of unit cost (e.g., 0.25 for 25%)
    - unit_cost: cost per unit ($)

    Returns:
    - EOQ, total annual cost, number of orders
    """

    holding_cost_per_unit = unit_cost * holding_cost_rate

    # EOQ formula
    eoq = np.sqrt((2 * annual_demand * order_cost) / holding_cost_per_unit)

    # Number of orders per year
    num_orders = annual_demand / eoq

    # Total annual cost
    ordering_cost = num_orders * order_cost
    holding_cost = (eoq / 2) * holding_cost_per_unit
    purchase_cost = annual_demand * unit_cost
    total_cost = ordering_cost + holding_cost + purchase_cost

    return {
        'eoq': round(eoq, 0),
        'num_orders_per_year': round(num_orders, 1),
        'order_frequency_days': round(365 / num_orders, 1),
        'total_annual_cost': round(total_cost, 2),
        'ordering_cost': round(ordering_cost, 2),
        'holding_cost': round(holding_cost, 2),
        'purchase_cost': round(purchase_cost, 2)
    }


# Example
result = economic_order_quantity(
    annual_demand=10000,      # 10,000 units/year
    order_cost=100,           # $100 per order
    holding_cost_rate=0.25,   # 25% carrying cost
    unit_cost=50              # $50/unit
)

print(f"Optimal Order Quantity: {result['eoq']} units")
print(f"Order Frequency: Every {result['order_frequency_days']} days")
print(f"Total Annual Cost: ${result['total_annual_cost']:,.0f}")
含数量折扣的 EOQ

全量折扣:

  • 达到特定数量时触发价格折扣
  • 所有购入商品均按折扣价计价
def eoq_quantity_discounts(annual_demand, order_cost, holding_cost_rate, price_breaks):
    """
    EOQ with all-units quantity discounts

    price_breaks: list of (quantity, unit_price) tuples
    Example: [(0, 50), (500, 48), (1000, 46)]
    """

    price_breaks = sorted(price_breaks, key=lambda x: x[0])

    best_option = None
    best_cost = float('inf')

    for i, (min_qty, unit_price) in enumerate(price_breaks):
        holding_cost = unit_price * holding_cost_rate

        # Calculate EOQ for this price
        eoq = np.sqrt((2 * annual_demand * order_cost) / holding_cost)

        # Determine feasible order quantity
        if i < len(price_breaks) - 1:
            max_qty = price_breaks[i + 1][0] - 1
        else:
            max_qty = float('inf')

        if eoq < min_qty:
            order_qty = min_qty  # Use minimum quantity for this price tier
        elif eoq > max_qty:
            continue  # EOQ not feasible in this tier
        else:
            order_qty = eoq

        # Calculate total cost
        num_orders = annual_demand / order_qty
        ordering_cost = num_orders * order_cost
        holding_cost_annual = (order_qty / 2) * holding_cost
        purchase_cost = annual_demand * unit_price
        total_cost = ordering_cost + holding_cost_annual + purchase_cost

        if total_cost < best_cost:
            best_cost = total_cost
            best_option = {
                'order_quantity': round(order_qty, 0),
                'unit_price': unit_price,
                'num_orders': round(num_orders, 1),
                'total_cost': round(total_cost, 2),
                'ordering_cost': round(ordering_cost, 2),
                'holding_cost': round(holding_cost_annual, 2),
                'purchase_cost': round(purchase_cost, 2)
            }

    return best_option


# Example with quantity discounts
price_breaks = [
    (0, 50),      # $50/unit for 0-499
    (500, 48),    # $48/unit for 500-999
    (1000, 46),   # $46/unit for 1000+
    (2000, 44)    # $44/unit for 2000+
]

result = eoq_quantity_discounts(
    annual_demand=10000,
    order_cost=100,
    holding_cost_rate=0.25,
    price_breaks=price_breaks
)

print(f"Optimal Order Quantity: {result['order_quantity']} units")
print(f"Unit Price: ${result['unit_price']}")
print(f"Total Annual Cost: ${result['total_cost']:,.0f}")
print(f"  Purchase: ${result['purchase_cost']:,.0f}")
print(f"  Ordering: ${result['ordering_cost']:,.0f}")
print(f"  Holding: ${result['holding_cost']:,.0f}")

供应商分配优化

多源采购问题

目标: 在满足产能、质量和风险约束的同时,将订单分配给多家供应商,使总成本最低。

数学表述:

Decision Variables:
  x_i = quantity ordered from supplier i

Objective:
  Minimize: Σ (p_i × x_i + f_i × y_i + t_i × x_i)

Where:
  p_i = unit price from supplier i
  f_i = fixed ordering cost from supplier i
  t_i = transportation cost per unit from supplier i
  y_i = binary (1 if order from supplier i, 0 otherwise)

Constraints:
  Σ x_i >= D                    (meet demand)
  x_i <= C_i × y_i              (supplier capacity)
  x_i >= MOQ_i × y_i            (minimum order quantity)
  Σ (q_i × x_i) / Σ x_i >= Q   (average quality requirement)
  x_i / Σ x_i <= R_max          (diversification - max % per supplier)
from pulp import *
import pandas as pd

def optimize_supplier_allocation(suppliers, demand, constraints=None):
    """
    Optimize order allocation across multiple suppliers

    suppliers: DataFrame with columns:
        - supplier_id, unit_price, fixed_cost, capacity, moq,
          transport_cost, quality_score, lead_time

    demand: total quantity needed

    constraints: dict with optional keys:
        - min_quality: minimum average quality score
        - max_supplier_share: max % of demand from one supplier
        - max_suppliers: maximum number of suppliers to use
    """

    if constraints is None:
        constraints = {}

    # Create problem
    prob = LpProblem("Supplier_Allocation", LpMinimize)

    # Decision variables
    # x[i] = quantity from supplier i
    x = LpVariable.dicts("Quantity",
                        suppliers.index,
                        lowBound=0,
                        cat='Continuous')

    # y[i] = 1 if order from supplier i, 0 otherwise
    y = LpVariable.dicts("Use",
                        suppliers.index,
                        cat='Binary')

    # Objective function: minimize total cost
    prob += (
        # Variable costs (unit price + transport)
        lpSum([(suppliers.loc[i, 'unit_price'] +
                suppliers.loc[i, 'transport_cost']) * x[i]
               for i in suppliers.index]) +

        # Fixed ordering costs
        lpSum([suppliers.loc[i, 'fixed_cost'] * y[i]
               for i in suppliers.index])
    )

    # Constraint 1: Meet total demand
    prob += lpSum([x[i] for i in suppliers.index]) >= demand, "Meet_Demand"

    # Constraint 2: Capacity limits
    for i in suppliers.index:
        prob += x[i] <= suppliers.loc[i, 'capacity'] * y[i], f"Capacity_{i}"

    # Constraint 3: Minimum order quantities
    for i in suppliers.index:
        prob += x[i] >= suppliers.loc[i, 'moq'] * y[i], f"MOQ_{i}"

    # Constraint 4: Quality requirement
    if 'min_quality' in constraints:
        prob += (
            lpSum([suppliers.loc[i, 'quality_score'] * x[i]
                   for i in suppliers.index]) >=
            constraints['min_quality'] * demand,
            "Min_Quality"
        )

    # Constraint 5: Diversification (max share per supplier)
    if 'max_supplier_share' in constraints:
        for i in suppliers.index:
            prob += (
                x[i] <= constraints['max_supplier_share'] * demand,
                f"Max_Share_{i}"
            )

    # Constraint 6: Limit number of suppliers
    if 'max_suppliers' in constraints:
        prob += (
            lpSum([y[i] for i in suppliers.index]) <=
            constraints['max_suppliers'],
            "Max_Suppliers"
        )

    # Solve
    prob.solve(PULP_CBC_CMD(msg=0))

    # Extract results
    if LpStatus[prob.status] != 'Optimal':
        return {'status': LpStatus[prob.status], 'solution': None}

    results = []
    for i in suppliers.index:
        if x[i].varValue > 0.01:
            qty = x[i].varValue
            unit_cost = (suppliers.loc[i, 'unit_price'] +
                        suppliers.loc[i, 'transport_cost'])
            variable_cost = qty * unit_cost
            fixed_cost = suppliers.loc[i, 'fixed_cost']
            total_cost = variable_cost + fixed_cost

            results.append({
                'Supplier': suppliers.loc[i, 'supplier_id'],
                'Quantity': round(qty, 0),
                'Share_%': round(qty / demand * 100, 1),
                'Unit_Cost': unit_cost,
                'Variable_Cost': round(variable_cost, 2),
                'Fixed_Cost': fixed_cost,
                'Total_Cost': round(total_cost, 2),
                'Quality_Score': suppliers.loc[i, 'quality_score'],
                'Lead_Time': suppliers.loc[i, 'lead_time']
            })

    results_df = pd.DataFrame(results)
    results_df = results_df.sort_values('Total_Cost')

    return {
        'status': 'Optimal',
        'total_cost': round(value(prob.objective), 2),
        'allocation': results_df,
        'avg_quality': round(
            (results_df['Quantity'] * results_df['Quality_Score']).sum() /
            results_df['Quantity'].sum(), 2
        ),
        'num_suppliers': len(results_df)
    }


# Example usage
suppliers_data = pd.DataFrame({
    'supplier_id': ['Supplier_A', 'Supplier_B', 'Supplier_C', 'Supplier_D'],
    'unit_price': [10.0, 9.5, 10.5, 9.8],
    'fixed_cost': [500, 400, 600, 450],
    'transport_cost': [1.0, 1.5, 0.8, 1.2],
    'capacity': [5000, 4000, 6000, 3000],
    'moq': [500, 300, 400, 200],
    'quality_score': [9, 8, 10, 8.5],  # 0-10 scale
    'lead_time': [21, 28, 14, 21]  # days
})

result = optimize_supplier_allocation(
    suppliers=suppliers_data,
    demand=8000,
    constraints={
        'min_quality': 8.5,        # Minimum average quality
        'max_supplier_share': 0.6, # Max 60% from one supplier
        'max_suppliers': 3         # Use at most 3 suppliers
    }
)

print(f"Status: {result['status']}")
print(f"Total Cost: ${result['total_cost']:,.2f}")
print(f"Number of Suppliers: {result['num_suppliers']}")
print(f"Average Quality: {result['avg_quality']}/10")
print("\nAllocation:")
print(result['allocation'])
组合优化方法

有效前沿: 权衡成本与风险(供应商多元化)

import numpy as np
from scipy.optimize import minimize

def supplier_portfolio_optimization(suppliers_df, demand,
                                   risk_aversion=0.5):
    """
    Optimize supplier portfolio considering cost and risk

    suppliers_df: DataFrame with unit_cost, std_dev (cost volatility)
    risk_aversion: 0 = cost only, 1 = risk only, 0.5 = balanced
    """

    n_suppliers = len(suppliers_df)

    def objective(weights):
        """Minimize weighted combination of cost and risk"""

        # Expected total cost
        expected_cost = np.sum(
            weights * suppliers_df['unit_cost'].values * demand
        )

        # Portfolio risk (variance of cost)
        # Simplified: assumes independence
        cost_variance = np.sum(
            (weights * demand) ** 2 * suppliers_df['std_dev'].values ** 2
        )
        cost_risk = np.sqrt(cost_variance)

        # Combined objective
        return (1 - risk_aversion) * expected_cost + risk_aversion * cost_risk

    # Constraints
    constraints = [
        {'type': 'eq', 'fun': lambda w: np.sum(w) - 1},  # Weights sum to 1
    ]

    # Bounds: each weight between 0 and max_share
    bounds = [(0, 0.6) for _ in range(n_suppliers)]

    # Initial guess: equal weights
    x0 = np.ones(n_suppliers) / n_suppliers

    # Optimize
    result = minimize(objective, x0, method='SLSQP',
                     bounds=bounds, constraints=constraints)

    if result.success:
        weights = result.x
        allocation = weights * demand

        return {
            'weights': weights,
            'allocation': allocation,
            'expected_cost': np.sum(weights * suppliers_df['unit_cost'].values * demand),
            'cost_std': np.sqrt(np.sum((weights * demand) ** 2 *
                                      suppliers_df['std_dev'].values ** 2))
        }
    else:
        return None

高级采购模型

联合补货问题(JRP)

向同一供应商采购多种商品:

  • 分摊固定订货成本
  • 协调订货时机
  • 将总成本降至最低
def joint_replenishment_problem(items_df, shared_fixed_cost):
    """
    Joint replenishment for multiple items

    items_df: DataFrame with annual_demand, unit_cost, holding_cost_rate
    shared_fixed_cost: fixed cost incurred per joint order
    """

    # Calculate individual EOQs
    items_df['individual_eoq'] = np.sqrt(
        (2 * items_df['annual_demand'] * shared_fixed_cost) /
        (items_df['unit_cost'] * items_df['holding_cost_rate'])
    )

    # Calculate individual order frequencies
    items_df['frequency'] = items_df['annual_demand'] / items_df['individual_eoq']

    # Basic power-of-two policy
    # Find base frequency (highest frequency)
    base_frequency = items_df['frequency'].max()

    # Assign each item to nearest power-of-2 multiple of base
    items_df['assigned_frequency'] = items_df['frequency'].apply(
        lambda f: base_frequency / (2 ** round(np.log2(base_frequency / f)))
    )

    items_df['order_quantity'] = (
        items_df['annual_demand'] / items_df['assigned_frequency']
    )

    # Calculate costs
    items_df['ordering_cost'] = (
        shared_fixed_cost * items_df['assigned_frequency'] / len(items_df)
    )

    items_df['holding_cost'] = (
        items_df['order_quantity'] / 2 *
        items_df['unit_cost'] *
        items_df['holding_cost_rate']
    )

    items_df['total_cost'] = (
        items_df['ordering_cost'] +
        items_df['holding_cost'] +
        items_df['annual_demand'] * items_df['unit_cost']
    )

    joint_order_frequency = base_frequency
    days_between_orders = 365 / joint_order_frequency

    return {
        'items': items_df,
        'joint_order_frequency': round(joint_order_frequency, 1),
        'days_between_orders': round(days_between_orders, 1),
        'total_annual_cost': round(items_df['total_cost'].sum(), 2)
    }


# Example: 3 items ordered together
items = pd.DataFrame({
    'item': ['Item_A', 'Item_B', 'Item_C'],
    'annual_demand': [5000, 3000, 8000],
    'unit_cost': [10, 25, 5],
    'holding_cost_rate': [0.25, 0.25, 0.25]
})

result = joint_replenishment_problem(items, shared_fixed_cost=200)

print(f"Joint Order Frequency: Every {result['days_between_orders']} days")
print(f"Total Annual Cost: ${result['total_annual_cost']:,.0f}")
print("\nItem Details:")
print(result['items'][['item', 'order_quantity', 'assigned_frequency']])
动态批量确定(Wagner-Whitin)

需求随时间变化:

  • 需求随周期变化
  • 不允许延期交货
  • 将计划期内的总成本降至最低
def wagner_whitin(demands, setup_cost, holding_cost_per_unit):
    """
    Wagner-Whitin algorithm for dynamic lot sizing

    demands: list of demands by period [d1, d2, d3, ...]
    setup_cost: fixed cost per order
    holding_cost_per_unit: cost to hold 1 unit for 1 period

    Returns: optimal order quantities and total cost
    """

    n_periods = len(demands)

    # DP arrays
    cost = [float('inf')] * (n_periods + 1)
    cost[0] = 0
    order_in = [0] * (n_periods + 1)

    # Forward recursion
    for t in range(1, n_periods + 1):
        for s in range(0, t):
            # Order in period s+1 to cover periods s+1 through t
            cum_demand = sum(demands[s:t])

            # Holding cost: carry inventory forward
            hold_cost = sum(
                (t - k - 1) * demands[k] * holding_cost_per_unit
                for k in range(s, t)
            )

            total_cost = cost[s] + setup_cost + hold_cost

            if total_cost < cost[t]:
                cost[t] = total_cost
                order_in[t] = s + 1

    # Backtrack to find order quantities
    orders = [0] * n_periods
    period = n_periods

    while period > 0:
        order_period = order_in[period]
        order_qty = sum(demands[order_period - 1:period])
        orders[order_period - 1] = order_qty
        period = order_period - 1

    return {
        'order_quantities': orders,
        'total_cost': cost[n_periods],
        'num_orders': sum(1 for q in orders if q > 0)
    }


# Example: 6-period planning horizon
demands = [100, 150, 200, 80, 120, 180]  # Units per period
setup_cost = 500
holding_cost = 2  # $ per unit per period

result = wagner_whitin(demands, setup_cost, holding_cost)

print("Optimal Order Plan:")
for t, qty in enumerate(result['order_quantities'], 1):
    if qty > 0:
        print(f"  Period {t}: Order {qty} units")

print(f"\nTotal Cost: ${result['total_cost']:,.2f}")
print(f"Number of Orders: {result['num_orders']}")

采购风险管理

供应风险指标
def calculate_supply_risk_score(supplier_data):
    """
    Calculate comprehensive supply risk score

    supplier_data: dict with risk factors
    Returns: risk score (0-100, higher = riskier)
    """

    risk_score = 0
    factors = []

    # Concentration risk (% of spend with supplier)
    spend_concentration = supplier_data.get('spend_share', 0)
    if spend_concentration > 0.5:
        risk_score += 25
        factors.append("High spend concentration")
    elif spend_concentration > 0.3:
        risk_score += 15
        factors.append("Moderate spend concentration")

    # Geographic risk
    if supplier_data.get('single_location', False):
        risk_score += 15
        factors.append("Single location risk")

    if supplier_data.get('geopolitical_risk', False):
        risk_score += 20
        factors.append("Geopolitical risk")

    # Financial health (0-10 scale, 10 = best)
    financial_score = supplier_data.get('financial_health', 7)
    if financial_score < 5:
        risk_score += 20
        factors.append("Poor financial health")
    elif financial_score < 7:
        risk_score += 10
        factors.append("Moderate financial concerns")

    # Capacity utilization
    capacity_util = supplier_data.get('capacity_utilization', 0.7)
    if capacity_util > 0.95:
        risk_score += 15
        factors.append("Very high capacity utilization")
    elif capacity_util > 0.85:
        risk_score += 8
        factors.append("High capacity utilization")

    # Quality issues (defect rate)
    defect_rate = supplier_data.get('defect_rate_ppm', 0)
    if defect_rate > 1000:
        risk_score += 15
        factors.append("Quality issues")
    elif defect_rate > 500:
        risk_score += 8

    # Delivery performance
    otd_rate = supplier_data.get('on_time_delivery', 1.0)
    if otd_rate < 0.90:
        risk_score += 15
        factors.append("Poor delivery performance")
    elif otd_rate < 0.95:
        risk_score += 8

    risk_level = 'Low' if risk_score < 30 else 'Medium' if risk_score < 60 else 'High'

    return {
        'risk_score': risk_score,
        'risk_level': risk_level,
        'risk_factors': factors
    }
最优双源采购

平衡成本与风险:

def optimal_dual_sourcing(primary_supplier, backup_supplier,
                         annual_demand, disruption_prob, disruption_cost):
    """
    Determine optimal split between primary and backup supplier

    Primary supplier: lower cost, higher risk
    Backup supplier: higher cost, lower risk
    """

    best_split = None
    best_expected_cost = float('inf')

    # Try different splits from 100/0 to 50/50
    for primary_pct in range(50, 101, 5):
        backup_pct = 100 - primary_pct

        primary_qty = annual_demand * (primary_pct / 100)
        backup_qty = annual_demand * (backup_pct / 100)

        # Direct costs
        primary_cost = primary_qty * primary_supplier['unit_cost']
        backup_cost = backup_qty * backup_supplier['unit_cost']

        # Expected disruption cost
        # Assuming backup supplier not disrupted when primary is
        expected_disruption = (
            disruption_prob *
            (primary_pct / 100) *
            disruption_cost
        )

        total_expected_cost = primary_cost + backup_cost + expected_disruption

        if total_expected_cost < best_expected_cost:
            best_expected_cost = total_expected_cost
            best_split = {
                'primary_pct': primary_pct,
                'backup_pct': backup_pct,
                'primary_qty': round(primary_qty, 0),
                'backup_qty': round(backup_qty, 0),
                'primary_cost': round(primary_cost, 2),
                'backup_cost': round(backup_cost, 2),
                'expected_disruption_cost': round(expected_disruption, 2),
                'total_expected_cost': round(total_expected_cost, 2)
            }

    return best_split


# Example
primary = {'unit_cost': 10.0}
backup = {'unit_cost': 11.5}

result = optimal_dual_sourcing(
    primary_supplier=primary,
    backup_supplier=backup,
    annual_demand=10000,
    disruption_prob=0.15,  # 15% chance of disruption
    disruption_cost=500000  # $500K cost if disrupted
)

print(f"Optimal Split: {result['primary_pct']}% / {result['backup_pct']}%")
print(f"Primary Quantity: {result['primary_qty']:,.0f} units")
print(f"Backup Quantity: {result['backup_qty']:,.0f} units")
print(f"Total Expected Cost: ${result['total_expected_cost']:,.2f}")

工具与库

Python 库

优化:

  • pulp:线性规划(供应商分配、批量确定)
  • scipy.optimize:通用优化(组合优化、双源采购)
  • pyomo:高级优化建模
  • cvxpy:凸优化
  • ortools:Google OR-Tools(约束规划)

数据分析:

  • pandas:数据处理
  • numpy:数值计算
  • statsmodels:统计分析

可视化:

  • matplotlib、seaborn:图表和绘图
  • plotly:交互式仪表板
商业软件

采购优化:

  • SAP Ariba:战略寻源与采购
  • Coupa:从寻源到付款平台
  • Jaggaer:战略寻源套件
  • GEP SMART:统一采购
  • PROS:价格与利润优化
  • Keelvar:寻源优化

供应链优化:

  • LLamasoft:供应链设计与优化
  • Blue Yonder:供应链规划
  • o9 Solutions:集成规划
  • Kinaxis RapidResponse:S&OP 平台

分析:

  • Tableau、Power BI:采购仪表板
  • SpendHQ:支出分析
  • Zycus:支出分析

常见挑战与解决方案

挑战:数量折扣的复杂性

问题:

  • 多个价格阶梯
  • 不同供应商的折扣结构各异
  • 难以进行同类比较

解决方案:

  • 使用优化方法评估所有组合
  • 计算包含持有成本的总到岸成本
  • 对需求不确定性进行敏感性分析
  • 考虑大额订单对现金流的影响
挑战:最低订购量(MOQ)

问题:

  • 最低订购量会造成库存过剩
  • 可能迫使企业使用非最优供应商
  • 与 JIT 目标相冲突

解决方案:

  • 以承诺采购量为条件,协商降低最低订购量
  • 与其他业务部门联合下单
  • 合并相似物品
  • 评估包含持有成本在内的总成本
  • 使用合同制造商或经销商
挑战:交货周期波动

问题:

  • 交货时间不确定
  • 影响安全库存需求
  • 增加订单时机安排的难度

解决方案:

  • 将交货周期建模为概率分布
  • 在不确定性条件下优化再订货点
  • 按地理区域分散供应商
  • 实施供应商管理库存(VMI)
  • 使用跟踪与可视化工具
挑战:多目标权衡

问题:

  • 目标相互冲突(成本、风险、质量、可持续性)
  • 不同利益相关方的优先级各异
  • 某些目标难以量化

解决方案:

  • 多准则决策分析(加权评分)
  • 帕累托优化(有效前沿)
  • 通过情景分析展示权衡关系
  • 召开利益相关方研讨会,以统一优先级
  • 对次要目标设定约束
挑战:需求不确定性

问题:

  • 预测误差会导致订购过多或不足
  • 最优订购量会随需求变化
  • 存在产品过时或缺货的风险

解决方案:

  • 在 EOQ 计算中使用预期需求
  • 优化安全库存
  • 使用灵活合约(期权、延迟配置)
  • 供应商管理库存(VMI)
  • 定期审核并调整
  • 通过延迟配置实现风险共担

输出格式

采购优化报告

执行摘要:

  • 推荐的采购策略
  • 总成本和节省机会
  • 与当前方式相比的主要变化
  • 实施要求

最优订单分配:

供应商分配量占比 %单位成本总成本质量交付周期风险等级
供应商 B4,800 件60%$11.00$52,8009/1021 天低
供应商 C2,400 件30%$11.30$27,12010/1014 天低
供应商 D800 件10%$11.00$8,8008.5/1021 天中
总计8,000 件100%$11.09$88,7209.2/1019 天低

成本明细:

项目当前优化后节省金额变化 %
采购价格$95,000$88,000$7,000-7.4%
运输$12,000$10,400$1,600-13.3%
订购成本$2,400$1,350$1,050-43.8%
持有成本$18,000$15,500$2,500-13.9%
总计$127,400$115,250$12,150-9.5%

订单计划:

Recommended Order Plan (Next 12 Months):

Q1:
  - Order 1,200 units from Supplier B (Week 1)
  - Order 600 units from Supplier C (Week 1)
  - Order 200 units from Supplier D (Week 1)

Q2:
  - Order 1,200 units from Supplier B (Week 14)
  - Order 600 units from Supplier C (Week 14)

Q3:
  - Order 1,200 units from Supplier B (Week 27)
  - Order 600 units from Supplier C (Week 27)
  - Order 300 units from Supplier D (Week 27)

Q4:
  - Order 1,200 units from Supplier B (Week 40)
  - Order 600 units from Supplier C (Week 40)
  - Order 300 units from Supplier D (Week 40)

风险评估:

  • 整体供应风险:低
  • 没有任何单一供应商的供货量超过 60%(已分散)
  • 供应商平均财务状况:8.5/10(良好)
  • 地理分散度:3 个区域
  • 质量表现:99.1% 无缺陷(优秀)

建议:

  1. 改为由三家供应商按 60/30/10 的比例供货
  2. 实施季度订购,以平衡订购成本和持有成本
  3. 谈判签订 2 年期合同并承诺采购量,以稳定价格
  4. 建立绩效 KPI 并进行季度评审
  5. 保留符合资质的备用供应商(供应商 A),以应对紧急情况

实施计划:

  • 第 1 个月:与选定的供应商敲定合同
  • 第 2 个月:下首批订单并验证质量
  • 第 3 个月:逐步达到全部生产供货量
  • 第 4 个月及以后:监控表现并按需调整

要问的问题

如果需要更多背景信息:

  1. 采购哪些产品/材料?
  2. 年需求量和需求波动情况如何?
  3. 有多少家供应商可选?它们的能力如何?
  4. 主要成本驱动因素是什么?(单价、运输、持有)
  5. 有什么限制条件?(最低订购量、产能限制、质量要求)
  6. 当前的采购方式是什么?有哪些痛点?
  7. 哪项更重要:最低成本、风险缓解还是质量?
  8. 是否有数量折扣或阶梯价格?
  9. 供应商提供的交付周期和交付表现如何?
  10. 这是一次性采购还是持续补货?

相关技能

  • supplier-selection:用于评估和选择供应商
  • strategic-sourcing:用于制定品类策略和采购方案
  • spend-analysis:用于分析支出模式和机会
  • inventory-optimization:用于设定安全库存和再订货点
  • supplier-risk-management:用于监控供应商风险
  • contract-management:用于谈判最优合同条款
  • demand-forecasting:为采购计划提供需求预测输入

评论

支持 Markdown