REFACTOR ALL APIs fix 1

This commit is contained in:
Urtzi Alfaro
2025-10-07 07:15:07 +02:00
parent 38fb98bc27
commit 7c72f83c51
47 changed files with 1821 additions and 270 deletions

View File

@@ -26,6 +26,10 @@ import {
GetForecastsParams,
ForecastingHealthResponse,
MultiDayForecastResponse,
ScenarioSimulationRequest,
ScenarioSimulationResponse,
ScenarioComparisonRequest,
ScenarioComparisonResponse,
} from '../types/forecasting';
export class ForecastingService {
@@ -258,6 +262,43 @@ export class ForecastingService {
);
}
// ===================================================================
// SCENARIO SIMULATION - PROFESSIONAL/ENTERPRISE ONLY
// Backend: services/forecasting/app/api/scenario_operations.py
// ===================================================================
/**
* Run a "what-if" scenario simulation on forecasts
* POST /tenants/{tenant_id}/forecasting/analytics/scenario-simulation
*
* **PROFESSIONAL/ENTERPRISE ONLY**
*/
async simulateScenario(
tenantId: string,
request: ScenarioSimulationRequest
): Promise<ScenarioSimulationResponse> {
return apiClient.post<ScenarioSimulationResponse, ScenarioSimulationRequest>(
`${this.baseUrl}/${tenantId}/forecasting/analytics/scenario-simulation`,
request
);
}
/**
* Compare multiple scenario simulations
* POST /tenants/{tenant_id}/forecasting/analytics/scenario-comparison
*
* **PROFESSIONAL/ENTERPRISE ONLY**
*/
async compareScenarios(
tenantId: string,
request: ScenarioComparisonRequest
): Promise<ScenarioComparisonResponse> {
return apiClient.post<ScenarioComparisonResponse, ScenarioComparisonRequest>(
`${this.baseUrl}/${tenantId}/forecasting/analytics/scenario-comparison`,
request
);
}
// ===================================================================
// Health Check
// ===================================================================

View File

