feat(P1-3): 相关成本决策模块 — 官方25%权重'商业决策分析'核心

新增 /predict/relevant-decision API + 前端Tab(3场景):
1. 自制vs外购: 相关成本比较+无差别点(增量成本视角)
2. 特殊订单: 剩余产能下价格>变动成本即接受(增量利润)
3. 产品组合(约束理论): 单位约束资源边际贡献排序

验证: 自制850k vs 外购900k→自制; 特殊订单单位贡献25→接受;
      产品组合B产品单位约束贡献50最高→优先
This commit is contained in:
Hermes CI Fix
2026-08-26 22:38:37 +08:00
parent 35974514da
commit e330545d59
3 changed files with 227 additions and 0 deletions
+91
View File
@@ -161,6 +161,97 @@ def api_cvp_detailed(data: dict):
raise HTTPException(400, f"CVP详细分析失败: {str(e)}")
# ── 相关成本决策(CMA P2商业决策分析25%权重核心) ──────────────────
@router.post("/relevant-decision")
def api_relevant_decision(data: dict):
"""相关成本决策分析(CMA P2 商业决策分析核心内容)
场景: make-or-buy自制外购 / special-order特殊订单 / product-mix产品组合
"""
try:
decision_type = data.get("type", "make_or_buy")
if decision_type == "make_or_buy":
# 自制vs外购决策
# 相关成本 = 增量成本(只有随决策变化的成本才是相关的)
make_var_cost = float(data.get("make_variable_cost", 0)) # 自制单位变动成本
make_fixed = float(data.get("make_fixed_cost", 0)) # 自制新增固定成本
buy_price = float(data.get("buy_price", 0)) # 外购单价
demand = float(data.get("demand", 0)) # 需求量
existing_fixed = float(data.get("existing_fixed_cost", 0)) # 现有固定成本(无关成本,自制不增加则忽略)
make_total = make_var_cost * demand + make_fixed
buy_total = buy_price * demand
diff = buy_total - make_total # >0自制省钱
return {
"type": "自制vs外购",
"make_total_cost": round(make_total, 2),
"buy_total_cost": round(buy_total, 2),
"difference": round(diff, 2),
"recommendation": "自制" if diff > 0 else "外购",
"reason": f"自制总成本{make_total:.2f} vs 外购总成本{buy_total:.2f}{'自制节省' + str(round(diff,2)) if diff > 0 else '外购节省' + str(round(-diff,2))}",
"unit_make_cost": round(make_var_cost + (make_fixed / demand if demand else 0), 2),
"unit_buy_price": buy_price,
"indifferent_point": round(make_fixed / (buy_price - make_var_cost), 2) if buy_price > make_var_cost else None,
"notes": "仅考虑相关成本(增量成本);现有固定成本若不受决策影响则无关",
}
elif decision_type == "special_order":
# 特殊订单决策(有剩余产能时,只要价格>单位变动成本即接受)
normal_price = float(data.get("normal_price", 0))
special_price = float(data.get("special_price", 0))
var_cost = float(data.get("variable_cost", 0))
order_qty = float(data.get("order_qty", 0))
capacity_used = float(data.get("capacity_used", 0)) # 特殊订单占用产能%
extra_fixed = float(data.get("extra_fixed_cost", 0)) # 一次性额外固定成本
contribution_per_unit = special_price - var_cost
total_contribution = contribution_per_unit * order_qty - extra_fixed
accept = total_contribution > 0 and capacity_used <= 100
return {
"type": "特殊订单",
"unit_contribution": round(contribution_per_unit, 2),
"total_contribution": round(total_contribution, 2),
"extra_fixed_cost": extra_fixed,
"capacity_used_pct": capacity_used,
"recommendation": "接受" if accept else "拒绝",
"reason": f"单价{special_price} - 变动成本{var_cost} = 单位贡献{contribution_per_unit:.2f}" +
(f",共{total_contribution:.2f} > 0 且产能{capacity_used}%够用 → 接受(增量利润)" if accept else
f",总贡献{total_contribution:.2f} ≤ 0 或产能不足 → 拒绝"),
"notes": "有剩余产能时,只要价格>变动成本且不冲击正常市场即可接受;固定成本无关",
}
elif decision_type == "product_mix":
# 产品组合决策(约束理论:单位约束资源的边际贡献最大者优先)
products = data.get("products", []) # [{name, price, var_cost, constraint_usage, demand}]
results = []
for p in products:
cm_per_unit = float(p.get("price", 0)) - float(p.get("var_cost", 0))
cm_per_constraint = cm_per_unit / float(p.get("constraint_usage", 1))
results.append({
"name": p.get("name", ""),
"unit_contribution": round(cm_per_unit, 2),
"constraint_usage": float(p.get("constraint_usage", 1)),
"contribution_per_constraint": round(cm_per_constraint, 2),
"demand": float(p.get("demand", 0)),
})
# 按单位约束资源贡献排序(约束理论优先)
results.sort(key=lambda x: x["contribution_per_constraint"], reverse=True)
return {
"type": "产品组合(约束理论)",
"ranking": results,
"recommendation": f"优先生产「{results[0]['name']}」(单位约束贡献{results[0]['contribution_per_constraint']}最高)",
"notes": "瓶颈资源下,按单位约束资源的边际贡献排序,而非单位边际贡献",
}
raise HTTPException(400, "未知决策类型: " + str(decision_type))
except Exception as e:
raise HTTPException(400, f"相关成本决策失败: {str(e)}")
# ── 现金流预测(AI事前预警) ────────────────────────────────────