Files
bakery-ia/services/ai_insights/app/impact/impact_estimator.py
2025-11-05 13:34:56 +01:00

321 lines
10 KiB
Python

"""Impact estimation for AI Insights."""
from typing import Dict, Any, Optional, Tuple
from decimal import Decimal
from datetime import datetime, timedelta
class ImpactEstimator:
"""
Estimate potential impact of recommendations.
Calculates expected business value in terms of:
- Cost savings (euros)
- Revenue increase (euros)
- Waste reduction (euros or percentage)
- Efficiency gains (hours or percentage)
- Quality improvements (units or percentage)
"""
def estimate_procurement_savings(
self,
current_price: Decimal,
predicted_price: Decimal,
order_quantity: Decimal,
timeframe_days: int = 30
) -> Tuple[Decimal, str, str]:
"""
Estimate savings from opportunistic buying.
Args:
current_price: Current unit price
predicted_price: Predicted future price
order_quantity: Quantity to order
timeframe_days: Time horizon for prediction
Returns:
tuple: (impact_value, impact_unit, impact_type)
"""
savings_per_unit = predicted_price - current_price
if savings_per_unit > 0:
total_savings = savings_per_unit * order_quantity
return (
round(total_savings, 2),
'euros',
'cost_savings'
)
return (Decimal('0.0'), 'euros', 'cost_savings')
def estimate_waste_reduction_savings(
self,
current_waste_rate: float,
optimized_waste_rate: float,
monthly_volume: Decimal,
avg_cost_per_unit: Decimal
) -> Tuple[Decimal, str, str]:
"""
Estimate savings from waste reduction.
Args:
current_waste_rate: Current waste rate (0-1)
optimized_waste_rate: Optimized waste rate (0-1)
monthly_volume: Monthly volume
avg_cost_per_unit: Average cost per unit
Returns:
tuple: (impact_value, impact_unit, impact_type)
"""
waste_reduction_rate = current_waste_rate - optimized_waste_rate
units_saved = monthly_volume * Decimal(str(waste_reduction_rate))
savings = units_saved * avg_cost_per_unit
return (
round(savings, 2),
'euros/month',
'waste_reduction'
)
def estimate_forecast_improvement_value(
self,
current_mape: float,
improved_mape: float,
avg_monthly_revenue: Decimal
) -> Tuple[Decimal, str, str]:
"""
Estimate value from forecast accuracy improvement.
Better forecasts reduce:
- Stockouts (lost sales)
- Overproduction (waste)
- Emergency orders (premium costs)
Args:
current_mape: Current forecast MAPE
improved_mape: Improved forecast MAPE
avg_monthly_revenue: Average monthly revenue
Returns:
tuple: (impact_value, impact_unit, impact_type)
"""
# Rule of thumb: 1% MAPE improvement = 0.5% revenue impact
mape_improvement = current_mape - improved_mape
revenue_impact_pct = mape_improvement * 0.5 / 100
revenue_increase = avg_monthly_revenue * Decimal(str(revenue_impact_pct))
return (
round(revenue_increase, 2),
'euros/month',
'revenue_increase'
)
def estimate_production_efficiency_gain(
self,
time_saved_minutes: int,
batches_per_month: int,
labor_cost_per_hour: Decimal = Decimal('15.0')
) -> Tuple[Decimal, str, str]:
"""
Estimate value from production efficiency improvements.
Args:
time_saved_minutes: Minutes saved per batch
batches_per_month: Number of batches per month
labor_cost_per_hour: Labor cost per hour
Returns:
tuple: (impact_value, impact_unit, impact_type)
"""
hours_saved_per_month = (time_saved_minutes * batches_per_month) / 60
cost_savings = Decimal(str(hours_saved_per_month)) * labor_cost_per_hour
return (
round(cost_savings, 2),
'euros/month',
'efficiency_gain'
)
def estimate_safety_stock_optimization(
self,
current_safety_stock: Decimal,
optimal_safety_stock: Decimal,
holding_cost_per_unit_per_day: Decimal,
stockout_cost_reduction: Decimal = Decimal('0.0')
) -> Tuple[Decimal, str, str]:
"""
Estimate impact of safety stock optimization.
Args:
current_safety_stock: Current safety stock level
optimal_safety_stock: Optimal safety stock level
holding_cost_per_unit_per_day: Daily holding cost
stockout_cost_reduction: Reduction in stockout costs
Returns:
tuple: (impact_value, impact_unit, impact_type)
"""
stock_reduction = current_safety_stock - optimal_safety_stock
if stock_reduction > 0:
# Savings from reduced holding costs
daily_savings = stock_reduction * holding_cost_per_unit_per_day
monthly_savings = daily_savings * 30
total_savings = monthly_savings + stockout_cost_reduction
return (
round(total_savings, 2),
'euros/month',
'cost_savings'
)
elif stock_reduction < 0:
# Cost increase but reduces stockouts
daily_cost = abs(stock_reduction) * holding_cost_per_unit_per_day
monthly_cost = daily_cost * 30
net_savings = stockout_cost_reduction - monthly_cost
if net_savings > 0:
return (
round(net_savings, 2),
'euros/month',
'cost_savings'
)
return (Decimal('0.0'), 'euros/month', 'cost_savings')
def estimate_supplier_switch_savings(
self,
current_supplier_price: Decimal,
alternative_supplier_price: Decimal,
monthly_order_quantity: Decimal,
quality_difference_score: float = 0.0 # -1 to 1
) -> Tuple[Decimal, str, str]:
"""
Estimate savings from switching suppliers.
Args:
current_supplier_price: Current supplier unit price
alternative_supplier_price: Alternative supplier unit price
monthly_order_quantity: Monthly order quantity
quality_difference_score: Quality difference (-1=worse, 0=same, 1=better)
Returns:
tuple: (impact_value, impact_unit, impact_type)
"""
price_savings = (current_supplier_price - alternative_supplier_price) * monthly_order_quantity
# Adjust for quality difference
# If quality is worse, reduce estimated savings
quality_adjustment = 1 + (quality_difference_score * 0.1) # ±10% max adjustment
adjusted_savings = price_savings * Decimal(str(quality_adjustment))
return (
round(adjusted_savings, 2),
'euros/month',
'cost_savings'
)
def estimate_yield_improvement_value(
self,
current_yield_rate: float,
predicted_yield_rate: float,
production_volume: Decimal,
product_price: Decimal
) -> Tuple[Decimal, str, str]:
"""
Estimate value from production yield improvements.
Args:
current_yield_rate: Current yield rate (0-1)
predicted_yield_rate: Predicted yield rate (0-1)
production_volume: Monthly production volume
product_price: Product selling price
Returns:
tuple: (impact_value, impact_unit, impact_type)
"""
yield_improvement = predicted_yield_rate - current_yield_rate
if yield_improvement > 0:
additional_units = production_volume * Decimal(str(yield_improvement))
revenue_increase = additional_units * product_price
return (
round(revenue_increase, 2),
'euros/month',
'revenue_increase'
)
return (Decimal('0.0'), 'euros/month', 'revenue_increase')
def estimate_demand_pattern_value(
self,
pattern_strength: float, # 0-1
potential_revenue_increase: Decimal,
implementation_cost: Decimal = Decimal('0.0')
) -> Tuple[Decimal, str, str]:
"""
Estimate value from acting on demand patterns.
Args:
pattern_strength: Strength of detected pattern (0-1)
potential_revenue_increase: Potential monthly revenue increase
implementation_cost: One-time implementation cost
Returns:
tuple: (impact_value, impact_unit, impact_type)
"""
# Discount by pattern strength (confidence)
expected_value = potential_revenue_increase * Decimal(str(pattern_strength))
# Amortize implementation cost over 6 months
monthly_cost = implementation_cost / 6
net_value = expected_value - monthly_cost
return (
round(max(Decimal('0.0'), net_value), 2),
'euros/month',
'revenue_increase'
)
def estimate_composite_impact(
self,
impacts: list[Dict[str, Any]]
) -> Tuple[Decimal, str, str]:
"""
Combine multiple impact estimations.
Args:
impacts: List of impact dicts with 'value', 'unit', 'type'
Returns:
tuple: (total_impact_value, impact_unit, impact_type)
"""
total_savings = Decimal('0.0')
total_revenue = Decimal('0.0')
for impact in impacts:
value = Decimal(str(impact['value']))
impact_type = impact['type']
if impact_type == 'cost_savings':
total_savings += value
elif impact_type == 'revenue_increase':
total_revenue += value
# Combine both types
total_impact = total_savings + total_revenue
if total_impact > 0:
# Determine primary type
primary_type = 'cost_savings' if total_savings > total_revenue else 'revenue_increase'
return (
round(total_impact, 2),
'euros/month',
primary_type
)
return (Decimal('0.0'), 'euros/month', 'cost_savings')