# API de Asignacion Inteligente **Modulo:** MAI-005 (Despacho) **Version:** 1.0.0 **Fecha:** 2026-01-27 --- ## Descripcion General API REST para el Centro de Despacho Inteligente. Permite sugerir, asignar y reasignar unidades a viajes usando el algoritmo de scoring ponderado. --- ## Endpoints ### POST /api/dispatch/suggest Sugiere las mejores unidades disponibles para un viaje basandose en el algoritmo de scoring. **Request:** ```typescript interface SuggestRequest { tripId: string; // ID del viaje a asignar maxResults?: number; // Maximo de sugerencias (default: 5) maxDistanceKm?: number; // Radio maximo de busqueda (default: 50) requiredSkills?: string[]; // Skills requeridos (ej: ['HAZMAT', 'refrigerado']) preferredUnitTypes?: string[]; // Tipos de unidad preferidos excludeUnitIds?: string[]; // Unidades a excluir } ``` **Response:** ```typescript interface SuggestResponse { tripId: string; origin: { latitude: number; longitude: number; address: string; }; suggestions: AssignmentSuggestion[]; generatedAt: string; // ISO 8601 timestamp algorithm: string; // 'weighted_score_v1' } interface AssignmentSuggestion { rank: number; // 1-based ranking unitId: string; unitCode: string; // Ej: 'U-005' unitType: string; // Ej: 'TRACTOCAMION' operatorId: string; operatorName: string; totalScore: number; // 0-100 scoreBreakdown: { distance: { value: number; // Score 0-100 weight: number; // Peso aplicado (ej: 0.40) weighted: number; // value * weight distanceKm: number; // Distancia real en km }; capacity: { value: number; weight: number; weighted: number; unitCapacity: number; // Capacidad de la unidad requiredCapacity: number; // Capacidad requerida }; availability: { value: number; weight: number; weighted: number; status: string; // Estado actual shiftActive: boolean; // Turno activo }; skills: { value: number; weight: number; weighted: number; matched: string[]; // Skills que coinciden missing: string[]; // Skills faltantes extra: string[]; // Skills adicionales }; }; currentLocation: { latitude: number; longitude: number; lastUpdate: string; }; estimatedArrival: string; // ISO 8601 timestamp estimatedTravelMinutes: number; } ``` **Ejemplo de Request:** ```json { "tripId": "550e8400-e29b-41d4-a716-446655440000", "maxResults": 3, "maxDistanceKm": 30, "requiredSkills": ["HAZMAT"] } ``` **Ejemplo de Response:** ```json { "tripId": "550e8400-e29b-41d4-a716-446655440000", "origin": { "latitude": 19.4326, "longitude": -99.1332, "address": "Av. Insurgentes Sur 1234, CDMX" }, "suggestions": [ { "rank": 1, "unitId": "unit-001", "unitCode": "U-005", "unitType": "TRACTOCAMION", "operatorId": "op-001", "operatorName": "Juan Perez Garcia", "totalScore": 87.5, "scoreBreakdown": { "distance": { "value": 95, "weight": 0.40, "weighted": 38, "distanceKm": 7.2 }, "capacity": { "value": 100, "weight": 0.25, "weighted": 25, "unitCapacity": 25, "requiredCapacity": 20 }, "availability": { "value": 100, "weight": 0.20, "weighted": 20, "status": "AVAILABLE", "shiftActive": true }, "skills": { "value": 90, "weight": 0.15, "weighted": 13.5, "matched": ["HAZMAT"], "missing": [], "extra": ["refrigerado"] } }, "currentLocation": { "latitude": 19.4126, "longitude": -99.1532, "lastUpdate": "2026-01-27T10:30:00Z" }, "estimatedArrival": "2026-01-27T10:45:00Z", "estimatedTravelMinutes": 15 } ], "generatedAt": "2026-01-27T10:30:15Z", "algorithm": "weighted_score_v1" } ``` --- ### POST /api/dispatch/assign Asigna una unidad a un viaje. **Request:** ```typescript interface AssignRequest { tripId: string; // ID del viaje unitId: string; // ID de la unidad a asignar operatorId: string; // ID del operador notes?: string; // Notas de asignacion overrideValidation?: boolean; // Forzar asignacion (requiere permiso especial) } ``` **Response:** ```typescript interface AssignResponse { success: boolean; assignmentId: string; // ID del registro de asignacion tripId: string; unitId: string; operatorId: string; assignedAt: string; // ISO 8601 timestamp assignedBy: string; // Usuario que asigno scoreAtAssignment: number | null; // Score si vino de sugerencia warnings?: string[]; // Advertencias (ej: turno por terminar) } ``` **Ejemplo de Request:** ```json { "tripId": "550e8400-e29b-41d4-a716-446655440000", "unitId": "unit-001", "operatorId": "op-001", "notes": "Asignacion prioritaria por cliente VIP" } ``` **Ejemplo de Response:** ```json { "success": true, "assignmentId": "asgn-001", "tripId": "550e8400-e29b-41d4-a716-446655440000", "unitId": "unit-001", "operatorId": "op-001", "assignedAt": "2026-01-27T10:35:00Z", "assignedBy": "admin@transportes.com", "scoreAtAssignment": 87.5, "warnings": ["Turno del operador termina en 2 horas"] } ``` --- ### POST /api/dispatch/reassign Reasigna un viaje a una unidad diferente, registrando el motivo. **Request:** ```typescript interface ReassignRequest { tripId: string; // ID del viaje newUnitId: string; // Nueva unidad newOperatorId: string; // Nuevo operador reason: ReassignReason; // Motivo de reasignacion reasonDetail?: string; // Detalle adicional } type ReassignReason = | 'UNIT_BREAKDOWN' // Falla mecanica | 'OPERATOR_UNAVAILABLE' // Operador no disponible | 'CUSTOMER_REQUEST' // Solicitud del cliente | 'OPTIMIZATION' // Optimizacion de rutas | 'SKILL_MISMATCH' // Skills no coinciden | 'CAPACITY_ISSUE' // Problema de capacidad | 'OTHER'; // Otro motivo ``` **Response:** ```typescript interface ReassignResponse { success: boolean; reassignmentId: string; tripId: string; previousUnit: { unitId: string; unitCode: string; operatorId: string; operatorName: string; }; newUnit: { unitId: string; unitCode: string; operatorId: string; operatorName: string; }; reason: ReassignReason; reasonDetail: string | null; reassignedAt: string; reassignedBy: string; } ``` **Ejemplo de Request:** ```json { "tripId": "550e8400-e29b-41d4-a716-446655440000", "newUnitId": "unit-002", "newOperatorId": "op-002", "reason": "UNIT_BREAKDOWN", "reasonDetail": "Falla en sistema de frenos detectada en checklist" } ``` --- ### GET /api/dispatch/units/available Obtiene todas las unidades disponibles para asignacion. **Query Parameters:** | Parametro | Tipo | Descripcion | |-----------|------|-------------| | latitude | number | Latitud del punto de referencia | | longitude | number | Longitud del punto de referencia | | maxDistanceKm | number | Radio maximo (default: 100) | | unitType | string | Filtrar por tipo de unidad | | skills | string | Skills requeridos (separados por coma) | | minCapacity | number | Capacidad minima | | page | number | Pagina (default: 1) | | limit | number | Registros por pagina (default: 20) | **Response:** ```typescript interface AvailableUnitsResponse { units: AvailableUnit[]; pagination: { page: number; limit: number; total: number; totalPages: number; }; referencePoint: { latitude: number; longitude: number; } | null; } interface AvailableUnit { unitId: string; unitCode: string; unitType: string; plateNumber: string; capacity: number; capacityUnit: string; // 'TON', 'M3', etc. status: string; currentLocation: { latitude: number; longitude: number; lastUpdate: string; address: string | null; }; distanceKm: number | null; // Distancia al punto de referencia operator: { id: string; name: string; shiftStart: string | null; shiftEnd: string | null; skills: string[]; }; features: string[]; // Caracteristicas (GPS, refrigeracion, etc.) } ``` **Ejemplo de Request:** ``` GET /api/dispatch/units/available?latitude=19.4326&longitude=-99.1332&maxDistanceKm=30&skills=HAZMAT ``` --- ### GET /api/dispatch/logs Obtiene el historial de acciones de despacho para auditoria. **Query Parameters:** | Parametro | Tipo | Descripcion | |-----------|------|-------------| | tripId | string | Filtrar por viaje | | unitId | string | Filtrar por unidad | | operatorId | string | Filtrar por operador | | action | string | Filtrar por accion (SUGGESTED, ASSIGNED, REASSIGNED, CANCELLED) | | performedBy | string | Filtrar por usuario | | fromDate | string | Fecha inicio (ISO 8601) | | toDate | string | Fecha fin (ISO 8601) | | page | number | Pagina (default: 1) | | limit | number | Registros por pagina (default: 50) | **Response:** ```typescript interface DispatchLogsResponse { logs: DispatchLogEntry[]; pagination: { page: number; limit: number; total: number; totalPages: number; }; } interface DispatchLogEntry { id: string; tripId: string; tripCode: string; // Codigo legible del viaje action: 'SUGGESTED' | 'ASSIGNED' | 'REASSIGNED' | 'CANCELLED'; unitId: string; unitCode: string; operatorId: string; operatorName: string; performedBy: string; // Usuario que realizo la accion reason: string | null; // Motivo (para REASSIGNED/CANCELLED) previousUnitId: string | null; // Unidad anterior (para REASSIGNED) previousUnitCode: string | null; scoreAtAssignment: number | null; // Score si aplica timestamp: string; // ISO 8601 metadata: Record; // Datos adicionales } ``` **Ejemplo de Request:** ``` GET /api/dispatch/logs?tripId=550e8400-e29b-41d4-a716-446655440000&fromDate=2026-01-01T00:00:00Z ``` --- ## DTOs Comunes ### TripSummary ```typescript interface TripSummary { id: string; code: string; customer: { id: string; name: string; }; origin: { latitude: number; longitude: number; address: string; scheduledTime: string; }; destination: { latitude: number; longitude: number; address: string; scheduledTime: string; }; requirements: { capacity: number; capacityUnit: string; unitType: string; skills: string[]; }; priority: 'LOW' | 'NORMAL' | 'HIGH' | 'URGENT'; status: string; } ``` ### UnitSummary ```typescript interface UnitSummary { id: string; code: string; type: string; plateNumber: string; capacity: number; capacityUnit: string; status: string; operator: { id: string; name: string; } | null; location: { latitude: number; longitude: number; lastUpdate: string; } | null; } ``` --- ## Codigos de Error | Codigo | HTTP Status | Descripcion | |--------|-------------|-------------| | DISPATCH_001 | 404 | Viaje no encontrado | | DISPATCH_002 | 404 | Unidad no encontrada | | DISPATCH_003 | 404 | Operador no encontrado | | DISPATCH_004 | 400 | Viaje ya tiene unidad asignada | | DISPATCH_005 | 400 | Unidad no disponible | | DISPATCH_006 | 400 | Operador no disponible | | DISPATCH_007 | 400 | Capacidad insuficiente | | DISPATCH_008 | 400 | Skills requeridos no cumplidos | | DISPATCH_009 | 400 | Unidad fuera de rango maximo | | DISPATCH_010 | 400 | Viaje no esta en estado asignable | | DISPATCH_011 | 400 | Motivo de reasignacion requerido | | DISPATCH_012 | 403 | Sin permisos para forzar asignacion | | DISPATCH_013 | 400 | Coordenadas invalidas | | DISPATCH_014 | 400 | Rango de fechas invalido | | DISPATCH_015 | 500 | Error calculando distancia | **Formato de Error:** ```typescript interface ErrorResponse { error: { code: string; // Ej: 'DISPATCH_005' message: string; // Mensaje legible details?: Record; // Detalles adicionales timestamp: string; }; } ``` **Ejemplo de Error:** ```json { "error": { "code": "DISPATCH_005", "message": "La unidad U-005 no esta disponible para asignacion", "details": { "unitId": "unit-001", "currentStatus": "EN_ROUTE", "currentTripId": "trip-999" }, "timestamp": "2026-01-27T10:40:00Z" } } ``` --- ## Autenticacion Todos los endpoints requieren autenticacion via JWT Bearer token: ``` Authorization: Bearer ``` ### Permisos Requeridos | Endpoint | Permiso | |----------|---------| | POST /dispatch/suggest | dispatch:read | | POST /dispatch/assign | dispatch:write | | POST /dispatch/reassign | dispatch:write | | GET /dispatch/units/available | dispatch:read | | GET /dispatch/logs | dispatch:audit | --- ## Rate Limiting | Endpoint | Limite | |----------|--------| | POST /dispatch/suggest | 30 req/min | | POST /dispatch/assign | 60 req/min | | POST /dispatch/reassign | 30 req/min | | GET /dispatch/units/available | 60 req/min | | GET /dispatch/logs | 30 req/min | --- *API Asignacion Inteligente - MAI-005 - ERP Transportistas - Sistema SIMCO v4.0.0*