/** * Orchestrator Service API Client * Handles coordinated workflows across Forecasting, Production, and Procurement services * * NEW in Sprint 2: Orchestrator Service coordinates the daily workflow: * 1. Forecasting Service → Get demand forecasts * 2. Production Service → Generate production schedule from forecast * 3. Procurement Service → Generate procurement plan from forecast + schedule */ import { apiClient } from '../client'; // ============================================================================ // ORCHESTRATOR WORKFLOW TYPES // ============================================================================ export interface OrchestratorWorkflowRequest { target_date?: string; // YYYY-MM-DD, defaults to tomorrow planning_horizon_days?: number; // Default: 14 // Forecasting options forecast_days_ahead?: number; // Default: 7 // Production options auto_schedule_production?: boolean; // Default: true production_planning_days?: number; // Default: 1 // Procurement options auto_create_purchase_orders?: boolean; // Default: true auto_approve_purchase_orders?: boolean; // Default: false safety_stock_percentage?: number; // Default: 20.00 // Orchestrator options skip_on_error?: boolean; // Continue to next step if one fails notify_on_completion?: boolean; // Send notification when done } export interface WorkflowStepResult { step: 'forecasting' | 'production' | 'procurement'; status: 'success' | 'failed' | 'skipped'; duration_ms: number; data?: any; error?: string; warnings?: string[]; } export interface OrchestratorWorkflowResponse { success: boolean; workflow_id: string; tenant_id: string; target_date: string; execution_date: string; total_duration_ms: number; steps: WorkflowStepResult[]; // Step-specific results forecast_result?: { forecast_id: string; total_forecasts: number; forecast_data: any; }; production_result?: { schedule_id: string; total_batches: number; total_quantity: number; }; procurement_result?: { plan_id: string; total_requirements: number; total_cost: string; purchase_orders_created: number; purchase_orders_auto_approved: number; }; warnings?: string[]; errors?: string[]; } export interface WorkflowExecutionSummary { id: string; tenant_id: string; target_date: string; status: 'running' | 'completed' | 'failed' | 'cancelled'; started_at: string; completed_at?: string; total_duration_ms?: number; steps_completed: number; steps_total: number; created_by?: string; } export interface WorkflowExecutionDetail extends WorkflowExecutionSummary { steps: WorkflowStepResult[]; forecast_id?: string; production_schedule_id?: string; procurement_plan_id?: string; warnings?: string[]; errors?: string[]; } // ============================================================================ // ORCHESTRATOR WORKFLOW API FUNCTIONS // ============================================================================ /** * Run the daily orchestrated workflow * This is the main entry point for coordinated planning * * Workflow: * 1. Forecasting Service: Get demand forecasts for target date * 2. Production Service: Generate production schedule from forecast * 3. Procurement Service: Generate procurement plan from forecast + schedule * * NEW in Sprint 2: Replaces autonomous schedulers with centralized orchestration */ export async function runDailyWorkflow( tenantId: string, request?: OrchestratorWorkflowRequest ): Promise { return apiClient.post( `/tenants/${tenantId}/orchestrator/run-daily-workflow`, request || {} ); } /** * Run workflow for a specific date */ export async function runWorkflowForDate( tenantId: string, targetDate: string, options?: Omit ): Promise { return runDailyWorkflow(tenantId, { ...options, target_date: targetDate }); } /** * Test workflow with sample data (for development/testing) */ export async function testWorkflow( tenantId: string ): Promise { return apiClient.post( `/tenants/${tenantId}/orchestrator/test-workflow`, {} ); } /** * Get list of workflow executions */ export async function listWorkflowExecutions( tenantId: string, params?: { status?: WorkflowExecutionSummary['status']; date_from?: string; date_to?: string; limit?: number; offset?: number; } ): Promise { return apiClient.get( `/tenants/${tenantId}/orchestrator/executions`, { params } ); } /** * Get a single workflow execution by ID with full details */ export async function getWorkflowExecution( tenantId: string, executionId: string ): Promise { return apiClient.get( `/tenants/${tenantId}/orchestrator/executions/${executionId}` ); } /** * Get latest workflow execution */ export async function getLatestWorkflowExecution( tenantId: string ): Promise { const executions = await listWorkflowExecutions(tenantId, { limit: 1 }); if (executions.length === 0) { return null; } return getWorkflowExecution(tenantId, executions[0].id); } /** * Cancel a running workflow execution */ export async function cancelWorkflowExecution( tenantId: string, executionId: string ): Promise<{ message: string }> { return apiClient.post<{ message: string }>( `/tenants/${tenantId}/orchestrator/executions/${executionId}/cancel`, {} ); } /** * Retry a failed workflow execution */ export async function retryWorkflowExecution( tenantId: string, executionId: string ): Promise { return apiClient.post( `/tenants/${tenantId}/orchestrator/executions/${executionId}/retry`, {} ); } // ============================================================================ // ORCHESTRATOR STATUS & HEALTH // ============================================================================ export interface OrchestratorStatus { is_leader: boolean; scheduler_running: boolean; next_scheduled_run?: string; last_execution?: { id: string; target_date: string; status: string; completed_at: string; }; total_executions_today: number; total_successful_executions: number; total_failed_executions: number; } /** * Get orchestrator service status */ export async function getOrchestratorStatus( tenantId: string ): Promise { return apiClient.get( `/tenants/${tenantId}/orchestrator/status` ); } // ============================================================================ // ORCHESTRATOR CONFIGURATION // ============================================================================ export interface OrchestratorConfig { enabled: boolean; schedule_cron: string; // Cron expression for daily run default_planning_horizon_days: number; auto_create_purchase_orders: boolean; auto_approve_purchase_orders: boolean; safety_stock_percentage: number; notify_on_completion: boolean; notify_on_failure: boolean; skip_on_error: boolean; } /** * Get orchestrator configuration for tenant */ export async function getOrchestratorConfig( tenantId: string ): Promise { return apiClient.get( `/tenants/${tenantId}/orchestrator/config` ); } /** * Update orchestrator configuration */ export async function updateOrchestratorConfig( tenantId: string, config: Partial ): Promise { return apiClient.put( `/tenants/${tenantId}/orchestrator/config`, config ); } // ============================================================================ // HELPER FUNCTIONS // ============================================================================ /** * Format workflow duration for display */ export function formatWorkflowDuration(durationMs: number): string { if (durationMs < 1000) { return `${durationMs}ms`; } else if (durationMs < 60000) { return `${(durationMs / 1000).toFixed(1)}s`; } else { const minutes = Math.floor(durationMs / 60000); const seconds = Math.floor((durationMs % 60000) / 1000); return `${minutes}m ${seconds}s`; } } /** * Get workflow step status icon */ export function getWorkflowStepStatusIcon(status: WorkflowStepResult['status']): string { switch (status) { case 'success': return '✅'; case 'failed': return '❌'; case 'skipped': return '⏭️'; default: return '❓'; } } /** * Get workflow overall status color */ export function getWorkflowStatusColor(status: WorkflowExecutionSummary['status']): string { switch (status) { case 'completed': return 'green'; case 'running': return 'blue'; case 'failed': return 'red'; case 'cancelled': return 'gray'; default: return 'gray'; } }