@@ -215,35 +215,35 @@ export class OrdersService {
/**
* Get current procurement plan for today
* GET /tenants/{tenant_id}/orders/procurement/plans/current
* GET /tenants/{tenant_id}/orders/operations/procurement/plans/current
*/
static async getCurrentProcurementPlan(tenantId: string): Promise<ProcurementPlanResponse | null> {
return apiClient.get<ProcurementPlanResponse | null>(`/tenants/${tenantId}/orders/procurement/plans/current`);
return apiClient.get<ProcurementPlanResponse | null>(`/tenants/${tenantId}/orders/operations/procurement/plans/current`);
}
/**
* Get procurement plan by specific date
* GET /tenants/{tenant_id}/orders/procurement/plans/date/{plan_date}
* GET /tenants/{tenant_id}/orders/operations/procurement/plans/date/{plan_date}
*/
static async getProcurementPlanByDate(tenantId: string, planDate: string): Promise<ProcurementPlanResponse | null> {
return apiClient.get<ProcurementPlanResponse | null>(`/tenants/${tenantId}/orders/procurement/plans/date/${planDate}`);
return apiClient.get<ProcurementPlanResponse | null>(`/tenants/${tenantId}/orders/operations/procurement/plans/date/${planDate}`);
}
/**
* Get procurement plan by ID
* GET /tenants/{tenant_id}/orders/procurement/plans/id/{plan_id}
* GET /tenants/{tenant_id}/orders/operations/procurement/plans/id/{plan_id}
*/
static async getProcurementPlanById(tenantId: string, planId: string): Promise<ProcurementPlanResponse | null> {
return apiClient.get<ProcurementPlanResponse | null>(`/tenants/${tenantId}/orders/procurement/plans/id/${planId}`);
return apiClient.get<ProcurementPlanResponse | null>(`/tenants/${tenantId}/orders/operations/procurement/plans/id/${planId}`);
}
/**
* List procurement plans with filtering
* GET /tenants/{tenant_id}/orders/procurement/plans/
* GET /tenants/{tenant_id}/orders/operations/procurement/plans/
*/
static async getProcurementPlans(params: GetProcurementPlansParams): Promise<PaginatedProcurementPlans> {
const { tenant_id, status, start_date, end_date, limit = 50, offset = 0 } = params;
const queryParams = new URLSearchParams({
limit: limit.toString(),
offset: offset.toString(),
@@ -254,29 +254,29 @@ export class OrdersService {
if (end_date) queryParams.append('end_date', end_date);
return apiClient.get<PaginatedProcurementPlans>(
`/tenants/${tenant_id}/orders/procurement/plans?${queryParams.toString()}`
`/tenants/${tenant_id}/orders/operations/procurement/plans?${queryParams.toString()}`
);
}
/**
* Generate a new procurement plan
* POST /tenants/{tenant_id}/orders/procurement/plans/generate
* POST /tenants/{tenant_id}/orders/operations/procurement/plans/generate
*/
static async generateProcurementPlan(tenantId: string, request: GeneratePlanRequest): Promise<GeneratePlanResponse> {
return apiClient.post<GeneratePlanResponse>(`/tenants/${tenantId}/orders/procurement/plans/generate`, request);
return apiClient.post<GeneratePlanResponse>(`/tenants/${tenantId}/orders/operations/procurement/plans/generate`, request);
}
/**
* Update procurement plan status
* PUT /tenants/{tenant_id}/orders/procurement/plans/{plan_id}/status
* PUT /tenants/{tenant_id}/orders/operations/procurement/plans/{plan_id}/status
*/
static async updateProcurementPlanStatus(params: UpdatePlanStatusParams): Promise<ProcurementPlanResponse> {
const { tenant_id, plan_id, status } = params;
const queryParams = new URLSearchParams({ status });
return apiClient.put<ProcurementPlanResponse>(
`/tenants/${tenant_id}/orders/procurement/plans/${plan_id}/status?${queryParams.toString()}`,
`/tenants/${tenant_id}/orders/operations/procurement/plans/${plan_id}/status?${queryParams.toString()}`,
{}
);
}
@@ -291,45 +291,45 @@ export class OrdersService {
/**
* Get requirements for a specific plan
* GET /tenants/{tenant_id}/orders/procurement/plans/{plan_id}/requirements
* GET /tenants/{tenant_id}/orders/operations/procurement/plans/{plan_id}/requirements
*/
static async getPlanRequirements(params: GetPlanRequirementsParams): Promise<ProcurementRequirementResponse[]> {
const { tenant_id, plan_id, status, priority } = params;
const queryParams = new URLSearchParams();
if (status) queryParams.append('status', status);
if (priority) queryParams.append('priority', priority);
const url = `/tenants/${tenant_id}/orders/procurement/plans/${plan_id}/requirements${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;
const url = `/tenants/${tenant_id}/orders/operations/procurement/plans/${plan_id}/requirements${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;
return apiClient.get<ProcurementRequirementResponse[]>(url);
}
/**
* Get critical requirements across all plans
* GET /tenants/{tenant_id}/orders/procurement/requirements/critical
* GET /tenants/{tenant_id}/orders/operations/procurement/requirements/critical
*/
static async getCriticalRequirements(tenantId: string): Promise<ProcurementRequirementResponse[]> {
return apiClient.get<ProcurementRequirementResponse[]>(`/tenants/${tenantId}/orders/procurement/requirements/critical`);
return apiClient.get<ProcurementRequirementResponse[]>(`/tenants/${tenantId}/orders/operations/procurement/requirements/critical`);
}
/**
* Trigger daily scheduler manually
* POST /tenants/{tenant_id}/orders/procurement/scheduler/trigger
* POST /tenants/{tenant_id}/orders/operations/procurement/scheduler/trigger
*/
static async triggerDailyScheduler(tenantId: string): Promise<{ success: boolean; message: string; tenant_id: string }> {
return apiClient.post<{ success: boolean; message: string; tenant_id: string }>(
`/tenants/${tenantId}/orders/procurement/scheduler/trigger`,
`/tenants/${tenantId}/orders/operations/procurement/scheduler/trigger`,
{}
);
}
/**
* Get procurement service health
* GET /tenants/{tenant_id}/orders/procurement/health
* GET /tenants/{tenant_id}/orders/base/procurement/health
*/
static async getProcurementHealth(tenantId: string): Promise<{ status: string; service: string; procurement_enabled: boolean; timestamp: string }> {
return apiClient.get<{ status: string; service: string; procurement_enabled: boolean; timestamp: string }>(`/tenants/${tenantId}/orders/procurement/health`);
return apiClient.get<{ status: string; service: string; procurement_enabled: boolean; timestamp: string }>(`/tenants/${tenantId}/orders/base/procurement/health`);
}
// ===================================================================
@@ -339,51 +339,51 @@ export class OrdersService {
/**
* Recalculate an existing procurement plan
* POST /tenants/{tenant_id}/orders/procurement/plans/{plan_id}/recalculate
* POST /tenants/{tenant_id}/orders/operations/procurement/plans/{plan_id}/recalculate
*/
static async recalculateProcurementPlan(tenantId: string, planId: string): Promise<GeneratePlanResponse> {
return apiClient.post<GeneratePlanResponse>(
`/tenants/${tenantId}/orders/procurement/plans/${planId}/recalculate`,
`/tenants/${tenantId}/orders/operations/procurement/plans/${planId}/recalculate`,
{}
);
}
/**
* Approve a procurement plan with notes
* POST /tenants/{tenant_id}/orders/procurement/plans/{plan_id}/approve
* POST /tenants/{tenant_id}/orders/operations/procurement/plans/{plan_id}/approve
*/
static async approveProcurementPlan(tenantId: string, planId: string, request?: ApprovalRequest): Promise<ProcurementPlanResponse> {
return apiClient.post<ProcurementPlanResponse>(
`/tenants/${tenantId}/orders/procurement/plans/${planId}/approve`,
`/tenants/${tenantId}/orders/operations/procurement/plans/${planId}/approve`,
request || {}
);
}
/**
* Reject a procurement plan with notes
* POST /tenants/{tenant_id}/orders/procurement/plans/{plan_id}/reject
* POST /tenants/{tenant_id}/orders/operations/procurement/plans/{plan_id}/reject
*/
static async rejectProcurementPlan(tenantId: string, planId: string, request?: RejectionRequest): Promise<ProcurementPlanResponse> {
return apiClient.post<ProcurementPlanResponse>(
`/tenants/${tenantId}/orders/procurement/plans/${planId}/reject`,
`/tenants/${tenantId}/orders/operations/procurement/plans/${planId}/reject`,
request || {}
);
}
/**
* Create purchase orders automatically from procurement plan
* POST /tenants/{tenant_id}/orders/procurement/plans/{plan_id}/create-purchase-orders
* POST /tenants/{tenant_id}/orders/operations/procurement/plans/{plan_id}/create-purchase-orders
*/
static async createPurchaseOrdersFromPlan(tenantId: string, planId: string, autoApprove: boolean = false): Promise<CreatePOsResult> {
return apiClient.post<CreatePOsResult>(
`/tenants/${tenantId}/orders/procurement/plans/${planId}/create-purchase-orders`,
`/tenants/${tenantId}/orders/operations/procurement/plans/${planId}/create-purchase-orders`,
{ auto_approve: autoApprove }
);
}
/**
* Link a procurement requirement to a purchase order
* POST /tenants/{tenant_id}/orders/procurement/requirements/{requirement_id}/link-purchase-order
* POST /tenants/{tenant_id}/orders/operations/procurement/requirements/{requirement_id}/link-purchase-order
*/
static async linkRequirementToPurchaseOrder(
tenantId: string,
@@ -391,14 +391,14 @@ export class OrdersService {
request: LinkRequirementToPORequest
): Promise<{ success: boolean; message: string; requirement_id: string; purchase_order_id: string }> {
return apiClient.post<{ success: boolean; message: string; requirement_id: string; purchase_order_id: string }>(
`/tenants/${tenantId}/orders/procurement/requirements/${requirementId}/link-purchase-order`,
`/tenants/${tenantId}/orders/operations/procurement/requirements/${requirementId}/link-purchase-order`,
request
);
}
/**
* Update delivery status for a requirement
* PUT /tenants/{tenant_id}/orders/procurement/requirements/{requirement_id}/delivery-status
* PUT /tenants/{tenant_id}/orders/operations/procurement/requirements/{requirement_id}/delivery-status
*/
static async updateRequirementDeliveryStatus(
tenantId: string,
@@ -406,7 +406,7 @@ export class OrdersService {
request: UpdateDeliveryStatusRequest
): Promise<{ success: boolean; message: string; requirement_id: string; delivery_status: string }> {
return apiClient.put<{ success: boolean; message: string; requirement_id: string; delivery_status: string }>(
`/tenants/${tenantId}/orders/procurement/requirements/${requirementId}/delivery-status`,
`/tenants/${tenantId}/orders/operations/procurement/requirements/${requirementId}/delivery-status`,
request
);
}

View File

@@ -188,10 +188,10 @@ export class RecipesService {
/**
* Get recipe statistics for dashboard
* GET /tenants/{tenant_id}/recipes/statistics/dashboard
* GET /tenants/{tenant_id}/recipes/dashboard/statistics
*/
async getRecipeStatistics(tenantId: string): Promise<RecipeStatisticsResponse> {
return apiClient.get<RecipeStatisticsResponse>(`${this.baseUrl}/${tenantId}/recipes/statistics/dashboard`);
return apiClient.get<RecipeStatisticsResponse>(`${this.baseUrl}/${tenantId}/recipes/dashboard/statistics`);
}
/**

View File

@@ -100,7 +100,7 @@ export class SalesService {
}
async getProductCategories(tenantId: string): Promise<string[]> {
return apiClient.get<string[]>(`${this.baseUrl}/${tenantId}/sales/sales/categories`);
return apiClient.get<string[]>(`${this.baseUrl}/${tenantId}/sales/categories`);
}
// ===================================================================

View File

@@ -89,27 +89,27 @@ export class SubscriptionService {
}
async validatePlanUpgrade(tenantId: string, planKey: string): Promise<PlanUpgradeValidation> {
return apiClient.get<PlanUpgradeValidation>(`${this.baseUrl}/${tenantId}/validate-upgrade/${planKey}`);
return apiClient.get<PlanUpgradeValidation>(`${this.baseUrl}/subscriptions/${tenantId}/validate-upgrade/${planKey}`);
}
async upgradePlan(tenantId: string, planKey: string): Promise<PlanUpgradeResult> {
return apiClient.post<PlanUpgradeResult>(`${this.baseUrl}/${tenantId}/upgrade?new_plan=${planKey}`, {});
return apiClient.post<PlanUpgradeResult>(`${this.baseUrl}/subscriptions/${tenantId}/upgrade?new_plan=${planKey}`, {});
}
async canAddLocation(tenantId: string): Promise<{ can_add: boolean; reason?: string; current_count?: number; max_allowed?: number }> {
return apiClient.get(`${this.baseUrl}/${tenantId}/can-add-location`);
return apiClient.get(`${this.baseUrl}/subscriptions/${tenantId}/can-add-location`);
}
async canAddProduct(tenantId: string): Promise<{ can_add: boolean; reason?: string; current_count?: number; max_allowed?: number }> {
return apiClient.get(`${this.baseUrl}/${tenantId}/can-add-product`);
return apiClient.get(`${this.baseUrl}/subscriptions/${tenantId}/can-add-product`);
}
async canAddUser(tenantId: string): Promise<{ can_add: boolean; reason?: string; current_count?: number; max_allowed?: number }> {
return apiClient.get(`${this.baseUrl}/${tenantId}/can-add-user`);
return apiClient.get(`${this.baseUrl}/subscriptions/${tenantId}/can-add-user`);
}
async hasFeature(tenantId: string, featureName: string): Promise<{ has_feature: boolean; feature_value?: any; plan?: string; reason?: string }> {
return apiClient.get(`${this.baseUrl}/${tenantId}/features/${featureName}`);
return apiClient.get(`${this.baseUrl}/subscriptions/${tenantId}/features/${featureName}`);
}
formatPrice(amount: number): string {

View File

@@ -173,7 +173,7 @@ class TrainingService {
*/
getTrainingWebSocketUrl(tenantId: string, jobId: string): string {
const baseWsUrl = apiClient.getAxiosInstance().defaults.baseURL?.replace(/^http/, 'ws');
return `${baseWsUrl}/ws/tenants/${tenantId}/training/jobs/${jobId}/live`;
return `${baseWsUrl}/tenants/${tenantId}/training/jobs/${jobId}/live`;
}
/**

View File

@@ -90,7 +90,7 @@ export interface ForecastResponse {
// Metadata
created_at: string; // ISO datetime string
processing_time_ms?: number | null;
features_used?: Record<string, any> | null;
features?: Record<string, any> | null;
}
/**
@@ -260,3 +260,165 @@ export interface PredictionsPerformanceParams {
export interface MessageResponse {
message: string;
}
// ================================================================
// SCENARIO SIMULATION TYPES - PROFESSIONAL/ENTERPRISE ONLY
// ================================================================
/**
* Types of scenarios available for simulation
* Backend: ScenarioType enum in schemas/forecasts.py (lines 114-123)
*/
export enum ScenarioType {
WEATHER = 'weather',
COMPETITION = 'competition',
EVENT = 'event',
PRICING = 'pricing',
PROMOTION = 'promotion',
HOLIDAY = 'holiday',
SUPPLY_DISRUPTION = 'supply_disruption',
CUSTOM = 'custom'
}
/**
* Weather scenario parameters
* Backend: WeatherScenario in schemas/forecasts.py (lines 126-130)
*/
export interface WeatherScenario {
temperature_change?: number | null; // Temperature change in °C (-30 to +30)
precipitation_change?: number | null; // Precipitation change in mm (0-100)
weather_type?: string | null; // Weather type (heatwave, cold_snap, rainy, etc.)
}
/**
* Competition scenario parameters
* Backend: CompetitionScenario in schemas/forecasts.py (lines 133-137)
*/
export interface CompetitionScenario {
new_competitors: number; // Number of new competitors (1-10)
distance_km: number; // Distance from location in km (0.1-10)
estimated_market_share_loss: number; // Estimated market share loss (0-0.5)
}
/**
* Event scenario parameters
* Backend: EventScenario in schemas/forecasts.py (lines 140-145)
*/
export interface EventScenario {
event_type: string; // Type of event (festival, sports, concert, etc.)
expected_attendance: number; // Expected attendance
distance_km: number; // Distance from location in km (0-50)
duration_days: number; // Duration in days (1-30)
}
/**
* Pricing scenario parameters
* Backend: PricingScenario in schemas/forecasts.py (lines 148-151)
*/
export interface PricingScenario {
price_change_percent: number; // Price change percentage (-50 to +100)
affected_products?: string[] | null; // List of affected product IDs
}
/**
* Promotion scenario parameters
* Backend: PromotionScenario in schemas/forecasts.py (lines 154-158)
*/
export interface PromotionScenario {
discount_percent: number; // Discount percentage (0-75)
promotion_type: string; // Type of promotion (bogo, discount, bundle, etc.)
expected_traffic_increase: number; // Expected traffic increase (0-2.0 = 0-200%)
}
/**
* Request schema for scenario simulation
* Backend: ScenarioSimulationRequest in schemas/forecasts.py (lines 161-189)
*/
export interface ScenarioSimulationRequest {
scenario_name: string; // Name for this scenario (3-200 chars)
scenario_type: ScenarioType;
inventory_product_ids: string[]; // Products to simulate (min 1)
start_date: string; // ISO date string
duration_days?: number; // Default: 7, range: 1-30
// Scenario-specific parameters (provide based on scenario_type)
weather_params?: WeatherScenario | null;
competition_params?: CompetitionScenario | null;
event_params?: EventScenario | null;
pricing_params?: PricingScenario | null;
promotion_params?: PromotionScenario | null;
// Custom scenario parameters
custom_multipliers?: Record<string, number> | null;
// Comparison settings
include_baseline?: boolean; // Default: true
}
/**
* Impact of scenario on a specific product
* Backend: ScenarioImpact in schemas/forecasts.py (lines 192-199)
*/
export interface ScenarioImpact {
inventory_product_id: string;
baseline_demand: number;
simulated_demand: number;
demand_change_percent: number;
confidence_range: [number, number];
impact_factors: Record<string, any>;
}
/**
* Response schema for scenario simulation
* Backend: ScenarioSimulationResponse in schemas/forecasts.py (lines 202-256)
*/
export interface ScenarioSimulationResponse {
id: string;
tenant_id: string;
scenario_name: string;
scenario_type: ScenarioType;
// Simulation parameters
start_date: string; // ISO date string
end_date: string; // ISO date string
duration_days: number;
// Results
baseline_forecasts?: ForecastResponse[] | null;
scenario_forecasts: ForecastResponse[];
// Impact summary
total_baseline_demand: number;
total_scenario_demand: number;
overall_impact_percent: number;
product_impacts: ScenarioImpact[];
// Insights and recommendations
insights: string[];
recommendations: string[];
risk_level: string; // low, medium, high
// Metadata
created_at: string; // ISO datetime string
processing_time_ms: number;
}
/**
* Request to compare multiple scenarios
* Backend: ScenarioComparisonRequest in schemas/forecasts.py (lines 259-261)
*/
export interface ScenarioComparisonRequest {
scenario_ids: string[]; // 2-5 scenario IDs to compare
}
/**
* Response comparing multiple scenarios
* Backend: ScenarioComparisonResponse in schemas/forecasts.py (lines 264-270)
*/
export interface ScenarioComparisonResponse {
scenarios: ScenarioSimulationResponse[];
comparison_matrix: Record<string, Record<string, any>>;
best_case_scenario_id: string;
worst_case_scenario_id: string;
recommended_action: string;
}

View File

@@ -401,7 +401,7 @@ export interface ModelMetricsResponse {
rmse: number; // Root Mean Square Error
r2_score: number;
training_samples: number;
features_used: string[];
features?: string[]; // Features used by the model
model_type: string;
created_at?: string | null; // ISO datetime string
last_used_at?: string | null; // ISO datetime string