erp-transportistas-v2/docs/02-definicion-modulos/MAI-005-despacho/API-ASIGNACION.md
Adrian Flores Cortes 6ed7f9e2ec [BACKUP] Pre-restructure workspace backup 2026-01-29
- Updated docs and inventory files
- Added new architecture docs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 17:35:54 -06:00

14 KiB

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:

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:

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:

{
  "tripId": "550e8400-e29b-41d4-a716-446655440000",
  "maxResults": 3,
  "maxDistanceKm": 30,
  "requiredSkills": ["HAZMAT"]
}

Ejemplo de Response:

{
  "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:

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:

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:

{
  "tripId": "550e8400-e29b-41d4-a716-446655440000",
  "unitId": "unit-001",
  "operatorId": "op-001",
  "notes": "Asignacion prioritaria por cliente VIP"
}

Ejemplo de Response:

{
  "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:

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:

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:

{
  "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:

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:

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<string, any>;     // Datos adicionales
}

Ejemplo de Request:

GET /api/dispatch/logs?tripId=550e8400-e29b-41d4-a716-446655440000&fromDate=2026-01-01T00:00:00Z

DTOs Comunes

TripSummary

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

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:

interface ErrorResponse {
  error: {
    code: string;                    // Ej: 'DISPATCH_005'
    message: string;                 // Mensaje legible
    details?: Record<string, any>;   // Detalles adicionales
    timestamp: string;
  };
}

Ejemplo de Error:

{
  "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 <token>

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