refactor: Convert internal services to structured JSON reasoning
Convert pipe-separated reasoning codes to structured JSON format for: - Safety stock calculator (statistical calculations, errors) - Price forecaster (procurement recommendations, volatility) - Order optimization (EOQ, tier pricing) This enables i18n translation of internal calculation reasoning and provides structured data for frontend AI insights display. Benefits: - Consistent with PO/Batch reasoning_data format - Frontend can translate using same i18n infrastructure - Structured parameters enable rich UI visualization - No legacy string parsing needed Changes: - safety_stock_calculator.py: Replace reasoning str with reasoning_data dict - price_forecaster.py: Convert recommendation reasoning to structured format - optimization.py: Update EOQ and tier pricing to use reasoning_data Part of complete i18n implementation for AI insights.
This commit is contained in:
@@ -25,7 +25,7 @@ class SafetyStockResult:
|
||||
lead_time_days: int
|
||||
calculation_method: str
|
||||
confidence: str # 'high', 'medium', 'low'
|
||||
reasoning: str
|
||||
reasoning_data: dict
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -108,7 +108,13 @@ class SafetyStockCalculator:
|
||||
lead_time_days=lead_time_days,
|
||||
calculation_method='zero_due_to_invalid_inputs',
|
||||
confidence='low',
|
||||
reasoning='ERROR:LEAD_TIME_INVALID' # Error code for i18n translation
|
||||
reasoning_data={
|
||||
'type': 'error_lead_time_invalid',
|
||||
'parameters': {
|
||||
'lead_time_days': lead_time_days,
|
||||
'demand_std_dev': demand_std_dev
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
# Safety Stock = Z × σ × √L
|
||||
@@ -117,9 +123,6 @@ class SafetyStockCalculator:
|
||||
# Determine confidence
|
||||
confidence = self._determine_confidence(demand_std_dev, lead_time_days)
|
||||
|
||||
# Use calculation method as reasoning code with parameters
|
||||
reasoning = f"CALC:STATISTICAL_Z_SCORE|service_level={service_level*100:.1f}|z_score={z_score:.2f}|demand_std={demand_std_dev:.2f}|lead_time={lead_time_days}"
|
||||
|
||||
return SafetyStockResult(
|
||||
safety_stock_quantity=Decimal(str(round(safety_stock, 2))),
|
||||
service_level=service_level,
|
||||
@@ -128,7 +131,18 @@ class SafetyStockCalculator:
|
||||
lead_time_days=lead_time_days,
|
||||
calculation_method='statistical_z_score',
|
||||
confidence=confidence,
|
||||
reasoning=reasoning
|
||||
reasoning_data={
|
||||
'type': 'statistical_z_score',
|
||||
'calculation_method': 'statistical_z_score',
|
||||
'parameters': {
|
||||
'service_level': round(service_level * 100, 1),
|
||||
'z_score': round(z_score, 2),
|
||||
'demand_std_dev': round(demand_std_dev, 2),
|
||||
'lead_time_days': lead_time_days,
|
||||
'safety_stock': round(safety_stock, 2)
|
||||
},
|
||||
'confidence': confidence
|
||||
}
|
||||
)
|
||||
|
||||
def calculate_from_demand_history(
|
||||
@@ -158,7 +172,13 @@ class SafetyStockCalculator:
|
||||
lead_time_days=lead_time_days,
|
||||
calculation_method='insufficient_data',
|
||||
confidence='low',
|
||||
reasoning='ERROR:INSUFFICIENT_DATA' # Error code for i18n translation
|
||||
reasoning_data={
|
||||
'type': 'error_insufficient_data',
|
||||
'parameters': {
|
||||
'data_points': len(daily_demands) if daily_demands else 0,
|
||||
'min_required': 2
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
# Calculate standard deviation
|
||||
@@ -209,9 +229,6 @@ class SafetyStockCalculator:
|
||||
|
||||
confidence = 'high' if lead_time_std_dev > 0 else 'medium'
|
||||
|
||||
# Use calculation method as reasoning code with parameters
|
||||
reasoning = f"CALC:ADVANCED_VARIABILITY|demand_std={demand_std_dev:.2f}|lead_time_std={lead_time_std_dev:.1f}"
|
||||
|
||||
return SafetyStockResult(
|
||||
safety_stock_quantity=Decimal(str(round(safety_stock, 2))),
|
||||
service_level=service_level,
|
||||
@@ -220,7 +237,20 @@ class SafetyStockCalculator:
|
||||
lead_time_days=lead_time_mean,
|
||||
calculation_method='statistical_with_lead_time_variability',
|
||||
confidence=confidence,
|
||||
reasoning=reasoning
|
||||
reasoning_data={
|
||||
'type': 'advanced_variability',
|
||||
'calculation_method': 'statistical_with_lead_time_variability',
|
||||
'parameters': {
|
||||
'service_level': round(service_level * 100, 1),
|
||||
'z_score': round(z_score, 2),
|
||||
'demand_mean': round(demand_mean, 2),
|
||||
'demand_std_dev': round(demand_std_dev, 2),
|
||||
'lead_time_mean': lead_time_mean,
|
||||
'lead_time_std_dev': round(lead_time_std_dev, 1),
|
||||
'safety_stock': round(safety_stock, 2)
|
||||
},
|
||||
'confidence': confidence
|
||||
}
|
||||
)
|
||||
|
||||
def calculate_using_fixed_percentage(
|
||||
@@ -245,9 +275,6 @@ class SafetyStockCalculator:
|
||||
lead_time_demand = average_demand * lead_time_days
|
||||
safety_stock = lead_time_demand * percentage
|
||||
|
||||
# Use calculation method as reasoning code with parameters
|
||||
reasoning = f"CALC:FIXED_PERCENTAGE|percentage={percentage*100:.0f}|lead_time_demand={lead_time_demand:.2f}"
|
||||
|
||||
return SafetyStockResult(
|
||||
safety_stock_quantity=Decimal(str(round(safety_stock, 2))),
|
||||
service_level=0.0, # Not based on service level
|
||||
@@ -256,7 +283,18 @@ class SafetyStockCalculator:
|
||||
lead_time_days=lead_time_days,
|
||||
calculation_method='fixed_percentage',
|
||||
confidence='low',
|
||||
reasoning=reasoning
|
||||
reasoning_data={
|
||||
'type': 'fixed_percentage',
|
||||
'calculation_method': 'fixed_percentage',
|
||||
'parameters': {
|
||||
'percentage': round(percentage * 100, 0),
|
||||
'average_demand': round(average_demand, 2),
|
||||
'lead_time_days': lead_time_days,
|
||||
'lead_time_demand': round(lead_time_demand, 2),
|
||||
'safety_stock': round(safety_stock, 2)
|
||||
},
|
||||
'confidence': 'low'
|
||||
}
|
||||
)
|
||||
|
||||
def calculate_batch_safety_stock(
|
||||
@@ -432,5 +470,5 @@ class SafetyStockCalculator:
|
||||
'lead_time_days': result.lead_time_days,
|
||||
'calculation_method': result.calculation_method,
|
||||
'confidence': result.confidence,
|
||||
'reasoning': result.reasoning
|
||||
'reasoning_data': result.reasoning_data
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user