{
  "openapi": "3.1.0",
  "info": {
    "title": "Cargoffer API",
    "version": "2.167.161241",
    "contact": {
      "name": "Soporte Cargoffer",
      "email": "support@cargoffer.com",
      "url": "https://cargoffer.com"
    },
    "termsOfService": "https://cargoffer.com/terms-and-conditions",
    "description": "API de transporte y logística de Cargoffer. Subastas, empresas, camioneros, flotas, eCMR y pagos. Tutoriales en /docs/tutorials/."
  },
  "servers": [
    {
      "url": "https://api.pro.cargoffer.com",
      "description": "Producción"
    }
  ],
  "tags": [
    {
      "name": "Address"
    },
    {
      "name": "Auctions"
    },
    {
      "name": "Auth"
    },
    {
      "name": "Bids"
    },
    {
      "name": "Contracts"
    },
    {
      "name": "Countries"
    },
    {
      "name": "Deliveries"
    },
    {
      "name": "Files"
    },
    {
      "name": "Hscode"
    },
    {
      "name": "Invoices"
    },
    {
      "name": "Map-points"
    },
    {
      "name": "Parkings"
    },
    {
      "name": "Payment"
    },
    {
      "name": "payment-methods"
    },
    {
      "name": "payment-processing"
    },
    {
      "name": "payment-settings"
    },
    {
      "name": "payment-setup"
    },
    {
      "name": "payment-terms"
    },
    {
      "name": "Settings"
    },
    {
      "name": "Stations"
    },
    {
      "name": "stripe-connect"
    },
    {
      "name": "stripe-onboarding"
    },
    {
      "name": "Truckers-categories"
    },
    {
      "name": "Truckers-cia"
    },
    {
      "name": "Truckers-dgt"
    },
    {
      "name": "Truckers-drivers"
    },
    {
      "name": "Truckers-fee"
    },
    {
      "name": "Truckers-minimal"
    },
    {
      "name": "Truckers-tickets"
    },
    {
      "name": "Truckers-users"
    },
    {
      "name": "Vehicles"
    }
  ],
  "paths": {
    "/api/address/": {
      "get": {
        "tags": [
          "address"
        ],
        "summary": "Get address list",
        "description": "Obtiene el listado completo de direcciones asociadas a una compañía. \n\n**Funcionalidad:**\n- Devuelve todas las direcciones registradas para la compañía autenticada\n- Soporta paginación y ordenamiento\n- Marca automáticamente la dirección principal (isDefault: true)\n\n**Casos de uso:**\n- Mostrar el listado de direcciones en interfaces de usuario\n- Seleccionar una dirección para operaciones posteriores\n- Verificar direcciones existentes antes de crear nuevas\n\n**Ejemplo de uso:**\n```bash\ncurl -X GET 'https://api.demo.cargoffer.com/api/address' \\\n  -H 'X-API-KEY: your_api_key'\n```\n",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Número de página para paginación (opcional, por defecto 1)",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Límite de resultados por página (opcional, 1-100, por defecto 10)",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/200AddressList",
            "description": "Lista de direcciones con formato paginado",
            "examples": {
              "application/json": {
                "docs": [
                  {
                    "id": "507f1f77bcf86cd799439011",
                    "name": "Oficina Central",
                    "street": "Calle Mayor 123",
                    "city": "Madrid",
                    "postalCode": "28013",
                    "country": "España",
                    "isDefault": true
                  }
                ],
                "total": 1,
                "limit": 10,
                "page": 1,
                "pages": 1
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "examples": {
              "application/json": {
                "error": "Parámetros de paginación inválidos"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "examples": {
              "application/json": {
                "error": "API Key inválida o faltante"
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "address"
        ],
        "summary": "Create new address",
        "description": "Registra una nueva dirección asociada a la compañía autenticada.\n\n**Funcionalidad:**\n- Crea una nueva dirección con los datos proporcionados\n- Valida el formato del teléfono (si se proporciona)\n- Asocia automáticamente la dirección a la compañía\n- Si se proporcionan coordenadas (lat/lng), completa automáticamente los datos de dirección\n\n**Campos requeridos:**\n- name: Nombre descriptivo de la dirección\n- street: Calle y número\n- city: Ciudad\n- postalCode: Código postal\n- country: País\n\n**Campos opcionales:**\n- phone: Teléfono (debe tener formato válido)\n- lat/lng: Coordenadas geográficas para autocompletar dirección\n- company_name: Nombre de la compañía para la dirección\n\n**Ejemplo de request:**\n```json\n{\n  \"name\": \"Almacén Principal\",\n  \"street\": \"Avenida Industrial 456\",\n  \"city\": \"Barcelona\",\n  \"postalCode\": \"08001\",\n  \"country\": \"España\",\n  \"phone\": \"+34931234567\",\n  \"lat\": 41.3851,\n  \"lng\": 2.1734,\n  \"company_name\": \"Logística Barcelona S.L.\"\n}\n```\n",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Address"
              },
              "examples": {
                "basic": {
                  "summary": "Basic Address",
                  "value": {
                    "name": "Oficina Central",
                    "street": "Calle Mayor 123",
                    "city": "Madrid",
                    "postalCode": "28013",
                    "country": "España"
                  }
                },
                "complete": {
                  "summary": "Address with coordinates",
                  "value": {
                    "name": "Almacén Norte",
                    "street": "Calle del Comercio 45",
                    "city": "Bilbao",
                    "postalCode": "48001",
                    "country": "España",
                    "phone": "+34941234567",
                    "lat": 43.263,
                    "lng": -2.935
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/200Address",
            "description": "Dirección creada exitosamente",
            "examples": {
              "application/json": {
                "id": "507f1f77bcf86cd799439011",
                "name": "Almacén Principal",
                "street": "Avenida Industrial 456",
                "city": "Barcelona",
                "postalCode": "08001",
                "country": "España",
                "isDefault": false,
                "createdAt": "2025-08-12T10:25:00+00:00"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "examples": {
              "application/json": {
                "error": "Formato de teléfono inválido"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "examples": {
              "application/json": {
                "error": "No autorizado - API Key requerida"
              }
            }
          }
        }
      }
    },
    "/api/address/{id}": {
      "put": {
        "tags": [
          "address"
        ],
        "summary": "Edit existing address",
        "description": "Actualiza los datos de una dirección existente asociada a la compañía.\n\n**Funcionalidad:**\n- Actualiza los campos proporcionados de la dirección\n- Valida que la dirección pertenezca a la compañía autenticada\n- Si se proporcionan nuevas coordenadas (lat/lng), actualiza los datos de dirección\n- Valida el formato del teléfono si se modifica\n\n**Campos editables:**\n- name: Nombre descriptivo\n- street: Calle y número\n- city: Ciudad\n- postalCode: Código postal\n- country: País\n- phone: Teléfono (formato válido)\n- lat/lng: Coordenadas para actualizar dirección\n- company_name: Nombre de compañía asociada\n\n**Restricciones:**\n- Solo se pueden editar direcciones pertenecientes a la compañía autenticada\n- El ID de dirección debe ser válido y existente\n\n**Ejemplo de request:**\n```json\n{\n  \"name\": \"Almacén Principal (Nueva ubicación)\",\n  \"street\": \"Avenida Industrial 789\",\n  \"city\": \"Barcelona\",\n  \"postalCode\": \"08002\",\n  \"country\": \"España\",\n  \"phone\": \"+34931234567\",\n  \"lat\": 41.3951,\n  \"lng\": 2.1834,\n  \"company_name\": \"Logística Barcelona S.L.\"\n}\n```\n",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "readOnly": true
            },
            "description": "ID de la dirección generado por el sistema",
            "example": "507f1f77bcf86cd799439011"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Address"
              },
              "examples": {
                "partial": {
                  "summary": "Partial update",
                  "value": {
                    "name": "Oficina Central (Renovada)",
                    "phone": "+34987654321"
                  }
                },
                "full": {
                  "summary": "Full Update",
                  "value": {
                    "name": "Nueva Sede Corporativa",
                    "street": "Paseo de la Castellana 200",
                    "city": "Madrid",
                    "postalCode": "28046",
                    "country": "España",
                    "lat": 40.4578,
                    "lng": -3.6921
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/200Address",
            "description": "Dirección actualizada exitosamente",
            "examples": {
              "application/json": {
                "id": "507f1f77bcf86cd799439011",
                "name": "Almacén Principal (Nueva ubicación)",
                "street": "Avenida Industrial 789",
                "city": "Barcelona",
                "postalCode": "08002",
                "country": "España",
                "isDefault": false,
                "updatedAt": "2025-08-12T10:26:00+00:00"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "examples": {
              "application/json": {
                "error": "Dirección no encontrada o no pertenece a la compañía"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "examples": {
              "application/json": {
                "error": "No autorizado - API Key requerida"
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "address"
        ],
        "summary": "Delete address",
        "description": "Elimina una dirección existente asociada a la compañía.\n\n**Funcionalidad:**\n- Elimina permanentemente la dirección especificada\n- Remueve la referencia de la dirección de la lista de direcciones de la compañía\n- Valida que la dirección pertenezca a la compañía autenticada\n\n**Restricciones:**\n- Solo se pueden eliminar direcciones pertenecientes a la compañía autenticada\n- El ID de dirección debe ser válido y existente\n- No se puede eliminar una dirección que esté siendo usada en operaciones activas\n\n**Ejemplo de uso:**\n```bash\ncurl -X DELETE 'https://api.demo.cargoffer.com/api/address/507f1f77bcf86cd799439011' \\\n  -H 'X-API-KEY: your_api_key'\n```\n",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "readOnly": true
            },
            "description": "ID de la dirección generado por el sistema",
            "example": "507f1f77bcf86cd799439011"
          }
        ],
        "responses": {
          "200": {
            "description": "Dirección eliminada exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "ID de la dirección eliminada",
                      "example": "507f1f77bcf86cd799439011"
                    }
                  }
                },
                "examples": {
                  "application/json": {
                    "id": "507f1f77bcf86cd799439011"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "examples": {
              "application/json": {
                "error": "Dirección no encontrada o no pertenece a la compañía"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "examples": {
              "application/json": {
                "error": "No autorizado - API Key requerida"
              }
            }
          }
        }
      }
    },
    "/api/auction/active": {
      "get": {
        "tags": [
          "auction"
        ],
        "summary": "Get active auctions",
        "description": "Obtiene la lista de subastas activas disponibles para pujar. \nLas subastas activas son aquellas que:\n- Tienen estado 'published'\n- La fecha actual está entre date_start y date_end\n- No han sido cerradas o canceladas\n\nParámetros opcionales de query:\n- page: Número de página (default 1)\n- limit: Items por página (default valor de ITEMS_PAGE en .env)\n- search: Texto para buscar en service_code, etl_cargo_method, pallets_type o description\n- status: Filtro por estado (draft, published, closed, cancelled)\n- minDate/maxDate: Rango de fechas para date_start o etl_date\n\nEjemplo de uso:\n```\nGET /api/auction/active?page=1&limit=10&search=urgente\n```\n\nLa respuesta incluye paginación y los campos básicos de cada subasta.\n",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/200AuctionList",
            "description": "Lista paginada de subastas activas. Cada item incluye:\n- service_code: Identificador único\n- etl_date/etd_date: Fechas de carga/descarga  \n- status: Estado actual\n- cargo_height/cargo_weight: Dimensiones\n- bid_current: Puja actual (si existe)\n"
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error en los parámetros de búsqueda o validación.\nEjemplos:\n- Formato de fecha inválido\n- Valores de paginación incorrectos\n"
          }
        }
      }
    },
    "/api/auction/{serviceCode}": {
      "get": {
        "tags": [
          "auction"
        ],
        "summary": "Get auction details",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Obtiene todos los detalles de una subasta específica identificada por su serviceCode.\n\nRequisitos:\n- El usuario debe pertenecer a la compañía propietaria de la subasta\n- La subasta debe existir y no estar eliminada\n\nCampos incluidos en la respuesta:\n- Todos los campos básicos (serviceCode, status, dates)\n- Información completa de carga/descarga (etl/etd)\n- Lista de pujas realizadas (si existen)\n- Información del ganador (si la subasta está cerrada)\n- Documentos asociados (contratos, firmas)\n\nEjemplo de uso:\n```\nGET /api/auction/ABC123\n```\n\nLa respuesta incluye todos los campos del modelo Auction más relaciones pobladas.\n",
        "parameters": [
          {
            "name": "serviceCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Código único de identificación de la subasta.\nFormato: 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos aleatorios (ej: VIGMURnT4FN).\nSe genera automáticamente al crear la subasta.\n"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/200Auction",
            "description": "Detalles completos de la subasta incluyendo:\n- Información básica (serviceCode, status, dates)\n- Direcciones de carga/descarga (etl/etd_address)\n- Lista de pujas (bids) con historial\n- Ganador actual (bidWinner) si aplica\n- Documentos asociados (contracts, signatures)\n"
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error al procesar la solicitud. Posibles causas:\n- serviceCode no válido o no encontrado\n- Formato de parámetros incorrecto\n"
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "description": "No autorizado. El usuario no tiene permisos para ver esta subasta.\nRequiere autenticación válida y pertenecer a la compañía propietaria.\n"
          }
        }
      },
      "put": {
        "tags": [
          "auction"
        ],
        "summary": "Edit existing auction",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Permite editar los detalles de una subasta existente en estado 'draft' o 'published'.\n\nRequisitos:\n- La subasta debe existir y pertenecer a la compañía del usuario\n- Solo se pueden editar subastas en estado 'draft' o 'published'\n- El usuario debe tener permisos de edición\n\nValidaciones realizadas:\n1. Verifica que la subasta exista y sea editable\n2. Valida horarios y fechas (etl_date < etd_date)\n3. Comprueba dimensiones de carga (peso > 0, altura > 0)\n4. Verifica direcciones de carga/descarga\n\nCampos editables:\n- etl_date: Fecha/hora de carga\n- etd_date: Fecha/hora de descarga\n- cargo_weight: Peso de la carga (kg)\n- cargo_height: Altura de la carga (m)\n- etl_address/etd_address: IDs de direcciones\n- description: Descripción adicional\n- pallets_type: Tipo de pallets\n- pallets_num: Número de pallets\n\nEjemplo de body request:\n```json\n{\n  \"etl_date\": \"2025-08-15T08:00:00Z\",\n  \"etd_date\": \"2025-08-15T18:00:00Z\",\n  \"cargo_weight\": 1500,\n  \"cargo_height\": 2.8,\n  \"etl_address\": \"60a1b2c3d4e5f6a1b2c3d4e5\",\n  \"etd_address\": \"60a1b2c3d4e5f6a1b2c3d4e6\",\n  \"description\": \"Carga urgente de productos perecederos\"\n}\n```\n\nLa respuesta incluye la subasta actualizada con los nuevos valores.\n",
        "parameters": [
          {
            "name": "serviceCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Código único de identificación de la subasta a editar.\nFormato: 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos aleatorios (ej: VIGMURnT4FN).\nSe genera automáticamente al crear la subasta.\n"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Auction"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/200Auction"
          },
          "400": {
            "$ref": "#/components/responses/400Error"
          },
          "401": {
            "$ref": "#/components/responses/401Error"
          }
        }
      },
      "delete": {
        "tags": [
          "auction"
        ],
        "summary": "Delete auction",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Elimina permanentemente una subasta existente.\n\nRequisitos:\n- La subasta debe estar en estado 'draft' o 'planned'\n- El usuario debe ser el creador o administrador de la compañía\n- No debe tener pujas asociadas ni estar publicada\n\nValidaciones realizadas:\n1. Verifica que la subasta exista y sea eliminable\n2. Comprueba que no tenga pujas asociadas\n3. Confirma que el usuario tiene permisos\n4. Elimina el registro de la base de datos\n\nEjemplo de uso:\n```\nDELETE /api/auction/ABC123\n```\n\nLa respuesta confirma la eliminación exitosa con el serviceCode.\n",
        "parameters": [
          {
            "name": "serviceCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Código único de identificación de la subasta a eliminar.\nFormato: 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos aleatorios (ej: VIGMURnT4FN).\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Subasta eliminada exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "serviceCode": {
                      "type": "string",
                      "description": "Código de la subasta eliminada (ej VIGMURnT4FN)"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error al eliminar la subasta. Posibles causas:\n- La subasta no está en estado borrador\n- Ya tiene pujas asociadas\n- No se encontró la subasta\n"
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "description": "No autorizado. Requiere:\n- Autenticación válida\n- Ser el creador o administrador de la compañía\n"
          }
        }
      }
    },
    "/api/auction/publish/{serviceCode}": {
      "put": {
        "tags": [
          "auction"
        ],
        "summary": "Publish auction",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Cambia el estado de una subasta de 'draft' a 'published', haciéndola visible para pujas.\n\nRequisitos:\n- La subasta debe estar en estado 'draft'\n- Debe tener todas las fechas válidas (date_start, date_end, etl_date, etd_date)\n- El usuario debe ser propietario o administrador de la compañía\n\nValidaciones realizadas:\n1. Verifica que la subasta exista y sea editable\n2. Comprueba que las fechas sean coherentes:\n  - date_start debe ser posterior a la fecha actual, o la misma fecha pero una hora posterior\n  - date_end debe ser posterior a date_start, con un minimo de 4 horas\n  - etl_date debe estar entre date_start y date_end\n3. Valida dimensiones de carga (peso y altura)\n\nEjemplo de uso:\n```\nPUT /api/auction/publish/ABC123\n```\n\nLa respuesta incluye la subasta actualizada con estado 'published'.\n",
        "parameters": [
          {
            "name": "serviceCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Código único de la subasta a publicar. Formato 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos aleatorios (ej VIGMURnT4FN)."
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/200Auction",
            "description": "Subasta publicada exitosamente. Campos relevantes:\n- status: Cambiado a 'published'\n- date_start: Actualizado si era nulo\n- updatedAt: Fecha de última modificación\n"
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error en la publicación. Posibles causas:\n- La subasta no está en estado draft\n- Fechas inválidas o incoherentes\n- Dimensiones de carga no válidas\n- Falta información requerida\n"
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "description": "No autorizado. El usuario no tiene permisos para publicar esta subasta.\n"
          }
        }
      }
    },
    "/api/auction/acceptCurrent/{serviceCode}": {
      "post": {
        "tags": [
          "auction"
        ],
        "summary": "Accept current bid",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Acepta la oferta actual para una subasta, cerrando la subasta y asignando el transporte al ganador.\n\nRequisitos:\n- La subasta debe estar en estado 'published'\n- Debe tener al menos una puja válida\n- El usuario debe ser propietario o administrador de la compañía\n- La subasta no debe estar ya cerrada o cancelada\n\nAcciones realizadas:\n1. Cierra la subasta (status = 'closed')\n2. Asigna el ganador (bidWinner)\n3. Genera un contrato preliminar\n4. Crea un registro de entrega (delivery)\n\nEjemplo de uso:\n```\nPOST /api/auction/acceptCurrent/ABC123\n```\n\nLa respuesta incluye la subasta actualizada con estado 'closed' y el ganador asignado.\n",
        "parameters": [
          {
            "name": "serviceCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Código único de la subasta a cerrar. Formato 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos aleatorios (ej VIGMURnT4FN)."
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/200Auction",
            "description": "Subasta cerrada exitosamente. Campos relevantes:\n- status: Cambiado a 'closed'\n- bidWinner: Información del transportista ganador\n- award_price: Precio acordado\n- delivery: ID del registro de entrega creado\n"
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error al aceptar la puja. Posibles causas:\n- La subasta no tiene pujas válidas\n- La subasta ya está cerrada/cancelada\n- El usuario no tiene permisos\n"
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "description": "No autorizado. El usuario no tiene permisos para cerrar esta subasta.\nRequiere ser propietario o administrador de la compañía.\n"
          }
        }
      }
    },
    "/api/auction/private": {
      "post": {
        "tags": [
          "auction"
        ],
        "summary": "Create Private Auction",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Crea una subasta privada visible solo para transportistas específicos de la compañía.\n\nRequisitos:\n- La compañía debe ser multitennant\n- Debe especificar vehicle (ID del vehículo) y trucker (ID del transportista)\n- Ambos (vehículo y transportista) deben pertenecer a la compañía\n\nDiferencias con subastas públicas:\n- No visible en el marketplace general\n- No requiere periodo de pujas\n- Crea directamente un delivery asociado\n- No genera historial de pujas\n\nEjemplo de body request:\n```json\n{\n  \"vehicle\": \"60a1b2c3d4e5f6a1b2c3d4e5\",\n  \"trucker\": \"60a1b2c3d4e5f6a1b2c3d4e6\",\n  \"etl_date\": \"2025-08-15T08:00:00Z\",\n  \"etd_date\": \"2025-08-15T18:00:00Z\",\n  \"cargo_weight\": 1000,\n  \"cargo_height\": 2.5\n}\n```\n\nLa respuesta incluye el delivery creado directamente.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Auction"
              },
              "example": {
                "vehicle": "60a1b2c3d4e5f6a1b2c3d4e5",
                "trucker": "60a1b2c3d4e5f6a1b2c3d4e6",
                "etl_date": "2025-08-15T08:00:00+00:00",
                "etd_date": "2025-08-15T18:00:00+00:00",
                "cargo_weight": 1000,
                "cargo_height": 2.5
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/200Auction",
            "description": "Delivery creado exitosamente. Campos relevantes:\n- status: 'assigned' (asignado directamente)\n- trucker_user: ID del transportista asignado\n- trucker_vehicle: ID del vehículo asignado\n- auction: null (no asociado a subasta pública)\n"
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error al crear la subasta privada. Posibles causas:\n- La compañía no es multitennant\n- Vehicle o trucker no existen o no pertenecen a la compañía\n- Fechas o dimensiones inválidas\n"
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "description": "No autorizado. Requiere:\n- Autenticación válida\n- Compañía multitennant\n- Permisos de administrador\n"
          }
        }
      }
    },
    "/api/auction/": {
      "get": {
        "tags": [
          "auction"
        ],
        "summary": "Get auction list",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Obtiene el listado completo de subastas de la compañía, incluyendo borradores, activas y cerradas.\n\nCaracterísticas:\n- Listado paginado\n- Ordenado por fecha de actualización (más recientes primero)\n- Filtrado por estado, fechas y búsqueda textual\n- Incluye relaciones básicas (direcciones, pujas, ganador)\n\nParámetros opcionales de query:\n- page: Número de página (default 1)\n- limit: Items por página (default valor de ITEMS_PAGE en .env)\n- status: Filtro por estado (draft, published, closed, cancelled)\n- search: Texto para buscar en service_code o descripción\n- minDate/maxDate: Rango de fechas para creación o modificación\n\nEjemplo de uso:\n```\nGET /api/auction?page=1&limit=20&status=published&search=urgente\n```\n\nLa respuesta incluye metadatos de paginación y los campos básicos de cada subasta.\n",
        "responses": {
          "200": {
            "$ref": "#/components/responses/200AuctionList",
            "description": "Lista paginada de subastas. Cada item incluye:\n- service_code: Identificador único (ej: VIGMURnT4FN)\n- status: Estado actual\n- etl_date/etd_date: Fechas de carga/descarga\n- cargo_weight/cargo_height: Dimensiones\n- bid_current: Puja actual (si aplica)\n- bidWinner: Ganador (si aplica)\n- createdAt/updatedAt: Fechas creación/actualización\n\nMetadatos de paginación:\n- total: Total de items\n- pages: Total de páginas\n- page: Página actual\n- limit: Items por página\n"
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error en los parámetros de búsqueda. Posibles causas:\n- Valores de paginación inválidos\n- Formato de fechas incorrecto\n- Estado no válido\n"
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "description": "No autorizado. Requiere autenticación válida y pertenecer a la compañía.\n"
          }
        }
      },
      "post": {
        "tags": [
          "auction"
        ],
        "summary": "Create auction",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Crea una nueva subasta pública de transporte.\n\nRequisitos:\n- Usuario autenticado con compañía válida\n- Fechas coherentes (etl_date < etd_date)\n- Dimensiones de carga válidas (peso > 0, altura > 0)\n- Direcciones de carga/descarga válidas\n\nValidaciones realizadas:\n1. Genera un service_code único basado en direcciones\n2. Valida horarios y fechas\n3. Verifica dimensiones de carga\n4. Crea registro en base de datos\n\nCampos obligatorios:\n- etl_date: Fecha/hora de carga\n- etd_date: Fecha/hora de descarga  \n- cargo_weight: Peso de la carga (kg)\n- cargo_height: Altura de la carga (m)\n- etl_address/etd_address: IDs de direcciones\n\nEjemplo de body request:\n```json\n{\n  \"etl_date\": \"2025-08-15T08:00:00Z\",\n  \"etd_date\": \"2025-08-15T18:00:00Z\",\n  \"cargo_weight\": 1000,\n  \"cargo_height\": 2.5,\n  \"etl_address\": \"60a1b2c3d4e5f6a1b2c3d4e5\",\n  \"etd_address\": \"60a1b2c3d4e5f6a1b2c3d4e6\"\n}\n```\n\nLa respuesta incluye la subasta creada con su service_code único.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Auction"
              },
              "example": {
                "etl_date": "2025-08-15T08:00:00+00:00",
                "etd_date": "2025-08-15T18:00:00+00:00",
                "cargo_weight": 1000,
                "cargo_height": 2.5,
                "etl_address": "60a1b2c3d4e5f6a1b2c3d4e5",
                "etd_address": "60a1b2c3d4e5f6a1b2c3d4e6",
                "description": "Carga urgente de Madrid a Barcelona"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/200Auction",
            "description": "Subasta creada exitosamente. Campos relevantes:\n- service_code: Identificador único generado (ej: VIGMURnT4FN)\n- status: 'draft' (requiere publicación explícita)\n- etl_address/etd_address: Direcciones pobladas\n- createdAt: Fecha de creación\n"
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error al crear la subasta. Posibles causas:\n- Fechas inválidas o incoherentes\n- Dimensiones de carga no válidas\n- Direcciones no encontradas\n- Falta información requerida\n"
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "description": "No autorizado. Requiere:\n- Autenticación válida\n- Compañía activa\n- Permisos para crear subastas\n"
          }
        }
      }
    },
    "/api/auction/sign": {
      "get": {
        "tags": [
          "auction"
        ],
        "summary": "Get pending signature auctions",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Obtiene el listado de subastas cerradas que requieren firma por parte de la compañía o transportista.\n\nCriterios de inclusión:\n- Subastas en estado 'closed'\n- Con ganador asignado (bidWinner)\n- Donde la compañía o el transportista no han firmado aún\n- Ordenadas por fecha de cierre (más recientes primero)\n\nParámetros opcionales de query:\n- page: Número de página (default 1)\n- limit: Items por página (default valor de ITEMS_PAGE en .env)\n- signedBy: Filtro por firmante pendiente ('company' o 'trucker')\n\nEjemplo de uso:\n```\nGET /api/auction/sign?page=1&limit=10&signedBy=company\n```\n\nLa respuesta incluye paginación y los campos básicos de cada subasta.\n",
        "responses": {
          "200": {
            "$ref": "#/components/responses/200AuctionList",
            "description": "Lista paginada de subastas pendientes de firma. Cada item incluye:\n- service_code: Identificador único (ej: VIGMURnT4FN)\n- status: 'closed'\n- bidWinner: Información del transportista ganador\n- signed_by_company/signed_by_trucker: Estados de firma\n- createdAt/closedAt: Fechas relevantes\n"
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error en los parámetros de búsqueda. Posibles causas:\n- Valores de paginación inválidos\n- Filtro signedBy no válido\n"
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "description": "No autorizado. Requiere autenticación válida y pertenecer a la compañía.\n"
          }
        }
      }
    },
    "/api/auction/sign/{serviceCode}": {
      "get": {
        "tags": [
          "auction"
        ],
        "summary": "Get signature status",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Obtiene el estado actual de las firmas (compañía y transportista) para una subasta específica.\n\nRequisitos:\n- La subasta debe estar en estado 'closed'\n- Debe tener un ganador asignado (bidWinner)\n- El usuario debe pertenecer a la compañía propietaria o ser el transportista ganador\n\nCampos devueltos:\n- signed: Estado general de firma (ambas partes)\n- signedAt: Fecha de última firma\n- signed_by_company: Firma de la compañía (true/false)\n- signed_by_trucker: Firma del transportista (true/false)\n- company_signature: Firma digital de la compañía (si existe)\n- trucker_signature: Firma digital del transportista (si existe)\n\nEjemplo de uso:\n```\nGET /api/auction/sign/ABC123\n```\n\nLa respuesta incluye el estado detallado de las firmas.\n",
        "parameters": [
          {
            "name": "serviceCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Código único de la subasta a consultar. Formato 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos aleatorios (ej VIGMURnT4FN)."
          }
        ],
        "responses": {
          "200": {
            "description": "Estado de firma detallado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "signed": {
                      "type": "boolean",
                      "description": "Indica si ambas partes han firmado"
                    },
                    "signedAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Fecha de la última firma realizada",
                      "example": "2019-09-20T16:03:18.575000+00:00"
                    },
                    "signed_by_company": {
                      "type": "boolean",
                      "description": "Indica si la compañía ha firmado"
                    },
                    "signed_by_trucker": {
                      "type": "boolean",
                      "description": "Indica si el transportista ha firmado"
                    },
                    "company_signature": {
                      "type": "string",
                      "description": "Firma digital de la compañía (base64)"
                    },
                    "trucker_signature": {
                      "type": "string",
                      "description": "Firma digital del transportista (base64)"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error al consultar el estado. Posibles causas:\n- Subasta no encontrada o no cerrada\n- Sin ganador asignado\n- Parámetros inválidos\n"
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "description": "No autorizado. El usuario no tiene permisos para ver este estado.\nRequiere ser:\n- Miembro de la compañía propietaria\n- Transportista ganador de la subasta\n"
          }
        }
      },
      "post": {
        "tags": [
          "auction"
        ],
        "summary": "Add to favorites",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Añade una subasta a la lista de favoritos del usuario.\n\nRequisitos:\n- La subasta debe existir\n- El usuario debe pertenecer a la compañía propietaria\n- No debe estar ya en favoritos\n\nAcciones realizadas:\n1. Crea un registro en la colección AuctionFavourite\n2. Asocia la subasta al usuario y compañía\n3. Permite acceso rápido a subastas frecuentes\n\nCampos obligatorios:\n- serviceCode: Identificador de la subasta. Formato: 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos aleatorios (ej: VIGMURnT4FN).\n- fav_name: Nombre descriptivo para el favorito\n\nEjemplo de body request:\n```json\n{\n  \"serviceCode\": \"ABC123\",\n  \"fav_name\": \"Madrid-Barcelona semanal\"\n}\n```\n\nLa respuesta confirma la creación del favorito.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Auction"
              },
              "example": {
                "serviceCode": "ABC123",
                "fav_name": "Madrid-Barcelona semanal"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Favorito añadido exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "serviceCode": {
                      "type": "string",
                      "description": "Código de la subasta añadida a favoritos (ej VIGMURnT4FN)"
                    },
                    "favorite": {
                      "type": "boolean",
                      "description": "Confirmación de éxito (true)"
                    },
                    "id": {
                      "type": "string",
                      "description": "ID del registro favorito creado"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error al añadir a favoritos. Posibles causas:\n- La subasta ya está en favoritos\n- Subasta no encontrada\n- Falta información requerida\n"
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "description": "No autorizado. Requiere:\n- Autenticación válida\n- Pertenecer a la compañía propietaria\n"
          }
        }
      },
      "delete": {
        "tags": [
          "auction"
        ],
        "summary": "Revoke signature",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Revoca la firma digital de una subasta por parte de la compañía.\n\nRequisitos:\n- La subasta debe estar en estado 'closed'\n- Debe tener una firma previa de la compañía\n- El usuario debe ser administrador o propietario\n- No debe haber delivery asociado creado\n\nAcciones realizadas:\n1. Elimina la firma digital de la compañía\n2. Cambia el estado signed_by_company a false\n3. Elimina cualquier contrato generado\n4. Elimina el delivery si no ha sido aceptado\n\nEjemplo de uso:\n```\nDELETE /api/auction/sign/ABC123\n```\n\nLa respuesta confirma la revocación exitosa.\n",
        "parameters": [
          {
            "name": "serviceCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Código único de la subasta a revocar firma. Formato 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos aleatorios (ej VIGMURnT4FN)."
          }
        ],
        "responses": {
          "200": {
            "description": "Firma revocada exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "serviceCode": {
                      "type": "string",
                      "description": "Código de la subasta afectada (ej VIGMURnT4FN)"
                    },
                    "revoked": {
                      "type": "boolean",
                      "description": "Confirmación de revocación exitosa"
                    },
                    "deliveryDeleted": {
                      "type": "boolean",
                      "description": "Indica si se eliminó el delivery asociado"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error al revocar firma. Posibles causas:\n- La subasta no tiene firma previa\n- Ya existe un delivery aceptado\n- El usuario no tiene permisos\n- La subasta no está en estado válido\n"
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "description": "No autorizado. Requiere permisos de administrador/owner.\n"
          }
        }
      }
    },
    "/api/auction/contract": {
      "post": {
        "tags": [
          "auction"
        ],
        "summary": "Generate contract for auction",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Crea un contrato preliminar para una subasta cerrada con ganador asignado.\n\nRequisitos:\n- La subasta debe estar en estado 'closed'\n- Debe tener un ganador asignado (bidWinner)\n- El usuario debe ser administrador de la compañía\n- No debe existir contrato previo generado\n\nProceso interno:\n1. Valida que la subasta cumpla los requisitos\n2. Genera documento PDF con plantilla 'file_contract'\n3. Almacena el PDF en S3/almacenamiento local\n4. Actualiza la subasta con referencia al contrato\n5. Notifica al transportista ganador\n\nCampos obligatorios en body:\n- serviceCode: Identificador de la subasta. Formato: 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos aleatorios (ej: VIGMURnT4FN).\n- award_price: Precio acordado (debe ser >= bid_current)\n- special_conditions: Términos especiales (opcional)\n- payment_terms: Condiciones de pago (opcional)\n\nEjemplo de body request:\n```json\n{\n  \"serviceCode\": \"ABC123\",\n  \"award_price\": 1200,\n  \"special_conditions\": \"Carga frágil - manejar con cuidado\",\n  \"payment_terms\": \"50% anticipo, 50% al entregar\"\n}\n```\n\nLa respuesta incluye la subasta actualizada con los datos del contrato.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Auction"
              },
              "example": {
                "serviceCode": "ABC123",
                "award_price": 1200,
                "special_conditions": "Carga frágil - manejar con cuidado",
                "payment_terms": "50% anticipo, 50% al entregar",
                "insurance_details": "Seguro total incluido"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/200Auction",
            "description": "Contrato generado exitosamente. Campos relevantes:\n- contract_url: URL del PDF generado\n- contract_version: 1 (versión inicial)\n- contract_createdAt: Fecha de generación\n- award_price: Precio acordado\n- special_conditions: Términos especiales\n"
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error al generar contrato. Posibles causas:\n- Subasta no encontrada o no cerrada\n- Sin ganador asignado\n- Precio menor al de la puja ganadora\n- Ya existe contrato generado\n- Campos obligatorios faltantes\n"
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "description": "No autorizado. Requiere:\n- API Key válida\n- Permisos de administrador\n- Pertenecer a la compañía propietaria\n"
          },
          "409": {
            "description": "Conflicto. Posibles causas:\n- Subasta ya tiene contrato generado\n- Operación concurrente en curso\n"
          }
        }
      }
    },
    "/api/auction/contract/{serviceCode}": {
      "get": {
        "tags": [
          "auction"
        ],
        "summary": "Download PDF contract",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Genera y descarga el contrato PDF de una subasta cerrada y firmada.\n\nRequisitos:\n- Subasta en estado 'closed' con serviceCode válido\n- Firmas completas (compañía y transportista)\n- Usuario pertenece a compañía propietaria o es transportista ganador\n\nProceso interno:\n1. Valida estado de subasta y firmas\n2. Genera PDF con plantilla 'file_contract'\n3. Incluye datos de:\n   - Subasta (origen, destino, fechas)\n   - Compañía contratante\n   - Transportista ganador\n   - Términos y condiciones\n4. Configura cabeceras para descarga directa\n\nCampos incluidos en el contrato:\n- Datos de la subasta (serviceCode - ej: VIGMURnT4FN, fechas, direcciones)\n- Información de las partes (nombres, contactos)\n- Precio acordado y condiciones de pago\n- Términos legales específicos por país\n\nEjemplo de uso:\n```\nGET /api/auction/contract/ABC123\n```\n\nLa respuesta es un stream del PDF con cabeceras para descarga automática.\n",
        "parameters": [
          {
            "name": "serviceCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Código único de la subasta asociada al contrato.\nFormato: 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos aleatorios (ej: VIGMURnT4FN).\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Contrato PDF generado exitosamente",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            },
            "headers": {
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                },
                "description": "Cabecera para descarga con nombre de archivo.\nFormato: \"attachment; filename=CONTRACT_{serviceCode}.pdf\"\nEjemplo: \"attachment; filename=CONTRACT_ABC123.pdf\"\n"
              },
              "Content-Type": {
                "schema": {
                  "type": "string"
                },
                "description": "application/pdf"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error al generar contrato. Posibles causas:\n- Subasta no encontrada o no cerrada (status != 'closed')\n- Faltan firmas (signed_by_company o signed_by_trucker = false)\n- No hay ganador asignado (bidWinner = null)\n- Error al renderizar plantilla PDF\n"
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "description": "No autorizado. Requiere:\n- API Key válida\n- Ser miembro de compañía propietaria O transportista ganador\n- Permisos para ver contratos\n"
          },
          "404": {
            "description": "Contrato no encontrado. Posibles causas:\n- Subasta no existe\n- Contrato no generado previamente\n- Permisos insuficientes\n"
          }
        }
      },
      "put": {
        "tags": [
          "auction"
        ],
        "summary": "Update existing contract",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Actualiza los términos de un contrato existente para una subasta cerrada.\n\nRequisitos:\n- La subasta debe estar en estado 'closed'\n- Debe tener contrato generado previamente\n- El usuario debe ser administrador de la compañía\n- No debe haber delivery en estado 'in_progress' o 'completed'\n\nCampos actualizables:\n- award_price: Precio acordado (requiere confirmación)\n- special_conditions: Términos especiales\n- payment_terms: Condiciones de pago\n- insurance_details: Detalles de seguro\n- etl_date/etd_date: Fechas (con validación de coherencia)\n\nValidaciones realizadas:\n1. Verifica que la subasta exista y esté cerrada\n2. Comprueba permisos del usuario\n3. Valida coherencia de fechas y precios\n4. Actualiza contrato y regenera PDF\n\nEjemplo de body request:\n```json\n{\n  \"award_price\": 1200,\n  \"special_conditions\": \"Carga frágil - manejar con cuidado\",\n  \"payment_terms\": \"50% anticipo, 50% al entregar\",\n  \"insurance_details\": \"Seguro total incluido\"\n}\n```\n\nLa respuesta incluye el contrato actualizado.\n",
        "parameters": [
          {
            "name": "serviceCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Código único de la subasta asociada al contrato.\nFormato: 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos aleatorios (ej: VIGMURnT4FN).\n"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Auction"
              },
              "example": {
                "award_price": 1200,
                "special_conditions": "Carga frágil - manejar con cuidado",
                "payment_terms": "50% anticipo, 50% al entregar",
                "insurance_details": "Seguro total incluido"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/200Auction",
            "description": "Contrato actualizado exitosamente. Campos relevantes:\n- award_price: Nuevo precio acordado\n- special_conditions: Términos actualizados\n- contract_updatedAt: Fecha de última modificación\n- contract_version: Incrementa en 1\n"
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error al actualizar contrato. Posibles causas:\n- Subasta no encontrada o no cerrada\n- Delivery en estado no editable\n- Campos inválidos o faltantes\n- Precio menor al mínimo aceptable\n"
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "description": "No autorizado. Requiere:\n- API Key válida\n- Permisos de administrador\n- Pertenecer a la compañía propietaria\n"
          },
          "409": {
            "description": "Conflicto. Posibles causas:\n- Contrato ya firmado por ambas partes\n- Delivery en progreso o completado\n- Versión desactualizada (concurrent update)\n"
          }
        }
      },
      "delete": {
        "tags": [
          "auction"
        ],
        "summary": "Delete contract",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Elimina permanentemente el contrato asociado a una subasta.\n\nRequisitos:\n- La subasta debe estar en estado 'closed'\n- No debe haber delivery asociado en estado 'in_progress' o 'completed'\n- El usuario debe ser administrador de la compañía\n- No debe haber firmas digitales registradas\n\nAcciones realizadas:\n1. Elimina el documento PDF del contrato del almacenamiento\n2. Limpia las referencias al contrato en la subasta\n3. Registra la acción en el historial de auditoría\n4. Notifica al transportista si ya había sido informado\n\nValidaciones realizadas:\n1. Verifica que la subasta exista y esté cerrada\n2. Comprueba que no haya entregas en progreso\n3. Valida permisos del usuario\n4. Confirma que no hay firmas digitales\n\nEjemplo de uso:\n```\nDELETE /api/auction/contract/ABC123\n```\n\nLa respuesta confirma la eliminación exitosa.\n",
        "parameters": [
          {
            "name": "serviceCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Código único de la subasta asociada al contrato.\nFormato: 3 letras del origen, 3 letras del destino y 5 caracteres alfanumericos aleatorios (ej: VIGMURnT4FN).\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Contrato eliminado exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "serviceCode": {
                      "type": "string",
                      "description": "Código de la subasta afectada (ej VIGMURnT4FN)"
                    },
                    "deleted": {
                      "type": "boolean",
                      "description": "Confirmación de eliminación exitosa"
                    },
                    "deletedAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Fecha/hora de eliminación"
                    },
                    "deliveryStatus": {
                      "type": "string",
                      "description": "Estado del delivery asociado al momento de eliminar.\nValores posibles: 'none', 'pending', 'cancelled'\n"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error al eliminar contrato. Posibles causas:\n- Subasta no encontrada o no cerrada\n- Delivery en estado no permitido\n- Firmas digitales existentes\n- Permisos insuficientes\n"
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "description": "No autorizado. Requiere:\n- API Key válida\n- Permisos de administrador\n- Pertenecer a la compañía propietaria\n"
          },
          "409": {
            "description": "Conflicto. Posibles causas:\n- Contrato ya firmado por alguna parte\n- Delivery en progreso o completado\n- Operación concurrente en curso\n"
          }
        }
      }
    },
    "/api/auction/favorites": {
      "get": {
        "tags": [
          "auction"
        ],
        "summary": "Get list of favorite auctions",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Obtiene el listado completo de subastas marcadas como favoritas por la compañía.\n\nCaracterísticas:\n- Listado paginado\n- Ordenado por fecha de creación (más recientes primero)\n- Incluye las subastas asociadas con sus datos completos\n- Filtrado opcional por texto y fechas\n\nParámetros opcionales de query:\n- page: Número de página (default 1)\n- limit: Items por página (default valor de ITEMS_PAGE en .env)\n- search: Texto para buscar en fav_name o description\n- minDate/maxDate: Rango de fechas de creación del favorito\n\nEjemplo de uso:\n```\nGET /api/auction/favorites?page=1&limit=10&search=urgente\n```\n\nLa respuesta incluye metadatos de paginación y los campos básicos de cada subasta favorita.\n",
        "responses": {
          "200": {
            "$ref": "#/components/responses/200AuctionList",
            "description": "Lista paginada de subastas favoritas. Cada item incluye:\n- Todos los campos estándar de Auction\n- Campos adicionales del favorito:\n  - fav_name: Nombre descriptivo asignado\n  - createdAt: Fecha de creación del favorito\n  - updatedAt: Fecha de última actualización\n\nMetadatos de paginación:\n- total: Total de favoritos\n- pages: Total de páginas\n- page: Página actual\n- limit: Items por página\n"
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error en los parámetros de búsqueda. Posibles causas:\n- Valores de paginación inválidos\n- Formato de fechas incorrecto\n"
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "description": "No autorizado. Requiere:\n- Autenticación válida\n- Pertenecer a la compañía propietaria\n"
          }
        }
      },
      "post": {
        "tags": [
          "auction"
        ],
        "summary": "Add auction to favorites",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Añade una subasta a la lista de favoritos de la compañía para acceso rápido y seguimiento.\n\nRequisitos:\n- La subasta debe existir y ser visible para la compañía\n- No debe estar ya en favoritos\n- El usuario debe tener permisos de edición\n\nAcciones realizadas:\n1. Crea un registro en la colección AuctionFavourite\n2. Asocia la subasta a la compañía\n3. Permite filtrado y acceso rápido a subastas frecuentes\n\nCampos obligatorios:\n- serviceCode: Identificador único de la subasta. Formato: 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos aleatorios (ej: VIGMURnT4FN).\n- fav_name: Nombre descriptivo para identificar el favorito\n\nEjemplo de body request:\n```json\n{\n  \"serviceCode\": \"ABC123\",\n  \"fav_name\": \"Ruta Madrid-Barcelona semanal\",\n  \"description\": \"Carga recurrente los lunes\"\n}\n```\n\nLa respuesta incluye el ID del favorito creado.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Auction"
              },
              "example": {
                "serviceCode": "ABC123",
                "fav_name": "Ruta Madrid-Barcelona semanal",
                "description": "Carga recurrente los lunes"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Favorito creado exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "ID del favorito creado"
                    },
                    "serviceCode": {
                      "type": "string",
                      "description": "Código de la subasta añadida (ej VIGMURnT4FN)"
                    },
                    "favorite": {
                      "type": "boolean",
                      "description": "Confirmación de éxito (true)"
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Fecha de creación",
                      "example": "2019-09-20T16:03:18.575000+00:00"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error al crear el favorito. Posibles causas:\n- La subasta ya está en favoritos\n- Subasta no encontrada\n- Falta información requerida\n"
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "description": "No autorizado. Requiere:\n- Autenticación válida\n- Pertenecer a la compañía propietaria\n- Permisos de edición\n"
          }
        }
      }
    },
    "/api/auction/favorites/{id}": {
      "get": {
        "tags": [
          "auction"
        ],
        "summary": "Get Favorite Auction Details",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Obtiene los detalles completos de una subasta marcada como favorita, incluyendo:\n- Información completa de la subasta original\n- Metadatos del favorito (nombre personalizado, fechas)\n- Relaciones pobladas (direcciones, pujas, ganador si aplica)\n\nRequisitos:\n- El ID del favorito debe ser válido y existir\n- El usuario debe pertenecer a la compañía propietaria\n- La subasta asociada no debe haber sido eliminada\n\nValidaciones realizadas:\n1. Verifica que el favorito exista y pertenezca a la compañía\n2. Comprueba que la subasta asociada siga existiendo\n3. Pobla todas las relaciones relevantes\n\nEjemplo de uso:\n```\nGET /api/auction/favorites/60a1b2c3d4e5f6a1b2c3d4e5\n```\n\nLa respuesta incluye tanto los datos de la subasta como los metadatos específicos del favorito.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID único del registro favorito en la base de datos.\nFormato: ObjectId de MongoDB (24 caracteres hexadecimales)\nEjemplo: 60a1b2c3d4e5f6a1b2c3d4e5\n"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/200Auction",
            "description": "Respuesta detallada que incluye:\n- Todos los campos estándar de Auction:\n  * serviceCode (ej: VIGMURnT4FN), status, dates, dimensions\n  * etl/etd_address (direcciones completas)\n  * bids (si existen)\n  * bidWinner (si aplica)\n- Campos específicos del favorito:\n  * fav_name: Nombre descriptivo asignado\n  * description: Descripción opcional\n  * createdAt: Fecha de creación del favorito\n  * updatedAt: Fecha de última actualización\n"
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error al obtener el favorito. Posibles causas:\n- ID con formato inválido\n- Favorito no encontrado\n- Subasta asociada eliminada\n- Parámetros inválidos\n"
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "description": "No autorizado. Requiere:\n- Autenticación válida con API Key\n- Pertenecer a la compañía propietaria del favorito\n- Permisos de lectura\n"
          }
        }
      },
      "put": {
        "tags": [
          "auction"
        ],
        "summary": "Update favorite auction metadata",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Actualiza los metadatos de una subasta marcada como favorita, permitiendo:\n- Modificar el nombre descriptivo (fav_name)\n- Actualizar la descripción\n- Añadir/editar etiquetas de clasificación\n- Cambiar otros campos personalizados\n\nRequisitos:\n- El ID del favorito debe ser válido y existir\n- El usuario debe ser propietario o administrador\n- La subasta asociada debe seguir existiendo\n- Solo se pueden actualizar campos específicos (no la subasta en sí)\n\nValidaciones realizadas:\n1. Verifica que el favorito exista y pertenezca a la compañía\n2. Comprueba que la subasta asociada siga existiendo\n3. Valida los campos actualizables\n\nCampos actualizables:\n- fav_name: Nuevo nombre descriptivo (obligatorio)\n- description: Descripción ampliada (opcional)\n- tags: Array de etiquetas para clasificación\n- is_recurrent: Marca si es una ruta recurrente\n\nEjemplo de body request completo:\n```json\n{\n  \"fav_name\": \"Madrid-Barcelona mensual\",\n  \"description\": \"Carga recurrente cada primer lunes de mes\",\n  \"tags\": [\"recurrente\", \"urgente\"],\n  \"is_recurrent\": true\n}\n```\n\nLa respuesta incluye el favorito actualizado con los nuevos valores.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID único del registro favorito en MongoDB.\nFormato: 24 caracteres hexadecimales (ObjectId)\nEjemplo: 60a1b2c3d4e5f6a1b2c3d4e5\n"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Auction"
              },
              "example": {
                "fav_name": "Madrid-Barcelona mensual",
                "description": "Carga recurrente cada primer lunes de mes",
                "tags": [
                  "recurrente",
                  "urgente"
                ],
                "is_recurrent": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Favorito actualizado exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "ID del favorito actualizado"
                    },
                    "updated": {
                      "type": "boolean",
                      "description": "Confirmación de éxito (true)"
                    },
                    "fav_name": {
                      "type": "string",
                      "description": "Nuevo nombre descriptivo asignado"
                    },
                    "description": {
                      "type": "string",
                      "description": "Nueva descripción (si se actualizó)"
                    },
                    "tags": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Lista de etiquetas actualizadas"
                    },
                    "is_recurrent": {
                      "type": "boolean",
                      "description": "Estado de recurrencia actualizado"
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Fecha/hora de última actualización",
                      "example": "2019-09-20T16:03:18.575000+00:00"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error al actualizar. Posibles causas:\n- ID con formato inválido\n- Campos no permitidos en la actualización\n- Faltan campos obligatorios (fav_name)\n- Subasta asociada eliminada\n"
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "description": "No autorizado. Requiere:\n- Autenticación válida con API Key\n- Pertenecer a la compañía propietaria\n- Permisos de edición\n"
          }
        }
      },
      "delete": {
        "tags": [
          "auction"
        ],
        "summary": "Remove auction from favorites",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Elimina permanentemente una subasta de la lista de favoritos de la compañía.\n\nRequisitos:\n- El ID del favorito debe existir en la base de datos\n- El usuario debe pertenecer a la compañía propietaria\n- No debe haber operaciones pendientes asociadas al favorito\n\nValidaciones realizadas:\n1. Verifica que el favorito exista y pertenezca a la compañía\n2. Comprueba que no haya operaciones pendientes asociadas\n3. Elimina el registro de forma permanente\n\nDiferencias con PUT:\n- Eliminación permanente vs actualización\n- No requiere body request\n- No se puede deshacer\n\nEjemplo de uso:\n```\nDELETE /api/auction/favorites/60a1b2c3d4e5f6a1b2c3d4e5\n```\n\nLa respuesta incluye confirmación y metadatos de la eliminación.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID único del registro favorito en MongoDB.\nFormato: 24 caracteres hexadecimales (ObjectId)\nEjemplo: 60a1b2c3d4e5f6a1b2c3d4e5\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Favorito eliminado exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "ID del favorito eliminado"
                    },
                    "removed": {
                      "type": "boolean",
                      "description": "Confirmación de éxito (true)"
                    },
                    "serviceCode": {
                      "type": "string",
                      "description": "Código de la subasta asociada (ej VIGMURnT4FN)"
                    },
                    "deletedAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Fecha/hora de eliminación",
                      "example": "2019-09-20T16:03:18.575000+00:00"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "description": "Error al eliminar. Posibles causas:\n- ID con formato inválido\n- Favorito no encontrado\n- Operaciones pendientes asociadas\n- El usuario no es propietario\n"
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "description": "No autorizado. Requiere:\n- Autenticación válida con API Key\n- Pertenecer a la compañía propietaria\n- Permisos de administrador\n"
          }
        }
      }
    },
    "/api/delivery/active": {
      "get": {
        "tags": [
          "delivery"
        ],
        "summary": "Get active deliveries",
        "description": "Obtiene la lista de entregas actualmente activas (con fecha de entrega futura).\n\nLas entregas activas son aquellas que:\n- Tienen estado 'pending' o 'in_progress'\n- Su fecha de entrega (etd_date) es posterior a la fecha actual\n- Pertenecen a la compañía del usuario autenticado\n\nParámetros opcionales de filtrado:\n- page: Número de página para paginación (por defecto 1)\n- limit: Número máximo de resultados por página (por defecto ITEMS_PAGE)\n- minDate: Fecha mínima para filtrar por fecha de inicio o carga\n- maxDate: Fecha máxima para filtrar por fecha de fin o descarga\n- search: Texto para buscar en código de servicio, método de carga, tipo de palets o descripción\n- status: Estado de la entrega (pending, in_progress, completed, cancelled)\n\nEjemplo de uso:\n1. Monitorizar entregas en curso\n2. Planificar recursos para próximas entregas\n3. Filtrar entregas por fechas o estados\n",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/pageParam"
          },
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/minDateParam"
          },
          {
            "$ref": "#/components/parameters/maxDateParam"
          },
          {
            "$ref": "#/components/parameters/searchParam"
          },
          {
            "$ref": "#/components/parameters/statusParam"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/200DeliveryList",
            "examples": {
              "application/json": [
                {
                  "serviceCode": "VIGMURnT4FN",
                  "status": "in_progress",
                  "date_eta": "2023-05-15T08:00:00+00:00",
                  "etl_date": "2023-05-10T10:00:00+00:00",
                  "etd_date": "2023-05-20T12:00:00+00:00",
                  "pallets_num": 12,
                  "etl_address": {
                    "city": "Barcelona",
                    "country": "ES"
                  },
                  "etd_address": {
                    "city": "Madrid",
                    "country": "ES"
                  }
                }
              ]
            }
          },
          "400": {
            "$ref": "#/components/responses/400Error"
          }
        }
      }
    },
    "/api/delivery/{serviceCode}": {
      "get": {
        "tags": [
          "delivery"
        ],
        "summary": "Get delivery details",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Obtiene información detallada de una entrega específica identificada por su código de servicio.\n\nLa respuesta incluye:\n- Información básica de la entrega (código, estado, fechas)\n- Direcciones de carga (ETL) y descarga (ETD)\n- Detalles del vehículo asignado\n- Información de subastas relacionadas\n- Mensajes asociados (si los hay)\n- Indicador si permite enviar mensajes (can_message)\n\nRequisitos:\n- El código de servicio debe existir\n- La entrega debe pertenecer a la compañía del usuario autenticado\n\nEjemplo de uso:\n1. Verificar estado actual de una entrega\n2. Obtener información para seguimiento\n3. Consultar detalles para resolver incidencias\n",
        "parameters": [
          {
            "name": "serviceCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Código único de identificación de la entrega. Formato 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos aleatorios (ej VIGMURnT4FN).",
            "example": "VIGMURnT4FN"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/200Delivery",
            "examples": {
              "application/json": {
                "serviceCode": "VIGMURnT4FN",
                "status": "in_progress",
                "createdAt": "2023-05-01T10:00:00+00:00",
                "updatedAt": "2023-05-10T15:30:00+00:00",
                "date_eta": "2023-05-15T08:00:00+00:00",
                "etl_date": "2023-05-10T10:00:00+00:00",
                "etd_date": "2023-05-20T12:00:00+00:00",
                "etl_address": {
                  "street": "Carrer de la Llacuna",
                  "number": "162",
                  "city": "Barcelona",
                  "country": "ES"
                },
                "etd_address": {
                  "street": "Calle de Alcalá",
                  "number": "45",
                  "city": "Madrid",
                  "country": "ES"
                },
                "trucker_vehicle": {
                  "plate": "1234ABC",
                  "type": "Camión rígido"
                },
                "auctions": [
                  {
                    "id": "AUC-001",
                    "status": "completed"
                  }
                ],
                "can_message": true,
                "messages": []
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "examples": {
              "application/json": {
                "error": "SERVICE_CODE_NOT_FOUND"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "examples": {
              "application/json": {
                "error": "NOT_ALLOWED"
              }
            }
          }
        }
      }
    },
    "/api/delivery/cmr/{serviceCode}": {
      "get": {
        "tags": [
          "delivery"
        ],
        "summary": "Download CMR document",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Genera y descarga el documento CMR (Conocimiento de Transporte Internacional de Mercancías por Carretera) en formato PDF.\n\nEl documento incluye:\n- Datos del remitente y destinatario\n- Lugar y fecha de carga/descarga\n- Naturaleza de la mercancía\n- Número de bultos\n- Peso bruto\n- Instrucciones especiales\n\nRequisitos:\n- El código de servicio debe existir\n- La entrega debe pertenecer a la compañía del usuario\n- La entrega debe tener información completa para generar el CMR\n\nEjemplo de uso:\n1. Generar CMR para entregas internacionales\n2. Proporcionar documentación a transportistas\n3. Cumplir con requisitos legales de transporte\n",
        "parameters": [
          {
            "name": "serviceCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Código único de identificación de la entrega. Formato 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos aleatorios (ej VIGMURnT4FN).",
            "example": "VIGMURnT4FN"
          }
        ],
        "responses": {
          "200": {
            "description": "Documento CMR en formato PDF",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            },
            "headers": {
              "Content-Disposition": {
                "description": "Nombre del archivo descargado",
                "schema": {
                  "type": "string",
                  "example": "attachment; filename=CMR_VIGMURnT4FN.pdf"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "examples": {
              "application/json": {
                "error": "DELIVERY_NOT_FOUND"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "examples": {
              "application/json": {
                "error": "NOT_ALLOWED"
              }
            }
          }
        }
      }
    },
    "/api/delivery/download/{serviceCode}": {
      "get": {
        "tags": [
          "delivery"
        ],
        "summary": "Download delivery documents",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Descarga un archivo ZIP con todos los documentos relacionados a una entrega específica.\n\nEl archivo ZIP puede contener:\n- Documento CMR (si aplica)\n- Facturas o albaranes\n- Fotos de la mercancía\n- Documentación aduanera\n- Otros documentos adjuntos\n\nRequisitos:\n- El código de servicio debe existir\n- La entrega debe pertenecer a la compañía del usuario\n- Debe haber documentos disponibles para descargar\n\nEjemplo de uso:\n1. Obtener toda la documentación de una entrega\n2. Compartir documentos con clientes o proveedores\n3. Archivar documentación de entregas completadas\n",
        "parameters": [
          {
            "name": "serviceCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Código único de identificación de la entrega. Formato 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos aleatorios (ej VIGMURnT4FN).",
            "example": "VIGMURnT4FN"
          }
        ],
        "responses": {
          "200": {
            "description": "Archivo ZIP con documentos",
            "content": {
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            },
            "headers": {
              "Content-Disposition": {
                "description": "Nombre del archivo descargado",
                "schema": {
                  "type": "string",
                  "example": "attachment; filename=DOCS_VIGMURnT4FN.zip"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "examples": {
              "application/json": {
                "error": "SERVICE_CODE_NOT_FOUND"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "examples": {
              "application/json": {
                "error": "NOT_ALLOWED"
              }
            }
          }
        }
      }
    },
    "/api/delivery/": {
      "get": {
        "tags": [
          "delivery"
        ],
        "summary": "Get full list of deliveries",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Recupera todas las entregas asociadas a la compañía del usuario, con soporte para paginación y filtrado avanzado.\n\nParámetros de filtrado disponibles:\n- page: Número de página (por defecto 1)\n- limit: Resultados por página (por defecto ITEMS_PAGE)\n- minDate/maxDate: Rango de fechas de creación\n- minETL/maxETL: Rango de fechas de carga (ETL)\n- minETD/maxETD: Rango de fechas de descarga (ETD)\n- status: Estado de la entrega\n- search: Búsqueda en código, descripción o tipo de palets\n- cargo_type: Tipo de carga (pallets u otros)\n\nLa respuesta incluye metadatos de paginación y lista de entregas con:\n- Código de servicio\n- Estado actual\n- Fechas relevantes (creación, carga, descarga)\n- Direcciones de carga/descarga\n- Información básica de la mercancía\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/pageParam"
          },
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/minDateParam"
          },
          {
            "$ref": "#/components/parameters/maxDateParam"
          },
          {
            "$ref": "#/components/parameters/searchParam"
          },
          {
            "$ref": "#/components/parameters/statusParam"
          },
          {
            "name": "minETL",
            "in": "query",
            "description": "Fecha mínima de carga (ETL) (formato YYYY-MM-DD)",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2019-09-20T00:00:00+00:00"
            }
          },
          {
            "name": "maxETL",
            "in": "query",
            "description": "Fecha máxima de carga (ETL) (formato YYYY-MM-DD)",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2019-09-20T00:00:00+00:00"
            }
          },
          {
            "name": "minETD",
            "in": "query",
            "description": "Fecha mínima de descarga (ETD) (formato YYYY-MM-DD)",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "maxETD",
            "in": "query",
            "description": "Fecha máxima de descarga (ETD) (formato YYYY-MM-DD)",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "cargo_type",
            "in": "query",
            "description": "Tipo de carga (pallets u otros)",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "pallets",
                "other"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/200DeliveryList",
            "examples": {
              "application/json": {
                "total": 42,
                "limit": 20,
                "page": 1,
                "pages": 3,
                "docs": [
                  {
                    "serviceCode": "SVC-2023-001",
                    "status": "completed",
                    "createdAt": "2023-05-01T10:00:00+00:00",
                    "etl_date": "2023-05-10T10:00:00+00:00",
                    "etd_date": "2023-05-15T12:00:00+00:00",
                    "pallets_num": 8,
                    "etl_address": {
                      "city": "Barcelona",
                      "country": "ES"
                    },
                    "etd_address": {
                      "city": "Lyon",
                      "country": "FR"
                    }
                  },
                  {
                    "serviceCode": "SVC-2023-002",
                    "status": "in_progress",
                    "createdAt": "2023-05-05T09:30:00+00:00",
                    "etl_date": "2023-05-12T08:00:00+00:00",
                    "etd_date": "2023-05-18T14:00:00+00:00",
                    "pallets_num": 15,
                    "etl_address": {
                      "city": "Madrid",
                      "country": "ES"
                    },
                    "etd_address": {
                      "city": "Paris",
                      "country": "FR"
                    }
                  }
                ]
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "examples": {
              "application/json": {
                "error": "INVALID_DATE_RANGE"
              }
            }
          }
        }
      }
    },
    "/api/delivery/msg/{serviceCode}": {
      "get": {
        "tags": [
          "delivery"
        ],
        "summary": "Get delivery messages",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Recupera todos los mensajes asociados a una entrega específica.\n\nLos mensajes incluyen:\n- Contenido del mensaje\n- Autor (nombre completo del usuario)\n- Fecha y hora de creación\n- Estado de lectura (si aplica)\n\nRequisitos:\n- El código de servicio debe existir\n- La entrega debe pertenecer a la compañía del usuario\n- La entrega debe estar activa (can_message: true)\n\nEjemplo de uso:\n1. Consultar historial de comunicación sobre una entrega\n2. Verificar actualizaciones o instrucciones recientes\n3. Revisar conversaciones anteriores\n",
        "parameters": [
          {
            "name": "serviceCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Código único de identificación de la entrega. Formato 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos aleatorios (ej VIGMURnT4FN).",
            "example": "VIGMURnT4FN"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/200Messages",
            "examples": {
              "application/json": [
                {
                  "authorId": "usr_123",
                  "authorName": "Juan Pérez",
                  "message": "La entrega llegará con 1 hora de retraso",
                  "createdAt": "2023-05-15T14:30:00+00:00"
                },
                {
                  "authorId": "usr_456",
                  "authorName": "María Gómez",
                  "message": "Confirmada recepción de mercancía",
                  "createdAt": "2023-05-15T16:45:00+00:00"
                }
              ]
            }
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "examples": {
              "application/json": {
                "error": "SERVICE_CODE_NOT_FOUND"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "examples": {
              "application/json": {
                "error": "NOT_ALLOWED"
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "delivery"
        ],
        "summary": "Send message about delivery",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Envía un nuevo mensaje relacionado con una entrega específica.\n\nEl mensaje será visible para:\n- Usuarios de la misma compañía\n- Transportistas asignados (si aplica)\n- Personal administrativo con permisos\n\nRequisitos:\n- El código de servicio debe existir\n- La entrega debe pertenecer a la compañía del usuario\n- La entrega debe estar activa (can_message: true)\n- El cuerpo del mensaje no debe estar vacío\n\nEjemplo de uso:\n1. Notificar incidencias en la entrega\n2. Proporcionar instrucciones adicionales\n3. Confirmar recepción o cambios\n",
        "parameters": [
          {
            "name": "serviceCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Código único de identificación de la entrega. Formato 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos aleatorios (ej VIGMURnT4FN).",
            "example": "VIGMURnT4FN"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Contenido del mensaje a enviar",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Message"
              },
              "examples": {
                "basicMessage": {
                  "value": {
                    "text": "La entrega llegará con 30 minutos de retraso debido a tráfico"
                  }
                },
                "detailedMessage": {
                  "value": {
                    "text": "Problema con documentación aduanera. Se requiere nueva factura comercial con los siguientes cambios: [...]"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/200Messages",
            "examples": {
              "application/json": [
                {
                  "authorId": "usr_789",
                  "authorName": "Carlos Ruiz",
                  "message": "Mensaje de prueba",
                  "createdAt": "2023-05-16T09:15:00+00:00"
                }
              ]
            }
          },
          "400": {
            "$ref": "#/components/responses/400Error",
            "examples": {
              "application/json": {
                "error": "MESSAGE_CONTENT_REQUIRED"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/401Error",
            "examples": {
              "application/json": {
                "error": "NOT_ALLOWED"
              }
            }
          }
        }
      }
    },
    "/api/oil/": {
      "get": {
        "tags": [
          "oilStations"
        ],
        "summary": "Get list of service stations",
        "description": "Endpoint para obtener un listado paginado de todas las estaciones de servicio registradas en el sistema.\nPermite filtrar y ordenar los resultados según diferentes criterios.\n\n### Funcionalidad:\n- Devuelve un array de objetos OilStation con información detallada de cada estación\n- Soporta paginación mediante parámetros de query (limit, page)\n- Permite ordenar resultados por diferentes campos (ej: createdAt)\n- Incluye solo campos relevantes en la respuesta para optimizar el payload\n\n### Casos de uso:\n- Mostrar listado de estaciones en panel administrativo\n- Integración con sistemas de geolocalización\n- Sincronización con aplicaciones móviles\n\n### Ejemplo de respuesta:\n```json\n[\n  {\n    \"id\": \"5f8d0d55b54764421b7156da\",\n    \"name\": \"Estación Central\",\n    \"address\": \"Calle Mayor 123\",\n    \"city\": \"Madrid\",\n    \"country\": \"ES\",\n    \"latitude\": 40.4168,\n    \"longitude\": -3.7038,\n    \"fuels\": [\"diesel\", \"gasolina95\"],\n    \"isActive\": true\n  }\n]\n```\n",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Número máximo de resultados por página",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 10,
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Número de página a recuperar",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1,
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Operación exitosa. Devuelve array de estaciones de servicio.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/OilStation"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "oilStations"
        ],
        "summary": "Create new service station",
        "description": "Endpoint para registrar una nueva estación de servicio en el sistema.\n\n### Requisitos:\n- Campos obligatorios: name, address, city, country\n- Coordenadas geográficas (latitude, longitude) deben ser válidas\n- El campo fuels debe ser un array de strings con los tipos de combustible disponibles\n\n### Validaciones realizadas:\n- Comprueba que no exista otra estación con el mismo nombre en la misma ciudad\n- Verifica el formato de las coordenadas geográficas\n- Valida que los campos requeridos estén presentes\n\n### Casos de uso:\n- Registro inicial de estaciones en el sistema\n- Migración de datos desde otros sistemas\n- Creación masiva mediante scripts\n\n### Ejemplo de petición:\n```json\n{\n  \"name\": \"Estación Norte\",\n  \"address\": \"Avenida Principal 456\",\n  \"city\": \"Barcelona\",\n  \"country\": \"ES\",\n  \"latitude\": 41.3851,\n  \"longitude\": 2.1734,\n  \"fuels\": [\"diesel\", \"gasolina95\", \"gasolina98\"],\n  \"isActive\": true\n}\n```\n",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OilStation"
              },
              "example": {
                "name": "Estación Norte",
                "address": "Avenida Principal 456",
                "city": "Barcelona",
                "country": "ES",
                "latitude": 41.3851,
                "longitude": 2.1734,
                "fuels": [
                  "diesel",
                  "gasolina95",
                  "gasolina98"
                ],
                "isActive": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Estación creada exitosamente. Devuelve los datos de la estación creada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OilStation"
                }
              }
            }
          },
          "400": {
            "description": "Error en la validación de datos. Puede deberse a:\n- Faltan campos obligatorios\n- Formato incorrecto en coordenadas\n- Nombre de estación ya existe en la ciudad\n"
          }
        }
      }
    },
    "/api/oil/{id}": {
      "put": {
        "tags": [
          "oilStations"
        ],
        "summary": "Update service station",
        "description": "Endpoint para modificar los datos de una estación de servicio existente.\n\n### Campos modificables:\n- Todos los campos del esquema OilStation pueden ser actualizados\n- El ID no puede ser modificado\n- Campos requeridos deben mantenerse presentes\n\n### Validaciones realizadas:\n- Verifica que la estación exista\n- Comprueba que no se duplique el nombre en la misma ciudad\n- Valida el formato de coordenadas geográficas\n- Asegura que los campos obligatorios permanezcan\n\n### Casos de uso:\n- Actualización de datos por cambios físicos en la estación\n- Corrección de información errónea\n- Actualización de tipos de combustible disponibles\n\n### Ejemplo de petición:\n```json\n{\n  \"name\": \"Estación Norte Renovada\",\n  \"address\": \"Avenida Principal 456\",\n  \"city\": \"Barcelona\",\n  \"country\": \"ES\",\n  \"latitude\": 41.3851,\n  \"longitude\": 2.1734,\n  \"fuels\": [\"diesel\", \"gasolina95\", \"gasolina98\", \"GLP\"],\n  \"isActive\": true\n}\n```\n",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "ID único de la estación a actualizar",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "5f8d0d55b54764421b7156da"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OilStation"
              },
              "example": {
                "name": "Estación Norte Renovada",
                "address": "Avenida Principal 456",
                "city": "Barcelona",
                "country": "ES",
                "latitude": 41.3851,
                "longitude": 2.1734,
                "fuels": [
                  "diesel",
                  "gasolina95",
                  "gasolina98",
                  "GLP"
                ],
                "isActive": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Estación actualizada exitosamente. Devuelve los datos actualizados.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OilStation"
                }
              }
            }
          },
          "400": {
            "description": "Error en la validación de datos. Puede deberse a:\n- Faltan campos obligatorios\n- Formato incorrecto en coordenadas\n- Nombre de estación ya existe en la ciudad\n"
          },
          "404": {
            "description": "Estación no encontrada con el ID proporcionado"
          }
        }
      },
      "delete": {
        "tags": [
          "oilStations"
        ],
        "summary": "Delete service station",
        "description": "Endpoint para eliminar permanentemente una estación de servicio del sistema.\n\n### Proceso de eliminación:\n- Elimina el registro de la estación de la base de datos\n- Elimina cualquier imagen asociada de S3 (si existe)\n- Actualiza las referencias en otros sistemas\n\n### Consideraciones:\n- La operación es irreversible\n- No se puede eliminar una estación con operaciones activas asociadas\n- Se recomienda desactivar (isActive: false) en lugar de eliminar cuando sea posible\n\n### Casos de uso:\n- Eliminación de estaciones duplicadas\n- Cierre definitivo de estaciones\n- Limpieza de datos de prueba\n\n### Ejemplo de uso:\n```\nDELETE /api/oil/5f8d0d55b54764421b7156da\nHeaders:\n  X-API-KEY: your-api-key\n```\n",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "ID único de la estación a eliminar",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "5f8d0d55b54764421b7156da"
          }
        ],
        "responses": {
          "204": {
            "description": "Estación eliminada exitosamente. No devuelve contenido.\nCódigo de estado 204 indica éxito sin contenido de respuesta.\n"
          },
          "404": {
            "description": "Estación no encontrada con el ID proporcionado.\nVerifique que el ID sea correcto y que la estación exista.\n"
          }
        }
      }
    },
    "/api/truckers/": {
      "get": {
        "tags": [
          "truckers"
        ],
        "summary": "Find nearby carriers",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Obtiene una lista de transportistas disponibles dentro de un radio específico desde una ubicación dada.\n\n**Casos de uso:**\n- Encontrar transportistas disponibles para asignar a una carga\n- Mostrar transportistas cercanos en un mapa\n- Filtrar transportistas por proximidad\n\n**Notas:**\n- Requiere autenticación mediante API Key\n- La distancia se calcula en kilómetros\n- El radio máximo permitido es de 100 km\n",
        "parameters": [
          {
            "name": "lat",
            "in": "query",
            "required": true,
            "schema": {
              "type": "number",
              "format": "float",
              "minimum": -90,
              "maximum": 90
            },
            "description": "Latitud geográfica en formato decimal (WGS84).\nEjemplo: 40.4168 (para Madrid)\nRango válido: -90 a 90\nPrecisión recomendada: 6 decimales\n"
          },
          {
            "name": "lng",
            "in": "query",
            "required": true,
            "schema": {
              "type": "number",
              "format": "float",
              "minimum": -180,
              "maximum": 180
            },
            "description": "Longitud geográfica en formato decimal (WGS84).\nEjemplo: -3.7038 (para Madrid)\nRango válido: -180 a 180\nPrecisión recomendada: 6 decimales\n"
          },
          {
            "name": "radius",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "float",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "description": "Radio de búsqueda en kilómetros desde la ubicación dada.\nValor por defecto: 10 km\nMínimo permitido: 1 km\nMáximo permitido: 100 km\nEjemplos:\n  - 5 (busca en un radio de 5 km)\n  - 25 (busca en un radio de 25 km)\n"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/200Truckers"
          },
          "403": {
            "$ref": "#/components/responses/403Error"
          }
        }
      }
    },
    "/api/vehicle/": {
      "get": {
        "tags": [
          "vehicle"
        ],
        "summary": "List company vehicles",
        "description": "Obtiene la lista paginada de vehículos asociados a la compañía del usuario autenticado.\n\n**Funcionalidad:**\n- Devuelve vehículos ordenados por matrícula (plate)\n- Soporta paginación mediante parámetros query (page, limit)\n- Campos retornados: cargo_type, plate, image, itv, vehicle_type, shipping_type, fresh_cargo_temp\n\n**Casos de uso:**\n- Mostrar listado de vehículos en panel de administración\n- Seleccionar vehículo para asignar a un transporte\n\n**Ejemplo de respuesta exitosa:**\n```json\n{\n  \"docs\": [\n    {\n      \"plate\": \"1234ABC\",\n      \"vehicle_type\": \"truck\",\n      \"image\": \"https://s3.amazonaws.com/vehicles/1234ABC.jpg\",\n      \"itv\": \"https://s3.amazonaws.com/documents/ITV_1234ABC.pdf\",\n      \"shipping_type\": \"dry\",\n      \"cargo_type\": [\"construction\", \"industrial\"]\n    }\n  ],\n  \"total\": 1,\n  \"limit\": 10,\n  \"page\": 1,\n  \"pages\": 1\n}\n```\n",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Número de página a recuperar (por defecto 1)",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Cantidad máxima de resultados por página (por defecto 10)",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/200VehicleList"
          },
          "400": {
            "$ref": "#/components/responses/400Error"
          },
          "401": {
            "$ref": "#/components/responses/401Error"
          }
        }
      },
      "post": {
        "tags": [
          "vehicle"
        ],
        "summary": "Create New Vehicle",
        "description": "Registra un nuevo vehículo en el sistema con soporte para subida de documentos (ITV) e imágenes.\n\n**Campos requeridos:**\n- `vehicle_type`: Tipo de vehículo (debe ser uno de los valores permitidos)\n- `plate`: Matrícula del vehículo (formato validado)\n- `shipping_type`: Tipo de transporte (debe ser uno de los valores permitidos)\n- `cargo_type`: Array de tipos de carga permitidos para este vehículo\n\n**Validaciones:**\n- Si shipping_type es \"fresh\", se requiere fresh_cargo_temp\n- Todos los tipos (vehicle_type, shipping_type, cargo_type) son validados contra listas permitidas\n- La matrícula (plate) es validada con expresión regular\n\n**Tipos permitidos:**\n- Vehículos: ${Vehicle.validTypes.join(', ')}\n- Transportes: ${Vehicle.validShippingTypes.join(', ')}\n- Cargas: ${Vehicle.validCargoTypes.join(', ')}\n\n**Ejemplo de request:**\n```json\n{\n  \"vehicle_type\": \"truck\",\n  \"plate\": \"1234ABC\",\n  \"shipping_type\": \"dry\",\n  \"cargo_type\": [\"construction\", \"industrial\"],\n  \"fresh_cargo_temp\": null,\n  \"image\": \"(binary)\",\n  \"itv\": \"(binary)\"\n}\n```\n",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "vehicle_type": {
                    "type": "string",
                    "enum": [
                      "truck",
                      "van",
                      "trailer",
                      "semitrailer"
                    ],
                    "description": "Tipo de vehículo",
                    "example": "truck"
                  },
                  "plate": {
                    "type": "string",
                    "description": "Matrícula del vehículo",
                    "pattern": "^[0-9]{4}[A-Z]{3}$",
                    "example": "1234ABC"
                  },
                  "shipping_type": {
                    "type": "string",
                    "enum": [
                      "dry",
                      "fresh",
                      "frozen"
                    ],
                    "description": "Tipo de transporte",
                    "example": "dry"
                  },
                  "cargo_type": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "construction",
                        "industrial",
                        "agricultural",
                        "perishable"
                      ]
                    },
                    "description": "Tipos de carga permitidos",
                    "example": [
                      "construction",
                      "industrial"
                    ]
                  },
                  "fresh_cargo_temp": {
                    "type": "number",
                    "nullable": true,
                    "description": "Temperatura requerida para carga refrigerada (solo si shipping_type=fresh)",
                    "example": 4
                  },
                  "image": {
                    "type": "string",
                    "format": "binary",
                    "description": "Imagen principal del vehículo"
                  },
                  "itv": {
                    "type": "string",
                    "format": "binary",
                    "description": "Documento de ITV vigente"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/200Vehicle"
          },
          "400": {
            "description": "Posibles errores:\n- INVALID_VEHICLE_TYPE\n- INVALID_SHIPPING_TYPE  \n- INVALID_CARGO_TYPES\n- FRESH_CARGO_TEMP_NOT_FOUND\n- Error en validación de matrícula\n",
            "$ref": "#/components/responses/400Error"
          },
          "401": {
            "$ref": "#/components/responses/401Error"
          }
        }
      }
    },
    "/api/vehicle/{id}": {
      "put": {
        "tags": [
          "vehicle"
        ],
        "summary": "Edit existing vehicle",
        "description": "Actualiza los datos de un vehículo existente. \n\n**Diferencias con creación:**\n- Todos los campos son opcionales (solo se actualizan los enviados)\n- No se requiere enviar todos los campos nuevamente\n- Se mantienen los valores anteriores para campos no incluidos\n\n**Validaciones:**\n- El vehículo debe existir y pertenecer a la compañía\n- Los tipos (vehicle_type, shipping_type, cargo_type) son validados si se envían\n- La matrícula (plate) es validada si se envía\n\n**Ejemplo de request:**\n```json\n{\n  \"vehicle_type\": \"van\",\n  \"shipping_type\": \"fresh\",\n  \"fresh_cargo_temp\": 4,\n  \"image\": \"(binary)\"\n}\n```\n",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID del vehículo a editar",
            "example": "507f1f77bcf86cd799439011"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "vehicle_type": {
                    "type": "string",
                    "enum": [
                      "truck",
                      "van",
                      "trailer",
                      "semitrailer"
                    ],
                    "description": "Nuevo tipo de vehículo (opcional)",
                    "example": "van"
                  },
                  "plate": {
                    "type": "string",
                    "description": "Nueva matrícula (opcional)",
                    "pattern": "^[0-9]{4}[A-Z]{3}$",
                    "example": "4321XYZ"
                  },
                  "shipping_type": {
                    "type": "string",
                    "enum": [
                      "dry",
                      "fresh",
                      "frozen"
                    ],
                    "description": "Nuevo tipo de transporte (opcional)",
                    "example": "fresh"
                  },
                  "cargo_type": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "construction",
                        "industrial",
                        "agricultural",
                        "perishable"
                      ]
                    },
                    "description": "Nuevos tipos de carga permitidos (opcional)",
                    "example": [
                      "perishable"
                    ]
                  },
                  "fresh_cargo_temp": {
                    "type": "number",
                    "nullable": true,
                    "description": "Nueva temperatura para carga refrigerada (opcional)",
                    "example": 4
                  },
                  "image": {
                    "type": "string",
                    "format": "binary",
                    "description": "Nueva imagen del vehículo (opcional)"
                  },
                  "itv": {
                    "type": "string",
                    "format": "binary",
                    "description": "Nuevo documento de ITV (opcional)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/200Vehicle"
          },
          "400": {
            "description": "Posibles errores:\n- VEHICLE_NOT_FOUND\n- INVALID_VEHICLE_TYPE\n- INVALID_SHIPPING_TYPE\n- INVALID_CARGO_TYPES\n- Error en validación de matrícula\n",
            "$ref": "#/components/responses/400Error"
          },
          "401": {
            "$ref": "#/components/responses/401Error"
          }
        }
      },
      "delete": {
        "tags": [
          "vehicle"
        ],
        "summary": "Delete vehicle",
        "description": "Elimina permanentemente un vehículo del sistema.\n\n**Requisitos:**\n- El vehículo debe existir y pertenecer a la compañía\n- No puede estar asignado a transportes activos\n\n**Comportamiento:**\n- Elimina el vehículo de la colección de vehículos\n- Lo remueve de la lista de vehículos de la compañía\n- Los documentos asociados (ITV, imágenes) permanecen en S3\n\n**Ejemplo de respuesta exitosa:**\n```json\n{\n  \"id\": \"507f1f77bcf86cd799439011\",\n  \"message\": \"Vehículo eliminado correctamente\"\n}\n```\n",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID del vehículo a eliminar",
            "example": "507f1f77bcf86cd799439011"
          }
        ],
        "responses": {
          "200": {
            "description": "Vehículo eliminado exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "ID del vehículo eliminado"
                    },
                    "message": {
                      "type": "string",
                      "description": "Mensaje de confirmación"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Posibles errores:\n- VEHICLE_NOT_FOUND\n- VEHICLE_IN_USE (si está asignado a transportes)\n",
            "$ref": "#/components/responses/400Error"
          },
          "401": {
            "$ref": "#/components/responses/401Error"
          }
        }
      }
    },
    "/company/address/": {
      "get": {
        "deprecated": false,
        "description": "Devuelve un listado paginado de todas las direcciones asociadas a la compañía del usuario autenticado.\n\n**Características:**\n- Paginación (20 resultados por página por defecto)\n- Filtrado opcional por código de país\n- Ordenación por nombre y compañía\n\n**Ejemplo de respuesta:**\n```json\n{\n  \"docs\": [\n    {\n      \"_id\": \"507f1f77bcf86cd799439011\",\n      \"name\": \"Oficina Central\",\n      \"company_name\": \"CargoOffer SL\",\n      \"phone\": \"+34912345678\",\n      \"street\": \"Calle Ejemplo 123\",\n      \"city\": \"Madrid\",\n      \"zipcode\": \"28045\",\n      \"country\": \"ES\",\n      \"location\": {\n        \"type\": \"Point\",\n        \"coordinates\": [-3.703790, 40.416775]\n      },\n      \"isDefault\": true,\n      \"can_be_deleted\": false\n    }\n  ],\n  \"total\": 1,\n  \"limit\": 20,\n  \"page\": 1,\n  \"pages\": 1\n}\n```\n\n**Notas:**\n- Las direcciones principales (isDefault=true) aparecen primero\n- Requiere token JWT válido\n- Los números de teléfono (phone) mostrados tienen formato válido de España, Francia o Portugal\n",
        "parameters": [
          {
            "description": "Número de página a recuperar (1-based)",
            "example": 1,
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Número máximo de resultados por página (máximo 100)",
            "example": 20,
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Filtro opcional por código de país (2 letras ISO, case-sensitive)",
            "example": "ES",
            "in": "query",
            "name": "countryCode",
            "required": false,
            "schema": {
              "maxLength": 2,
              "minLength": 2,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "examples": {},
              "schema": {
                "properties": {},
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddressListResponse"
                }
              }
            },
            "description": "List of addresses",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Unauthorized",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Not found",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get company addresses",
        "tags": [
          "Address"
        ]
      },
      "post": {
        "deprecated": false,
        "description": "Crea una nueva dirección asociada a la compañía del usuario autenticado. \nEste endpoint permite registrar ubicaciones físicas como almacenes, oficinas o puntos de carga/descarga.\n\n**Flujo de operación:**\n1. Autenticación mediante JWT válido\n2. Validación de campos obligatorios y formatos\n3. Procesamiento de datos de Google Maps (geocodificación inversa)\n4. Si isDefault=true, desmarca cualquier dirección principal existente\n5. Persistencia en base de datos\n6. Retorno de la dirección creada con su ID\n\n**Validaciones importantes:**\n- Requiere token JWT válido con permisos de administrador/editor\n- Campo 'name' obligatorio (3-100 caracteres)\n- Datos de Google Maps deben incluir:\n  * formatted_address: Dirección completa formateada\n  * geometry.location: Coordenadas {lat, lng}\n- Si isDefault=true, desmarca cualquier dirección principal existente\n\n**Casos de uso típicos:**\n- Registrar nueva sede de la compañía\n- Añadir almacén logístico\n- Crear punto de recogida para envíos\n\n**Nota importante sobre teléfonos:**\n- Los números de teléfono (phone) deben tener formato válido de España, Francia o Portugal\n\n**Requiere suscripción activa** (validado por middleware isPaymentUpdate)\n\n**Timestamps son convertidos a UTC** por middleware checkUTC\n\n**Ejemplo de solicitud:**\n```http\nPOST /company/address\nAuthorization: Bearer {token}\nContent-Type: application/json\n\n{\n  \"name\": \"Almacén Logístico\",\n  \"company_name\": \"CargoOffer SL\",\n  \"phone\": \"+34912345678\",\n  \"addressGoogleMaps\": {\n    \"formatted_address\": \"Calle de la Logística, 123, 28045 Madrid, España\",\n    \"geometry\": {\n      \"location\": {\n        \"lat\": 40.123456,\n        \"lng\": -3.987654\n      }\n    }\n  },\n  \"isDefault\": false\n}\n```\n\n**Ejemplo de respuesta exitosa:**\n```json\n{\n  \"_id\": \"507f1f77bcf86cd799439011\",\n  \"name\": \"Almacén Logístico\",\n  \"company_name\": \"CargoOffer SL\",\n  \"phone\": \"+34912345678\",\n  \"street\": \"Calle de la Logística, 123\",\n  \"city\": \"Madrid\",\n  \"zipcode\": \"28045\",\n  \"country\": \"ES\",\n  \"location\": {\n    \"type\": \"Point\",\n    \"coordinates\": [-3.987654, 40.123456]\n  },\n  \"isDefault\": false,\n  \"can_be_deleted\": true\n}\n```\n\n**Errores comunes:**\n- 400 Bad Request: Datos faltantes o inválidos\n- 401 Unauthorized: Token JWT inválido o faltante\n- 403 Forbidden: Permisos insuficientes\n- 404 Not Found: Compañía no encontrada\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "addressGoogleMaps": {
                  "formatted_address": "Calle de la Logística, 123, 28045 Madrid, España",
                  "geometry": {
                    "location": {
                      "lat": 40.123456,
                      "lng": -3.987654
                    }
                  }
                },
                "company_name": "CargoOffer SL",
                "isDefault": false,
                "name": "Almacén Logístico",
                "phone": "+34912345678"
              },
              "schema": {
                "$ref": "#/components/schemas/CreateAddressRequest",
                "properties": {}
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "_id": "68f9f8bf0e38c035ea821e58",
                    "company_name": "CargoOffer SL",
                    "createdAt": "2025-10-23T09:43:27.719Z",
                    "deleted": false,
                    "destinations": [],
                    "location": {
                      "coordinates": [
                        0,
                        0
                      ],
                      "type": "Point"
                    },
                    "name": "Almacén Logístico",
                    "phone": "+34912345678",
                    "street_address": "",
                    "street_number": ""
                  },
                  "status": 200
                },
                "schema": {
                  "$ref": "#/components/schemas/Address"
                }
              }
            },
            "description": "Successful operation",
            "headers": {}
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "enum": [
                        "MISSING_FIELD_NAME",
                        "INVALID_GOOGLE_MAPS_DATA",
                        "INVALID_ADDRESS_FORMAT"
                      ],
                      "example": "INVALID_GOOGLE_MAPS_DATA",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Invalid request",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Unauthorized",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Not found",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Create new address",
        "tags": [
          "Address"
        ]
      },
      "put": {
        "deprecated": false,
        "description": "Actualiza los datos de una dirección existente identificada por el campo '_id' en el body de la solicitud.\n\n**Flujo de operación:**\n1. Autenticación mediante JWT válido\n2. Validación del campo '_id' obligatorio en el body\n3. Verificación de que la dirección pertenece a la compañía del usuario\n4. Validación de campos y formatos\n5. Procesamiento de datos de Google Maps (geocodificación inversa)\n6. Si is_default=true, desmarca cualquier dirección principal existente\n7. Persistencia de cambios en base de datos\n8. Retorno de la dirección actualizada\n\n**Características importantes:**\n- El ID de la dirección debe enviarse en el campo '_id' del body\n- Requiere token JWT válido con permisos de administrador/editor\n- Campo 'name' obligatorio (3-100 caracteres)\n- Datos de Google Maps deben incluir:\n  * formatted_address: Dirección completa formateada\n  * geometry.location: Coordenadas {lat, lng}\n- Si is_default=true, desmarca cualquier dirección principal existente\n\n**Ejemplo de solicitud:**\n```http\nPUT /company/address\nAuthorization: Bearer {token}\nContent-Type: application/json\n\n{\n  \"_id\": \"507f1f77bcf86cd799439011\",\n  \"name\": \"Almacén Logístico Actualizado\",\n  \"company_name\": \"CargoOffer SL\",\n  \"phone\": \"+34912345678\",\n  \"address_google_maps\": {\n    \"formatted_address\": \"Calle de la Logística, 123, 28045 Madrid, España\",\n    \"geometry\": {\n      \"location\": {\n        \"lat\": 40.123456,\n        \"lng\": -3.987654\n      }\n    }\n  },\n  \"is_default\": false\n}\n```\n\n**Ejemplo de respuesta exitosa:**\n```json\n{\n  \"_id\": \"507f1f77bcf86cd799439011\",\n  \"name\": \"Almacén Logístico Actualizado\",\n  \"company_name\": \"CargoOffer SL\",\n  \"phone\": \"+34912345678\",\n  \"street\": \"Calle de la Logística, 123\",\n  \"city\": \"Madrid\",\n  \"zipcode\": \"28045\",\n  \"country\": \"ES\",\n  \"location\": {\n    \"type\": \"Point\",\n    \"coordinates\": [-3.987654, 40.123456]\n  },\n  \"is_default\": false,\n  \"can_be_deleted\": true\n}\n```\n\n**Errores comunes:**\n- 400 Bad Request: Datos faltantes o inválidos, especialmente '_id' faltante\n- 401 Unauthorized: Token JWT inválido o faltante\n- 403 Forbidden: Permisos insuficientes\n- 404 Not Found: Dirección no encontrada o no pertenece a la compañía\n**Nota importante sobre teléfonos:**\n- Los números de teléfono (phone) deben tener formato válido de España, Francia o Portugal\n\n**Comportamiento de isDefault:**\n- Si isDefault=true: Establece como nueva dirección principal (desmarca otras)\n- Si isDefault=false: Elimina referencia de principal si la tenía\n- Solo una dirección puede tener isDefault=true por compañía\n\n**Timestamps son convertidos a UTC** por middleware checkUTC\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "_id": "64917511ef73c37ccae60bc4",
                "address_google_maps": {
                  "formatted_address": "Calle de la Logística, 123, 28045 Madrid, España",
                  "geometry": {
                    "location": {
                      "lat": 40.123456,
                      "lng": -3.987654
                    }
                  }
                },
                "company_name": "CargoOffer SL",
                "is_default": false,
                "name": "Almacén Logístico Actualizado",
                "phone": "+34912345678"
              },
              "schema": {
                "$ref": "#/components/schemas/CreateAddressRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "_id": "64917511ef73c37ccae60bc4",
                    "city": "ribeira",
                    "company_name": "CargoOffer SL",
                    "country": "españa",
                    "createdAt": "2024-10-02T18:24:37.606Z",
                    "deleted": false,
                    "destinations": [],
                    "location": {
                      "coordinates": [
                        -8.995213099999999,
                        42.5668541
                      ],
                      "type": "Point"
                    },
                    "name": "Almacén Logístico Actualizado",
                    "name_address": "Estrada Deán Norte, 117, 15960 Ribeira, A Coruña, España",
                    "phone": "+34912345678",
                    "placeId": "ChIJLdx3TsE5Lw0Rmh7qsvQUqII",
                    "province": "A Coruña",
                    "state": "galicia",
                    "street_address": "Estrada Deán Norte",
                    "street_number": "117",
                    "zipcode": "15960"
                  },
                  "status": 200
                },
                "schema": {
                  "$ref": "#/components/schemas/Address"
                }
              }
            },
            "description": "Dirección actualizada correctamente",
            "headers": {}
          },
          "400": {
            "description": "Datos de entrada inválidos",
            "headers": {}
          },
          "401": {
            "description": "No autorizado (token JWT inválido o faltante)",
            "headers": {}
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "example": "NOT_ALLOWED",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "NOT_ALLOWED - La dirección no pertenece a esta compañía",
            "headers": {}
          },
          "404": {
            "description": "Dirección no encontrada",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Update address",
        "tags": [
          "Address"
        ]
      }
    },
    "/company/address/bulk/": {
      "post": {
        "deprecated": false,
        "description": "Importa múltiples direcciones desde un archivo CSV.\n\n**Características:**\n- Procesamiento masivo de direcciones\n- Soporta formato Total Logistik y estándar\n- Validación individual de cada fila\n- Respuesta detallada con éxitos y errores\n\n**Formato CSV esperado:**\n- Campos válidos: name, company_name, phone, lat, lng\n- Delimitador: coma\n- Primera fila: cabeceras\n\n**Requiere suscripción activa** (validado por middleware isPaymentUpdate)\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "required": [
                "data"
              ],
              "schema": {
                "properties": {
                  "data": {
                    "description": "Archivo CSV con direcciones a importar",
                    "format": "binary",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "ko": [
                    {
                      "data": {
                        "lat": "invalid",
                        "lng": -3.70379,
                        "name": "Oficina Norte"
                      },
                      "error": "Latitud inválida",
                      "row": 3
                    }
                  ],
                  "ok": [
                    {
                      "_id": "507f1f77bcf86cd799439011",
                      "company_name": "CargoOffer SL",
                      "isDefault": false,
                      "location": {
                        "coordinates": [
                          -3.70379,
                          40.416775
                        ],
                        "type": "Point"
                      },
                      "name": "Almacén Central",
                      "phone": "+34912345678"
                    }
                  ]
                },
                "schema": {
                  "properties": {
                    "ko": {
                      "description": "Filas que no pudieron procesarse",
                      "items": {
                        "properties": {
                          "data": {
                            "description": "Datos de la fila que falló",
                            "type": "object"
                          },
                          "error": {
                            "description": "Descripción del error",
                            "type": "string"
                          },
                          "row": {
                            "description": "Número de fila con error",
                            "type": "integer"
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "ok": {
                      "description": "Direcciones creadas exitosamente",
                      "items": {
                        "$ref": "#/components/schemas/Address"
                      },
                      "type": "array"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Importación completada"
          },
          "400": {
            "description": "Formato de archivo inválido"
          },
          "401": {
            "description": "No autorizado"
          },
          "403": {
            "description": "Suscripción no activa (isPaymentUpdate)"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Bulk import addresses from CSV",
        "tags": [
          "Address"
        ]
      }
    },
    "/company/address/bulk/notes/{lang}": {
      "get": {
        "deprecated": false,
        "description": "Obtiene instrucciones detalladas y notas para la importación masiva de direcciones.\n\n**Incluye información sobre:**\n- Formato del CSV requerido\n- Validaciones aplicadas\n- Campos obligatorios y opcionales\n- Errores comunes y cómo evitarlos\n",
        "parameters": [
          {
            "description": "Idioma de las instrucciones",
            "example": "es",
            "in": "path",
            "name": "lang",
            "required": true,
            "schema": {
              "enum": [
                "es",
                "en"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "description": "Instrucciones en formato texto plano"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get bulk import instructions",
        "tags": [
          "Address"
        ]
      }
    },
    "/company/address/bulk/template": {
      "get": {
        "deprecated": false,
        "description": "Descarga una plantilla CSV con el formato correcto para importación masiva de direcciones.\n\n**La plantilla incluye:**\n- Cabeceras correctas\n- Una fila de ejemplo\n- Formato compatible con el endpoint de importación\n",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "text/csv": {
                "schema": {
                  "example": "name,company_name,phone,lat,lng\nCargoffer,Cargoffer SL,604015221,42.22638,-8.7226",
                  "type": "string"
                }
              }
            },
            "description": "Archivo CSV con plantilla",
            "headers": {
              "Content-Disposition": {
                "schema": {
                  "example": "attachment; filename=\"template.csv\"",
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Download CSV template for bulk import",
        "tags": [
          "Address"
        ]
      }
    },
    "/company/address/edit": {
      "post": {
        "deprecated": false,
        "description": "Alternativa a PUT /company/address usando método POST.\n\n**Nota:** Este endpoint existe para clientes HTTP que no soportan el método PUT.\nFuncionalmente es idéntico a PUT /company/address.\n\n**Prioridad de ID:** body._id (usado) > query.id\n\n**Timestamps son convertidos a UTC** por middleware checkUTC.\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "_id": "64917511ef73c37ccae60bc4",
                "address_google_maps": {
                  "formatted_address": "Calle de la Logística, 123, 28045 Madrid, España",
                  "geometry": {
                    "location": {
                      "lat": 40.123456,
                      "lng": -3.987654
                    }
                  }
                },
                "company_name": "CargoOffer SL",
                "is_default": false,
                "name": "Almacén Logístico Actualizado",
                "phone": "+34912345678"
              },
              "schema": {
                "$ref": "#/components/schemas/CreateAddressRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Address"
                }
              }
            },
            "description": "Dirección actualizada correctamente"
          },
          "400": {
            "description": "Datos de entrada inválidos"
          },
          "401": {
            "description": "No autorizado"
          },
          "403": {
            "description": "NOT_ALLOWED - La dirección no pertenece a esta compañía"
          },
          "404": {
            "description": "Dirección no encontrada"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Update address (POST alternative)",
        "tags": [
          "Address"
        ]
      }
    },
    "/company/address/edit/{id}": {
      "post": {
        "deprecated": false,
        "description": "Alternativa a PUT /company/address/{id} usando método POST con ID en la ruta.\n\n**Nota:** Este endpoint existe para clientes HTTP que no soportan el método PUT.\nFuncionalmente es idéntico a PUT /company/address/{id}.\n\n**Prioridad de ID:** params.id (usado) > body._id > query.id\n\n**Timestamps son convertidos a UTC** por middleware checkUTC.\n",
        "parameters": [
          {
            "description": "ID de la dirección a actualizar",
            "example": "64917511ef73c37ccae60bc4",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "example": {
                  "addressGoogleMaps": {
                    "formatted_address": "Avenida Principal, 456, 28080 Madrid, España",
                    "geometry": {
                      "location": {
                        "lat": 40.456789,
                        "lng": -3.654321
                      }
                    }
                  },
                  "company_name": "CargoOffer SL",
                  "isDefault": true,
                  "name": "Oficina Central Actualizada",
                  "phone": "+34987654321"
                },
                "properties": {
                  "addressGoogleMaps": {
                    "properties": {
                      "formatted_address": {
                        "type": "string"
                      },
                      "geometry": {
                        "properties": {
                          "location": {
                            "properties": {
                              "lat": {
                                "type": "number"
                              },
                              "lng": {
                                "type": "number"
                              }
                            },
                            "type": "object"
                          }
                        },
                        "type": "object"
                      }
                    },
                    "type": "object"
                  },
                  "company_name": {
                    "maxLength": 100,
                    "minLength": 2,
                    "type": "string"
                  },
                  "isDefault": {
                    "type": "boolean"
                  },
                  "name": {
                    "maxLength": 100,
                    "minLength": 3,
                    "type": "string"
                  },
                  "phone": {
                    "maxLength": 20,
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Address"
                }
              }
            },
            "description": "Dirección actualizada correctamente"
          },
          "400": {
            "description": "Datos de entrada inválidos"
          },
          "401": {
            "description": "No autorizado"
          },
          "403": {
            "description": "NOT_ALLOWED - La dirección no pertenece a esta compañía"
          },
          "404": {
            "description": "Dirección no encontrada"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Update specific address by ID (POST alternative)",
        "tags": [
          "Address"
        ]
      }
    },
    "/company/address/export": {
      "get": {
        "deprecated": false,
        "description": "Genera un archivo CSV con todas las direcciones de la compañía.\n\n**Características:**\n- Formato estándar CSV con encabezados\n- Incluye todos los campos de dirección\n- Coordenadas separadas en longitud/latitud\n\n**Ejemplo de respuesta:**\n```csv\n\"ID\",\"Name\",\"Company\",\"Phone\",\"Street\",\"StreetNumber\",\"City\",\"State\",\"Zipcode\",\"Country\",\"gps_long\",\"gps_lat\",\"Neighborhood\",\"Province\",\"NameAddress\"\n\"507f1f77bcf86cd799439011\",\"Oficina Central\",\"CargoOffer SL\",\"+34912345678\",\"Calle Ejemplo 123\",\"123\",\"Madrid\",\"Madrid\",\"28045\",\"ES\",-3.703790,40.416775,\"Centro\",\"Madrid\",\"Calle Ejemplo 123, 123, 28045 Madrid, España\"\n```\n\n**Notas:**\n- Requiere token JWT válido\n- El archivo se descarga directamente\n- Formato compatible con Excel y otras herramientas\n",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "text/csv": {
                "example": "\"ID\",\"Name\",\"Company\",\"Phone\",\"Street\",\"StreetNumber\",\"City\",\"State\",\"Zipcode\",\"Country\",\"gps_long\",\"gps_lat\",\"isDefault\",\"Neighborhood\",\"Province\",\"NameAddress\"\n\"507f1f77bcf86cd799439011\",\"Oficina Central\",\"CargoOffer SL\",\"+34912345678\",\"Calle Ejemplo 123\",\"123\",\"Madrid\",\"Madrid\",\"28045\",\"ES\",-3.703790,40.416775,true,\"Centro\",\"Madrid\",\"Calle Ejemplo 123, 123, 28045 Madrid, España\"\n\"507f1f77bcf86cd799439012\",\"Almacén Norte\",\"CargoOffer SL\",\"+34987654321\",\"Avenida Norte 456\",\"456\",\"Barcelona\",\"Cataluña\",\"08001\",\"ES\",2.168594,41.387397,false,\"Sant Martí\",\"Barcelona\",\"Avenida Norte 456, 456, 08001 Barcelona, España\"\n",
                "schema": {
                  "$ref": "#/components/schemas/ExportResponse"
                }
              }
            },
            "description": "Archivo CSV con direcciones",
            "headers": {}
          },
          "401": {
            "description": "No autorizado (token JWT inválido o faltante)",
            "headers": {}
          },
          "404": {
            "description": "Compañía no encontrada",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Export addresses to CSV",
        "tags": [
          "Address"
        ]
      }
    },
    "/company/address/simple": {
      "post": {
        "deprecated": false,
        "description": "Crea una nueva dirección de manera simplificada usando únicamente coordenadas y datos básicos. \nEste endpoint está optimizado para casos donde se conocen las coordenadas exactas y se necesita \ncrear una dirección rápidamente sin la complejidad de los datos de Google Maps.\n\n**Flujo de operación:**\n1. Autenticación mediante JWT válido\n2. Validación de campos mínimos obligatorios (lat, lng, name, company_name)\n3. Geocodificación inversa automática desde coordenadas\n4. Creación automática de dirección completa\n5. Asociación a la compañía del usuario\n6. Retorno de la dirección creada\n\n**Ventajas de este endpoint:**\n- Requiere solo 4 campos obligatorios vs ~15 del endpoint estándar\n- Geocodificación automática desde coordenadas\n- Reutiliza la lógica probada del sistema de importación masiva\n- Mantiene la misma calidad de datos que la creación normal\n\n**Casos de uso típicos:**\n- Aplicaciones móviles con GPS\n- Integración desde sistemas de terceros con coordenadas\n- Creación rápida desde mapas interactivos\n- Bulk creation simplificado\n\n**Ejemplo de solicitud:**\n```http\nPOST /company/address/simple\nAuthorization: Bearer {token}\nContent-Type: application/json\n\n{\n  \"lat\": 40.416775,\n  \"lng\": -3.703790,\n  \"name\": \"Oficina Central\",\n  \"company_name\": \"CargoOffer SL\",\n  \"phone\": \"+34912345678\",\n  \"isDefault\": false\n}\n```\n\n**Ejemplo de respuesta exitosa:**\n```json\n{\n  \"_id\": \"507f1f77bcf86cd799439011\",\n  \"name\": \"Oficina Central\",\n  \"company_name\": \"CargoOffer SL\",\n  \"phone\": \"+34912345678\",\n  \"street\": \"Calle de Alcalá, 42\",\n  \"city\": \"madrid\",\n  \"zipcode\": \"28014\",\n  \"country\": \"es\",\n  \"location\": {\n    \"type\": \"Point\",\n    \"coordinates\": [-3.703790, 40.416775]\n  },\n  \"is_default\": false,\n  \"can_be_deleted\": true\n}\n```\n**Nota importante sobre teléfonos:**\n- Los números de teléfono (phone) deben tener formato válido de España, Francia o Portugal\n\n**Requiere suscripción activa** (validado por middleware isPaymentUpdate)\n\n**Timestamps son convertidos a UTC** por middleware checkUTC\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "company_name": "CargoOffer SL",
                "isDefault": false,
                "lat": 40.416775,
                "lng": -3.70379,
                "name": "Oficina Central",
                "phone": "+34912345678"
              },
              "schema": {
                "example": {
                  "company_name": "CargoOffer SL",
                  "isDefault": false,
                  "lat": 40.416775,
                  "lng": -3.70379,
                  "name": "Oficina Central",
                  "phone": "+34912345678"
                },
                "properties": {
                  "company_name": {
                    "description": "Razón social de la compañía en esta dirección",
                    "example": "CargoOffer SL",
                    "maxLength": 100,
                    "minLength": 2,
                    "type": "string"
                  },
                  "isDefault": {
                    "default": false,
                    "description": "Indica si esta será la dirección principal de la compañía",
                    "example": false,
                    "type": "boolean"
                  },
                  "lat": {
                    "description": "Latitud en grados decimales (WGS84)",
                    "example": 40.416775,
                    "maximum": 90,
                    "minimum": -90,
                    "type": "number"
                  },
                  "lng": {
                    "description": "Longitud en grados decimales (WGS84)",
                    "example": -3.70379,
                    "maximum": 180,
                    "minimum": -180,
                    "type": "number"
                  },
                  "name": {
                    "description": "Nombre descriptivo de la dirección",
                    "example": "Oficina Central",
                    "maxLength": 100,
                    "minLength": 3,
                    "type": "string"
                  },
                  "phone": {
                    "description": "Teléfono de contacto (opcional, formato internacional recomendado)",
                    "example": "+34912345678",
                    "maxLength": 20,
                    "type": "string"
                  }
                },
                "required": [
                  "lat",
                  "lng",
                  "name",
                  "company_name"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Address"
                }
              }
            },
            "description": "Dirección creada exitosamente",
            "headers": {}
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "enum": [
                        "MISSING_FIELD_LAT",
                        "MISSING_FIELD_LNG",
                        "MISSING_FIELD_NAME",
                        "MISSING_FIELD_COMPANY_NAME",
                        "INVALID_COORDINATES"
                      ],
                      "example": "MISSING_FIELD_LAT",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Error de validación - Campos requeridos faltantes o formato inválido",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "enum": [
                        "USER_NOT_FOUND",
                        "CIA_NOT_FOUND"
                      ],
                      "example": "CIA_NOT_FOUND",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Error de autenticación - Token inválido o compañía no encontrada",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "example": "USER_NOT_FOUND",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Usuario no encontrado",
            "headers": {}
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "enum": [
                        "CANNOT_SAVE_ADDRESS",
                        "INTERNAL_ERROR"
                      ],
                      "example": "CANNOT_SAVE_ADDRESS",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Error interno del servidor",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Create simplified address",
        "tags": [
          "Address"
        ]
      }
    },
    "/company/address/{id}": {
      "delete": {
        "deprecated": false,
        "description": "Elimina una dirección existente de la compañía.\n\n**Validaciones:**\n- Verifica que la dirección pertenece a la compañía del usuario autenticado\n- NO valida el campo can_be_deleted (se puede eliminar cualquier dirección propia)\n- Permite eliminar la última dirección de la compañía\n\n**Ejemplo de respuesta exitosa:**\n```json\n{\n  \"_id\": \"507f1f77bcf86cd799439011\"\n}\n```\n\n**Notas:**\n- Operación irreversible\n- Requiere token JWT válido\n- Solo se pueden eliminar direcciones propias de la compañía\n",
        "parameters": [
          {
            "description": "ID de la dirección a eliminar",
            "example": "64917618ef73c37ccae60bfc",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {},
              "schema": {
                "properties": {},
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "_id": {
                      "description": "ID de la dirección eliminada",
                      "example": "507f1f77bcf86cd799439011",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Dirección eliminada correctamente",
            "headers": {}
          },
          "401": {
            "description": "No autorizado (token JWT inválido o faltante)",
            "headers": {}
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "example": "NOT_ALLOWED",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "NOT_ALLOWED - La dirección no pertenece a esta compañía",
            "headers": {}
          },
          "404": {
            "description": "Dirección no encontrada",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Delete address",
        "tags": [
          "Address"
        ]
      },
      "put": {
        "deprecated": false,
        "description": "Actualiza una dirección existente identificada por su ID. Permite modificar todos los campos excepto el ID.\n\n**Flujo de actualización:**\n1. Validación de permisos (admin/editor)\n2. Verificación de que la dirección pertenece a la compañía del usuario\n3. Si isDefault=true:\n   - Desmarca la dirección principal actual\n   - Establece esta dirección como nueva principal\n4. Actualización de datos en base de datos\n\n**Campos actualizables:**\n- name: Nuevo nombre descriptivo\n- company_name: Razón social en la dirección\n- phone: Teléfono de contacto\n- addressGoogleMaps: Datos geográficos completos\n- isDefault: Bandera de dirección principal\n\n**Ejemplo de solicitud:**\n```json\nPUT /company/address/507f1f77bcf86cd799439011\n{\n  \"name\": \"Oficina Central Actualizada\",\n  \"company_name\": \"CargoOffer SL\",\n  \"phone\": \"+34987654321\",\n  \"addressGoogleMaps\": {\n    \"formatted_address\": \"Avenida Principal, 456, 28080 Madrid, España\",\n    \"geometry\": {\n      \"location\": {\n        \"lat\": 40.456789,\n        \"lng\": -3.654321\n      }\n    }\n  },\n  \"isDefault\": true\n}\n```\n\n**Ejemplo de respuesta exitosa:**\n```json\n{\n  \"_id\": \"507f1f77bcf86cd799439011\",\n  \"name\": \"Oficina Central Actualizada\",\n  \"company_name\": \"CargoOffer SL\",\n  \"phone\": \"+34987654321\",\n  \"street\": \"Avenida Principal, 456\",\n  \"city\": \"Madrid\",\n  \"zipcode\": \"28080\",\n  \"country\": \"ES\",\n  \"location\": {\n    \"type\": \"Point\",\n    \"coordinates\": [-3.654321, 40.456789]\n  },\n  \"isDefault\": true,\n  \"can_be_deleted\": false\n}\n```\n\n**Casos de error comunes:**\n- 400: Datos de entrada inválidos o incompletos\n- 401: Token JWT inválido o faltante\n- 403: Usuario sin permisos suficientes\n- 404: Dirección no encontrada o no pertenece a la compañía\n\n**Notas importantes:**\n- Requiere autenticación JWT válida\n- Los cambios en addressGoogleMaps disparan geocodificación inversa\n- Los números de teléfono (phone) deben tener formato válido de España, Francia o Portugal\n\n**Comportamiento de isDefault:**\n- Si isDefault=true: Establece como nueva dirección principal (desmarca otras)\n- Si isDefault=false: Elimina referencia de principal si la tenía\n- Solo una dirección puede tener isDefault=true por compañía\n\n**Timestamps son convertidos a UTC** por middleware checkUTC\n",
        "parameters": [
          {
            "description": "ID de la dirección a actualizar",
            "example": "64917511ef73c37ccae60bc4",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "example": {
                  "addressGoogleMaps": {
                    "formatted_address": "Avenida Principal, 456, 28080 Madrid, España",
                    "geometry": {
                      "location": {
                        "lat": 40.456789,
                        "lng": -3.654321
                      }
                    }
                  },
                  "company_name": "CargoOffer SL",
                  "isDefault": true,
                  "name": "Oficina Central Actualizada",
                  "phone": "+34987654321"
                },
                "properties": {
                  "addressGoogleMaps": {
                    "properties": {
                      "formatted_address": {
                        "type": "string"
                      },
                      "geometry": {
                        "properties": {
                          "location": {
                            "properties": {
                              "lat": {
                                "type": "number"
                              },
                              "lng": {
                                "type": "number"
                              }
                            },
                            "type": "object"
                          }
                        },
                        "type": "object"
                      }
                    },
                    "type": "object"
                  },
                  "company_name": {
                    "maxLength": 100,
                    "minLength": 2,
                    "type": "string"
                  },
                  "isDefault": {
                    "type": "boolean"
                  },
                  "name": {
                    "maxLength": 100,
                    "minLength": 3,
                    "type": "string"
                  },
                  "phone": {
                    "maxLength": 20,
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Address"
                }
              }
            },
            "description": "Dirección actualizada correctamente",
            "headers": {}
          },
          "400": {
            "description": "Datos de entrada inválidos",
            "headers": {}
          },
          "401": {
            "description": "No autorizado (token JWT inválido o faltante)",
            "headers": {}
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "example": "NOT_ALLOWED",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "NOT_ALLOWED - La dirección no pertenece a esta compañía",
            "headers": {}
          },
          "404": {
            "description": "Dirección no encontrada",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Update specific address",
        "tags": [
          "Address"
        ]
      }
    },
    "/company/apikey/": {
      "get": {
        "deprecated": false,
        "description": "## Propósito\nRecupera todas las API Keys creadas por el usuario autenticado, permitiendo auditar\ny gestionar las credenciales de acceso programático propias.\n\n## Objetivo\nProporcionar al usuario visibilidad sobre sus API Keys activas, incluyendo sus tipos\ny códigos temporales necesarios para eliminación.\n\n## Casos de Uso\n- Auditar qué API Keys están activas para el usuario\n- Obtener el temp_code necesario para eliminar una key específica\n- Verificar el tipo de permisos de cada key\n- Identificar keys antiguas que deberían rotarse\n\n## Flujo de operación\n1. Valida que el usuario exista y pertenezca a una compañía\n2. Busca todas las claves asociadas al ID del usuario\n3. Oculta parcialmente las claves por seguridad (muestra primeros 8 y últimos 4 caracteres)\n4. Devuelve la lista de claves con sus metadatos\n\n## Mecanismo de Ocultamiento\nLas API Keys se muestran parcialmente:\n- Formato: `sk_live_*****5678` (primeros 8 + asteriscos + últimos 4 caracteres)\n- Las claves completas solo se muestran al momento de creación\n- No es posible recuperar la clave completa posteriormente\n\n## Consideraciones importantes\n- Solo devuelve claves propias del usuario (no de otros usuarios de la compañía)\n- Las claves eliminadas (soft delete) no aparecen en la lista\n- El campo `temp_code` es necesario para operaciones de eliminación\n",
        "operationId": "listUserApiKeys",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "apikeys": [
                    {
                      "createdAt": "2024-01-15T10:30:00.000Z",
                      "is_bot": false,
                      "key": "sk_live_*****cdef",
                      "temp_code": "2c3srejxqa176128918935tzv",
                      "type": "delivery_invoice"
                    },
                    {
                      "createdAt": "2024-01-10T08:15:00.000Z",
                      "is_bot": false,
                      "key": "sk_live_*****1234",
                      "temp_code": "5j8kpqwxyz923746512849abc",
                      "type": "auction"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/ApikeyList"
                }
              }
            },
            "description": "Lista de API Keys del usuario",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "NO_TOKEN": {
                    "value": {
                      "code": 401,
                      "message": "NO_TOKEN",
                      "status": false
                    }
                  },
                  "TOKEN_NOT_VALID": {
                    "value": {
                      "code": 401,
                      "message": "TOKEN_NOT_VALID",
                      "status": false
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autenticado",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "APIKEYS_NOT_FOUND": {
                    "value": {
                      "code": 404,
                      "message": "APIKEYS_NOT_FOUND",
                      "status": false
                    }
                  },
                  "USER_NOT_FOUND": {
                    "value": {
                      "code": 404,
                      "message": "USER_NOT_FOUND",
                      "status": false
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado o sin API Keys",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Get User API Keys",
        "tags": [
          "Apikey"
        ]
      },
      "post": {
        "deprecated": false,
        "description": "## Propósito\nCrea una nueva API Key para el usuario autenticado asociada a su compañía, permitiendo\nautenticación programática para integraciones externas y automatizaciones.\n\n## Objetivo\nGenerar credenciales seguras de tipo API Key que permitan acceso programático a los recursos\nde la compañía según el tipo de permisos especificado.\n\n## Casos de Uso\n- Integración con sistemas externos de gestión de transporte\n- Automatización de creación de subastas mediante scripts\n- Generación automática de facturas desde plataformas de terceros\n- Desarrollo de aplicaciones personalizadas que consumen la API de CargoOffer\n\n## Límites\n- **Máximo 4 API Keys por compañía**\n- Si se alcanza el límite, se retornará error 403 con código MAX_APIKEYS\n- Cada key es única e irrecuperable una vez creada\n\n## Flujo de operación\n```mermaid\nflowchart TD\n  A[Receive Request] --> B{Usuario Autenticado?}\n  B -->|No| C[401 Unauthorized]\n  B -->|Sí| D{Usuario Existe?}\n  D -->|No| E[404 USER_NOT_FOUND]\n  D -->|Sí| F{Compañía Existe?}\n  F -->|No| G[404 COMPANY_NOT_FOUND]\n  F -->|Sí| H{API Keys < 4?}\n  H -->|No| I[403 MAX_APIKEYS]\n  H -->|Sí| J{Tipo Válido?}\n  J -->|No| K[400 Validación Mongoose]\n  J -->|Sí| L[Generar Key 40 chars]\n  L --> M[Generar temp_code]\n  M --> N[Guardar en DB]\n  N --> O[201 Created + Key Completa]\n```\n\n## Consideraciones importantes\n- La API Key solo se muestra completa en el momento de creación\n- No se puede recuperar posteriormente, debe almacenarse de forma segura\n- La clave incluye un carácter de checksum para validación\n- Las eliminaciones son soft delete (mongoose-delete plugin)\n\n## Notas de Seguridad\n- Almacenar la API Key en un gestor de secretos o variable de entorno\n- No compartir la clave en repositorios públicos\n- Rotar las claves periódicamente por seguridad\n- Usar el tipo de permiso más restrictivo necesario\n",
        "operationId": "createApiKey",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "type": "delivery_invoice"
              },
              "schema": {
                "properties": {
                  "type": {
                    "default": "delivery_invoice",
                    "description": "Tipo de API Key que define el alcance de sus permisos:\n- admin: Acceso completo a todas las funcionalidades administrativas\n- auction: Acceso específico para operaciones relacionadas con subastas\n- delivery_invoice: Acceso específico para operaciones de facturas de entrega\n\nLos tipos no son acumulativos; cada clave tiene acceso solo a su ámbito definido.\n",
                    "enum": [
                      "admin",
                      "auction",
                      "delivery_invoice"
                    ],
                    "example": "delivery_invoice",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "company": "507f1f77bcf86cd799439011",
                  "createdAt": "2024-01-15T10:30:00.000Z",
                  "is_bot": false,
                  "key": "sk_live_1234567890abcdef1234567890abcdef12345678",
                  "temp_code": "2c3srejxqa176128918935tzv",
                  "type": "delivery_invoice",
                  "user": "507f1f77bcf86cd799439012"
                },
                "schema": {
                  "$ref": "#/components/schemas/Apikey"
                }
              }
            },
            "description": "API Key creada exitosamente",
            "headers": {}
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "ERROR_CREATE_APIKEY": {
                    "summary": "Error al crear la API Key",
                    "value": {
                      "code": 400,
                      "message": "ERROR_CREATE_APIKEY",
                      "status": false
                    }
                  },
                  "INVALID_TYPE": {
                    "summary": "Tipo de API Key inválido",
                    "value": {
                      "code": 400,
                      "message": "Validation failed: type is invalid",
                      "status": false
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error de validación",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "NO_TOKEN": {
                    "value": {
                      "code": 401,
                      "message": "NO_TOKEN",
                      "status": false
                    }
                  },
                  "TOKEN_NOT_VALID": {
                    "value": {
                      "code": 401,
                      "message": "TOKEN_NOT_VALID",
                      "status": false
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autenticado",
            "headers": {}
          },
          "403": {
            "content": {
              "application/json": {
                "examples": {
                  "MAX_APIKEYS": {
                    "summary": "Máximo de 4 API Keys por compañía alcanzado",
                    "value": {
                      "code": 403,
                      "message": "MAX_APIKEYS",
                      "status": false
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Límite de API Keys alcanzado",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "COMPANY_NOT_FOUND": {
                    "value": {
                      "code": 404,
                      "message": "COMPANY_NOT_FOUND",
                      "status": false
                    }
                  },
                  "USER_NOT_FOUND": {
                    "value": {
                      "code": 404,
                      "message": "USER_NOT_FOUND",
                      "status": false
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario o compañía no encontrados"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Create New API Key",
        "tags": [
          "Apikey"
        ]
      }
    },
    "/company/apikey/{tempCode}": {
      "delete": {
        "deprecated": false,
        "description": "## Propósito\nElimina permanentemente una API Key creada por el usuario autenticado, revocando\nel acceso programático asociado a esa credencial.\n\n## Objetivo\nPermitir a los usuarios revocar sus propias API Keys cuando ya no son necesarias\no cuando se sospecha que han sido comprometidas.\n\n## Casos de Uso\n- Revocar acceso de una integración descontinuada\n- Eliminar una key comprometida o filtrada accidentalmente\n- Rotar credenciales eliminando la antigua y creando una nueva\n- Limpiar keys de prueba que ya no se utilizan\n\n## Flujo de operación\n```mermaid\nflowchart TD\n  A[Receive DELETE Request] --> B{Usuario Autenticado?}\n  B -->|No| C[401 Unauthorized]\n  B -->|Sí| D{Usuario Existe?}\n  D -->|No| E[404 USER_NOT_FOUND]\n  D -->|Sí| F{temp_code Proporcionado?}\n  F -->|No| G[400 TEMP_CODE_NOT_PROVIDED]\n  F -->|Sí| H{API Key Existe?}\n  H -->|No| I[404 APIKEY_NOT_FOUND]\n  H -->|Sí| J{Key Pertenece al Usuario?}\n  J -->|No| K[401 CANT_DELETE]\n  J -->|Sí| L[Soft Delete Key]\n  L --> M[200 APIKEY_DELETED]\n```\n\n## Consideraciones importantes\n- Solo se pueden eliminar claves propias del usuario\n- Usa el temp_code (no la clave real) para identificar la API Key\n- La eliminación es soft delete (mongoose-delete plugin)\n- La clave eliminada no aparecerá en listados futuros\n- No se puede deshacer la eliminación mediante la API pública\n\n## Formato del temp_code\n- String alfanumérico de aproximadamente 25 caracteres\n- Ejemplo: `2c3srejxqa176128918935tzv`\n- Se obtiene de GET /company/apikey/ o al crear la key\n\n## Notas de Seguridad\n- Verificar que todas las integraciones que usan la key sean actualizadas antes de eliminarla\n- Considerar crear una nueva key antes de eliminar la antigua para evitar downtime\n",
        "operationId": "deleteUserApiKey",
        "parameters": [
          {
            "description": "Código temporal único de la API Key a eliminar.\nString alfanumérico generado aleatoriamente de aproximadamente 25 caracteres.\n\nEste código se obtiene de la respuesta de GET /company/apikey/ o POST /company/apikey/.\n",
            "example": "2c3srejxqa176128918935tzv",
            "in": "path",
            "name": "tempCode",
            "required": true,
            "schema": {
              "maxLength": 30,
              "minLength": 20,
              "pattern": "^[a-z0-9]{20,30}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "key": "sk_live_*****cdef",
                  "message": "APIKEY_DELETED"
                },
                "schema": {
                  "$ref": "#/components/schemas/ApikeyDeleted"
                }
              }
            },
            "description": "API Key eliminada exitosamente",
            "headers": {}
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "TEMP_CODE_NOT_PROVIDED": {
                    "value": {
                      "code": 400,
                      "message": "TEMP_CODE_NOT_PROVIDED",
                      "status": false
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "temp_code no proporcionado"
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "CANT_DELETE": {
                    "summary": "Intento de eliminar API Key de otro usuario",
                    "value": {
                      "code": 401,
                      "message": "CANT_DELETE",
                      "status": false
                    }
                  },
                  "NO_TOKEN": {
                    "value": {
                      "code": 401,
                      "message": "NO_TOKEN",
                      "status": false
                    }
                  },
                  "TOKEN_NOT_VALID": {
                    "value": {
                      "code": 401,
                      "message": "TOKEN_NOT_VALID",
                      "status": false
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autenticado o intento de eliminar key ajena",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "APIKEY_NOT_FOUND": {
                    "value": {
                      "code": 404,
                      "message": "APIKEY_NOT_FOUND",
                      "status": false
                    }
                  },
                  "COMPANY_NOT_FOUND": {
                    "value": {
                      "code": 404,
                      "message": "COMPANY_NOT_FOUND",
                      "status": false
                    }
                  },
                  "USER_NOT_FOUND": {
                    "value": {
                      "code": 404,
                      "message": "USER_NOT_FOUND",
                      "status": false
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "API Key no encontrada",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Delete User API Key",
        "tags": [
          "Apikey"
        ]
      }
    },
    "/company/auction": {
      "get": {
        "deprecated": false,
        "description": "Obtiene la lista paginada de subastas creadas por la compañía autenticada con filtros avanzados.\n\n## Propósito\nProporcionar acceso completo al historial de subastas con capacidades de búsqueda y filtrado.\n\n## Objetivo\nPermitir a las empresas gestionar, buscar y filtrar sus subastas por múltiples criterios como\nfechas, estado, tipo de carga, y búsqueda de texto libre.\n\n## Casos de Uso\n- Visualizar historial completo de envíos de la empresa\n- Buscar subastas por código de servicio o descripción\n- Filtrar por rango de fechas de creación, carga o descarga\n- Filtrar por estado (draft, published, awarded, etc.)\n- Excluir borradores o subastas vacías de los resultados\n- Integración con sistemas externos de gestión logística\n",
        "parameters": [
          {
            "description": "Número de página solicitada (basado en 1)",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "example": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Cantidad máxima de resultados por página",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "example": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Fecha mínima de creación de la subasta (YYYY-MM-DD)",
            "in": "query",
            "name": "minCreate",
            "required": false,
            "schema": {
              "example": "2025-01-01",
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Fecha máxima de creación de la subasta (YYYY-MM-DD)",
            "in": "query",
            "name": "maxCreate",
            "required": false,
            "schema": {
              "example": "2025-12-31",
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Fecha mínima de inicio de la subasta (date_start >= esta fecha)",
            "in": "query",
            "name": "minAuction",
            "required": false,
            "schema": {
              "example": "2025-02-01",
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Fecha máxima de fin de la subasta (date_end <= esta fecha)",
            "in": "query",
            "name": "maxAuction",
            "required": false,
            "schema": {
              "example": "2025-02-28",
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Fecha mínima de inicio de subasta (date_start)",
            "in": "query",
            "name": "minStart",
            "required": false,
            "schema": {
              "example": "2025-02-15",
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Fecha máxima de inicio de subasta (date_start)",
            "in": "query",
            "name": "maxStart",
            "required": false,
            "schema": {
              "example": "2025-03-15",
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Fecha mínima de fin de subasta (date_end)",
            "in": "query",
            "name": "minEnd",
            "required": false,
            "schema": {
              "example": "2025-02-20",
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Fecha máxima de fin de subasta (date_end)",
            "in": "query",
            "name": "maxEnd",
            "required": false,
            "schema": {
              "example": "2025-03-20",
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Fecha mínima de carga (ETL - Estimated Time of Loading)",
            "in": "query",
            "name": "minETL",
            "required": false,
            "schema": {
              "example": "2025-02-10",
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Fecha máxima de carga (ETL)",
            "in": "query",
            "name": "maxETL",
            "required": false,
            "schema": {
              "example": "2025-03-10",
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Fecha mínima de descarga (ETD - Estimated Time of Delivery)",
            "in": "query",
            "name": "minETD",
            "required": false,
            "schema": {
              "example": "2025-02-12",
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Fecha máxima de descarga (ETD)",
            "in": "query",
            "name": "maxETD",
            "required": false,
            "schema": {
              "example": "2025-03-12",
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Busca en date_start O etl_date >= esta fecha",
            "in": "query",
            "name": "minDate",
            "required": false,
            "schema": {
              "example": "2025-02-01",
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Busca en date_end O etd_date <= esta fecha",
            "in": "query",
            "name": "maxDate",
            "required": false,
            "schema": {
              "example": "2025-03-01",
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Búsqueda de texto libre en múltiples campos:\n- service_code (código de servicio)\n- etl_cargo_method (método de carga)\n- pallets_type (tipo de pallets)\n- description (descripción del envío)\n",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "example": "Barcelona Madrid",
              "maxLength": 200,
              "minLength": 1,
              "type": "string"
            }
          },
          {
            "description": "Filtrar por tipo de carga específico",
            "in": "query",
            "name": "cargo_type",
            "required": false,
            "schema": {
              "enum": [
                "pallets",
                "full",
                "package",
                "trailer"
              ],
              "example": "full",
              "type": "string"
            }
          },
          {
            "description": "Filtrar por estado de la subasta",
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "enum": [
                "draft",
                "planned",
                "published",
                "empty",
                "awarded",
                "approved",
                "locked",
                "canceled",
                "rejected"
              ],
              "example": "published",
              "type": "string"
            }
          },
          {
            "description": "Si false, excluye subastas en estado 'draft' de los resultados",
            "in": "query",
            "name": "show_draft",
            "required": false,
            "schema": {
              "default": false,
              "example": true,
              "type": "boolean"
            }
          },
          {
            "description": "Si false, excluye subastas en estado 'empty' de los resultados",
            "in": "query",
            "name": "show_empty",
            "required": false,
            "schema": {
              "default": false,
              "example": false,
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "docs": [
                    {
                      "bids_count": 5,
                      "cargo_type": "full",
                      "cargo_weight": 2500,
                      "date_end": "2025-02-15T20:00:00Z",
                      "date_start": "2025-02-15T08:00:00Z",
                      "etd_address": {
                        "city": "Madrid",
                        "country": "España",
                        "zipcode": "28001"
                      },
                      "etl_address": {
                        "city": "Barcelona",
                        "country": "España",
                        "zipcode": "08001"
                      },
                      "service_code": "POLLIS7jq7n",
                      "start_price": 500,
                      "status": "published"
                    }
                  ],
                  "hasNextPage": false,
                  "hasPrevPage": false,
                  "limit": 20,
                  "page": 1,
                  "totalDocs": 15,
                  "totalPages": 1
                },
                "schema": {
                  "$ref": "#/components/schemas/AuctionListResponse"
                }
              }
            },
            "description": "Lista de subastas obtenida exitosamente"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "CIA_NOT_FOUND",
                  "message": "No se encontró la compañía asociada al usuario"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Compañía no encontrada"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "List company auctions",
        "tags": [
          "Auction - Management"
        ]
      },
      "post": {
        "deprecated": false,
        "description": "Crea una nueva subasta de transporte de mercancías siguiendo un flujo estructado.\nRequiere autenticación JWT válida y verificación de plan de pago activo.\n\n## Propósito\nPermite a las empresas publicar ofertas de transporte para recibir pujas de transportistas.\n\n## Objetivo\nFacilitar la creación de subastas públicas o privadas de transporte con toda la información\nnecesaria para que los transportistas puedan ofertar por el servicio.\n\n## Casos de Uso\n- Crear subasta pública para recibir múltiples ofertas de transportistas\n- Generar borrador de subasta para revisar antes de publicar\n- Guardar configuraciones de rutas frecuentes como favoritos\n\n## Flujo de Validación\n```mermaid\nflowchart TD\n  A[Recibir Request] --> B{Usuario Autenticado?}\n  B -->|No| C[401 Unauthorized]\n  B -->|Sí| D{Plan de Pago Activo?}\n  D -->|No| E[404 No Payment Plan]\n  D -->|Sí| F{Fechas en UTC?}\n  F -->|No| G[400 Invalid Date Format]\n  F -->|Sí| H{Direcciones Válidas?}\n  H -->|No| I[400 Invalid Address]\n  H -->|Sí| J{Pesos/Volumen Positivos?}\n  J -->|No| K[400 Invalid Cargo]\n  J -->|Sí| L[Crear Subasta - 201]\n```\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "award_price": 400,
                "cargo_type": "full",
                "cargo_weight": 2500,
                "date_end": "2035-10-21T23:59:59Z",
                "date_start": "2035-10-21T00:00:00Z",
                "description": "Envío de prueba desde Congalsa a Mercadona Sevilla",
                "etd_address": "6491753fef73c37ccae60bd2",
                "etd_date": "2035-10-24T18:00:00Z",
                "etl_address": "6491759bef73c37ccae60be0",
                "etl_date": "2035-10-24T08:00:00Z",
                "start_price": 500,
                "status": "published"
              },
              "schema": {
                "$ref": "#/components/schemas/AuctionCreateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "cargo_type": "full",
                  "cargo_weight": 2500,
                  "created_at": "2025-02-11T14:30:00Z",
                  "service_code": "POLLIS7jq7n",
                  "start_price": 500,
                  "status": "published"
                },
                "schema": {
                  "$ref": "#/components/schemas/AuctionResponse"
                }
              }
            },
            "description": "Subasta creada exitosamente"
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "invalid_date": {
                    "summary": "Invalid date",
                    "value": {
                      "error": "INVALID_DATE_FORMAT",
                      "message": "Las fechas deben estar en formato UTC (YYYY-MM-DDTHH:mm:ssZ)"
                    }
                  },
                  "invalid_weight": {
                    "summary": "Invalid weight",
                    "value": {
                      "error": "INVALID_CARGO_WEIGHT",
                      "message": "El peso debe ser un número positivo entre 0 y 44000 kg"
                    }
                  },
                  "missing_field": {
                    "summary": "Missing required field",
                    "value": {
                      "error": "MISSING_REQUIRED_FIELD",
                      "message": "Los campos etl_address, etd_address, date_start son obligatorios"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error de validación"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "no_company": {
                    "summary": "Company not found",
                    "value": {
                      "error": "CIA_NOT_FOUND",
                      "message": "No se encontró la compañía asociada al usuario"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Compañía no encontrada o sin plan de pago"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Create New Transport Auction",
        "tags": [
          "Auction - Management"
        ]
      },
      "put": {
        "deprecated": false,
        "description": "Permite modificar una subasta existente. Solo se pueden editar subastas en estado 'draft'.\n\n## Propósito\nProporcionar capacidad de edición de subastas antes de su publicación.\n\n## Objetivo\nPermitir ajustes y correcciones en los datos de la subasta mientras permanece en borrador.\n\n## Casos de Uso\n- Corregir errores en direcciones o fechas antes de publicar\n- Ajustar peso, volumen o tipo de carga\n- Modificar precio inicial o de adjudicación\n- Actualizar descripción o notas adicionales\n- Cambiar estado a 'published' para iniciar período de pujas\n\n## Lógica de Publicación Automática\n\nCuando se actualiza el estado a \"published\", el sistema ejecuta automáticamente:\n\n```mermaid\nflowchart LR\n  A[Status = published] --> B{date_start válida?}\n  B -->|No definida| C[date_start = NOW]\n  B -->|En el pasado| C\n  B -->|Futura| D[Mantener date_start]\n  C --> E[Validar datos completos]\n  D --> E\n  E --> F{Validación OK?}\n  F -->|No| G[400 Validation Error]\n  F -->|Sí| H[Enviar notificaciones]\n  H --> I[Publicar en marketplace]\n```\n\n**Ajuste automático de date_start:**\n- Si date_start no está definida O date_start < fecha actual\n- Se establece date_start = new Date() (fecha/hora actual)\n- Garantiza que subastas publicadas tengan fecha de inicio válida\n\n**Validaciones adicionales al publicar:**\n- date_start y date_end deben existir y ser futuras\n- etl_date y etd_date deben ser válidas\n- Peso y altura de carga deben ser positivos\n- Direcciones deben existir y ser válidas\n\n**Restricción importante:**\n- Solo se pueden editar subastas en estado 'draft'\n- Una vez publicada, locked o awarded, NO se puede editar\n- Para modificar una subasta publicada, usar draft-from-empty\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "cargo_weight": 2800,
                "description": "Envío actualizado con nuevo peso",
                "service_code": "POLLIS7jq7n",
                "status": "draft"
              },
              "schema": {
                "$ref": "#/components/schemas/AuctionUpdateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "cargo_weight": 2800,
                  "service_code": "POLLIS7jq7n",
                  "status": "draft",
                  "updated_at": "2025-02-11T15:45:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/AuctionResponse"
                }
              }
            },
            "description": "Subasta actualizada exitosamente"
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "invalid_status": {
                    "summary": "Estado inválido",
                    "value": {
                      "error": "INVALID_STATUS",
                      "message": "El estado proporcionado no es válido"
                    }
                  },
                  "not_draft": {
                    "summary": "Subasta no editable",
                    "value": {
                      "error": "AUCTION_NOT_EDITABLE",
                      "message": "Solo se pueden editar subastas en estado 'draft'"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error de validación"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "NOT_FOUND",
                  "message": "Subasta no encontrada"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Subasta no encontrada"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Update existing auction",
        "tags": [
          "Auction - Management"
        ]
      }
    },
    "/company/auction/acceptCurrent": {
      "post": {
        "deprecated": false,
        "description": "Acepta la puja ganadora (bidWinner) y ejecuta el proceso de cierre de subasta.\n\n## Propósito\nFormalizar la aceptación de una oferta e iniciar el proceso de ejecución del transporte.\n\n## Objetivo\nCerrar exitosamente una subasta adjudicándola al transportista ganador y generando documentación.\n\n## Casos de Uso\n- Aceptar la mejor oferta económica recibida\n- Adjudicar transporte urgente al primer oferente válido\n- Cerrar subasta después de evaluación de ofertas\n\n## Proceso Ejecutado\n1. Valida que exista bidWinner\n2. Ejecuta cronJobAuc.closeAuctionWin() que:\n   - Cambia estado a 'awarded'\n   - Genera contrato y delivery\n   - Envía notificaciones al transportista\n   - Actualiza registros de facturación\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "service_code": {
                    "description": "Código único de la subasta",
                    "example": "POLLIS7jq7n",
                    "maxLength": 50,
                    "minLength": 5,
                    "type": "string"
                  }
                },
                "required": [
                  "service_code"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "data": {
                      "type": "object"
                    },
                    "status": {
                      "example": true,
                      "type": "boolean"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Puja aceptada exitosamente"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Accept current winning bid",
        "tags": [
          "Auction - Actions"
        ]
      }
    },
    "/company/auction/active": {
      "get": {
        "deprecated": false,
        "description": "Retorna solo las subastas con fecha de finalización (date_end) mayor o igual a la fecha actual.\n\n## Propósito\nProporcionar acceso rápido a subastas que están actualmente recibiendo pujas.\n\n## Objetivo\nFacilitar el monitoreo en tiempo real de subastas activas para toma de decisiones rápidas.\n\n## Casos de Uso\n- Dashboard de subastas en curso\n- Monitoreo de pujas entrantes en tiempo real\n- Alertas de subastas próximas a finalizar\n- Integraciones con sistemas de notificaciones\n",
        "parameters": [
          {
            "description": "Número de página",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "example": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Cantidad de resultados por página",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 10,
              "example": 10,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuctionListResponse"
                }
              }
            },
            "description": "Lista de subastas activas obtenida exitosamente"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Get active auctions",
        "tags": [
          "Auction - Actions"
        ]
      }
    },
    "/company/auction/bulk": {
      "post": {
        "description": "Crea múltiples subastas desde archivo CSV.\n\n## Propósito\nFacilitar carga masiva para alto volumen.\n\n## Objetivo\nAutomatizar creación de múltiples subastas.\n\n## Casos de Uso\n- Importar planificación mensual\n- Cargar desde ERP\n- Automatizar logística enterprise\n\n## Límites\n- Máximo: 500 filas\n- Tamaño: 5MB\n",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "properties": {
                  "data": {
                    "description": "Archivo CSV",
                    "format": "binary",
                    "type": "string"
                  }
                },
                "required": [
                  "data"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "errors": {
                      "items": {
                        "properties": {
                          "error": {
                            "type": "string"
                          },
                          "row": {
                            "type": "integer"
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "success": {
                      "example": 48,
                      "type": "integer"
                    },
                    "total": {
                      "example": 50,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Proceso completado"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Bulk upload from CSV",
        "tags": [
          "Auction - Bulk"
        ]
      }
    },
    "/company/auction/bulk/notes/{lang}": {
      "get": {
        "description": "Retorna instrucciones para carga masiva.\n\n## Propósito\nProporcionar guía completa.\n\n## Objetivo\nMinimizar errores con documentación clara.\n",
        "parameters": [
          {
            "in": "path",
            "name": "lang",
            "required": true,
            "schema": {
              "default": "en",
              "enum": [
                "es",
                "en"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "description": "Instrucciones obtenidas"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Get instructions",
        "tags": [
          "Auction - Bulk"
        ]
      }
    },
    "/company/auction/bulk/template": {
      "get": {
        "description": "Genera plantilla CSV con estructura correcta.\n\n## Propósito\nProporcionar guía para carga masiva.\n\n## Objetivo\nReducir errores con formato correcto.\n",
        "responses": {
          "200": {
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "description": "Plantilla generada"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Download CSV template",
        "tags": [
          "Auction - Bulk"
        ]
      }
    },
    "/company/auction/contract": {
      "post": {
        "description": "Actualiza el contrato con imágenes de entrega y comentarios.\n\n## Propósito\nDocumentar visualmente la entrega.\n\n## Objetivo\nProveer evidencia fotográfica del estado de mercancía.\n\n## Casos de Uso\n- Documentar entrega con fotos\n- Registrar incidencias visuales\n- Completar proceso documentado\n\n**Nota:** Este endpoint aplica únicamente a auctions en estado `approved` (es decir, que tienen un envío asociado).\n",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "properties": {
                  "comment": {
                    "example": "Entrega sin incidencias",
                    "maxLength": 1000,
                    "type": "string"
                  },
                  "images": {
                    "items": {
                      "format": "binary",
                      "type": "string"
                    },
                    "maxItems": 6,
                    "type": "array"
                  },
                  "service_code": {
                    "example": "POLLIS7jq7n",
                    "type": "string"
                  }
                },
                "required": [
                  "service_code"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Contrato actualizado"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Update contract with images",
        "tags": [
          "Auction - Contract"
        ]
      }
    },
    "/company/auction/contract/{service_code}": {
      "delete": {
        "description": "Cancela un servicio de transporte.\n\n## Propósito\nPermitir cancelación cuando sea necesario.\n\n## Objetivo\nFacilitar gestión de imprevistos.\n\n## Casos de Uso\n- Cancelar por cambios en logística\n- Anular por problemas\n",
        "parameters": [
          {
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "example": "POLLIS7jq7n",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cancelado exitosamente"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Cancel service",
        "tags": [
          "Auction - Contract"
        ]
      },
      "get": {
        "description": "Genera y descarga el contrato en formato PDF.\n\n## Propósito\nProporcionar documento contractual oficial.\n\n## Objetivo\nFacilitar descarga para archivo y gestión documental.\n\n## Casos de Uso\n- Descargar para archivo físico\n- Adjuntar a sistemas ERP\n- Auditoría de documentación\n",
        "parameters": [
          {
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "example": "POLLIS7jq7n",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/pdf": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "PDF generado"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Download contract PDF",
        "tags": [
          "Auction - Contract"
        ]
      }
    },
    "/company/auction/draft-from-empty/{service_code}": {
      "put": {
        "deprecated": false,
        "description": "Convierte una subasta en estado 'empty' en un borrador editable.\n\n## Propósito\nProporcionar capacidad de recuperación de subastas vacías para completar su información.\n\n## Objetivo\nEvitar pérdida de subastas iniciadas permitiendo su edición desde estado vacío.\n\n## Casos de Uso\n- Recuperar subasta creada sin datos completos\n- Retomar edición de subasta guardada parcialmente\n- Aprovechar service_code ya generado\n",
        "parameters": [
          {
            "description": "Código de la subasta vacía a convertir",
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "example": "ACOPOLF9d9F",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "service_code": {
                      "example": "ACOPOLF9d9F",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Borrador creado exitosamente"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Convert empty auction to draft",
        "tags": [
          "Auction - Management"
        ]
      }
    },
    "/company/auction/favorites": {
      "get": {
        "deprecated": false,
        "description": "Retorna lista paginada de todas las rutas favoritas guardadas por la compañía.\n\n## Propósito\nProporcionar acceso a todas las plantillas de rutas guardadas.\n\n## Objetivo\nFacilitar la selección y reutilización de configuraciones frecuentes.\n\n## Casos de Uso\n- Autocompletar formularios de creación de subastas\n- Buscar plantillas de rutas específicas\n- Gestionar catálogo de rutas frecuentes\n- Integración con sistemas de planificación\n",
        "parameters": [
          {
            "description": "Número de página",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "example": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Cantidad de resultados por página",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "example": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "docs": {
                      "items": {
                        "$ref": "#/components/schemas/AuctionFavoriteResponse"
                      },
                      "type": "array"
                    },
                    "hasNextPage": {
                      "example": false,
                      "type": "boolean"
                    },
                    "hasPrevPage": {
                      "example": false,
                      "type": "boolean"
                    },
                    "limit": {
                      "example": 20,
                      "type": "integer"
                    },
                    "page": {
                      "example": 1,
                      "type": "integer"
                    },
                    "totalDocs": {
                      "example": 15,
                      "type": "integer"
                    },
                    "totalPages": {
                      "example": 1,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Lista de favoritos obtenida exitosamente"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "List company favorite routes",
        "tags": [
          "Auction - Favorites"
        ]
      },
      "post": {
        "deprecated": false,
        "description": "Guarda una configuración de ruta frecuente como favorito para reutilización futura.\n\n## Propósito\nFacilitar la creación rápida de subastas recurrentes mediante plantillas guardadas.\n\n## Objetivo\nMejorar la eficiencia operativa permitiendo reutilizar configuraciones de rutas frecuentes.\n\n## Casos de Uso\n- Guardar rutas recurrentes semanales o mensuales\n- Crear plantillas para clientes habituales\n- Reutilizar configuraciones complejas de carga\n- Agilizar proceso de creación de subastas similares\n\n## Datos Guardados\n- Direcciones de carga y descarga (etl_address, etd_address)\n- Configuración de carga (tipo, peso, volumen, pallets)\n- Métodos de carga/descarga\n- Configuración de temperatura (si es refrigerado)\n- Horarios preferenciales de carga/descarga\n- Descripción y notas\n",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "cargo_type": "full",
                "cargo_weight": 2500,
                "description": "Envío semanal de mercancía general",
                "etd_address": "6491753fef73c37ccae60bd2",
                "etl_address": "6491759bef73c37ccae60be0",
                "fav_name": "Barcelona-Madrid Semanal",
                "pallets_num": 20,
                "pallets_type": "european"
              },
              "schema": {
                "$ref": "#/components/schemas/AuctionFavoriteRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "status": {
                      "example": true,
                      "type": "boolean"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Favorito creado exitosamente"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Create favorite route",
        "tags": [
          "Auction - Favorites"
        ]
      },
      "put": {
        "deprecated": false,
        "description": "Actualiza la configuración de un favorito existente.\nEl ID puede proporcionarse en el body como \"_id\".\n\n## Propósito\nPermitir ajustes en plantillas de rutas guardadas.\n\n## Objetivo\nMantener actualizadas las configuraciones de rutas frecuentes.\n\n## Casos de Uso\n- Actualizar direcciones después de cambio de almacén\n- Ajustar pesos o volúmenes según nuevos productos\n- Modificar horarios preferenciales\n- Corregir errores en configuración guardada\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/AuctionFavoriteRequest"
                  },
                  {
                    "properties": {
                      "_id": {
                        "description": "ID del favorito a editar",
                        "example": "65f8a1b2c3d4e5f6g7h8i9j0",
                        "type": "string"
                      }
                    },
                    "required": [
                      "_id"
                    ],
                    "type": "object"
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "status": {
                      "example": true,
                      "type": "boolean"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Favorito actualizado exitosamente"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Edit existing favorite",
        "tags": [
          "Auction - Favorites"
        ]
      }
    },
    "/company/auction/favorites/edit": {
      "post": {
        "deprecated": false,
        "description": "Actualiza la configuración de uno o más favoritos existentes mediante un array en el body.\n\n## Propósito\nPermitir edición múltiple de favoritos en una sola petición.\n\n## Objetivo\nMejorar la eficiencia al actualizar varias rutas favoritas simultáneamente.\n\n## Casos de Uso\n- Actualizar múltiples rutas después de cambio de almacén\n- Modificar configuraciones de varios favoritos a la vez\n- Sincronizar favoritos con sistema externo\n",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "favorites": [
                  {
                    "_id": "65f8a1b2c3d4e5f6g7h8i9j0",
                    "cargo_weight": 3000,
                    "fav_name": "Barcelona-Madrid Semanal"
                  },
                  {
                    "_id": "65f8a1b2c3d4e5f6g7h8i9j1",
                    "cargo_type": "pallets",
                    "fav_name": "Valencia-Sevilla"
                  }
                ]
              },
              "schema": {
                "properties": {
                  "favorites": {
                    "description": "Array de favoritos a actualizar",
                    "items": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/AuctionFavoriteRequest"
                        },
                        {
                          "properties": {
                            "_id": {
                              "description": "ID del favorito a editar",
                              "example": "65f8a1b2c3d4e5f6g7h8i9j0",
                              "type": "string"
                            }
                          },
                          "required": [
                            "_id"
                          ],
                          "type": "object"
                        }
                      ]
                    },
                    "type": "array"
                  }
                },
                "required": [
                  "favorites"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "status": {
                      "example": true,
                      "type": "boolean"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Favoritos actualizados exitosamente"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Editar favoritos (bulk)",
        "tags": [
          "Auctions",
          "Auctions - Favorites"
        ]
      }
    },
    "/company/auction/favorites/edit/{id}": {
      "post": {
        "deprecated": false,
        "description": "Actualiza un favorito específico usando el ID en la URL mediante método POST.\nAlternativa a PUT /favorites/{id} con semántica POST.\n\n## Propósito\nProporcionar endpoint POST para edición de favorito individual.\n\n## Objetivo\nFacilitar integración con clientes que prefieren POST sobre PUT.\n",
        "parameters": [
          {
            "description": "ID del favorito a editar",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "65f8a1b2c3d4e5f6g7h8i9j0",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "cargo_weight": 3000,
                "description": "Ruta actualizada con nuevo peso",
                "fav_name": "Barcelona-Madrid Semanal (Actualizado)"
              },
              "schema": {
                "$ref": "#/components/schemas/AuctionFavoriteRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "status": {
                      "example": true,
                      "type": "boolean"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Favorito actualizado exitosamente"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Editar favorito específico por ID en path",
        "tags": [
          "Auctions",
          "Auctions - Favorites"
        ]
      }
    },
    "/company/auction/favorites/{id}": {
      "delete": {
        "deprecated": false,
        "description": "Elimina permanentemente un favorito de la lista.\n\n## Propósito\nPermitir limpieza de plantillas obsoletas o no utilizadas.\n\n## Objetivo\nMantener organizado el catálogo de rutas favoritas.\n\n## Casos de Uso\n- Eliminar rutas que ya no se utilizan\n- Limpiar favoritos duplicados\n- Remover plantillas de clientes inactivos\n",
        "parameters": [
          {
            "description": "ID del favorito a eliminar",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "65f8a1b2c3d4e5f6g7h8i9j0",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "_id": {
                      "example": "65f8a1b2c3d4e5f6g7h8i9j0",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Favorito eliminado exitosamente"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Delete favorite route",
        "tags": [
          "Auction - Favorites"
        ]
      },
      "put": {
        "deprecated": false,
        "description": "Actualiza un favorito específico usando el ID en la URL.\nAlternativa a PUT /favorites con ID en body.\n\n## Propósito\nProporcionar endpoint RESTful estándar para actualización.\n\n## Objetivo\nFacilitar integración con clientes HTTP estándar.\n",
        "parameters": [
          {
            "description": "ID del favorito a editar",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "65f8a1b2c3d4e5f6g7h8i9j0",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AuctionFavoriteRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "status": {
                      "example": true,
                      "type": "boolean"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Favorito actualizado exitosamente"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Edit favorite by path ID",
        "tags": [
          "Auction - Favorites"
        ]
      }
    },
    "/company/auction/lock/{service_code}": {
      "put": {
        "deprecated": false,
        "description": "Bloquea una subasta para evitar modificaciones y nuevas pujas.\n\n## Propósito\nFinalizar el período de recepción de ofertas y preparar para adjudicación.\n\n## Objetivo\nCerrar el proceso de pujas para proceder con la evaluación final y selección.\n\n## Casos de Uso\n- Finalizar período de pujas antes de tiempo límite\n- Preparar subasta para adjudicación inmediata\n- Evitar nuevas ofertas durante evaluación\n\n## Estados Válidos\nSolo se pueden bloquear subastas en: planned, empty, published\n",
        "parameters": [
          {
            "description": "Código único de la subasta a bloquear",
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "example": "SANMADLhhH7",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "properties": {},
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuctionResponse"
                }
              }
            },
            "description": "Subasta bloqueada exitosamente"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Block auction",
        "tags": [
          "Auction - Actions"
        ]
      }
    },
    "/company/auction/private": {
      "post": {
        "deprecated": false,
        "description": "Crea una subasta privada con asignación directa a un transportista específico.\n\n## Propósito\nFacilitar envíos directos sin proceso de subasta pública.\n\n## Objetivo\nPermitir asignación directa a transportistas de confianza o con acuerdos previos.\n\n## Casos de Uso\n- Transporte especializado con transportista certificado\n- Envíos urgentes con transportista disponible inmediatamente\n- Relaciones comerciales establecidas con tarifas negociadas\n- Mercancías peligrosas con licencias específicas\n",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "award_price": 400,
                "cargo_type": "full",
                "cargo_weight": 2500,
                "description": "Envío privado urgente",
                "etd_address": "6491753fef73c37ccae60bd2",
                "etd_date": "2035-10-24T18:00:00Z",
                "etl_address": "6491759bef73c37ccae60be0",
                "etl_date": "2035-10-24T08:00:00Z",
                "start_price": 500,
                "trucker": "68fb5137037ce71173a57e12",
                "vehicle": "68fb50d1037ce71173a57dab"
              },
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/AuctionCreateRequest"
                  },
                  {
                    "properties": {
                      "trucker": {
                        "description": "ID del transportista asignado",
                        "example": "68fb5137037ce71173a57e12",
                        "type": "string"
                      },
                      "vehicle": {
                        "description": "ID del vehículo específico (opcional)",
                        "example": "68fb50d1037ce71173a57dab",
                        "type": "string"
                      }
                    },
                    "required": [
                      "trucker"
                    ],
                    "type": "object"
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuctionResponse"
                }
              }
            },
            "description": "Subasta privada creada exitosamente"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Create private auction",
        "tags": [
          "Auction - Management"
        ]
      }
    },
    "/company/auction/providers": {
      "post": {
        "deprecated": false,
        "description": "Crea una subasta dirigida exclusivamente a una lista de proveedores (TaxIDs) seleccionados.\n\n## Propósito\nRestringir la visibilidad de una subasta a proveedores preseleccionados.\n\n## Objetivo\nFacilitar procesos de cotización cerrados con proveedores homologados.\n\n## Casos de Uso\n- Invitación a transportistas homologados\n- Cotización cerrada con proveedores preferentes\n- Cumplimiento de políticas de compras con lista aprobada\n",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "cargo_type": "full",
                "cargo_weight": 2500,
                "date_end": "2035-10-21T23:59:59Z",
                "date_start": "2035-10-21T00:00:00Z",
                "description": "Subasta para proveedores seleccionados",
                "etd_address": "6491753fef73c37ccae60bd2",
                "etd_date": "2035-10-24T18:00:00Z",
                "etl_address": "6491759bef73c37ccae60be0",
                "etl_date": "2035-10-24T08:00:00Z",
                "providers": [
                  "B12345678",
                  "A87654321"
                ],
                "start_price": 500
              },
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/AuctionCreateRequest"
                  },
                  {
                    "properties": {
                      "providers": {
                        "description": "Lista de TaxIDs de proveedores invitados",
                        "example": [
                          "B12345678",
                          "A87654321"
                        ],
                        "items": {
                          "type": "string"
                        },
                        "minItems": 1,
                        "type": "array"
                      }
                    },
                    "required": [
                      "providers"
                    ],
                    "type": "object"
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuctionResponse"
                }
              }
            },
            "description": "Subasta creada exitosamente"
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Create auction for specific providers",
        "tags": [
          "Auction - Management"
        ]
      }
    },
    "/company/auction/rejectCurrent/{service_code}": {
      "put": {
        "deprecated": false,
        "description": "Rechaza la puja ganadora (bidWinner) y recalcula la siguiente mejor oferta.\n\n## Propósito\nPermitir rechazo de ofertas no satisfactorias y continuar con el proceso de selección.\n\n## Objetivo\nProporcionar flexibilidad en la selección rechazando ofertas inadecuadas.\n\n## Casos de Uso\n- Rechazar oferta de transportista con mal historial\n- Declinar precio fuera de presupuesto\n- Eliminar transportista no confiable del proceso\n\n## Proceso de Rechazo\n1. Encuentra TODAS las pujas del usuario rechazado\n2. Elimina todas las pujas de ese usuario\n3. Recalcula la mejor puja entre las restantes\n4. Actualiza bidWinner con la nueva mejor oferta\n",
        "parameters": [
          {
            "description": "Código único de la subasta",
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "example": "POLLIS7jq7n",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "example": "BID_DELETED",
                      "type": "string"
                    },
                    "status": {
                      "example": true,
                      "type": "boolean"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Puja rechazazada exitosamente"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Reject current winning bid",
        "tags": [
          "Auction - Actions"
        ]
      }
    },
    "/company/auction/sign": {
      "get": {
        "description": "Retorna lista paginada de subastas adjudicadas pendientes de firma.\n\n## Propósito\nFacilitar el seguimiento de contratos pendientes de formalización.\n\n## Objetivo\nProporcionar visibilidad de subastas que requieren firma digital.\n\n## Casos de Uso\n- Dashboard de pendientes de firma\n- Recordatorios de firmas pendientes\n- Gestión de contratos en formalización\n",
        "parameters": [
          {
            "in": "query",
            "name": "page",
            "schema": {
              "default": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 20,
              "minimum": 1,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuctionListResponse"
                }
              }
            },
            "description": "Lista obtenida exitosamente"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Get auctions pending signature",
        "tags": [
          "Auction - Contract"
        ]
      },
      "post": {
        "description": "Firma digitalmente una subasta adjudicada por parte de la compañía.\n\n## Propósito\nFormalizar digitalmente el acuerdo de transporte.\n\n## Objetivo\nCompletar el proceso legal de aceptación del contrato con firma electrónica.\n\n## Casos de Uso\n- Firmar contrato después de revisión\n- Iniciar generación de documentación\n- Activar inicio de servicio\n",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "properties": {
                  "service_code": {
                    "example": "POLLIS7jq7n",
                    "type": "string"
                  },
                  "signImage": {
                    "description": "Imagen de firma (opcional)",
                    "format": "binary",
                    "type": "string"
                  }
                },
                "required": [
                  "service_code"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuctionResponse"
                }
              }
            },
            "description": "Firmado exitosamente"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Sign auction digitally",
        "tags": [
          "Auction - Contract"
        ]
      }
    },
    "/company/auction/{service_code}": {
      "delete": {
        "deprecated": false,
        "description": "Elimina permanentemente una subasta. Solo aplicable a subastas en estado 'draft' o 'empty'.\n\n## Propósito\nPermitir eliminación de subastas no utilizadas o creadas por error.\n\n## Objetivo\nMantener limpia la base de datos eliminando borradores abandonados.\n\n## Casos de Uso\n- Eliminar borradores creados accidentalmente\n- Limpiar subastas vacías no utilizadas\n- Cancelar definitivamente subastas en preparación\n\n## Validaciones\n\n```mermaid\nflowchart TD\n  A[DELETE Request] --> B{Usuario Autenticado?}\n  B -->|No| C[401 Unauthorized]\n  B -->|Sí| D{Subasta Existe?}\n  D -->|No| E[404 Not Found]\n  D -->|Sí| F{Estado = draft o empty?}\n  F -->|No| G[400 Cannot Delete]\n  F -->|Sí| H{Tiene Pujas Aceptadas?}\n  H -->|Sí| I[400 Has Bids]\n  H -->|No| J[Eliminar Subasta - 200]\n```\n\n**Consecuencias:**\n- Eliminación permanente de todos los datos de la subasta\n- Operación NO se puede deshacer\n- Si tiene pujas no aceptadas, estas también se eliminan\n\n**Restricciones:**\n- Solo estados 'draft' o 'empty' pueden eliminarse\n- Compañía debe ser la propietaria\n- No puede tener pujas aceptadas o delivery asociado\n",
        "parameters": [
          {
            "description": "Código único de la subasta a eliminar",
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "example": "BADSEVLgjtf",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "service_code": {
                      "example": "BADSEVLgjtf",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Subasta eliminada exitosamente"
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "has_bids": {
                    "summary": "Tiene pujas aceptadas",
                    "value": {
                      "error": "HAS_ACCEPTED_BIDS",
                      "message": "No se puede eliminar una subasta con pujas aceptadas"
                    }
                  },
                  "invalid_status": {
                    "summary": "Estado no permite eliminación",
                    "value": {
                      "error": "CANNOT_DELETE",
                      "message": "Solo se pueden eliminar subastas en estado 'draft' o 'empty'"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Operación inválida"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "NOT_FOUND",
                  "message": "Subasta no encontrada o no pertenece a la compañía"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Subasta no encontrada"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Delete auction",
        "tags": [
          "Auction - Management"
        ]
      },
      "get": {
        "deprecated": false,
        "description": "Retorna información detallada de una subasta específica usando su código de servicio.\n\n## Propósito\nProporcionar acceso completo a todos los datos de una subasta incluyendo relaciones populadas.\n\n## Objetivo\nPermitir visualización detallada de subastas con toda la información necesaria para\ngestión, seguimiento y toma de decisiones.\n\n## Casos de Uso\n- Ver detalles completos antes de aceptar o rechazar pujas\n- Consultar historial de pujas recibidas\n- Verificar información de direcciones de carga y descarga\n- Revisar estado de firmas digitales\n- Acceder a delivery asociado si existe\n- Verificar ruta calculada con distancia\n\n## Datos Populados\n\nLa respuesta incluye los siguientes campos con información completa:\n\n- **etl_address**: Dirección de carga con location, name_address, name\n- **etd_address**: Dirección de descarga con location, name_address, name\n- **bids**: Array completo de todas las pujas recibidas\n- **bidWinner**: Puja ganadora actual (si existe)\n- **bidAsigned**: Puja asignada final (si existe)\n- **rejectedBy**: Array de IDs de usuarios rechazados\n- **delivery**: ID del delivery asociado (si existe)\n- **routeMinimal**: Objeto con distancia calculada en kilómetros\n",
        "parameters": [
          {
            "description": "Código único de la subasta",
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "example": "POLLIS7jq7n",
              "maxLength": 50,
              "minLength": 5,
              "pattern": "^[A-Z0-9]+$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "award_price": 400,
                  "bidAsigned": {
                    "_id": "65f8a1b2c3d4e5f6g7h8i9j2",
                    "price": 420,
                    "trucker_id": "65f8a1b2c3d4e5f6g7h8i9j3"
                  },
                  "bids": [
                    {
                      "_id": "65f8a1b2c3d4e5f6g7h8i9j0",
                      "created_at": "2025-02-14T10:30:00Z",
                      "price": 450,
                      "trucker_id": "65f8a1b2c3d4e5f6g7h8i9j1"
                    },
                    {
                      "_id": "65f8a1b2c3d4e5f6g7h8i9j2",
                      "created_at": "2025-02-14T11:15:00Z",
                      "price": 420,
                      "trucker_id": "65f8a1b2c3d4e5f6g7h8i9j3"
                    }
                  ],
                  "bids_count": 5,
                  "cargo_height": 250,
                  "cargo_type": "full",
                  "cargo_weight": 2500,
                  "custom_code": "ENV-2025-001",
                  "date_end": "2025-02-15T20:00:00Z",
                  "date_start": "2025-02-15T08:00:00Z",
                  "delivery": "65f8a1b2c3d4e5f6g7h8i9j4",
                  "description": "Envío de mercancía general",
                  "etd_address": {
                    "location": {
                      "coordinates": [
                        -3.7038,
                        40.4168
                      ]
                    },
                    "name": "Centro Distribución",
                    "name_address": "Zona Logística"
                  },
                  "etd_cargo_method": "back",
                  "etd_date": "2025-02-18T18:00:00Z",
                  "etd_extra_time": 30,
                  "etl_address": {
                    "location": {
                      "coordinates": [
                        2.1734,
                        41.3851
                      ]
                    },
                    "name": "Almacén Central",
                    "name_address": "Polígono Industrial"
                  },
                  "etl_cargo_method": "lateral",
                  "etl_date": "2025-02-18T09:00:00Z",
                  "etl_extra_time": 60,
                  "had_etd_cargo_method": true,
                  "had_etl_cargo_method": true,
                  "hscode": "8471.30",
                  "info_extra": "Carga frágil",
                  "is_fresh": false,
                  "is_imperial_measure": false,
                  "linear_meters": 13.6,
                  "pallets_num": 20,
                  "pallets_type": "european",
                  "plate_full_trailer": "ABC1234",
                  "rejectedBy": [],
                  "routeMinimal": {
                    "distance": 621.5
                  },
                  "service_code": "POLLIS7jq7n",
                  "signed_by_company": false,
                  "signed_by_trucker": false,
                  "start_price": 500,
                  "status": "published"
                },
                "schema": {
                  "$ref": "#/components/schemas/AuctionDetailResponse"
                }
              }
            },
            "description": "Detalles de la subasta obtenidos exitosamente"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "NOT_FOUND",
                  "message": "Subasta no encontrada"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Subasta no encontrada"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Get full auction details",
        "tags": [
          "Auction - Management"
        ]
      }
    },
    "/company/auctions/": {
      "get": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "GET /company/auctions/",
        "tags": [
          "Auction"
        ]
      }
    },
    "/company/auctions/:code": {
      "get": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "GET /company/auctions/:code",
        "tags": [
          "Auction"
        ]
      }
    },
    "/company/auctions/favorites": {
      "get": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "GET /company/auctions/favorites",
        "tags": [
          "Auction"
        ]
      }
    },
    "/company/auctions/image/:code": {
      "get": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "GET /company/auctions/image/:code",
        "tags": [
          "Auction"
        ]
      }
    },
    "/company/auctions/nearby": {
      "get": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "GET /company/auctions/nearby",
        "tags": [
          "Auction"
        ]
      }
    },
    "/company/auctions/search-location": {
      "get": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "GET /company/auctions/search-location",
        "tags": [
          "Auction"
        ]
      }
    },
    "/company/auth/account_type": {
      "get": {
        "deprecated": false,
        "description": "Endpoint para obtener la lista de tipos de cuenta disponibles para empresas.\nRetorna las configuraciones de cuenta con sus características y funcionalidades.\n\n### Flujo detallado:\n1. Cliente solicita tipos de cuenta\n2. Sistema retorna lista de configuraciones disponibles\n3. Cliente muestra opciones para que usuario seleccione durante registro\n\n### Tipos de cuenta disponibles:\n\n#### 1. Default (Básica)\n- **id**: 1\n- **value**: `default`\n- **Descripción**: Cuenta estándar para empresas que gestionan su propio transporte\n- **Características**:\n  - `default-optimization`: Optimización básica de rutas\n  - `default-network`: Acceso a red de transportistas\n  - `default-simplified`: Interfaz simplificada\n  - `default-transparency`: Transparencia en procesos\n\n#### 2. Multitenant (Multiempresa)\n- **id**: 2\n- **value**: `multitennant`\n- **Descripción**: Cuenta avanzada para empresas que gestionan flotas múltiples o subsidiarias\n- **Características**:\n  - `multitennant-customization`: Personalización avanzada\n  - `multitennant-confidenciality`: Confidencialidad de datos entre tenancies\n  - `multitennant-efficient`: Gestión eficiente de múltiples entidades\n\n### Ejemplo de respuesta exitosa:\n```json\n[\n  {\n    \"id\": 1,\n    \"value\": \"default\",\n    \"features\": [\n      \"default-optimization\",\n      \"default-network\",\n      \"default-simplified\",\n      \"default-transparency\"\n    ],\n    \"icon\": \"fa-solid fa-truck-fast\",\n    \"active\": true\n  },\n  {\n    \"id\": 2,\n    \"value\": \"multitennant\",\n    \"features\": [\n      \"multitennant-customization\",\n      \"multitennant-confidenciality\",\n      \"multitennant-efficient\"\n    ],\n    \"icon\": \"fa-solid fa-warehouse\",\n    \"active\": true\n  }\n]\n```\n",
        "operationId": "getAccountTypes",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "active": true,
                    "features": [
                      "default-optimization",
                      "default-network",
                      "default-simplified",
                      "default-transparency"
                    ],
                    "icon": "fa-solid fa-truck-fast",
                    "id": 1,
                    "value": "default"
                  },
                  {
                    "active": true,
                    "features": [
                      "multitennant-customization",
                      "multitennant-confidenciality",
                      "multitennant-efficient"
                    ],
                    "icon": "fa-solid fa-warehouse",
                    "id": 2,
                    "value": "multitennant"
                  }
                ],
                "schema": {
                  "items": {
                    "properties": {
                      "active": {
                        "description": "Indica si este tipo de cuenta está activo",
                        "example": true,
                        "type": "boolean"
                      },
                      "features": {
                        "description": "Lista de funcionalidades incluidas",
                        "example": [
                          "default-optimization",
                          "default-network"
                        ],
                        "items": {
                          "type": "string"
                        },
                        "type": "array"
                      },
                      "icon": {
                        "description": "Clase de icono FontAwesome",
                        "example": "fa-solid fa-truck-fast",
                        "type": "string"
                      },
                      "id": {
                        "description": "Identificador único del tipo de cuenta",
                        "example": 1,
                        "type": "integer"
                      },
                      "value": {
                        "description": "Valor interno del tipo de cuenta",
                        "enum": [
                          "default",
                          "multitennant"
                        ],
                        "example": "default",
                        "type": "string"
                      }
                    },
                    "type": "object"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Lista de tipos de cuenta disponibles"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "example": "NO_TOKEN",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No autorizado (si se implementa autenticación)"
          }
        },
        "security": [],
        "summary": "Get Available Account Types",
        "tags": [
          "Auth"
        ]
      }
    },
    "/company/auth/activate": {
      "post": {
        "deprecated": false,
        "description": "Endpoint para que un gestor reenvíe el email de activación a un usuario.\n\n## Propósito\nPermitir que los gestores/administradores reenvíen el email de activación\na usuarios registrados que no lo han recibido o su token ha expirado.\n\n## Casos de Uso\n- Usuario no recibió el email de activación tras el registro\n- El email de activación fue al spam o carpeta de promociones\n- El token anterior expiró y se necesita uno nuevo\n- Administrador reenvía activación a un usuario específico\n\n## Flujo del Proceso\n1. Cliente solicita reenvío de email mediante ID de usuario o email\n2. Middleware m.isGestor verifica que el usuario tiene rol de gestor/admin/dev (401 si no)\n3. Servidor busca usuario por ID o email proporcionado\n4. Si el usuario existe y no está activo, genera nuevo token\n5. Si el usuario ya está activo, puede enviar notificación pero no reactiva\n6. Se envía email de activación con el nuevo token\n7. Se retorna confirmación de envío al cliente\n8. Por seguridad, no se revela si el email existe cuando no se encuentra\n\n## Seguridad\n- Se genera nuevo token para seguridad\n- Requiere permisos de gestión (rol gestor, admin o dev)\n- No revela si el usuario existe (por seguridad)\n- Requiere parámetro ID o email (al menos uno)\n- Token anterior queda invalidado con el nuevo envío\n",
        "operationId": "resendActivationEmail",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "example": {
                  "email": "usuario@empresa.com"
                },
                "properties": {
                  "email": {
                    "description": "Email del usuario",
                    "format": "email",
                    "type": "string"
                  },
                  "id": {
                    "description": "ID del usuario",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "email": "usuario@empresa.com"
                },
                "schema": {
                  "properties": {
                    "email": {
                      "example": "usuario@empresa.com",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Email de activación reenviado"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "UNAUTHORIZED",
                  "status": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado - requiere rol de gestor"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND",
                  "status": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Resend Activation Email (Admin)",
        "tags": [
          "Auth"
        ]
      }
    },
    "/company/auth/activate/{token}": {
      "get": {
        "deprecated": false,
        "description": "Endpoint para activar una cuenta de empresa utilizando el token de activación\nenviado por correo electrónico, verificando el email y habilitando el acceso.\n\n## Propósito\nActivar una cuenta de empresa recién registrada mediante el token de activación\nenviado por correo electrónico, verificando el email y habilitando el acceso.\n\n## Casos de Uso\n- Usuario hace clic en el enlace de activación del email de registro\n- Usuario accede directamente a la URL con el token de activación\n- Sistema activa la cuenta tras verificar el token\n\n## Flujo del Proceso\n1. Usuario recibe email de bienvenida con enlace de activación\n2. Usuario hace clic en el enlace que apunta a este endpoint GET\n3. Servidor busca usuario por token de recuperación en base de datos\n4. Si el usuario existe y el token es válido, marca cuenta como activa\n5. Se establece status: true, emailVerified: true y emailVerifiedDate\n6. Se elimina el token de activación (marca como usado)\n7. Se envía email de confirmación de activación al usuario\n8. Se renderiza plantilla HTML de éxito\n9. Si el token no es válido, se renderiza error\n\n## Seguridad\n- Token es de un solo uso\n- Solo cuentas inactivas pueden ser activadas\n- Se verifica email en el momento de activación\n- Token tiene validez limitada\n- No requiere autenticación (usuario aún no puede acceder)\n",
        "operationId": "activateAccountByToken",
        "parameters": [
          {
            "description": "Token de activación recibido por email",
            "in": "path",
            "name": "token",
            "required": true,
            "schema": {
              "example": "xyz123abc456",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "text/html": {
                "example": "<html><body>Cuenta activada correctamente</body></html>",
                "schema": {
                  "type": "string"
                }
              }
            },
            "description": "Cuenta activada exitosamente"
          },
          "404": {
            "content": {
              "text/html": {
                "example": "<html><body>Token no válido o expirado</body></html>",
                "schema": {
                  "type": "string"
                }
              }
            },
            "description": "Token no encontrado o inválido"
          }
        },
        "security": [],
        "summary": "Activate Account (User Link)",
        "tags": [
          "Auth"
        ]
      }
    },
    "/company/auth/isComplete": {
      "get": {
        "deprecated": false,
        "description": "Endpoint para verificar si el perfil del usuario de empresa tiene todos\nlos datos obligatorios completos.\n\n## Propósito\nVerificar que el perfil del usuario de empresa esté completo antes de permitir\nciertas acciones que requieren información completa (crear subastas, publicar ofertas).\n\n## Casos de Uso\n- Sistema verifica completitud del perfil antes de permitir crear subastas\n- UI muestra indicador de perfil incompleto al usuario\n- Se muestra mensaje de acción requerida para completar perfil\n- Verificación periódica en dashboard del usuario\n\n## Flujo del Proceso\n1. Usuario autenticado solicita verificación de perfil\n2. Middleware m.isLoged verifica que el usuario está autenticado (401 si no)\n3. Cliente envía GET con token JWT en headers\n4. Servidor valida token JWT y extrae ID de usuario\n5. Servidor busca usuario en base de datos por ID\n6. Sistema verifica campos obligatorios del perfil\n7. Si todos los campos requeridos están completos, retorna true\n8. Si faltan campos requeridos, retorna false\n9. El indicador permite al frontend mostrar mensajes apropiados\n\n## Campos Verificados\n- Nombre completo\n- Email válido y verificado\n- Datos de empresa completos\n- Dirección fiscal configurada\n- Teléfono de contacto\n- Información de pagos configurada\n\n## Seguridad\n- Requiere usuario autenticado (JWT válido)\n- No expone información específica sobre qué campos faltan\n",
        "operationId": "checkProfileComplete",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "isCompleted": true
                },
                "schema": {
                  "properties": {
                    "isCompleted": {
                      "description": "true si el perfil está completo",
                      "type": "boolean"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Estado de completitud del perfil"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "UNAUTHORIZED",
                  "status": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autenticado"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Check if Profile is Complete",
        "tags": [
          "Auth"
        ]
      }
    },
    "/company/auth/login": {
      "post": {
        "deprecated": false,
        "description": "Endpoint para autenticar usuarios administrativos de empresas registradas.\nRealiza las siguientes operaciones:\n1. Valida formato de email y contraseña\n2. Busca usuario en base de datos (tanto en company_user como trucker_user como fallback)\n3. Verifica contraseña con hash almacenado\n4. Comprueba estado de la cuenta (activa/bloqueada)\n5. Registra última IP y fecha de acceso\n6. Genera token JWT con claims del usuario\n7. Registra acceso en historial\n8. Verifica/crea cuenta Stripe si no existe\n9. Obtiene estado de suscripción\n\n### Flujo detallado:\n1. Cliente envía email y contraseña en formato JSON\n2. Servidor valida campos obligatorios (400 si faltan)\n3. Busca usuario por email (400 si no existe)\n4. Verifica contraseña con bcrypt (400 si no coincide)\n5. Comprueba estado de cuenta (401 si inactiva/bloqueada)\n6. Actualiza últimos datos de acceso (IP, fecha)\n7. Genera token JWT con datos del usuario y empresa\n8. Verifica/crea cuenta Stripe si no existe\n9. Obtiene estado de suscripción del usuario\n10. Devuelve token con datos completos del usuario\n\n### Seguridad:\n- Requiere HTTPS\n- Contraseñas se transmiten hasheadas\n- Tokens tienen expiración configurable\n\n### Ejemplo de petición:\n```json\n{\n  \"email\": \"admin@empresa.com\",\n  \"password\": \"ContraseñaSegura123!\"\n}\n```\n\n### Ejemplo de respuesta exitosa:\n```json\n{\n  \"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\",\n  \"expiresIn\": 1712345678901,\n  \"_id\": \"507f1f77bcf86cd799439011\",\n  \"role\": \"admin\",\n  \"status\": true,\n  \"emailVerified\": true,\n  \"refresh_time\": 3,\n  \"name\": \"Juan\",\n  \"lastname\": \"Pérez García\",\n  \"country\": \"esp\",\n  \"accountType\": \"multitennant\",\n  \"hasSign\": true,\n  \"hasPaymentMethod\": true,\n  \"subscriptionStatus\": {\n    \"hasSubscription\": true,\n    \"subscriptionType\": \"Basic Plan\"\n  }\n}\n```\n\n### Códigos de error:\n- 400: Datos faltantes, formato inválido, o credenciales incorrectas\n- 401: Compañía no encontrada, usuario no activo, o cuenta bloqueada\n- 500: Error interno del servidor\n",
        "operationId": "companyLogin",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "email": "companytest@test.com",
                "password": "Test1234"
              },
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResponse"
                }
              }
            },
            "description": "Autenticación exitosa",
            "headers": {}
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "enum": [
                        "FORM_DATA_NOT_VALID",
                        "WRONG_CREDENTIALS"
                      ],
                      "example": "WRONG_CREDENTIALS",
                      "type": "string"
                    },
                    "status": {
                      "example": 400,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Datos faltantes o credenciales incorrectas",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "enum": [
                        "CIA_NOT_FOUND",
                        "USER_NOT_ACTIVE",
                        "ACCOUNT_BLOCKED"
                      ],
                      "example": "USER_NOT_ACTIVE",
                      "type": "string"
                    },
                    "status": {
                      "example": 401,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Usuario o compañía no válidos",
            "headers": {}
          }
        },
        "security": [],
        "summary": "Enterprise User Authentication",
        "tags": [
          "Auth"
        ]
      }
    },
    "/company/auth/recovery": {
      "post": {
        "deprecated": false,
        "description": "Endpoint para solicitar la recuperación de contraseña de usuarios de empresa.\nRealiza las siguientes operaciones:\n1. Valida el formato del email\n2. Busca el usuario en la base de datos\n3. Genera un token de recuperación único\n4. Envía email con enlace para restablecer contraseña\n\n### Flujo detallado:\n1. Cliente envía email en formato JSON\n2. Servidor valida formato de email (401 si inválido)\n3. Busca usuario por email\n4. Genera token aleatorio\n5. Almacena token en base de datos asociado al usuario\n6. Envía email con enlace que contiene el token\n7. El enlace redirige a formulario para nueva contraseña\n\n### Seguridad:\n- Por seguridad, siempre retorna 200 OK para no revelar emails registrados\n- Los tokens tienen validez limitada\n- Cada token es de un solo uso\n- Requiere HTTPS\n- Verifica también en usuarios deshabilitados (soft delete)\n\n### Comportamiento especial:\n- Si el email no existe: retorna 200 con `USER_NOT_FOUND`\n- Si el usuario está deshabilitado: retorna 200 con `USER_DISABLED`\n- Usuarios demo (cia@testing.com, ciamu@testing.com): retornan 200 sin enviar email\n\n### Ejemplo de petición:\n```json\n{\n  \"email\": \"usuario@empresa.com\"\n}\n```\n\n### Ejemplos de respuesta:\nEmail válido encontrado:\n```json\n{\n  \"op\": \"recovery\",\n  \"message\": \"RECOVERY_EMAIL_SENT\",\n  \"email\": \"usuario@empresa.com\"\n}\n```\n\nEmail no encontrado (por seguridad retorna 200):\n```json\n{\n  \"op\": \"recovery\",\n  \"message\": \"USER_NOT_FOUND\",\n  \"email\": \"noexiste@empresa.com\"\n}\n```\n\nUsuario deshabilitado:\n```json\n{\n  \"op\": \"recovery\",\n  \"message\": \"USER_DISABLED\",\n  \"email\": \"usuario@empresa.com\"\n}\n```\n\n### Códigos de error:\n- 401: Email no proporcionado\n- 500: Error interno del servidor\n",
        "operationId": "companyRecovery",
        "parameters": [
          {
            "description": "Idioma para el email de recuperación",
            "in": "query",
            "name": "lang",
            "required": false,
            "schema": {
              "default": "en",
              "enum": [
                "en",
                "es"
              ],
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RecoveryRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "email": {
                      "example": "usuario@empresa.com",
                      "format": "email",
                      "type": "string"
                    },
                    "message": {
                      "enum": [
                        "RECOVERY_EMAIL_SENT",
                        "USER_NOT_FOUND",
                        "USER_DISABLED"
                      ],
                      "example": "RECOVERY_EMAIL_SENT",
                      "type": "string"
                    },
                    "op": {
                      "enum": [
                        "recovery"
                      ],
                      "example": "recovery",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Procesado (siempre retorna 200 por seguridad)",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "example": "NOT_VALID",
                      "type": "string"
                    },
                    "status": {
                      "example": 401,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Email no proporcionado o formato inválido",
            "headers": {}
          }
        },
        "security": [],
        "summary": "Password Recovery Request",
        "tags": [
          "Auth"
        ]
      }
    },
    "/company/auth/recovery/{token}": {
      "get": {
        "deprecated": false,
        "description": "Endpoint para mostrar el formulario de cambio de contraseña tras recibir el email de recuperación.\n\n## Propósito\nRenderizar una plantilla HTML que permite al usuario establecer una nueva contraseña\nmediante el token de recuperación recibido por email.\n\n## Casos de Uso\n- Usuario hace clic en el enlace de recuperación del email\n- Usuario accede directamente a la URL con el token de recuperación\n- El navegador redirige al formulario de cambio de contraseña\n\n## Flujo del Proceso\n1. Usuario recibe email de recuperación con enlace al token\n2. Usuario hace clic en el enlace que apunta a este endpoint GET\n3. Servidor valida el token de recuperación en base de datos\n4. Si el token es válido y corresponde a un usuario, renderiza formulario HTML\n5. Si el token es inválido o ha expirado, muestra error\n6. El formulario permite ingresar nueva contraseña y confirmación\n7. Al enviar el formulario, se llama al endpoint POST /company/auth/recovery_password\n\n## Seguridad\n- El token es de un solo uso\n- El token tiene validez limitada\n- No requiere autenticación (usuario aún no puede acceder)\n- Se valida que el token exista y sea válido\n",
        "operationId": "getCompanyRecoveryPage",
        "parameters": [
          {
            "description": "Token de recuperación recibido por email",
            "in": "path",
            "name": "token",
            "required": true,
            "schema": {
              "example": "a1b2c3d4e5f6g7h8i9j0",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "text/html": {
                "example": "<html><body>Formulario de cambio de contraseña</body></html>",
                "schema": {
                  "type": "string"
                }
              }
            },
            "description": "Página HTML renderizada con formulario de cambio de contraseña"
          },
          "404": {
            "content": {
              "text/html": {
                "example": "<html><body>Token inválido o expirado</body></html>",
                "schema": {
                  "type": "string"
                }
              }
            },
            "description": "Token no encontrado o inválido"
          }
        },
        "security": [],
        "summary": "Render Password Recovery Page",
        "tags": [
          "Auth"
        ]
      }
    },
    "/company/auth/recovery_password": {
      "post": {
        "deprecated": false,
        "description": "Endpoint para restablecer la contraseña de un usuario utilizando el token de recuperación recibido por email.\n\n## Propósito\nPermitir que un usuario establezca una nueva contraseña después de olvidar la anterior,\nvalidando el token de recuperación recibido por correo electrónico.\n\n## Casos de Uso\n- Usuario completa el formulario de recuperación con nueva contraseña\n- Usuario envía los datos del formulario al backend\n- Sistema actualiza la contraseña y notifica el éxito\n\n## Flujo del Proceso\n1. Usuario completa el formulario de recuperación con nueva contraseña\n2. Cliente envía POST con token, password y password_confirm\n3. Servidor valida que las contraseñas estén presentes y no estén vacías\n4. Servidor valida que password y password_confirm coincidan\n5. Servidor busca usuario por token de recuperación en base de datos\n6. Si el token es válido, actualiza la contraseña con hash bcrypt\n7. Se elimina el token de recuperación (marca como usado)\n8. Se renderiza plantilla HTML de éxito\n9. Si el token no existe, renderiza error\n\n## Seguridad\n- Contraseña se hashea con bcrypt antes de guardar\n- Token se elimina tras uso (no reutilizable)\n- Contraseña nueva debe tener mínimo 8 caracteres\n- Se valida que password y password_confirm coincidan exactamente\n",
        "operationId": "changePasswordByToken",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "password": "NuevaContraseña123!",
                "password_confirm": "NuevaContraseña123!",
                "token": "a1b2c3d4e5f6g7h8i9j0"
              },
              "schema": {
                "properties": {
                  "password": {
                    "description": "Nueva contraseña",
                    "minLength": 8,
                    "type": "string"
                  },
                  "password_confirm": {
                    "description": "Confirmación de nueva contraseña",
                    "type": "string"
                  },
                  "token": {
                    "description": "Token de recuperación recibido por email",
                    "type": "string"
                  }
                },
                "required": [
                  "token",
                  "password",
                  "password_confirm"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "text/html": {
                "example": "<html><body>Contraseña actualizada correctamente</body></html>",
                "schema": {
                  "type": "string"
                }
              }
            },
            "description": "Página HTML de éxito"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "message": "PASSWORD_NOT_MATCH",
                  "status": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Contraseñas no coinciden o datos inválidos"
          },
          "404": {
            "content": {
              "text/html": {
                "example": "<html><body>Token no válido o expirado</body></html>",
                "schema": {
                  "type": "string"
                }
              }
            },
            "description": "Token no encontrado"
          }
        },
        "security": [],
        "summary": "Change Password with Recovery Token",
        "tags": [
          "Auth"
        ]
      }
    },
    "/company/auth/register": {
      "post": {
        "deprecated": false,
        "description": "Endpoint para registrar nuevas empresas y su usuario administrador inicial.\nRealiza las siguientes operaciones:\n1. Valida datos obligatorios (razón social, contraseñas coincidentes)\n2. Verifica unicidad del email\n3. Crea dirección fiscal\n4. Crea empresa en base de datos\n5. Crea usuario administrador\n6. Configura cuenta Stripe (customer + connected account)\n7. Envía email de bienvenida\n8. Crea tarifa inicial gratuita por 1 mes\n9. Vincula con proveedor existente si el taxid coincide\n\n### Flujo detallado:\n1. Cliente envía datos de empresa y usuario en formato JSON\n2. Servidor valida campos obligatorios (406 si faltan)\n3. Verifica taxid según normas fiscales españolas (406 si inválido)\n4. Comprueba unicidad de email (403 si existe)\n5. Crea dirección fiscal con datos proporcionados\n6. Registra empresa en base de datos\n7. Crea usuario administrador con contraseña hasheada\n8. Configura cuenta Stripe (customer + connected account) para pagos futuros\n9. Envía email de activación con token único\n10. Crea tarifa inicial gratuita por 30 días\n11. Si existe un company_provider con el mismo taxid, lo vincula\n\n### Requisitos de datos:\n- Taxid/NIF/CIF válido según normativa española (opcional pero recomendado)\n- Razón social completa\n- Email corporativo no registrado\n- Contraseña segura (mínimo 8 caracteres, mayúsculas, números y especiales)\n- Dirección fiscal completa\n\n### Estructura del request:\n```json\n{\n  \"user\": {\n    \"email\": \"admin@empresa.com\",\n    \"password\": \"ContraseñaSegura123!\",\n    \"password_confirm\": \"ContraseñaSegura123!\"\n  },\n  \"invoice_data\": {\n    \"taxid\": \"B12345678\",\n    \"email\": \"admin@empresa.com\",\n    \"socialName\": \"Transportes Ejemplo S.L.\",\n    \"phone\": \"+34123456789\"\n  },\n  \"socialName\": \"Transportes Ejemplo S.L.\",\n  \"address\": {\n    \"place_id\": \"ChIJgTwKgJcpQg0RaSKMYcHeNs\",\n    \"formatted_address\": \"Calle Mayor 123, Madrid, 28013, Spain\",\n    \"geometry\": {\n      \"location\": { \"lat\": 40.4168, \"lng\": -3.7038 }\n    },\n    \"phone\": \"+34123456789\"\n  }\n}\n```\n\n### Códigos de error:\n- 400: Error al crear dirección\n- 403: Email ya registrado (COMPANY_EXISTS)\n- 406: Datos faltantes, taxid inválido, contraseñas no coinciden, usuario ya existe, usuario deshabilitado recuperable, o error en asignación de compañía\n- 500: Error interno del servidor\n\n### Notas importantes:\n- El endpoint retorna un objeto vacío `{}` en caso de éxito\n- Si el email del usuario ya existe (incluso deshabilitado), retorna error\n- Si existe un company_provider con el mismo taxid, se vincula automáticamente\n- Se crea automáticamente una cuenta Stripe (customer + connected account)\n",
        "operationId": "companyRegister",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "address": {
                  "address_components": [
                    {
                      "long_name": "123",
                      "types": [
                        "street_number"
                      ]
                    },
                    {
                      "long_name": "Calle Mayor",
                      "types": [
                        "route"
                      ]
                    },
                    {
                      "long_name": "Madrid",
                      "types": [
                        "locality",
                        "administrative_area_level_2"
                      ]
                    },
                    {
                      "long_name": "Madrid",
                      "types": [
                        "administrative_area_level_1"
                      ]
                    },
                    {
                      "long_name": "28013",
                      "types": [
                        "postal_code"
                      ]
                    },
                    {
                      "long_name": "Spain",
                      "types": [
                        "country"
                      ]
                    }
                  ],
                  "formatted_address": "Calle Mayor 123, Madrid, 28013, Spain",
                  "geometry": {
                    "location": {
                      "lat": 40.4168,
                      "lng": -3.7038
                    }
                  },
                  "phone": "+34123456789",
                  "place_id": "ChIJgTwKgJcpQg0RaSKMYcHeNs"
                },
                "invoice_data": {
                  "email": "admin@transportesejemplo.com",
                  "phone": "+34123456789",
                  "socialName": "Transportes Ejemplo S.L.",
                  "taxid": "B12345678"
                },
                "socialName": "Transportes Ejemplo S.L.",
                "user": {
                  "email": "admin@transportesejemplo.com",
                  "password": "Transportes2024!",
                  "password_confirm": "Transportes2024!"
                }
              },
              "schema": {
                "$ref": "#/components/schemas/RegisterRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "example": {},
                  "type": "object"
                }
              }
            },
            "description": "Registro exitoso (retorna objeto vacío)",
            "headers": {}
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "example": "Error creating address",
                      "type": "string"
                    },
                    "status": {
                      "example": 400,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Error al crear dirección o UTC inválido",
            "headers": {}
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "existingCompany": {
                      "type": "object"
                    },
                    "message": {
                      "example": "COMPANY_EXISTS",
                      "type": "string"
                    },
                    "status": {
                      "example": 403,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Email ya registrado",
            "headers": {}
          },
          "406": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "enum": [
                        "NAME_REQUIRED",
                        "PASSWORD_NOT_MATCH",
                        "USER_ALREADY_EXISTS",
                        "USER_DISABLED_RECOVERABLE",
                        "CIA_ASIGN_FAILED",
                        "TRUCKER_DATA_INCOMPLETE"
                      ],
                      "example": "PASSWORD_NOT_MATCH",
                      "type": "string"
                    },
                    "status": {
                      "example": 406,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Error de validación",
            "headers": {}
          }
        },
        "security": [],
        "summary": "Register New Company",
        "tags": [
          "Auth"
        ]
      }
    },
    "/company/auth/register2": {
      "get": {
        "deprecated": false,
        "description": "Endpoint para validar tokens de invitación enviados a proveedores para participar en subastas.\nDecodifica el token JWT y retorna la información de la subasta y proveedor invitado.\n\n### Flujo detallado:\n1. Administrador de compañía invita a proveedor a una subasta\n2. Sistema genera token JWT con datos de subasta y proveedor\n3. Proveedor recibe email con enlace de registro\n4. Proveedor accede a enlace que llama este endpoint\n5. Sistema valida token y retorna datos\n6. Proveedor completa registro con datos precargados\n\n### Seguridad:\n- Token JWT firmado con secret del sistema\n- Token tiene expiración (time-to-live)\n- Valida que la subasta exista\n- Verifica si el usuario ya existe\n\n### Información del token:\nEl token JWT contiene:\n- `auction`: Código de servicio de la subasta\n- `email`: Email del proveedor invitado\n- `name`: Nombre del proveedor\n- `taxid`: NIF/CIF del proveedor\n- `exp`: Expiración del token\n\n### Ejemplo de petición:\n```\nGET /company/auth/register2?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\n```\n\n### Ejemplo de respuesta exitosa (token válido):\n```json\n{\n  \"status\": true,\n  \"data\": {\n    \"auction\": \"ABC123\",\n    \"email\": \"proveedor@transportes.com\",\n    \"name\": \"Transportes Rápidos S.L.\",\n    \"taxid\": \"B87654321\",\n    \"userExists\": false\n  }\n}\n```\n\n### Códigos de error:\n- 400: Token no proporcionado\n- 418: Token inválido, expirado, o subasta no encontrada\n",
        "operationId": "validateInvitationToken",
        "parameters": [
          {
            "description": "Token JWT de invitación enviado por email al proveedor",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWN0aW9uIjoiQUJDMTIzIiwiZW1haWwiOiJwcm92ZWVkb3JAdHJhbnNwb3J0ZXMuY29tIn0.signature",
            "in": "query",
            "name": "token",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "valid_token_existing_user": {
                    "summary": "Token válido, usuario ya existe",
                    "value": {
                      "data": {
                        "auction": "ABC123",
                        "email": "proveedor@transportes.com",
                        "name": "Transportes Rápidos S.L.",
                        "taxid": "B87654321",
                        "userExists": true
                      },
                      "status": true
                    }
                  },
                  "valid_token_new_user": {
                    "summary": "Token válido, usuario nuevo",
                    "value": {
                      "data": {
                        "auction": "ABC123",
                        "email": "proveedor@transportes.com",
                        "name": "Transportes Rápidos S.L.",
                        "taxid": "B87654321",
                        "userExists": false
                      },
                      "status": true
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "Datos de la invitación decodificados del token",
                      "properties": {
                        "auction": {
                          "description": "Código de servicio de la subasta",
                          "example": "ABC123",
                          "type": "string"
                        },
                        "email": {
                          "description": "Email del proveedor invitado",
                          "example": "proveedor@transportes.com",
                          "format": "email",
                          "type": "string"
                        },
                        "name": {
                          "description": "Nombre del proveedor",
                          "example": "Transportes Rápidos S.L.",
                          "type": "string"
                        },
                        "taxid": {
                          "description": "NIF/CIF del proveedor",
                          "example": "B87654321",
                          "type": "string"
                        },
                        "userExists": {
                          "description": "Indica si el usuario ya existe en el sistema",
                          "example": false,
                          "type": "boolean"
                        }
                      },
                      "type": "object"
                    },
                    "status": {
                      "description": "Indica si el token es válido",
                      "example": true,
                      "type": "boolean"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Token válido, datos de la invitación recuperados"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "example": "Token is required",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Token no proporcionado"
          },
          "418": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "enum": [
                        "Invalid or expired token",
                        "Auction not found"
                      ],
                      "example": "Invalid or expired token",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Token inválido, expirado, o subasta no encontrada"
          }
        },
        "security": [],
        "summary": "Validate Invitation Token for Provider Registration",
        "tags": [
          "Auth"
        ]
      }
    },
    "/company/auth/reset/{id}": {
      "post": {
        "deprecated": false,
        "description": "Endpoint para resetear la contraseña de un usuario (operación administrativa).\nGenera una nueva contraseña aleatoria y la envía por email.\n\n## Propósito\nPermitir que los administradores reseteen la contraseña de cualquier usuario\ngenerando una contraseña temporal segura y enviándola por email.\n\n## Casos de Uso\n- Administrador necesita resetear contraseña de un usuario bloqueado\n- Usuario no puede acceder y requiere intervención administrativa\n- Soporte técnico resetea credenciales de usuario\n\n## Flujo del Proceso\n1. Administrador solicita reset de contraseña de usuario\n2. Middleware m.isGestor verifica que el usuario tiene rol de gestor/admin/dev (401 si no)\n3. Cliente envía POST con ID del usuario en la URL\n4. Servidor busca usuario por ID en base de datos\n5. Si el usuario no existe, retorna error 404\n6. Si el usuario existe, genera contraseña aleatoria segura\n7. Hashea la nueva contraseña con bcrypt\n8. Guarda nueva contraseña en base de datos\n9. Envía email con la nueva contraseña generada\n10. Retorna datos del usuario actualizados (sin incluir la contraseña)\n11. Registra acción en historial de seguridad\n\n## Seguridad\n- Requiere autenticación y permisos de gestión (rol gestor, admin o dev)\n- Contraseña generada aleatoriamente es segura (mínimo 8 caracteres)\n- Se envía por email para asegurar que el usuario real la reciba\n- Requiere autorización adecuada para realizar esta operación\n",
        "operationId": "resetPasswordAdmin",
        "parameters": [
          {
            "description": "ID del usuario",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "507f1f77bcf86cd799439011",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "email": "usuario@empresa.com",
                  "id": "507f1f77bcf86cd799439011",
                  "name": "Juan"
                },
                "schema": {
                  "properties": {
                    "email": {
                      "type": "string"
                    },
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Contraseña reseteada exitosamente",
            "headers": {}
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "message": "ERROR_RESET_PASSWORD",
                  "status": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error al resetear contraseña",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "UNAUTHORIZED",
                  "status": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado - requiere rol de gestor",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND",
                  "status": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Reset User Password (Admin)",
        "tags": [
          "Auth"
        ]
      }
    },
    "/company/bid-auctions/": {
      "get": {
        "deprecated": false,
        "description": "## Propósito\nEndpoint para buscar subastas de transporte disponibles con filtros avanzados.\nPermite filtrar por ubicaciones de carga/descarga, tipo de carga y paginación.\n\n## Objetivo\nFacilitar a los transportistas la búsqueda de subastas publicadas que coincidan con\nsus capacidades y rutas de interés, optimizando el proceso de encuentros entre\noferta y demanda de transporte.\n\n## Casos de Uso\n- Transportista buscando cargas disponibles en una ruta específica (ej: Madrid a Barcelona)\n- Transportista con vehículos refrigerados buscando carga de temperatura controlada\n- Empresas verificando subastas activas en el mercado\n- Integración con sistemas de gestión de flotas para automatizar la búsqueda de cargas\n\n**Ejemplo de uso**:\n```bash\nGET /company/bid-auctions?addr_load=Madrid&addr_delivery=Barcelona&is_fresh=true&page=2\n```\n",
        "parameters": [
          {
            "description": "Número de página para paginación.\nValor por defecto 1. Mínimo 1.\n",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "example": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Cantidad máxima de resultados por página.\nValor por defecto 20. Rango permitido 1-100.\n",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "example": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Ciudad o dirección de carga para filtrar subastas.\nBúsqueda por coincidencia parcial (case insensitive).\nEjemplo: \"Madrid\" o \"28001\"\n",
            "in": "query",
            "name": "addr_load",
            "required": false,
            "schema": {
              "example": "Madrid",
              "maxLength": 100,
              "minLength": 2,
              "type": "string"
            }
          },
          {
            "description": "Ciudad o dirección de descarga para filtrar subastas.\nBúsqueda por coincidencia parcial (case insensitive).\nEjemplo: \"Barcelona\" o \"08001\"\n",
            "in": "query",
            "name": "addr_delivery",
            "required": false,
            "schema": {
              "example": "Barcelona",
              "maxLength": 100,
              "minLength": 2,
              "type": "string"
            }
          },
          {
            "description": "Filtro para carga refrigerada.\n- true: solo subastas que requieren refrigeración\n- false: solo subastas que no requieren refrigeración\n- omitir: incluir todos los tipos\n",
            "in": "query",
            "name": "is_fresh",
            "required": false,
            "schema": {
              "example": false,
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "docs": [
                    {
                      "_id": "64917511ef73c37ccae60bc4",
                      "date_end": "2025-10-26T18:00:00.000Z",
                      "etl_date": "2025-10-27T10:00:00.000Z",
                      "service_code": "TRANS-12345",
                      "status": "published"
                    }
                  ],
                  "limit": 20,
                  "page": 2,
                  "pages": 8,
                  "total": 150
                },
                "schema": {
                  "$ref": "#/components/schemas/AuctionsResponse"
                }
              }
            },
            "description": "Subastas encontradas exitosamente.\nDevuelve array de subastas con metadatos de paginación.\n",
            "headers": {}
          },
          "400": {
            "description": "Parámetros inválidos. Posibles causas:\n- Formato de página o límite incorrecto\n- Valor booleano inválido para is_fresh\n",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. Token JWT inválido, expirado o no proporcionado.\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Search available auctions",
        "tags": [
          "Bid Auctions"
        ]
      },
      "post": {
        "deprecated": false,
        "description": "## Propósito\nPermite a un transportista realizar una puja por una subasta de transporte disponible.\n\n## Objetivo\nPermitir que los transportistas oferten su precio por un servicio de transporte,\ncompitiendo con otros transportistas para ganar la subasta.\n\n## Casos de Uso\n- Transportista encontró una subasta interesante y quiere ofrecer su servicio\n- Transportista quiere mejorar su puja anterior con un monto menor\n- Integración con sistemas automatizados de pujas\n\n## Validaciones\n- El servicio debe estar en estado 'published'\n- El monto debe ser positivo y menor que la puja actual más baja (si existe)\n- El usuario debe tener permisos de transportista\n- No se pueden pujar en propias subastas\n\n**Ejemplo de request**:\n```json\n{\n  \"service_code\": \"TRANS-12345\",\n  \"amount\": 850.50\n}\n```\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "amount": 850.5,
                "service_code": "TRANS-12345"
              },
              "schema": {
                "properties": {
                  "amount": {
                    "description": "Monto de la puja en euros.\nDebe tener máximo 2 decimales.\nDebe ser menor que la puja actual (si existe).\n",
                    "example": 850.5,
                    "format": "float",
                    "minimum": 0.01,
                    "type": "number"
                  },
                  "service_code": {
                    "description": "Código único identificador del servicio/subasta.\nFormato: TRANS-XXXXX donde XXXXX son 5 dígitos.\n",
                    "example": "TRANS-12345",
                    "maxLength": 20,
                    "minLength": 10,
                    "pattern": "^TRANS-\\d{5}$",
                    "type": "string"
                  }
                },
                "required": [
                  "service_code",
                  "amount"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "64917511ef73c37ccae60bc4",
                  "bid_current": 850.5,
                  "bids": [
                    {
                      "_id": "507f1f77bcf86cd799439020",
                      "amount": 850.5,
                      "user": "507f1f77bcf86cd799439001"
                    }
                  ],
                  "created_at": "2025-10-25T14:30:00.000Z",
                  "service_code": "TRANS-12345",
                  "status": "published"
                },
                "schema": {
                  "$ref": "#/components/schemas/Auction"
                }
              }
            },
            "description": "Puja creada exitosamente.\nDevuelve los detalles actualizados de la subasta.\n",
            "headers": {}
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "error": "El monto de la puja debe ser menor que la puja actual",
                  "message": "BID_TOO_HIGH"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error en los datos de la puja. Posibles causas:\n- BID_NOT_VALID: Monto inválido (negativo o formato incorrecto)\n- SERVICE_CODE_NOT_PROVIDED: Código de servicio no proporcionado\n- AUCTION_NOT_ELIGIBLE: Servicio no encontrado o no elegible para pujas\n- BID_TOO_HIGH: Puja mayor que la puja actual más baja\n",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Token de autenticación no proporcionado",
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. Posibles causas:\n- Token JWT inválido o expirado\n- Usuario sin permisos de transportista\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Create a new bid",
        "tags": [
          "Bid Auctions"
        ]
      }
    },
    "/company/bid-auctions/mine": {
      "get": {
        "deprecated": false,
        "description": "## Propósito\nDevuelve un listado paginado de todas las subastas en las que el usuario autenticado\nha realizado pujas. Incluye subastas en estado 'published', 'awarded', 'approved', etc.\n\n## Objetivo\nPermitir a los transportistas gestionar sus pujas activas, revisar el estado de sus\nparticipaciones y filtrar según diferentes criterios de interés.\n\n## Casos de Uso\n- Transportista quiere ver todas sus pujas activas\n- Filtrar subastas que están pendientes de firma\n- Revisar subastas ganadas o perdidas\n- Ver historial completo de participaciones\n\n**Diagrama de Filtros**:\n```mermaid\nflowchart TD\n  A[GET /mine?filter=X] --> B{Tipo de filtro}\n\n  B -->|all| C[Todas las pujas del usuario]\n  B -->|open| D[Subastas publicadas<br/>date_end >= hoy<br/>status: published]\n  B -->|to_sign| E[Adjudicadas pendientes<br/>status: awarded<br/>signed_by_trucker: false]\n  B -->|awarded| F[Todas las adjudicadas<br/>status: awarded]\n  B -->|closed| G[Subastas cerradas<br/>status: locked/canceled/rejected]\n  B -->|lost| H[Perdidas<br/>status: approved<br/>usuario NO es ganador]\n  B -->|won| I[Ganadas<br/>status: approved<br/>usuario SÍ es ganador]\n\n  C --> J[Retornar AuctionsResponse]\n  D --> J\n  E --> J\n  F --> J\n  G --> J\n  H --> J\n  I --> J\n\n  style D fill:#90EE90\n  style E fill:#FFD700\n  style I fill:#87CEEB\n  style H fill:#FFB6C1\n```\n\n**Ejemplo de uso**:\n```bash\nGET /company/bid-auctions/mine?filter=to_sign&page=1&limit=10\n```\n\n**Campos destacados en la respuesta**:\n- `my_bid`: Monto de la puja del usuario\n- `bid_current`: Mejor puja actual\n- `status`: Estado actual de la subasta\n",
        "parameters": [
          {
            "description": "Número de página para paginación.\nValor por defecto 1. Mínimo 1.\n",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "example": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Cantidad máxima de resultados por página.\nValor por defecto 20. Rango permitido 1-50.\n",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "example": 20,
              "maximum": 50,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Filtra subastas por estado del usuario:\n- **all**: Todas las subastas con pujas del usuario (default)\n- **open**: Subastas publicadas activas (date_end >= hoy, status: published)\n- **to_sign**: Subastas adjudicadas pendientes de firma (status: awarded, signed_by_trucker: false)\n- **awarded**: Todas las subastas adjudicadas (status: awarded)\n- **closed**: Subastas cerradas (status: locked, canceled, rejected)\n- **lost**: Subastas aprobadas pero no ganadas (status: approved, usuario no es ganador)\n- **won**: Subastas aprobadas y ganadas (status: approved, usuario es ganador)\n",
            "in": "query",
            "name": "filter",
            "required": false,
            "schema": {
              "default": "all",
              "enum": [
                "all",
                "open",
                "to_sign",
                "awarded",
                "closed",
                "lost",
                "won"
              ],
              "example": "to_sign",
              "type": "string"
            }
          },
          {
            "description": "Ciudad o dirección de carga para filtrar.\nBúsqueda por coincidencia parcial (case insensitive).\n",
            "in": "query",
            "name": "addr_load",
            "required": false,
            "schema": {
              "example": "Madrid",
              "maxLength": 100,
              "minLength": 2,
              "type": "string"
            }
          },
          {
            "description": "Ciudad o dirección de descarga para filtrar.\nBúsqueda por coincidencia parcial (case insensitive).\n",
            "in": "query",
            "name": "addr_delivery",
            "required": false,
            "schema": {
              "example": "Barcelona",
              "maxLength": 100,
              "minLength": 2,
              "type": "string"
            }
          },
          {
            "description": "Filtrar por descripción de la carga.\nBúsqueda por coincidencia parcial (case insensitive).\n",
            "in": "query",
            "name": "description",
            "required": false,
            "schema": {
              "example": "Electrónica",
              "maxLength": 200,
              "minLength": 2,
              "type": "string"
            }
          },
          {
            "description": "Altura máxima de carga en centímetros.\nFiltra subastas con carga que no exceda esta altura.\n",
            "in": "query",
            "name": "cargo_height",
            "required": false,
            "schema": {
              "example": 150,
              "maximum": 500,
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "description": "Peso máximo de carga en kilogramos.\nFiltra subastas con carga que no exceda este peso.\n",
            "in": "query",
            "name": "cargo_weight",
            "required": false,
            "schema": {
              "example": 1500,
              "maximum": 50000,
              "minimum": 0,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "docs": [
                    {
                      "_id": "64917511ef73c37ccae60bc4",
                      "bid_current": 850.5,
                      "my_bid": 850.5,
                      "service_code": "TRANS-12345",
                      "signed_by_trucker": false,
                      "status": "awarded"
                    }
                  ],
                  "limit": 10,
                  "page": 1,
                  "pages": 3,
                  "total": 25
                },
                "schema": {
                  "$ref": "#/components/schemas/AuctionsResponse"
                }
              }
            },
            "description": "Listado de subastas obtenido exitosamente.\nIncluye metadatos de paginación y array de subastas con las pujas del usuario.\n",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Token de autenticación no proporcionado",
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. Posibles causas:\n- Token JWT inválido o expirado\n- Usuario no autenticado\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get my active auctions",
        "tags": [
          "Bid Auctions"
        ]
      }
    },
    "/company/bid-auctions/minimal/{service_code}": {
      "get": {
        "deprecated": false,
        "description": "## Propósito\nDevuelve los costos estimados para realizar una puja en una subasta.\nIncluye cálculo de distancia, peajes, combustible y tarifas de plataforma.\n\n## Objetivo\nPermitir a los transportistas calcular rápidamente el costo estimado de una puja\nsin tener que cargar todos los detalles de la subasta.\n\n## Casos de Uso\n- Verificación rápida de viabilidad económica de una subasta\n- Integración con sistemas que requieren respuestas ligeras\n- Widgets de dashboard con información resumida\n- Cálculo de margen de beneficio antes de pujar\n\n**Ejemplo de uso**:\n```bash\nGET /company/bid-auctions/minimal/TRANS-12345\n```\n\n**Nota**: Este endpoint devuelve información de costos completa, no solo\ndatos básicos de la puja. El nombre \"minimal\" se refiere a que es una\nrespuesta ligera comparada con obtener la subasta completa.\n",
        "parameters": [
          {
            "description": "Código único del servicio/subasta.\nFormato: TRANS-XXXXX donde XXXXX son 5 dígitos.\n",
            "example": "TRANS-12345",
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "maxLength": 20,
              "minLength": 10,
              "pattern": "^TRANS-\\d{5}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "distance": 450.5,
                  "duration": 320,
                  "fee": 4.2,
                  "fuel_cost": 180.0,
                  "percent_price": 50.0,
                  "toll_cost": 25.5,
                  "total": 1200.0,
                  "total_cost": 1250.5
                },
                "schema": {
                  "description": "Costos estimados para realizar una puja en una subasta.\nDevuelve información completa de costos para que el transportista\npueda calcular su viabilidad económica.\n",
                  "properties": {
                    "distance": {
                      "description": "Distancia de la ruta calculada en kilómetros.\nBasada en la ruta óptima entre origen y destino.\n",
                      "example": 450.5,
                      "format": "float",
                      "minimum": 0,
                      "type": "number"
                    },
                    "duration": {
                      "description": "Duración estimada del viaje en minutos.\nConsidera velocidad promedio y tráfico esperado.\n",
                      "example": 320,
                      "minimum": 0,
                      "type": "integer"
                    },
                    "fee": {
                      "description": "Tarifa de plataforma aplicada (porcentaje).\nEjemplo: 4.2 significa 4.2% sobre el costo base.\n",
                      "example": 4.2,
                      "format": "float",
                      "minimum": 0,
                      "type": "number"
                    },
                    "fuel_cost": {
                      "description": "Costo estimado de combustible para el viaje.\nCalculado según consumo promedio y distancia.\n",
                      "example": 180.0,
                      "format": "float",
                      "minimum": 0,
                      "type": "number"
                    },
                    "percent_price": {
                      "description": "Ajuste por volumen/peso (opcional).\nSolo presente si la carga excede la capacidad normal del vehículo.\nSe añade al costo total cuando aplica.\n",
                      "example": 50.0,
                      "format": "float",
                      "minimum": 0,
                      "nullable": true,
                      "type": "number"
                    },
                    "toll_cost": {
                      "description": "Costo estimado de peajes en la ruta.\nCalculado según peajes activos en el trayecto.\n",
                      "example": 25.5,
                      "format": "float",
                      "minimum": 0,
                      "type": "number"
                    },
                    "total": {
                      "description": "Costo de transporte base sin tarifa de plataforma.\nCalculado según distancia, duración y costos operativos.\n",
                      "example": 1200.0,
                      "format": "float",
                      "minimum": 0,
                      "type": "number"
                    },
                    "total_cost": {
                      "description": "Costo total con tarifa de plataforma aplicada.\nEs el monto final que el transportista pagaría por el servicio.\n",
                      "example": 1250.5,
                      "format": "float",
                      "minimum": 0,
                      "type": "number"
                    }
                  },
                  "required": [
                    "total_cost",
                    "total",
                    "fee",
                    "distance",
                    "duration",
                    "toll_cost",
                    "fuel_cost"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Información de costos obtenida exitosamente.\nDevuelve objeto completo con desglose de costos estimados.\n",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Token de autenticación no proporcionado",
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. Posibles causas:\n- NO_TOKEN: Token JWT inválido o expirado\n- Usuario no autenticado\n",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Subasta no encontrada",
                  "message": "SERVICE_CODE_NOT_PROVIDED"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Puja no encontrada. Posibles causas:\n- El código no existe\n- El usuario no tiene permisos para ver esta puja\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get minimal bid information with costs",
        "tags": [
          "Bid Auctions"
        ]
      }
    },
    "/company/bid-auctions/search-location": {
      "get": {
        "deprecated": false,
        "description": "## Propósito\nBusca y devuelve ubicaciones (ciudad, estado, país, código postal, provincia)\npresentes en subastas publicadas. Permite búsqueda por texto libre que coincide\ncon múltiples campos de dirección.\n\n## Objetivo\nFacilitar la autocompletación de campos de ubicación al buscar subastas,\nofreciendo sugerencias de ubicaciones válidas basadas en subastas activas.\n\n## Casos de Uso\n- Autocompletar campo de origen al buscar subastas\n- Descubrir ubicaciones disponibles en la plataforma\n- Filtrar subastas por ubicación con sugerencias inteligentes\n\n**Ejemplo de uso**:\n```bash\nGET /company/bid-auctions/search-location?search=Madrid&page=1&limit=10\n```\n\n**Nota**: Este endpoint utiliza caché para mejorar el rendimiento.\nBusca en TODAS las subastas publicadas, no solo las del usuario.\n",
        "parameters": [
          {
            "description": "Número de página para paginación.\nValor por defecto 1. Mínimo 1.\n",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "example": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Cantidad máxima de resultados por página.\nValor por defecto según ITEMS_PAGE del entorno. Rango permitido 1-100.\n",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Término de búsqueda para ubicaciones.\nBusca coincidencias parciales (case insensitive) en:\n- city (ciudad)\n- state (estado/región)\n- country (país)\n- zipcode (código postal)\n- neighborhood (barrio)\n- province (provincia)\n\nSe pueden usar múltiples términos separados por coma.\nEjemplo: \"Madrid,España\" buscará coincidencias en ambos términos.\n",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "example": "Madrid",
              "maxLength": 200,
              "type": "string"
            }
          },
          {
            "description": "Indica qué tipo de dirección buscar:\n- **etl**: Direcciones de carga (origen)\n- **etd**: Direcciones de descarga (destino)\n- omitir: busca en direcciones de carga por defecto\n",
            "in": "query",
            "name": "from",
            "required": false,
            "schema": {
              "enum": [
                "etl",
                "etd"
              ],
              "example": "etl",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "docs": [
                    {
                      "city": "Madrid",
                      "country": "España",
                      "neighborhood": "Centro",
                      "province": "Madrid",
                      "state": "Comunidad de Madrid",
                      "zipcode": 28001
                    },
                    {
                      "city": "Madrid",
                      "country": "España",
                      "neighborhood": "Chamberí",
                      "province": "Madrid",
                      "state": "Comunidad de Madrid",
                      "zipcode": 28013
                    }
                  ],
                  "hasNextPage": true,
                  "hasPrevPage": false,
                  "limit": 20,
                  "nextPage": 2,
                  "page": 1,
                  "pagingCounter": 1,
                  "prevPage": null,
                  "totalDocs": 45,
                  "totalPages": 3
                },
                "schema": {
                  "properties": {
                    "docs": {
                      "items": {
                        "properties": {
                          "city": {
                            "example": "Madrid",
                            "nullable": true,
                            "type": "string"
                          },
                          "country": {
                            "example": "España",
                            "nullable": true,
                            "type": "string"
                          },
                          "neighborhood": {
                            "example": "Centro",
                            "nullable": true,
                            "type": "string"
                          },
                          "province": {
                            "example": "Madrid",
                            "nullable": true,
                            "type": "string"
                          },
                          "state": {
                            "example": "Comunidad de Madrid",
                            "nullable": true,
                            "type": "string"
                          },
                          "zipcode": {
                            "example": 28001,
                            "nullable": true,
                            "type": "string"
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "hasNextPage": {
                      "example": true,
                      "type": "boolean"
                    },
                    "hasPrevPage": {
                      "example": false,
                      "type": "boolean"
                    },
                    "limit": {
                      "example": 20,
                      "type": "integer"
                    },
                    "nextPage": {
                      "example": 2,
                      "nullable": true,
                      "type": "integer"
                    },
                    "page": {
                      "example": 1,
                      "type": "integer"
                    },
                    "pagingCounter": {
                      "example": 1,
                      "type": "integer"
                    },
                    "prevPage": {
                      "example": null,
                      "nullable": true,
                      "type": "integer"
                    },
                    "totalDocs": {
                      "example": 45,
                      "type": "integer"
                    },
                    "totalPages": {
                      "example": 3,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Ubicaciones encontradas exitosamente.\nDevuelve array de ubicaciones con metadatos de paginación.\n",
            "headers": {}
          },
          "400": {
            "description": "Parámetros inválidos. Posibles causas:\n- Formato de página o límite incorrecto\n",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. Token JWT inválido, expirado o no proporcionado.\n",
            "headers": {}
          },
          "500": {
            "description": "Error interno del servidor.\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Search locations across all published auctions",
        "tags": [
          "Bid Auctions"
        ]
      }
    },
    "/company/bid-auctions/search-my-location": {
      "get": {
        "deprecated": false,
        "description": "## Propósito\nBusca y devuelve ubicaciones presentes únicamente en las subastas donde\nel usuario autenticado ha realizado pujas. Permite búsqueda por texto\nlibre que coincide con múltiples campos de dirección.\n\n## Objetivo\nOfrecer al usuario sugerencias de ubicaciones basadas en su historial\nde participación en subastas, facilitando la búsqueda de subastas\nrelacionadas con sus rutas de interés previas.\n\n## Casos de Uso\n- Autocompletar campo de ubicación filtrado por historial del usuario\n- Descubrir rutas frecuentes basadas en pujas anteriores\n- Buscar subastas similares a las que el usuario ya participó\n\n**Ejemplo de uso**:\n```bash\nGET /company/bid-auctions/search-my-location?search=Barcelona&page=1&limit=10\n```\n\n**Nota**: A diferencia de `/search-location`, este endpoint solo busca\nen subastas donde el usuario tiene pujas registradas.\n",
        "parameters": [
          {
            "description": "Número de página para paginación.\nValor por defecto 1. Mínimo 1.\n",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "example": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Cantidad máxima de resultados por página.\nValor por defecto según ITEMS_PAGE del entorno. Rango permitido 1-100.\n",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Término de búsqueda para ubicaciones.\nBusca coincidencias parciales (case insensitive) en:\n- city (ciudad)\n- state (estado/región)\n- country (país)\n- zipcode (código postal)\n- neighborhood (barrio)\n- province (provincia)\n\nSe pueden usar múltiples términos separados por coma.\nEjemplo: \"Barcelona,España\" buscará coincidencias en ambos términos.\nTambién acepta formato objeto: `search[city]=Barcelona`\n",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "example": "Barcelona",
              "maxLength": 200,
              "type": "string"
            }
          },
          {
            "description": "Indica qué tipo de dirección buscar:\n- **etl**: Direcciones de carga (origen)\n- **etd**: Direcciones de descarga (destino)\n- omitir: busca en direcciones de carga por defecto\n",
            "in": "query",
            "name": "from",
            "required": false,
            "schema": {
              "enum": [
                "etl",
                "etd"
              ],
              "example": "etd",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "docs": [
                    {
                      "city": "Barcelona",
                      "country": "España",
                      "neighborhood": "Ciutat Vella",
                      "province": "Barcelona",
                      "state": "Cataluña",
                      "zipcode": "08001"
                    },
                    {
                      "city": "Barcelona",
                      "country": "España",
                      "neighborhood": "Eixample",
                      "province": "Barcelona",
                      "state": "Cataluña",
                      "zipcode": "08013"
                    }
                  ],
                  "hasNextPage": false,
                  "hasPrevPage": false,
                  "limit": 20,
                  "nextPage": null,
                  "page": 1,
                  "pagingCounter": 1,
                  "prevPage": null,
                  "totalDocs": 12,
                  "totalPages": 1
                },
                "schema": {
                  "properties": {
                    "docs": {
                      "items": {
                        "properties": {
                          "city": {
                            "example": "Barcelona",
                            "nullable": true,
                            "type": "string"
                          },
                          "country": {
                            "example": "España",
                            "nullable": true,
                            "type": "string"
                          },
                          "neighborhood": {
                            "example": "Ciutat Vella",
                            "nullable": true,
                            "type": "string"
                          },
                          "province": {
                            "example": "Barcelona",
                            "nullable": true,
                            "type": "string"
                          },
                          "state": {
                            "example": "Cataluña",
                            "nullable": true,
                            "type": "string"
                          },
                          "zipcode": {
                            "example": "08001",
                            "nullable": true,
                            "type": "string"
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "hasNextPage": {
                      "example": false,
                      "type": "boolean"
                    },
                    "hasPrevPage": {
                      "example": false,
                      "type": "boolean"
                    },
                    "limit": {
                      "example": 20,
                      "type": "integer"
                    },
                    "nextPage": {
                      "example": null,
                      "nullable": true,
                      "type": "integer"
                    },
                    "page": {
                      "example": 1,
                      "type": "integer"
                    },
                    "pagingCounter": {
                      "example": 1,
                      "type": "integer"
                    },
                    "prevPage": {
                      "example": null,
                      "nullable": true,
                      "type": "integer"
                    },
                    "totalDocs": {
                      "example": 12,
                      "type": "integer"
                    },
                    "totalPages": {
                      "example": 1,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Ubicaciones del usuario encontradas exitosamente.\nDevuelve array de ubicaciones con metadatos de paginación.\n",
            "headers": {}
          },
          "400": {
            "description": "Parámetros inválidos. Posibles causas:\n- Formato de página o límite incorrecto\n",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Usuario no autenticado",
                  "message": "USER_NOT_LOGGED_IN"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. Posibles causas:\n- USER_NOT_LOGGED_IN: Token JWT inválido, expirado o no proporcionado\n- Usuario no autenticado\n",
            "headers": {}
          },
          "500": {
            "description": "Error interno del servidor.\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Search locations from user's auctions",
        "tags": [
          "Bid Auctions"
        ]
      }
    },
    "/company/bid-auctions/{service_code}": {
      "delete": {
        "deprecated": false,
        "description": "## Propósito\nPermite a un usuario eliminar una puja que haya realizado previamente,\nsiempre que la subasta aún no haya sido adjudicada.\n\n## Objetivo\nPermitir a los transportistas retractarse de una puja antes de que la subasta\nsea adjudicada, manteniendo la flexibilidad en el proceso de participación.\n\n## Casos de Uso\n- Transportista se da cuenta que cometió un error en el monto de la puja\n- Transportista ya no está disponible para la fecha de transporte\n- Transportista encontró una mejor oportunidad en otra subasta\n- Error en los datos de la puja que requiere corregirse\n\n## Restricciones\n- Solo se pueden eliminar pujas propias\n- La subasta debe estar en estado 'published'\n- Requiere autenticación mediante JWT\n- No se puede eliminar si la subasta ya fue adjudicada\n\n**Ejemplo de uso**:\n```bash\nDELETE /company/bid-auctions/TRANS-12345\n```\n",
        "parameters": [
          {
            "description": "Código único del servicio/subasta donde se eliminó la puja.\nFormato: TRANS-XXXXX donde XXXXX son 5 dígitos.\n",
            "example": "TRANS-12345",
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "maxLength": 20,
              "minLength": 10,
              "pattern": "^TRANS-\\d{5}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "message": "BID_DELETED",
                  "status": true
                },
                "schema": {
                  "properties": {
                    "message": {
                      "description": "Mensaje de confirmación",
                      "enum": [
                        "BID_DELETED"
                      ],
                      "example": "BID_DELETED",
                      "type": "string"
                    },
                    "status": {
                      "description": "Estado de la operación",
                      "example": true,
                      "type": "boolean"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Puja eliminada exitosamente.\nDevuelve mensaje de confirmación.\n",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Token de autenticación no proporcionado",
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. Posibles causas:\n- NO_TOKEN: Token JWT inválido o expirado\n- TOKEN_NOT_VALID: Token no válido para este usuario\n- Usuario no es el propietario de la puja\n",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "No se encontró puja del usuario en esta subasta",
                  "message": "BID_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No encontrado. Posibles causas:\n- SERVICE_CODE_NOT_PROVIDED: El código de servicio no existe\n- BID_NOT_FOUND: El usuario no tiene pujas en esta subasta\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Delete a bid",
        "tags": [
          "Bid Auctions"
        ]
      },
      "get": {
        "deprecated": false,
        "description": "## Propósito\nDevuelve todos los datos detallados de una subasta específica, incluyendo:\n- Información de carga/descarga\n- Especificaciones técnicas de la carga\n- Historial de pujas\n- Estado actual de la subasta\n\n## Objetivo\nProporcionar información completa para que los transportistas puedan tomar\ndecisiones informadas antes de realizar una puja.\n\n## Casos de Uso\n- Ver detalles completos antes de pujar\n- Revisar estado de una subasta en la que se participó\n- Integración con sistemas de gestión de flotas\n- Análisis de competencia y precios de mercado\n\n**Ejemplo de uso**:\n```bash\nGET /company/bid-auctions/TRANS-12345\n```\n\n**Campos computados incluidos**:\nEste endpoint extiende el schema base `Auction` con campos computados:\n- `canBid`: true si el usuario puede realizar pujas (estado: published)\n- `amWinner`: true si el usuario es el ganador (estado: awarded o approved)\n- `canSign`: true si el usuario puede firmar (ganador y no firmado por company)\n- `canSeeDelivery`: true si el usuario puede ver el delivery (ganador con delivery asociado)\n\n**Ubicación de código**: `src/features/company/bid-auction/company.details.controller.js`\n",
        "parameters": [
          {
            "description": "Código único identificador de la subasta.\nFormato: TRANS-XXXXX donde XXXXX son 5 dígitos.\n",
            "example": "TRANS-12345",
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "maxLength": 20,
              "minLength": 10,
              "pattern": "^TRANS-\\d{5}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "64917511ef73c37ccae60bc4",
                  "amWinner": false,
                  "bid_current": 850.5,
                  "canBid": true,
                  "canSeeDelivery": false,
                  "canSign": false,
                  "date_end": "2025-10-26T18:00:00.000Z",
                  "etl_date": "2025-10-27T10:00:00.000Z",
                  "service_code": "TRANS-12345",
                  "status": "published"
                },
                "schema": {
                  "$ref": "#/components/schemas/AuctionDetails"
                }
              }
            },
            "description": "Detalles completos de la subasta obtenidos exitosamente.\nIncluye toda la información técnica y comercial relevante,\nmás campos computados según el usuario actual.\n",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Token de autenticación no proporcionado",
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. Posibles causas:\n- NO_TOKEN: Token JWT inválido o expirado\n- Usuario no autenticado\n",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Subasta no encontrada",
                  "message": "SERVICE_CODE_NOT_PROVIDED"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Subasta no encontrada. Posibles causas:\n- El código no existe\n- La subasta fue eliminada\n- El usuario no tiene permisos para verla\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get full auction details",
        "tags": [
          "Bid Auctions"
        ]
      }
    },
    "/company/bid-auctions/{service_code}/sign": {
      "post": {
        "deprecated": false,
        "description": "## Propósito\nPermite al transportista ganador firmar digitalmente la adjudicación de una subasta.\nLa firma se realiza mediante una imagen de firma manuscrita cargada por el usuario.\n\n## Objetivo\nFormalizar legalmente la aceptación del transporte por parte del transportista ganador,\ngenerando automáticamente los documentos y registros necesarios para el proceso logístico.\n\n## Efectos Secundarios Automáticos ⚠️\nEste endpoint realiza automáticamente las siguientes operaciones al completarse:\n\n1. **Creación del Delivery**:\n   - Se crea un registro de delivery asociado a la subasta\n   - El delivery se inicializa con los datos de la subasta\n   - Se establece la relación auction.delivery → delivery._id\n\n2. **Generación del eCMR**:\n   - Se genera automáticamente el documento eCMR (Carta de Porte Digital)\n   - El eCMR se asocia al delivery recién creado\n   - Se inicializa con los datos de transporte de la subasta\n\n3. **Actualización de Estados**:\n   - La subasta se marca como firmada\n   - Se establece signed_by_trucker: true\n   - Se registra la fecha y hora de la firma\n\n4. **Relaciones entre Entidades**:\n   ```\n   Auction → Delivery → eCMR\n   (auction._id) (delivery.auction) (ecmr.delivery)\n   ```\n\n## Requisitos del Archivo de Firma\n- **Formato**: JPEG o PNG\n- **Tamaño máximo**: 5MB\n- **Resolución mínima**: 300x100px\n- **Contenido**: Imagen de firma manuscrita legible\n\n## Casos de Uso\n- Transportista ganador firma la aceptación de una subasta adjudicada\n- Sistema generando automáticamente delivery y eCMR tras la firma\n- Integración con flujos automatizados de gestión de transporte\n\n## Notas Importantes\n- Solo el transportista ganador puede firmar\n- La subasta debe estar en estado \"awarded\"\n- La firma es irreversible y genera registros permanentes\n- Los documentos generados estarán disponibles en los endpoints de delivery y ecmr\n\n**Diagrama del Flujo de Firma**:\n```mermaid\nflowchart LR\n  A[Usuario firma subasta] --> B{¿Es ganador?}\n  B -->|No| C[Error: 403 Forbidden]\n  B -->|Sí| D{¿Estado awarded?}\n  D -->|No| E[Error: 400 Bad Request]\n  D -->|Sí| F[Guardar imagen de firma]\n  F --> G[Marcar: signed_by_trucker = true]\n  G --> H[Crear Delivery]\n  H --> I[Generar eCMR]\n  I --> J[Establecer relaciones]\n  J --> K[Retornar Auction actualizada]\n\n  style C fill:#FFB6C1\n  style E fill:#FFB6C1\n  style K fill:#90EE90\n```\n\n**Ejemplo con cURL**:\n```bash\ncurl -X POST \\\n  -H \"Authorization: Bearer {token}\" \\\n  -F \"signImage=@firma.png\" \\\n  https://api.demo.cargoffer.com/company/bid-auctions/TRANS-12345/sign\n```\n\n**Ubicación de código**: `src/features/company/bid-auction/company.sign.js` líneas 100-118\n",
        "parameters": [
          {
            "description": "Código único de la subasta adjudicada.\nFormato: TRANS-XXXXX donde XXXXX son 5 dígitos.\n",
            "example": "TRANS-12345",
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "maxLength": 20,
              "minLength": 10,
              "pattern": "^TRANS-\\d{5}$",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "properties": {
                  "signImage": {
                    "description": "Imagen de la firma digital o documento de identidad.\nFormatos aceptados: JPEG, PNG (máx. 5MB)\n",
                    "format": "binary",
                    "type": "string"
                  }
                },
                "required": [
                  "signImage"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "64917511ef73c37ccae60bc4",
                  "created_at": "2025-10-25T14:30:00.000Z",
                  "delivery": "507f1f77bcf86cd799439100",
                  "service_code": "TRANS-12345",
                  "signed_by_company": true,
                  "signed_by_trucker": true,
                  "status": "completed"
                },
                "schema": {
                  "$ref": "#/components/schemas/Auction"
                }
              }
            },
            "description": "Firma registrada exitosamente.\nLa subasta pasa a estado 'completed' y se crea el delivery.\n",
            "headers": {}
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "error": "El archivo no es una imagen válida",
                  "message": "INVALID_FILE"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Datos de firma inválidos. Posibles causas:\n- INVALID_FILE: Archivo no es una imagen válida\n- FILE_TOO_LARGE: Tamaño excede el límite de 5MB\n- INVALID_FORMAT: Formato no soportado (solo JPEG/PNG)\n- AUCTION_NOT_SIGNED: La subasta no cumple requisitos para firma\n",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "El usuario no es el ganador de esta subasta",
                  "message": "NOT_WINNER"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. Posibles causas:\n- NO_TOKEN: Token JWT inválido o expirado\n- Usuario no es el ganador de la subasta\n- Subasta no está en estado 'awarded'\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Digitally sign an awarded auction",
        "tags": [
          "Bid Auctions"
        ]
      }
    },
    "/company/billing/current": {
      "get": {
        "deprecated": false,
        "description": "## Purpose\nCalcula y devuelve la facturación acumulada del mes en curso para la empresa\nautenticada, desglosada por categoría: usuarios, vehículos, transportistas y eCMRs.\n\n## Objective\nProporcionar a la empresa una vista en tiempo real de su consumo mensual actual\npara predecir el importe de la próxima factura y controlar el gasto.\n\n## Use Cases\n- Consultar el gasto acumulado del mes en curso en el dashboard financiero\n- Predecir el importe de la próxima factura antes de su emisión\n- Analizar el coste desglosado por tipo de recurso (usuarios, vehículos, etc.)\n- Verificar la próxima fecha de pago del plan contratado\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request] --> B{JWT or API Key valid?}\n  B -->|No| C[401 NO_TOKEN / TOKEN_NOT_VALID]\n  B -->|Yes| D{User found in DB?}\n  D -->|No| E[404 USER_NOT_FOUND]\n  D -->|Yes| F{Company found?}\n  F -->|No| G[404 COMPANY_NOT_FOUND]\n  F -->|Yes| H{Tariff exists?}\n  H -->|No| I[Auto-create tariff from Settings]\n  I --> J[Fetch current month invoice]\n  H -->|Yes| J\n  J --> K[Calculate pricing]\n  K --> L[200 OK - value + tariff]\n```\n\n## Notes\n- Requiere autenticación JWT o API Key (`apiKeyAuth`)\n- Los parámetros `dateStart` y `dateEnd` son **opcionales** y se aceptan tanto en query string como en body\n- Si no se especifican fechas, usa el mes actual completo\n- Si la empresa no tiene tarifa configurada, se crea automáticamente con precios por defecto de `Settings`\n- Los meses se internamente en formato `YYYY_MM` (ej: `\"2024_09\"`)\n- La `next_pay_date` se calcula como el último día del mes siguiente\n",
        "parameters": [
          {
            "description": "Fecha de inicio del periodo (opcional).\n\nFormato: `YYYY-MM-DD`.\nSi no se especifica, usa el inicio del mes actual.\n",
            "in": "query",
            "name": "dateStart",
            "required": false,
            "schema": {
              "example": "2024-09-01",
              "format": "date",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "type": "string"
            }
          },
          {
            "description": "Fecha de fin del periodo (opcional).\n\nFormato: `YYYY-MM-DD`.\nSi no se especifica, usa la fecha actual.\n",
            "in": "query",
            "name": "dateEnd",
            "required": false,
            "schema": {
              "example": "2024-09-30",
              "format": "date",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "tariff": {
                      "ecmr": 0.5,
                      "next_pay_date": "2024_10",
                      "truckers": 15,
                      "until_date": "2024-12-31T23:59:59Z",
                      "users": 10,
                      "vehicles": 5
                    },
                    "value": {
                      "ecmr": {
                        "price": 15.5,
                        "quantity": 31
                      },
                      "price": 150.5,
                      "truckers": {
                        "price": 60.0,
                        "quantity": 4
                      },
                      "users": {
                        "price": 30.0,
                        "quantity": 3
                      },
                      "vehicles": {
                        "price": 45.0,
                        "quantity": 9
                      }
                    }
                  },
                  "status": 200
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "tariff": {
                          "description": "Tarifa vigente aplicada a la empresa",
                          "properties": {
                            "ecmr": {
                              "description": "Precio unitario por eCMR emitido",
                              "example": 0.5,
                              "type": "number"
                            },
                            "next_pay_date": {
                              "description": "Próxima fecha de pago en formato YYYY_MM",
                              "example": "2024_10",
                              "type": "string"
                            },
                            "truckers": {
                              "description": "Precio unitario por transportista",
                              "example": 15,
                              "type": "number"
                            },
                            "until_date": {
                              "description": "Fecha de expiración de la tarifa vigente",
                              "example": "2024-12-31T23:59:59Z",
                              "format": "date-time",
                              "type": "string"
                            },
                            "users": {
                              "description": "Precio unitario por usuario",
                              "example": 10,
                              "type": "number"
                            },
                            "vehicles": {
                              "description": "Precio unitario por vehículo",
                              "example": 5,
                              "type": "number"
                            }
                          },
                          "type": "object"
                        },
                        "value": {
                          "description": "Costes calculados para el periodo",
                          "properties": {
                            "ecmr": {
                              "description": "Coste por eCMRs generados",
                              "properties": {
                                "price": {
                                  "example": 15.5,
                                  "type": "number"
                                },
                                "quantity": {
                                  "description": "Número de eCMRs emitidos",
                                  "example": 31,
                                  "type": "integer"
                                }
                              },
                              "type": "object"
                            },
                            "price": {
                              "description": "Precio total del periodo (suma de todas las categorías)",
                              "example": 150.5,
                              "type": "number"
                            },
                            "truckers": {
                              "description": "Coste por transportistas activos",
                              "properties": {
                                "price": {
                                  "example": 60.0,
                                  "type": "number"
                                },
                                "quantity": {
                                  "example": 4,
                                  "type": "integer"
                                }
                              },
                              "type": "object"
                            },
                            "users": {
                              "description": "Coste por usuarios activos",
                              "properties": {
                                "price": {
                                  "description": "Coste total de usuarios",
                                  "example": 30.0,
                                  "type": "number"
                                },
                                "quantity": {
                                  "description": "Número de usuarios activos",
                                  "example": 3,
                                  "type": "integer"
                                }
                              },
                              "type": "object"
                            },
                            "vehicles": {
                              "description": "Coste por vehículos activos",
                              "properties": {
                                "price": {
                                  "example": 45.0,
                                  "type": "number"
                                },
                                "quantity": {
                                  "example": 9,
                                  "type": "integer"
                                }
                              },
                              "type": "object"
                            }
                          },
                          "type": "object"
                        }
                      },
                      "type": "object"
                    },
                    "status": {
                      "example": 200,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Facturación calculada del mes actual",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "no_token": {
                    "summary": "Sin token de autenticación",
                    "value": {
                      "message": "NO_TOKEN"
                    }
                  },
                  "token_invalid": {
                    "summary": "Token inválido o expirado",
                    "value": {
                      "message": "TOKEN_NOT_VALID"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Token no proporcionado, inválido o cuenta bloqueada",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "company_not_found": {
                    "value": {
                      "message": "COMPANY_NOT_FOUND"
                    }
                  },
                  "user_not_found": {
                    "value": {
                      "message": "USER_NOT_FOUND"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario o empresa no encontrados",
            "headers": {}
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error al acceder a la base de datos",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Obtener facturación del mes actual",
        "tags": [
          "billing"
        ]
      }
    },
    "/company/billing/historical": {
      "get": {
        "deprecated": false,
        "description": "## Purpose\nDevuelve el histórico completo de facturación para un rango de fechas, incluyendo\nfacturas mensuales, entregas realizadas, y recursos activos (usuarios, transportistas,\nvehículos) en ese periodo.\n\n## Objective\nProporcionar una vista consolidada del historial financiero de la empresa para\nanálisis de costes, generación de reportes y auditorías internas.\n\n## Use Cases\n- Generar reportes financieros mensuales o anuales\n- Auditar deliveries realizadas y recursos utilizados en un periodo\n- Comparar tarifas y consumos entre diferentes periodos\n- Exportar datos a sistemas contables externos\n- Análisis de rentabilidad por ruta, vehículo o transportista\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request - dateStart, dateEnd] --> B{JWT or API Key valid?}\n  B -->|No| C[401 NO_TOKEN / TOKEN_NOT_VALID]\n  B -->|Yes| D{User found in DB?}\n  D -->|No| E[404 USER_NOT_FOUND]\n  D -->|Yes| F{Company found?}\n  F -->|No| G[404 COMPANY_NOT_FOUND]\n  F -->|Yes| H[Build months array YYYY_MM]\n  H --> I[Fetch invoices for all months]\n  I --> J[For each invoice: find related deliveries]\n  J --> K[Build users, truckers, vehicles arrays]\n  K --> L[Calculate pricing per invoice]\n  L --> M[200 OK - Full historical data]\n```\n\n## Notes\n- Requiere autenticación JWT o API Key (`apiKeyAuth`)\n- `dateStart` y `dateEnd` son opcionales técnicamente pero necesarios para retornar datos (sin fechas retorna vacío)\n- Las fechas se procesan mes a mes (granularidad mensual, no diaria)\n- El campo `last_used` en truckers y vehículos es **condicional**: solo aparece cuando hay deliveries asociados en el periodo\n- Los IDs de usuario, trucker y vehículo son únicos por periodo (sin duplicados)\n- Las entregas se obtienen de los `trucker_user` y `trucker_vehicle` presentes en las facturas del periodo\n- Los campos de cargo en deliveries provienen de `delivery.parseInvoice()`\n",
        "parameters": [
          {
            "description": "Fecha de inicio del periodo.\n\nFormato: `YYYY-MM-DD`.\nSe procesa con granularidad mensual (se extraerá el mes).\n",
            "in": "query",
            "name": "dateStart",
            "required": false,
            "schema": {
              "example": "2024-01-01",
              "format": "date",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "type": "string"
            }
          },
          {
            "description": "Fecha de fin del periodo.\n\nFormato: `YYYY-MM-DD`.\nSe procesa con granularidad mensual (se extraerá el mes).\n",
            "in": "query",
            "name": "dateEnd",
            "required": false,
            "schema": {
              "example": "2024-12-31",
              "format": "date",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "deliveries": [
                      {
                        "_id": "507f1f77bcf86cd799439011",
                        "cargo_height": 120,
                        "cargo_type": "electronics",
                        "cargo_weight": 1500,
                        "custom_code": "CUSTOM-123",
                        "date_eta": "2024-10-15T14:30:00Z",
                        "etd_address": {
                          "address": "Av. Principal 456",
                          "city": "Barcelona",
                          "country": "ES",
                          "postal_code": "08001"
                        },
                        "etd_cargo_method": "manual",
                        "etd_date": "2024-10-15T14:30:00Z",
                        "etd_date_end": "2024-10-15T16:00:00Z",
                        "etl_address": {
                          "address": "Calle Mayor 123",
                          "city": "Madrid",
                          "country": "ES",
                          "postal_code": "28013"
                        },
                        "etl_cargo_method": "forklift",
                        "etl_date": "2024-10-15T08:00:00Z",
                        "etl_date_end": "2024-10-15T10:00:00Z",
                        "hscode": "8471.30",
                        "is_fresh": false,
                        "is_imperial_measure": false,
                        "is_paid": true,
                        "is_private": false,
                        "linear_meters": 6.5,
                        "pallets_num": 10,
                        "pallets_type": "EUR",
                        "service_code": "CARGO-2024-000123",
                        "signed_by_company": true,
                        "signed_by_trucker": true,
                        "status": "completed"
                      }
                    ],
                    "invoices": [
                      {
                        "ecmr": {
                          "price": 15.5,
                          "quantity": 31
                        },
                        "price": 150.5,
                        "truckers": {
                          "price": 60.0,
                          "quantity": 4
                        },
                        "users": {
                          "price": 30.0,
                          "quantity": 3
                        },
                        "vehicles": {
                          "price": 45.0,
                          "quantity": 9
                        }
                      }
                    ],
                    "tariff": {
                      "ecmr": 0.5,
                      "next_pay_date": "2024_10",
                      "truckers": 15,
                      "until_date": "2024-12-31T23:59:59Z",
                      "users": 10,
                      "vehicles": 5
                    },
                    "truckers": [
                      {
                        "_id": "507f191e810c19729de860ea",
                        "last_login": "2024-10-09T15:20:00Z",
                        "last_used": {
                          "etd": "2024-10-15T14:30:00Z",
                          "etl": "2024-10-15T08:00:00Z",
                          "service_code": "CARGO-2024-000123",
                          "status": "completed"
                        },
                        "lastname": "Martínez Ruiz",
                        "name": "Pedro"
                      }
                    ],
                    "users": [
                      {
                        "_id": "507f1f77bcf86cd799439011",
                        "last_login": "2024-10-10T09:30:00Z",
                        "lastname": "García López",
                        "name": "Juan"
                      }
                    ],
                    "vehicles": [
                      {
                        "_id": "507f1f77bcf86cd799439022",
                        "last_used": {
                          "etd": "2024-10-15T14:30:00Z",
                          "etl": "2024-10-15T08:00:00Z",
                          "service_code": "CARGO-2024-000123",
                          "status": "completed"
                        },
                        "plate": "ABC-1234",
                        "shipping_type": "road",
                        "vehicle_type": "507f1f77bcf86cd799439033"
                      }
                    ]
                  },
                  "status": 200
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "deliveries": {
                          "description": "Entregas realizadas en el periodo asociadas a la empresa.\nCampos obtenidos de `delivery.parseInvoice()`.\n",
                          "items": {
                            "properties": {
                              "_id": {
                                "example": "507f1f77bcf86cd799439011",
                                "type": "string"
                              },
                              "cargo_height": {
                                "description": "Altura de la carga (cm)",
                                "example": 120,
                                "type": "number"
                              },
                              "cargo_type": {
                                "description": "Tipo de mercancía",
                                "example": "electronics",
                                "type": "string"
                              },
                              "cargo_weight": {
                                "description": "Peso de la carga (kg o lbs según is_imperial_measure)",
                                "example": 1500,
                                "type": "number"
                              },
                              "custom_code": {
                                "description": "Código personalizado del cliente",
                                "example": "CUSTOM-123",
                                "type": "string"
                              },
                              "date_eta": {
                                "description": "Fecha estimada de llegada",
                                "example": "2024-10-15T14:30:00Z",
                                "format": "date-time",
                                "type": "string"
                              },
                              "etd_address": {
                                "description": "Dirección de destino de la entrega",
                                "properties": {
                                  "address": {
                                    "example": "Av. Principal 456",
                                    "type": "string"
                                  },
                                  "city": {
                                    "example": "Barcelona",
                                    "type": "string"
                                  },
                                  "country": {
                                    "example": "ES",
                                    "type": "string"
                                  },
                                  "postal_code": {
                                    "example": "08001",
                                    "type": "string"
                                  }
                                },
                                "type": "object"
                              },
                              "etd_cargo_method": {
                                "description": "Método de descarga en destino",
                                "example": "manual",
                                "type": "string"
                              },
                              "etd_date": {
                                "description": "Fecha de entrega (Expected Time of Delivery)",
                                "example": "2024-10-15T14:30:00Z",
                                "format": "date-time",
                                "type": "string"
                              },
                              "etd_date_end": {
                                "description": "Fin de ventana de entrega (etd + etd_extra_time)",
                                "example": "2024-10-15T16:00:00Z",
                                "format": "date-time",
                                "type": "string"
                              },
                              "etl_address": {
                                "description": "Dirección de origen de la carga",
                                "properties": {
                                  "address": {
                                    "example": "Calle Mayor 123",
                                    "type": "string"
                                  },
                                  "city": {
                                    "example": "Madrid",
                                    "type": "string"
                                  },
                                  "country": {
                                    "example": "ES",
                                    "type": "string"
                                  },
                                  "postal_code": {
                                    "example": "28013",
                                    "type": "string"
                                  }
                                },
                                "type": "object"
                              },
                              "etl_cargo_method": {
                                "description": "Método de carga en origen",
                                "example": "forklift",
                                "type": "string"
                              },
                              "etl_date": {
                                "description": "Fecha de carga (Expected Time of Loading)",
                                "example": "2024-10-15T08:00:00Z",
                                "format": "date-time",
                                "type": "string"
                              },
                              "etl_date_end": {
                                "description": "Fin de ventana de carga (etl + etl_extra_time)",
                                "example": "2024-10-15T10:00:00Z",
                                "format": "date-time",
                                "type": "string"
                              },
                              "fresh_cargo_temp": {
                                "description": "Temperatura requerida (°C, solo si is_fresh=true)",
                                "example": -18,
                                "type": "number"
                              },
                              "hscode": {
                                "description": "Código HS de la mercancía",
                                "example": "8471.30",
                                "type": "string"
                              },
                              "is_fresh": {
                                "description": "Si es carga de temperatura controlada",
                                "example": false,
                                "type": "boolean"
                              },
                              "is_imperial_measure": {
                                "description": "Si las medidas están en sistema imperial",
                                "example": false,
                                "type": "boolean"
                              },
                              "is_paid": {
                                "description": "Si la entrega fue pagada",
                                "example": true,
                                "type": "boolean"
                              },
                              "is_private": {
                                "description": "Si la subasta fue privada",
                                "example": false,
                                "type": "boolean"
                              },
                              "linear_meters": {
                                "description": "Metros lineales ocupados",
                                "example": 6.5,
                                "type": "number"
                              },
                              "pallets_num": {
                                "description": "Número de palés",
                                "example": 10,
                                "type": "integer"
                              },
                              "pallets_type": {
                                "description": "Tipo de palés",
                                "example": "EUR",
                                "type": "string"
                              },
                              "plate_full_trailer": {
                                "description": "Matrícula del remolque completo",
                                "example": "TR-4567-AB",
                                "type": "string"
                              },
                              "service_code": {
                                "description": "Código de servicio de la entrega",
                                "example": "CARGO-2024-000123",
                                "type": "string"
                              },
                              "signed_by_company": {
                                "description": "Si fue firmada por la empresa",
                                "example": true,
                                "type": "boolean"
                              },
                              "signed_by_trucker": {
                                "description": "Si fue firmada por el transportista",
                                "example": true,
                                "type": "boolean"
                              },
                              "status": {
                                "description": "Estado de la entrega",
                                "example": "completed",
                                "type": "string"
                              }
                            },
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "invoices": {
                          "description": "Facturas mensuales con pricing calculado.\nUn elemento por cada mes en el rango con datos.\n",
                          "items": {
                            "properties": {
                              "ecmr": {
                                "properties": {
                                  "price": {
                                    "example": 15.5,
                                    "type": "number"
                                  },
                                  "quantity": {
                                    "example": 31,
                                    "type": "integer"
                                  }
                                },
                                "type": "object"
                              },
                              "price": {
                                "description": "Precio total del mes",
                                "example": 150.5,
                                "type": "number"
                              },
                              "truckers": {
                                "properties": {
                                  "price": {
                                    "example": 60.0,
                                    "type": "number"
                                  },
                                  "quantity": {
                                    "example": 4,
                                    "type": "integer"
                                  }
                                },
                                "type": "object"
                              },
                              "users": {
                                "properties": {
                                  "price": {
                                    "example": 30.0,
                                    "type": "number"
                                  },
                                  "quantity": {
                                    "example": 3,
                                    "type": "integer"
                                  }
                                },
                                "type": "object"
                              },
                              "vehicles": {
                                "properties": {
                                  "price": {
                                    "example": 45.0,
                                    "type": "number"
                                  },
                                  "quantity": {
                                    "example": 9,
                                    "type": "integer"
                                  }
                                },
                                "type": "object"
                              }
                            },
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "tariff": {
                          "description": "Tarifa vigente aplicada a la empresa en el periodo",
                          "properties": {
                            "ecmr": {
                              "description": "Precio unitario por eCMR",
                              "example": 0.5,
                              "type": "number"
                            },
                            "next_pay_date": {
                              "description": "Próxima fecha de pago en formato YYYY_MM",
                              "example": "2024_10",
                              "type": "string"
                            },
                            "truckers": {
                              "description": "Precio unitario por transportista",
                              "example": 15,
                              "type": "number"
                            },
                            "until_date": {
                              "example": "2024-12-31T23:59:59Z",
                              "format": "date-time",
                              "type": "string"
                            },
                            "users": {
                              "description": "Precio unitario por usuario",
                              "example": 10,
                              "type": "number"
                            },
                            "vehicles": {
                              "description": "Precio unitario por vehículo",
                              "example": 5,
                              "type": "number"
                            }
                          },
                          "type": "object"
                        },
                        "truckers": {
                          "description": "Transportistas únicos activos en el periodo",
                          "items": {
                            "properties": {
                              "_id": {
                                "example": "507f191e810c19729de860ea",
                                "type": "string"
                              },
                              "last_login": {
                                "example": "2024-10-09T15:20:00Z",
                                "format": "date-time",
                                "type": "string"
                              },
                              "last_used": {
                                "description": "Último delivery asociado al transportista.\n**Campo condicional**: solo presente si hay deliveries relacionados en el periodo.\n",
                                "properties": {
                                  "etd": {
                                    "example": "2024-10-15T14:30:00Z",
                                    "format": "date-time",
                                    "type": "string"
                                  },
                                  "etl": {
                                    "example": "2024-10-15T08:00:00Z",
                                    "format": "date-time",
                                    "type": "string"
                                  },
                                  "service_code": {
                                    "example": "CARGO-2024-000123",
                                    "type": "string"
                                  },
                                  "status": {
                                    "example": "completed",
                                    "type": "string"
                                  }
                                },
                                "type": "object"
                              },
                              "lastname": {
                                "example": "Martínez Ruiz",
                                "type": "string"
                              },
                              "name": {
                                "example": "Pedro",
                                "type": "string"
                              }
                            },
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "users": {
                          "description": "Usuarios únicos de la empresa activos en el periodo",
                          "items": {
                            "properties": {
                              "_id": {
                                "example": "507f1f77bcf86cd799439011",
                                "type": "string"
                              },
                              "last_login": {
                                "example": "2024-10-10T09:30:00Z",
                                "format": "date-time",
                                "type": "string"
                              },
                              "lastname": {
                                "example": "García López",
                                "type": "string"
                              },
                              "name": {
                                "example": "Juan",
                                "type": "string"
                              }
                            },
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "vehicles": {
                          "description": "Vehículos únicos activos en el periodo",
                          "items": {
                            "properties": {
                              "_id": {
                                "example": "507f1f77bcf86cd799439022",
                                "type": "string"
                              },
                              "last_used": {
                                "description": "Último delivery en el que participó el vehículo.\n**Campo condicional**: solo presente si hay deliveries relacionados.\n",
                                "properties": {
                                  "etd": {
                                    "example": "2024-10-15T14:30:00Z",
                                    "format": "date-time",
                                    "type": "string"
                                  },
                                  "etl": {
                                    "example": "2024-10-15T08:00:00Z",
                                    "format": "date-time",
                                    "type": "string"
                                  },
                                  "service_code": {
                                    "example": "CARGO-2024-000123",
                                    "type": "string"
                                  },
                                  "status": {
                                    "example": "completed",
                                    "type": "string"
                                  }
                                },
                                "type": "object"
                              },
                              "plate": {
                                "description": "Matrícula del vehículo",
                                "example": "ABC-1234",
                                "type": "string"
                              },
                              "shipping_type": {
                                "description": "Tipo de transporte",
                                "enum": [
                                  "road",
                                  "sea",
                                  "air",
                                  "rail"
                                ],
                                "example": "road",
                                "type": "string"
                              },
                              "vehicle_type": {
                                "description": "ID del tipo de vehículo",
                                "example": "507f1f77bcf86cd799439033",
                                "type": "string"
                              }
                            },
                            "type": "object"
                          },
                          "type": "array"
                        }
                      },
                      "type": "object"
                    },
                    "status": {
                      "example": 200,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Histórico completo de facturación del periodo",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "no_token": {
                    "summary": "Sin token de autenticación",
                    "value": {
                      "message": "NO_TOKEN"
                    }
                  },
                  "token_invalid": {
                    "summary": "Token inválido o expirado",
                    "value": {
                      "message": "TOKEN_NOT_VALID"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Token no proporcionado, inválido o cuenta bloqueada",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "company_not_found": {
                    "value": {
                      "message": "COMPANY_NOT_FOUND"
                    }
                  },
                  "user_not_found": {
                    "value": {
                      "message": "USER_NOT_FOUND"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario o empresa no encontrados",
            "headers": {}
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error al acceder a la base de datos",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Obtener histórico de facturación por rango de fechas",
        "tags": [
          "billing"
        ]
      }
    },
    "/company/billing/overage-policy": {
      "patch": {
        "deprecated": false,
        "description": "## Purpose\nPermite a un administrador de la empresa activar o desactivar la política de overage\nde pago, que controla si los recursos que superan los límites del plan se desactivan\nautomáticamente o se permiten con cargo adicional.\n\n## Objective\nDar control a los administradores sobre el comportamiento de facturación cuando\nse exceden los límites de recursos del plan contratado.\n\n## Use Cases\n- Activar overage de pago para evitar desactivación automática de recursos\n- Desactivar overage de pago para mantener el control estricto de costes\n- Cambiar la política de facturación desde el panel de administración\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive PATCH Request] --> B{JWT valid?}\n  B -->|No| C[401 NO_TOKEN / TOKEN_NOT_VALID]\n  B -->|Yes| D{User is admin?}\n  D -->|No| E[403 INSUFFICIENT_PERMISSIONS]\n  D -->|Yes| F{Company context exists?}\n  F -->|No| G[400 Company context required]\n  F -->|Yes| H{allowPaidOverage valid?}\n  H -->|No| I[400 INVALID_INPUT]\n  H -->|Yes| J[Update company.allowPaidOverage]\n  J --> K[Refresh JWT token with new flag]\n  K --> L[200 OK - { allowPaidOverage }]\n```\n\n## Notes\n- Requiere rol de **admin** o **dev** (`m.isAdmin`)\n- El campo `allowPaidOverage` es **obligatorio** y debe ser booleano (`true`/`false`) o string (`'true'`/`'false'`)\n- Tras actualizar, se refresca el token JWT en los headers `X-ACCESS_TOKEN` y `Authorization`\n- El flag se incluye en el payload del token para acceso inmediato desde el frontend\n",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "allowPaidOverage": true
              },
              "schema": {
                "properties": {
                  "allowPaidOverage": {
                    "description": "Política de overage de pago.\n- `true`: Permitir recursos adicionales con cargo extra\n- `false`: Desactivar recursos que superen los límites del plan\n",
                    "example": true,
                    "type": "boolean"
                  }
                },
                "required": [
                  "allowPaidOverage"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "allowPaidOverage": true
                  },
                  "status": 200
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "allowPaidOverage": {
                          "description": "Nuevo valor de la política de overage",
                          "example": true,
                          "type": "boolean"
                        }
                      },
                      "type": "object"
                    },
                    "status": {
                      "example": 200,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Política de overage actualizada correctamente",
            "headers": {
              "Authorization": {
                "description": "Token JWT actualizado (duplicado por compatibilidad)",
                "schema": {
                  "type": "string"
                }
              },
              "X-ACCESS_TOKEN": {
                "description": "Token JWT actualizado con el nuevo flag allowPaidOverage",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "company_context_required": {
                    "summary": "Contexto de empresa no disponible",
                    "value": {
                      "message": "Company context required"
                    }
                  },
                  "invalid_input": {
                    "summary": "Valor de allowPaidOverage inválido",
                    "value": {
                      "message": "INVALID_INPUT"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Entrada inválida o contexto de empresa faltante",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "no_token": {
                    "summary": "Sin token de autenticación",
                    "value": {
                      "message": "NO_TOKEN"
                    }
                  },
                  "token_invalid": {
                    "summary": "Token inválido o expirado",
                    "value": {
                      "message": "TOKEN_NOT_VALID"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Token no proporcionado o inválido",
            "headers": {}
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "message": "INSUFFICIENT_PERMISSIONS"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Permisos insuficientes (requiere rol admin o dev)",
            "headers": {}
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "message": "CANT_UPDATE"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error al actualizar la política",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Actualizar política de overage de la empresa",
        "tags": [
          "billing"
        ]
      }
    },
    "/company/billing/process-pending-reports": {
      "post": {
        "deprecated": false,
        "description": "## Purpose\nProcesa los reportes de medición pendientes que no pudieron enviarse a Stripe\nen intentos anteriores. Útil para reintentar reportes fallidos tras resolver\nproblemas de conectividad o configuración.\n\n## Objective\nGarantizar que todos los reportes de medición lleguen a Stripe, incluso tras\nfallos temporales, mediante un mecanismo de reintento controlado.\n\n## Use Cases\n- Reintentar envío de reportes fallidos por errores temporales\n- Procesar backlog de reportes acumulados\n- Mantener la consistencia entre mediciones locales y Stripe\n- Recuperación tras incidencias del servicio de Stripe\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive POST Request] --> B{JWT valid?}\n  B -->|No| C[401 NO_TOKEN / TOKEN_NOT_VALID]\n  B -->|Yes| D{User is admin?}\n  D -->|No| E[403 INSUFFICIENT_PERMISSIONS]\n  D -->|Yes| F[Fetch pending reports]\n  F --> G[Process in batches]\n  G --> H[200 OK - Process results]\n```\n\n## Notes\n- Requiere rol de **admin** o **dev** (`m.isAdmin`)\n- El parámetro `batchSize` es opcional (default: 50)\n- Los reportes se procesan en lotes para evitar sobrecarga\n- Solo se procesan reportes con estado pendiente o fallido\n",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "batchSize": 100
              },
              "schema": {
                "properties": {
                  "batchSize": {
                    "description": "Número máximo de reportes a procesar en esta ejecución.\nDefault: 50\n",
                    "example": 100,
                    "maximum": 500,
                    "minimum": 1,
                    "type": "integer"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "failed": 2,
                    "message": "Pending reports processed",
                    "processed": 45,
                    "succeeded": 43
                  },
                  "status": 200
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "failed": {
                          "description": "Número de reportes que fallaron",
                          "example": 2,
                          "type": "integer"
                        },
                        "message": {
                          "example": "Pending reports processed",
                          "type": "string"
                        },
                        "processed": {
                          "description": "Número de reportes procesados",
                          "example": 45,
                          "type": "integer"
                        },
                        "succeeded": {
                          "description": "Número de reportes enviados correctamente",
                          "example": 43,
                          "type": "integer"
                        }
                      },
                      "type": "object"
                    },
                    "status": {
                      "example": 200,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Reportes pendientes procesados"
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "no_token": {
                    "summary": "Sin token de autenticación",
                    "value": {
                      "message": "NO_TOKEN"
                    }
                  },
                  "token_invalid": {
                    "summary": "Token inválido o expirado",
                    "value": {
                      "message": "TOKEN_NOT_VALID"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Token no proporcionado o inválido",
            "headers": {}
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "message": "INSUFFICIENT_PERMISSIONS"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Permisos insuficientes (requiere rol admin o dev)",
            "headers": {}
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "message": "PROCESS_FAILED"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error al procesar reportes",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Procesar reportes pendientes de Stripe Meter",
        "tags": [
          "billing"
        ]
      }
    },
    "/company/billing/sync-resources": {
      "post": {
        "deprecated": false,
        "description": "## Purpose\nInicia una sincronización (backfill) de los recursos existentes de la empresa\ncon Stripe Meter. Este endpoint permite asegurar que todos los recursos históricos\nestén correctamente registrados en el sistema de medición de Stripe.\n\n## Objective\nProporcionar un mecanismo para sincronizar recursos existentes con Stripe Meter,\nútil tras migraciones, correcciones de datos o inicialización del sistema de billing.\n\n## Use Cases\n- Sincronizar recursos tras una migración de datos\n- Backfill de recursos históricos a Stripe Meter\n- Corregir discrepancias entre recursos locales y Stripe\n- Sincronizar recursos de una empresa específica (admin)\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive POST Request] --> B{JWT valid?}\n  B -->|No| C[401 NO_TOKEN / TOKEN_NOT_VALID]\n  B -->|Yes| D{User is admin?}\n  D -->|No| E[403 INSUFFICIENT_PERMISSIONS]\n  D -->|Yes| F{companyId in body?}\n  F -->|No| G[400 COMPANY_ID_REQUIRED]\n  F -->|Yes| H[Sync resources to Stripe Meter]\n  H --> I[200 OK - Sync results]\n```\n\n## Notes\n- Requiere rol de **admin** o **dev** (`m.isAdmin`)\n- El campo `companyId` en el body es **obligatorio** si no hay contexto de empresa\n- La sincronización puede tardar dependiendo del volumen de recursos\n- Los resultados incluyen conteos de recursos sincronizados y errores\n",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "companyId": "507f1f77bcf86cd799439011"
              },
              "schema": {
                "properties": {
                  "companyId": {
                    "description": "ID de la empresa a sincronizar.\nSi no se proporciona, se requiere contexto de empresa en la request.\n",
                    "example": "507f1f77bcf86cd799439011",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "errors": 2,
                    "message": "Sync completed",
                    "synced": 150
                  },
                  "status": 200
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "errors": {
                          "description": "Número de errores durante la sincronización",
                          "example": 2,
                          "type": "integer"
                        },
                        "message": {
                          "example": "Sync completed",
                          "type": "string"
                        },
                        "synced": {
                          "description": "Número de recursos sincronizados",
                          "example": 150,
                          "type": "integer"
                        }
                      },
                      "type": "object"
                    },
                    "status": {
                      "example": 200,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Sincronización completada"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "message": "COMPANY_ID_REQUIRED"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "companyId requerido",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "no_token": {
                    "summary": "Sin token de autenticación",
                    "value": {
                      "message": "NO_TOKEN"
                    }
                  },
                  "token_invalid": {
                    "summary": "Token inválido o expirado",
                    "value": {
                      "message": "TOKEN_NOT_VALID"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Token no proporcionado o inválido",
            "headers": {}
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "message": "INSUFFICIENT_PERMISSIONS"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Permisos insuficientes (requiere rol admin o dev)",
            "headers": {}
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "message": "SYNC_FAILED"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error durante la sincronización",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Sincronizar recursos existentes con Stripe Meter",
        "tags": [
          "billing"
        ]
      }
    },
    "/company/billing/sync-stats": {
      "get": {
        "deprecated": false,
        "description": "## Purpose\nDevuelve estadísticas sobre el estado de sincronización de recursos con Stripe Meter,\nincluyendo conteos de recursos sincronizados, pendientes y con errores.\n\n## Objective\nProporcionar visibilidad sobre el estado de la integración con Stripe Meter\npara monitorización y diagnóstico.\n\n## Use Cases\n- Monitorizar el estado de sincronización con Stripe\n- Diagnosticar problemas de integración\n- Verificar completitud del backfill de recursos\n- Dashboard de estado del sistema de billing\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive GET Request] --> B{JWT valid?}\n  B -->|No| C[401 NO_TOKEN / TOKEN_NOT_VALID]\n  B -->|Yes| D{companyId in query?}\n  D -->|Yes| E[Use specified companyId]\n  D -->|No| F[Use request company context]\n  E --> G[Fetch sync stats]\n  F --> G\n  G --> H[200 OK - Sync statistics]\n```\n\n## Notes\n- Requiere autenticación JWT (`m.isLoged`) - **no requiere rol admin**\n- El parámetro `companyId` es opcional; si no se proporciona, usa la empresa del contexto\n- Las estadísticas incluyen conteos por estado y métricas de rendimiento\n",
        "parameters": [
          {
            "description": "ID de la empresa para obtener estadísticas (opcional).\nSi no se proporciona, se usa la empresa del contexto de la request.\n",
            "in": "query",
            "name": "companyId",
            "required": false,
            "schema": {
              "example": "507f1f77bcf86cd799439011",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "errors": 5,
                    "lastSyncAt": "2024-10-15T14:30:00Z",
                    "pending": 15,
                    "synced": 180,
                    "totalResources": 200
                  },
                  "status": 200
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "errors": {
                          "description": "Recursos con error de sincronización",
                          "example": 5,
                          "type": "integer"
                        },
                        "lastSyncAt": {
                          "description": "Fecha de la última sincronización",
                          "example": "2024-10-15T14:30:00Z",
                          "format": "date-time",
                          "type": "string"
                        },
                        "pending": {
                          "description": "Recursos pendientes de sincronización",
                          "example": 15,
                          "type": "integer"
                        },
                        "synced": {
                          "description": "Recursos sincronizados con Stripe",
                          "example": 180,
                          "type": "integer"
                        },
                        "totalResources": {
                          "description": "Total de recursos de la empresa",
                          "example": 200,
                          "type": "integer"
                        }
                      },
                      "type": "object"
                    },
                    "status": {
                      "example": 200,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Estadísticas de sincronización"
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "no_token": {
                    "summary": "Sin token de autenticación",
                    "value": {
                      "message": "NO_TOKEN"
                    }
                  },
                  "token_invalid": {
                    "summary": "Token inválido o expirado",
                    "value": {
                      "message": "TOKEN_NOT_VALID"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Token no proporcionado o inválido",
            "headers": {}
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "message": "STATS_FAILED"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error al obtener estadísticas",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Obtener estadísticas de sincronización con Stripe",
        "tags": [
          "billing"
        ]
      }
    },
    "/company/categories/find": {
      "get": {
        "deprecated": false,
        "description": "## Propósito\nBusca coincidencias en códigos y descripciones de partidas (6 dígitos) y subpartidas\n(8-10 dígitos) del Sistema Armonizado. Permite búsqueda libre sin necesidad de conocer\nla jerarquía completa.\n\n## Objetivo\nFacilitar la búsqueda rápida de códigos HS cuando se conoce el nombre del producto o\nparcialmente el código, sin navegar la estructura jerárquica completa.\n\n## Casos de uso\n- **Búsqueda por producto**: Encontrar el código HS para \"atún\", \"maquinaria\", \"textiles\"\n- **Validación de códigos**: Verificar si un código existe y obtener su descripción\n- **Autocompletado**: Sugerir códigos mientras el usuario escribe el nombre de un producto\n- **Clasificación rápida**: Encontrar categorías sin explorar toda la jerarquía\n- **Integración con inventario**: Clasificar productos existentes en un catálogo\n\n## Características de la búsqueda\n- **Case-insensitive**: No distingue mayúsculas/minúsculas\n- **Coincidencia parcial**: Busca en títulos (códigos) y descripciones (labels)\n- **Límite de 40 resultados**: 20 partidas + 20 subpartidas\n- **Ordenación alfabética**: Resultados ordenados por código\n- **Múltiples orígenes**: Busca en colecciones de partidas y subpartidas\n\n## Flujo de búsqueda\n```mermaid\nflowchart TD\n  A[Recibir término] --> B{término válido?}\n  B -->|No| C[404 USER_NOT_FOUND]\n  B -->|Sí| D[Buscar en partidas - máx 20]\n  D --> E[Buscar en subpartidas - máx 20]\n  E --> F[Combinar resultados]\n  F --> G[Ordenar alfabéticamente]\n  G --> H[Retornar array]\n```\n\n## Notas importantes\n- Requiere autenticación con token JWT de empresa\n- El término puede venir de query param, path param o body (se extrae de todos)\n- El término se limpia de comillas simples antes de buscar\n- Mínimo 1 carácter requerido (se elimina espacios)\n- No retorna resultados si no hay coincidencias (array vacío, no 404)\n",
        "parameters": [
          {
            "description": "Término de búsqueda para coincidencia parcial en códigos o descripciones.\nSe busca en ambos campos (title y label) con regex case-insensitive.\n",
            "example": "caballo",
            "in": "query",
            "name": "term",
            "required": true,
            "schema": {
              "maxLength": 100,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "label": "caballos reproductores de raza pura",
                    "title": "010121",
                    "url": ""
                  },
                  {
                    "label": "caballos para carreras",
                    "title": "01012110",
                    "url": ""
                  },
                  {
                    "label": "otros caballos reproductores",
                    "title": "01012190",
                    "url": ""
                  }
                ],
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/HSCodeItem"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Códigos HS coincidentes con la búsqueda",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "NO_TOKEN",
                  "message": "Token de autenticación no proporcionado o inválido"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado - Token JWT inválido o faltante",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "USER_NOT_FOUND",
                  "message": "Término de búsqueda no proporcionado o inválido"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Término de búsqueda inválido o vacío",
            "headers": {}
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": "INTERNAL_ERROR",
                  "message": "Error al realizar la búsqueda de códigos HS"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error interno del servidor",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Search HS codes by term",
        "tags": [
          "Categories"
        ]
      }
    },
    "/company/cmr/send": {
      "post": {
        "deprecated": false,
        "description": "## Purpose\nEnvía el documento CMR (Carta de Porte Digital) asociado a un servicio de transporte a uno o varios destinatarios por correo electrónico. El documento PDF se genera y envía como archivo adjunto.\n\n## Objective\nPermitir a las compañías distribuir el documento eCMR a las partes interesadas (clientes, transportistas, autoridades) sin necesidad de descargarlo manualmente. El servicio ecmr_back gestiona el envío asíncrono de los emails.\n\n## Use Cases\n- Una compañía necesita enviar el CMR a su cliente para su contabilidad\n- Se requiere enviar el CMR al transportista para su archivo\n- Las autoridades aduaneras solicitan el documento CMR por email\n- La compañía necesita distribuir el CMR a múltiples departamentos internos\n\n## Payment Validation\n**Middleware: `mPlan.isPaymentUpdate`**\nEste endpoint valida el estado de pago de la compañía antes de procesar el envío:\n- Verifica que el token JWT sea válido\n- Verifica que la compañía tenga permisos de pago actualizados\n- Rechaza la solicitud si la compañía no tiene un pago activo o suscripción válida\nSi la validación de pago falla, se retornará un error 401/403 antes de procesar el envío.\n\n## Behavior\n1. Valida los parámetros `service_code` y `emails`\n2. Verifica que el usuario esté autenticado y tenga compañía válida\n3. Valida el estado de pago (middleware `isPaymentUpdate`)\n4. Reenvía la solicitud al servicio ecmr_back\n5. ecmr_back genera el PDF y envía los emails\n6. Retorna confirmación de envío (no confirmación de entrega)\n\n**Notes:**\n- El envío de emails es asíncrono: se confirma que se envió a la cola, no que se entregó\n- Cada destinatario recibirá el CMR como archivo PDF adjunto\n- Máximo 10 direcciones de email por solicitud\n- No se realiza tracking de apertura o entrega de los emails\n- El servicio puede tardar hasta 10 segundos en responder\n\n## Preconditions\n- User must be authenticated and exist in database\n- User must belong to a valid company\n- Company must have valid payment status (validated by isPaymentUpdate)\n- A delivery with the given service_code must exist for the company\n- The delivery must have an associated eCMR document\n\n## Error Codes\n**Validation Errors (400):**\n- `SERVICE_CODE_REQUIRED`: El parámetro `service_code` es obligatorio\n- `EMAILS_REQUIRED`: El parámetro `emails` es obligatorio y no puede estar vacío\n\n**Authentication Errors (401):**\n- `USER_NOT_FOUND`: El usuario autenticado no existe en la base de datos\n- `AUTHORIZATION_TOKEN_REQUIRED`: Falta el token en los headers o es inválido\n- Payment validation failed (via `isPaymentUpdate` middleware)\n\n**Not Found Errors (404):**\n- `NOT_FOUND`: No existe un delivery con ese `service_code` para esta compañía\n\n**Service Errors (503):**\n- Error al comunicarse con el servicio ecmr_back\n- Timeout en el envío de emails (10 segundos máximo)\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request] --> B{service_code Present?}\n  B -->|No| C[400 SERVICE_CODE_REQUIRED]\n  B -->|Yes| D{emails Present & Not Empty?}\n  D -->|No| E[400 EMAILS_REQUIRED]\n  D -->|Yes| F{User Authenticated?}\n  F -->|No| G[401 USER_NOT_FOUND]\n  F -->|Yes| H{Token Present?}\n  H -->|No| I[401 AUTHORIZATION_TOKEN_REQUIRED]\n  H -->|Yes| J{Payment Valid?}\n  J -->|No| K[401/403 Payment Failed]\n  J -->|Yes| L{User Exists in DB?}\n  L -->|No| M[401 USER_NOT_FOUND]\n  L -->|Yes| N{Company Found?}\n  N -->|No| O[401 COMPANY_NOT_FOUND]\n  N -->|Yes| P{Delivery Exists?}\n  P -->|No| Q[404 NOT_FOUND]\n  P -->|Yes| R{Call ecmr_back}\n  R -->|Success| S[200 Email Sent]\n  R -->|Service Error| T[503 Service Error]\n```\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "emails": [
                  "logistica@empresa.com",
                  "transportista@proveedor.com",
                  "cliente@cliente.com"
                ],
                "service_code": "TRANS-2023-0042"
              },
              "schema": {
                "$ref": "#/components/schemas/SendCMRRequest"
              }
            }
          },
          "description": "Datos necesarios para enviar el CMR por email.\nEl `service_code` identifica el delivery y `emails` contiene la lista de destinatarios.\n",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "sent": 3,
                    "service_code": "TRANS-2023-0042",
                    "timestamp": "2026-02-12T10:30:00.000Z"
                  },
                  "success": true
                },
                "schema": {
                  "$ref": "#/components/schemas/SendCMRResponse"
                }
              }
            },
            "description": "El CMR se ha enviado exitosamente a los destinatarios especificados.\nLa respuesta confirma que el email fue enviado a la cola de ecmr_back,\npero NO confirma la entrega efectiva a los destinatarios.\n"
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "emailsRequired": {
                    "summary": "Falta la lista de emails",
                    "value": {
                      "message": "EMAILS_REQUIRED"
                    }
                  },
                  "serviceCodeRequired": {
                    "summary": "Falta el código de servicio",
                    "value": {
                      "message": "SERVICE_CODE_REQUIRED"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Solicitud inválida - Parámetros faltantes o incorrectos.\n**Códigos de error:**\n- `SERVICE_CODE_REQUIRED`: Falta el parámetro `service_code`\n- `EMAILS_REQUIRED`: Falta el parámetro `emails` o está vacío (array con longitud 0)\n"
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "paymentInvalid": {
                    "summary": "Estado de pago inválido",
                    "value": {
                      "message": "PAYMENT_STATUS_INVALID"
                    }
                  },
                  "tokenRequired": {
                    "summary": "Token faltante",
                    "value": {
                      "message": "AUTHORIZATION_TOKEN_REQUIRED"
                    }
                  },
                  "userNotFound": {
                    "summary": "Usuario no encontrado",
                    "value": {
                      "message": "USER_NOT_FOUND"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado - Problemas de autenticación o pago.\n**Códigos de error:**\n- `USER_NOT_FOUND`: El usuario autenticado no existe en la base de datos\n- `AUTHORIZATION_TOKEN_REQUIRED`: Falta el token en los headers\n- Payment validation failed (middleware `isPaymentUpdate`): La compañía no tiene estado de pago válido\n"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "message": "PAYMENT_REQUIRED"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Prohibido - La compañía no tiene permisos de pago actualizados.\nEl middleware `isPaymentUpdate` rechazó la solicitud por falta de pago activo.\n"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No encontrado - El delivery no existe para esta compañía.\n**Códigos de error:**\n- `NOT_FOUND`: No existe un delivery con ese `service_code` para esta compañía\n"
          },
          "503": {
            "content": {
              "application/json": {
                "example": {
                  "message": "Service error from ecmr_back"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Servicio no disponible - Error al comunicarse con el servicio ecmr_back o timeout en el envío.\n**Posibles causas:**\n- El servicio ecmr_back no está disponible\n- Timeout esperando el envío de emails (10 segundos máximo)\n- Error interno en el servicio ecmr_back\n"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Send CMR document by email.",
        "tags": [
          "Cmr"
        ]
      }
    },
    "/company/cmr/{service_code}": {
      "get": {
        "deprecated": false,
        "description": "## Purpose\nRecupera el documento CMR (Carta de Porte Digital / eCMR) en formato PDF asociado a un servicio de transporte específico. El CMR es el documento legal obligatorio para el transporte internacional de mercancías por carretera según el Convenio CMR.\n\n## Objective\nPermitir a las compañías descargar el documento eCMR oficial en formato PDF para su archivo, impresión o compartir con clientes y autoridades. El documento es generado por el servicio especializado ecmr_back y entregado como archivo binario.\n\n## Use Cases\n- Una compañía necesita descargar el CMR para un envío que acaba de completarse\n- Un cliente solicita el documento CMR oficial para su contabilidad\n- Se requiere presentar el CMR a autoridades aduaneras en un control de transporte\n- La compañía necesita archivar el CMR digitalmente por requisitos legales\n\n## Architecture Note\nEste endpoint actúa como **proxy** hacia el servicio **ecmr_back** (microservicio especializado en gestión de documentos eCMR). El PDF es generado y entregado por ecmr_back, luego transmitido como stream binario al cliente.\n\n**Preconditions:**\n- User must be authenticated and exist in database\n- User must belong to a valid company\n- A delivery with the given service_code must exist for the company\n- The delivery must have an associated eCMR document\n\n**Error Codes:**\n- `USER_NOT_FOUND` (401): Authenticated user does not exist in database\n- `AUTHORIZATION_TOKEN_REQUIRED` (401): Token missing or invalid in request headers\n- `COMPANY_NOT_FOUND` (Internal): User has no associated company (rejected before API response)\n- `NOT_FOUND` (404): Delivery not found for this company/service_code\n- `FILE_NOT_AVAILABLE` (404): CMR PDF file not available in ecmr_back service\n- Service error (503): ecmr_back service unavailable or timeout\n\n**Validation Flow:**\n```mermaid\nflowchart TD\n  A[Receive Request] --> B{User Authenticated?}\n  B -->|No| C[401 USER_NOT_FOUND]\n  B -->|Yes| D{Token Present?}\n  D -->|No| E[401 AUTHORIZATION_TOKEN_REQUIRED]\n  D -->|Yes| F{User Exists in DB?}\n  F -->|No| G[401 USER_NOT_FOUND]\n  F -->|Yes| H{Company Found?}\n  H -->|No| I[401 COMPANY_NOT_FOUND]\n  H -->|Yes| J{Delivery Exists?}\n  J -->|No| K[404 NOT_FOUND]\n  J -->|Yes| L{Call ecmr_back}\n  L -->|Success| M[200 PDF Binary]\n  L -->|File Not Found| N[404 FILE_NOT_AVAILABLE]\n  L -->|Service Error| O[503 Service Error]\n```\n",
        "parameters": [
          {
            "description": "Código único del servicio de transporte para el cual se solicita el CMR.\nEste código es generado automáticamente al crear el delivery y permite identificar de forma unívoca la operación de transporte asociada.\n**Formato típico:** PREFIJO-AÑO-NÚMERO (ejemplo: TRANS-2023-0042)\n",
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "example": "TRANS-2023-0042",
              "maxLength": 50,
              "minLength": 5,
              "pattern": "^[A-Z0-9-]+$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/pdf": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "Documento CMR en formato PDF descargado exitosamente.\nEl archivo es transmitido como stream binario directo desde el servicio ecmr_back.\n",
            "headers": {
              "Cache-Control": {
                "description": "Directiva de caché para evitar transformaciones",
                "schema": {
                  "example": "no-transform",
                  "type": "string"
                }
              },
              "Content-Disposition": {
                "description": "Indica que el archivo debe ser descargado como adjunto con el nombre del archivo.\nFormato: attachment; filename=\"{service_code}.pdf\"\n",
                "schema": {
                  "example": "attachment; filename=\"TRANS-2023-0042.pdf\"",
                  "type": "string"
                }
              },
              "Content-Encoding": {
                "description": "Tipo de codificación (siempre identity para este endpoint)",
                "schema": {
                  "example": "identity",
                  "type": "string"
                }
              },
              "Content-Length": {
                "description": "Tamaño del archivo PDF en bytes",
                "schema": {
                  "example": 245632,
                  "type": "integer"
                }
              },
              "Content-Type": {
                "description": "Tipo de contenido del archivo",
                "schema": {
                  "example": "application/pdf",
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "tokenRequired": {
                    "summary": "Token faltante o inválido",
                    "value": {
                      "message": "AUTHORIZATION_TOKEN_REQUIRED"
                    }
                  },
                  "userNotFound": {
                    "summary": "Usuario no encontrado en base de datos",
                    "value": {
                      "message": "USER_NOT_FOUND"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado - El usuario no está autenticado o el token es inválido.\n**Posibles códigos de error:**\n- `USER_NOT_FOUND`: El usuario autenticado no existe en la base de datos\n- `AUTHORIZATION_TOKEN_REQUIRED`: Falta el token en los headers o es inválido\n"
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "fileNotAvailable": {
                    "summary": "Archivo CMR no disponible",
                    "value": {
                      "message": "FILE_NOT_AVAILABLE"
                    }
                  },
                  "notFound": {
                    "summary": "Delivery no encontrado",
                    "value": {
                      "message": "NOT_FOUND"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No encontrado - No existe un CMR disponible para el service_code proporcionado.\n**Posibles causas:**\n- El código de servicio es incorrecto o no pertenece a esta compañía\n- El delivery existe pero aún no se ha generado el CMR\n- El archivo CMR fue eliminado o no está disponible en ecmr_back\n**Códigos de error:**\n- `NOT_FOUND`: El delivery no existe para esta compañía\n- `FILE_NOT_AVAILABLE`: El CMR no está disponible en ecmr_back\n"
          },
          "503": {
            "content": {
              "application/json": {
                "example": {
                  "message": "Service error from ecmr_back"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Servicio no disponible - Error al comunicarse con el servicio ecmr_back o timeout en la generación del PDF.\n**Posibles causas:**\n- El servicio ecmr_back no está disponible\n- Timeout esperando la generación del PDF (30 segundos máximo)\n- Error interno en el servicio ecmr_back\n"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get CMR document in PDF format",
        "tags": [
          "Cmr"
        ]
      }
    },
    "/company/company_data/": {
      "get": {
        "deprecated": false,
        "description": "Este endpoint devuelve toda la información registrada de la compañía asociada al usuario autenticado.\nIncluye datos básicos, dirección, configuración de pago (Stripe) y estado de la firma digital.\n\n**Casos de uso:**\n- Mostrar información de la compañía en el perfil\n- Pre-cargar formularios de edición\n- Verificar datos de facturación\n\n**Ejemplo de respuesta exitosa:**\n```json\n{\n  \"name\": \"Transportes Ejemplo S.L.\",\n  \"socialName\": \"TRANSPORTES EJEMPLO SOCIEDAD LIMITADA\",\n  \"taxid\": \"B12345678\",\n  \"email\": \"contacto@ejemplo.com\",\n  \"phone\": \"+34911222333\",\n  \"address\": {\n    \"street\": \"Calle Ejemplo 123\",\n    \"city\": \"Madrid\",\n    \"zipcode\": \"28001\",\n    \"country\": \"España\"\n  },\n  \"payment_settings\": {\n    \"stripe_customer\": \"cus_123456789\"\n  },\n  \"sign\": \"data:image/png;base64,...\",\n  \"hasSign\": true,\n  \"payment\": \"premium\"\n}\n```\n",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "address": {
                    "city": "Madrid",
                    "country": "España",
                    "street": "Calle Ejemplo 123",
                    "zipcode": "28001"
                  },
                  "email": "contacto@ejemplo.com",
                  "hasSign": true,
                  "name": "Transportes Ejemplo S.L.",
                  "payment_settings": {
                    "stripe_customer": "cus_123456789"
                  },
                  "phone": "+34911222333",
                  "sign": "data:image/png;base64,...",
                  "socialName": "TRANSPORTES EJEMPLO SOCIEDAD LIMITADA",
                  "taxid": "B12345678"
                },
                "schema": {
                  "$ref": "#/components/schemas/CompanyData"
                }
              }
            },
            "description": "Datos completos de la compañía",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. Ocurre cuando:\n- El token JWT es inválido o ha expirado\n- El usuario no tiene permisos para acceder a estos datos\n",
            "headers": {}
          },
          "404": {
            "description": "Compañía no encontrada. Ocurre cuando:\n- El usuario no está asociado a ninguna compañía\n- La compañía fue eliminada\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Retrieves the company's complete data",
        "tags": [
          "Company Data"
        ]
      },
      "put": {
        "deprecated": false,
        "description": "Este endpoint permite actualizar la información de la compañía asociada al usuario autenticado.\nActualiza tanto los datos básicos como la dirección, y sincroniza los cambios con Stripe.\n\n**Campos actualizables:**\n- name: Nombre comercial\n- socialName: Razón social\n- taxid: NIF/CIF\n- email: Email de contacto\n- phone: Teléfono\n- address: Objeto con dirección completa\n\n**Validaciones:**\n- El email debe tener formato válido\n- La dirección se valida y normaliza con Google Maps\n- Los cambios se sincronizan automáticamente con Stripe\n\n**Ejemplo de petición:**\n```json\n{\n  \"companyData\": {\n    \"name\": \"Nuevo Nombre S.L.\",\n    \"socialName\": \"NUEVO NOMBRE SOCIEDAD LIMITADA\",\n    \"taxid\": \"B87654321\",\n    \"email\": \"nuevo@email.com\",\n    \"phone\": \"+34987654321\",\n    \"address\": {\n      \"street\": \"Calle Nueva 456\",\n      \"city\": \"Barcelona\",\n      \"zipcode\": \"08001\",\n      \"country\": \"España\"\n    }\n  }\n}\n```\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "companyData": {
                  "address": {
                    "city": "Barcelona",
                    "country": "España",
                    "street": "Calle Nueva 456",
                    "zipcode": "08001"
                  },
                  "email": "nuevo@email.com",
                  "name": "Nuevo Nombre S.L.",
                  "phone": "+34987654321",
                  "socialName": "NUEVO NOMBRE SOCIEDAD LIMITADA",
                  "taxid": "B87654321"
                }
              },
              "schema": {
                "$ref": "#/components/schemas/UpdateCompanyRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "address": {
                    "city": "Barcelona",
                    "country": "España",
                    "street": "Calle Nueva 456",
                    "zipcode": "08001"
                  },
                  "email": "nuevo@email.com",
                  "name": "Nuevo Nombre S.L.",
                  "payment_settings": {
                    "stripe_customer": "cus_987654321"
                  },
                  "phone": "+34987654321",
                  "socialName": "NUEVO NOMBRE SOCIEDAD LIMITADA",
                  "taxid": "B87654321"
                },
                "schema": {
                  "$ref": "#/components/schemas/CompanyData"
                }
              }
            },
            "description": "Datos actualizados de la compañía",
            "headers": {}
          },
          "400": {
            "description": "Entrada inválida. Ocurre cuando:\n- Faltan campos obligatorios\n- El formato del email es inválido\n- La dirección no puede ser validada\n",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. El token JWT es inválido o ha expirado.\nSe debe renovar el token mediante el endpoint de autenticación.\n",
            "headers": {}
          },
          "404": {
            "description": "Compañía no encontrada. Ocurre cuando:\n- El usuario no está asociado a ninguna compañía\n- La compañía fue eliminada\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Update the company data",
        "tags": [
          "Company Data"
        ]
      }
    },
    "/company/company_data/editSign": {
      "put": {
        "deprecated": false,
        "description": "Este endpoint permite subir o actualizar la firma digital de la compañía.\nLa firma se almacena como una imagen en formato base64 y se asocia al perfil de la compañía.\n\n**Requisitos:**\n- Formato de imagen: PNG, JPG o JPEG\n- Tamaño máximo: 2MB\n- Relación de aspecto recomendada: 3:1 (ancho:alto)\n\n**Casos de uso:**\n- Firmar documentos digitales\n- Mostrar firma en facturas y contratos\n\n**Ejemplo de petición:**\n```\nPUT /editSign\nContent-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW\nAuthorization: Bearer [token]\n\n----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"image\"; filename=\"firma.png\"\nContent-Type: image/png\n\n(binary data)\n----WebKitFormBoundary7MA4YWxkTrZu0gW--\n```\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "properties": {
                  "image": {
                    "description": "Archivo de imagen con la firma digital de la compañía.\n\n**Requisitos:**\n- Formatos aceptados: PNG, JPG, JPEG\n- Tamaño máximo: 2MB\n- Se recomienda relación de aspecto 3:1 (ancho:alto)\n\n**Proceso:**\n1. El archivo se convierte automáticamente a base64\n2. Se almacena con el prefijo data URI (ej: \"data:image/png;base64,...\")\n3. Se valida que sea una imagen válida\n\n**Ejemplo de uso:**\n```\nContent-Disposition: form-data; name=\"image\"; filename=\"firma.png\"\nContent-Type: image/png\n\n(binary data)\n```\n",
                    "example": "",
                    "format": "binary",
                    "type": "string"
                  }
                },
                "required": [
                  "image"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": "OK",
                "schema": {
                  "example": "OK",
                  "type": "string"
                }
              }
            },
            "description": "Firma actualizada correctamente",
            "headers": {}
          },
          "400": {
            "description": "Archivo inválido. Ocurre cuando:\n- El archivo no es una imagen válida\n- Supera el tamaño máximo permitido\n- Tiene un formato no soportado\n",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. El token JWT es inválido o ha expirado.\nSe debe renovar el token mediante el endpoint de autenticación.\n",
            "headers": {}
          },
          "404": {
            "description": "Compañía no encontrada. Ocurre cuando:\n- El usuario no está asociado a ninguna compañía\n- La compañía fue eliminada\n",
            "headers": {}
          },
          "500": {
            "description": "Error procesando el archivo. Ocurre cuando:\n- Hay un error al convertir la imagen a base64\n- Fallo al guardar en la base de datos\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Upload or edit the company's digital signature.",
        "tags": [
          "Company Data"
        ]
      }
    },
    "/company/company_data/isComplete": {
      "get": {
        "deprecated": false,
        "description": "Este endpoint verifica si todos los campos obligatorios del perfil de la compañía han sido completados.\nLa lógica de validación incluye comprobar que los datos básicos como nombre, dirección, email y teléfono estén presentes.\n\n**Casos de uso:**\n- Mostrar estado de completitud del perfil en el dashboard de la compañía\n- Validar requisitos previos antes de permitir ciertas acciones\n\n**Ejemplo de respuesta exitosa:**\n```json\n{\n  \"isCompleted\": true\n}\n```\n",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "isCompleted": true
                },
                "schema": {
                  "properties": {
                    "isCompleted": {
                      "description": "Whether user profile is complete",
                      "type": "boolean"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Estado de completitud del perfil",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. El token JWT es inválido o ha expirado.\nSe debe renovar el token mediante el endpoint de autenticación.\n",
            "headers": {}
          },
          "404": {
            "description": "Compañía no encontrada. Ocurre cuando:\n- El usuario no está asociado a ninguna compañía\n- La compañía fue eliminada\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Check if the company profile is complete.",
        "tags": [
          "Company Data"
        ]
      }
    },
    "/company/contact/": {
      "post": {
        "deprecated": false,
        "description": "## Purpose\nCrea un nuevo ticket de contacto/soporte en el sistema para que empresas y transportistas puedan reportar problemas o consultas.\n\n## Objective\nPermitir que los usuarios reporten incidencias y reciban soporte técnico del equipo de CargoOffer.\n\n## Use Cases\n- Una empresa no puede acceder a su cuenta y necesita ayuda\n- Un transportista tiene problemas con una entrega específica\n- Usuario requiere configuración de su cuenta\n- Reporte de bugs o problemas técnicos en la plataforma\n- Consultas generales no categorizadas\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request] --> B{Valid Author?}\n  B -->|No| C[400 Invalid Author]\n  B -->|Yes| D{Valid relatedTo?}\n  D -->|No| E[400 Invalid Category]\n  D -->|Yes| F{CheckUTC Valid?}\n  F -->|No| G[400 Invalid Timestamp]\n  F -->|Yes| H[Create Issue - 200]\n```\n\n## Notes\n- **Campo message**: Opcional en el modelo (puede crear tickets vacíos), pero se recomienda proporcionar descripción detallada\n- **relatedTo enum**: El modelo acepta 12 valores: config, delivery, auction, messages, others, user, trucker, address, document, bid, contract, userRegister\n- **Autenticación**: No requiere autenticación explícita (permite author=\"anonymous\")\n- **Middleware**: Valida timestamps UTC mediante checkUTC\n- **Códigos internos**: Cada ticket recibe un internal_code automático (ej: \"2025-12345\")\n",
        "operationId": "createContactIssue",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "basic": {
                  "summary": "Basic anonymous contact",
                  "value": {
                    "author": "anonymous",
                    "message": "No puedo acceder a mi cuenta",
                    "relatedTo": "config"
                  }
                },
                "truckerContact": {
                  "summary": "Trucker reporting auction issue",
                  "value": {
                    "author": "trucker",
                    "message": "Problema con la subasta de carga",
                    "relatedTo": "auction"
                  }
                },
                "userRegistration": {
                  "summary": "User registration issue",
                  "value": {
                    "author": "company",
                    "message": "No puedo completar el registro de nuevo usuario",
                    "relatedTo": "userRegister"
                  }
                },
                "withAttachments": {
                  "summary": "Company contact with attachments",
                  "value": {
                    "author": "company",
                    "files": [
                      "https://s3.cargoffer.com/support/2023/screenshot-error.png"
                    ],
                    "message": "Error al subir documentos de entrega",
                    "relatedTo": "delivery"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/ContactIssue"
              }
            }
          },
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "success": {
                    "value": {
                      "_id": "507f1f77bcf86cd799439011",
                      "asignedTo": null,
                      "author": "anonymous",
                      "createdAt": "2025-02-12T10:30:00Z",
                      "files": [],
                      "idAuthor": null,
                      "internal_code": "2025-12345",
                      "is_bot": false,
                      "message": "No puedo acceder a mi cuenta",
                      "messages": [],
                      "nameAuthor": null,
                      "relatedTo": "config",
                      "service_code": "",
                      "status": "pending",
                      "updatedAt": "2025-02-12T10:30:00Z"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ContactIssueResponse"
                }
              }
            },
            "description": "Ticket de contacto creado exitosamente",
            "headers": {}
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "invalidAuthor": {
                    "value": {
                      "error": "Author is not valid",
                      "message": "CAN_NOT_CREATE"
                    }
                  },
                  "invalidCategory": {
                    "value": {
                      "error": "relatedTo is not valid",
                      "message": "CAN_NOT_CREATE"
                    }
                  },
                  "invalidTimestamp": {
                    "value": {
                      "error": "UTC timestamp validation failed",
                      "message": "INVALID_TIMESTAMP"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Solicitud inválida. Posibles causas:\n- CAN_NOT_CREATE: Error general al crear el ticket\n- Tipo de autor no válido (author debe ser: anonymous, company, o trucker)\n- Categoría no reconocida (relatedTo inválido)\n- Timestamp UTC inválido (middleware checkUTC)\n",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Token not provided or invalid",
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. Token JWT inválido o expirado (para usuarios autenticados)",
            "headers": {}
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Unexpected error occurred",
                  "message": "INTERNAL_ERROR"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error interno del servidor al procesar la solicitud",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Create contact/support ticket",
        "tags": [
          "Contact"
        ]
      }
    },
    "/company/contracts/": {
      "get": {
        "deprecated": false,
        "description": "Obtiene un listado paginado de todos los contratos de transporte asociados a la empresa autenticada.\nLos contratos incluyen información completa sobre:\n- Datos de la empresa y transportista\n- Detalles del envío (origen, destino, tipo de carga)\n- Condiciones económicas\n- Estados de firma\n\nEjemplo de uso típico:\n1. Listar contratos pendientes de firma\n2. Consultar historial de envíos\n3. Verificar condiciones de contratos activos\n",
        "parameters": [
          {
            "description": "Page number for pagination",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Items per page",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 10,
              "minimum": 1,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "docs": [
                    {
                      "cargo_type": "pallets",
                      "company": "comp_123",
                      "custom_code": "ENVIO-123",
                      "delivery_price": "1200.00",
                      "etd_addr": "Avenida Diagonal 100, Barcelona",
                      "etl_addr": "Calle Mayor 10, Madrid",
                      "service_code": "CT2023-001",
                      "signed_by_company_date": null,
                      "signed_by_trucker_date": "2023-05-15T10:30:00.000Z",
                      "trucker_cia": "truck_456"
                    }
                  ],
                  "limit": 10,
                  "page": 1,
                  "totalDocs": 15,
                  "totalPages": 2
                },
                "schema": {
                  "$ref": "#/components/schemas/ContractListResponse"
                }
              }
            },
            "description": "Operación exitosa",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "no_token": {
                    "summary": "Token no proporcionado",
                    "value": {
                      "message": "NO_TOKEN",
                      "status": 401
                    }
                  },
                  "token_invalid": {
                    "summary": "Token inválido",
                    "value": {
                      "message": "TOKEN_NOT_VALID",
                      "status": 401
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "message": {
                      "enum": [
                        "NO_TOKEN",
                        "TOKEN_NOT_VALID"
                      ],
                      "type": "string"
                    },
                    "status": {
                      "example": 401,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No autorizado - Token JWT inválido o no proporcionado",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "company_not_found": {
                    "summary": "Compañía no encontrada",
                    "value": {
                      "message": "COMPANY_NOT_FOUND",
                      "status": 404
                    }
                  },
                  "contract_not_found": {
                    "summary": "Contrato no encontrado",
                    "value": {
                      "message": "CONTRACT_NOT_FOUND",
                      "status": 404
                    }
                  },
                  "user_not_found": {
                    "summary": "Usuario no encontrado",
                    "value": {
                      "message": "USER_NOT_FOUND",
                      "status": 404
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "message": {
                      "enum": [
                        "USER_NOT_FOUND",
                        "COMPANY_NOT_FOUND",
                        "CONTRACT_NOT_FOUND"
                      ],
                      "type": "string"
                    },
                    "status": {
                      "example": 404,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No se encontraron recursos requeridos",
            "headers": {}
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "example": "SERVICE_UNAVAILABLE",
                      "type": "string"
                    },
                    "status": {
                      "example": 503,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Error del servicio al obtener contratos",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get list of contracts",
        "tags": [
          "Contracts"
        ]
      },
      "post": {
        "deprecated": false,
        "description": "Envía una copia del contrato en formato PDF a una lista de direcciones de correo electrónico.\nEl sistema genera automáticamente el PDF con los datos actuales del contrato antes de enviarlo.\n\nCasos de uso típicos:\n- Envío a transportista para revisión previa a firma\n- Compartición con departamento financiero\n- Archivo interno en la empresa\n\nRequisitos:\n- El contrato debe existir y estar asociado a la empresa autenticada\n- Se debe proporcionar al menos una dirección de email válida\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "emails": [
                  "transportista@example.com",
                  "finanzas@empresa.com"
                ],
                "service_code": "CT2023-001"
              },
              "schema": {
                "$ref": "#/components/schemas/EmailRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "example": "MAIL_SENDED",
                  "type": "string"
                }
              }
            },
            "description": "Emails enviados correctamente",
            "headers": {}
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "emails_required": {
                    "summary": "Lista de emails no proporcionada o vacía",
                    "value": {
                      "message": "EMAILS_REQUIRED",
                      "status": 400
                    }
                  },
                  "service_code_required": {
                    "summary": "Código de servicio no proporcionado",
                    "value": {
                      "message": "SERVICE_CODE_REQUIRED",
                      "status": 400
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "message": {
                      "enum": [
                        "SERVICE_CODE_REQUIRED",
                        "EMAILS_REQUIRED"
                      ],
                      "type": "string"
                    },
                    "status": {
                      "example": 400,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Solicitud inválida. Posibles causas:\n- Código de servicio no proporcionado\n- Lista de emails vacía o no proporcionada\n",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "cant_get_pdf": {
                    "summary": "Error al generar PDF para adjuntar",
                    "value": {
                      "message": "CANT_GET_PDF_TO_ATTATCH",
                      "status": 401
                    }
                  },
                  "no_token": {
                    "summary": "Token no proporcionado",
                    "value": {
                      "message": "NO_TOKEN",
                      "status": 401
                    }
                  },
                  "token_invalid": {
                    "summary": "Token inválido",
                    "value": {
                      "message": "TOKEN_NOT_VALID",
                      "status": 401
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "message": {
                      "enum": [
                        "CANT_GET_PDF_TO_ATTATCH",
                        "NO_TOKEN",
                        "TOKEN_NOT_VALID"
                      ],
                      "type": "string"
                    },
                    "status": {
                      "example": 401,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No autorizado - Error al generar el PDF del contrato",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "company_not_found": {
                    "summary": "Compañía no encontrada",
                    "value": {
                      "message": "COMPANY_NOT_FOUND",
                      "status": 404
                    }
                  },
                  "contract_not_found": {
                    "summary": "Contrato no encontrado (bug en implementación - devuelve USER_NOT_FOUND)",
                    "value": {
                      "message": "USER_NOT_FOUND",
                      "status": 404
                    }
                  },
                  "user_not_found": {
                    "summary": "Usuario no encontrado",
                    "value": {
                      "message": "USER_NOT_FOUND",
                      "status": 404
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "message": {
                      "enum": [
                        "USER_NOT_FOUND",
                        "COMPANY_NOT_FOUND",
                        "CONTRACT_NOT_FOUND"
                      ],
                      "type": "string"
                    },
                    "status": {
                      "example": 404,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No se encontraron recursos requeridos",
            "headers": {}
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "example": "SERVICE_UNAVAILABLE",
                      "type": "string"
                    },
                    "status": {
                      "example": 503,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Error del servicio al generar o enviar el PDF",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Send contract by email",
        "tags": [
          "Contracts"
        ]
      }
    },
    "/company/contracts/{service_code}": {
      "get": {
        "deprecated": false,
        "description": "Genera y devuelve el contrato de transporte en formato PDF listo para imprimir o firmar.\nEl PDF incluye todos los datos del contrato con formato profesional:\n- Encabezado con logos y datos de las empresas\n- Detalles completos del envío\n- Condiciones económicas y de pago\n- Espacios para firmas digitales o físicas\n\nCaracterísticas del PDF generado:\n- Formato A4 estándar\n- Marcas de agua con código único\n- Diseño responsivo para impresión\n",
        "parameters": [
          {
            "description": "Contract service code",
            "example": "",
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/pdf": {
                "example": "(Binario del PDF de ejemplo no mostrado aquí)\n",
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "PDF generado correctamente. Contiene:\n- Todas las cláusulas contractuales\n- Datos actualizados de las partes\n- Sellos de autenticidad\n",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "cant_get_pdf": {
                    "summary": "Error al generar PDF",
                    "value": {
                      "message": "CANT_GET_PDF",
                      "status": 401
                    }
                  },
                  "no_token": {
                    "summary": "Token no proporcionado",
                    "value": {
                      "message": "NO_TOKEN",
                      "status": 401
                    }
                  },
                  "token_invalid": {
                    "summary": "Token inválido",
                    "value": {
                      "message": "TOKEN_NOT_VALID",
                      "status": 401
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "message": {
                      "enum": [
                        "CANT_GET_PDF",
                        "NO_TOKEN",
                        "TOKEN_NOT_VALID"
                      ],
                      "type": "string"
                    },
                    "status": {
                      "example": 401,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No autorizado. Posibles causas:\n- Token JWT inválido o expirado\n- Error al generar el PDF del contrato\n",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "contract_not_found": {
                    "summary": "Contrato no encontrado",
                    "value": {
                      "message": "CONTRACT_NOT_FOUND",
                      "status": 404
                    }
                  },
                  "user_not_found": {
                    "summary": "Usuario no encontrado",
                    "value": {
                      "message": "USER_NOT_FOUND",
                      "status": 404
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "message": {
                      "enum": [
                        "USER_NOT_FOUND",
                        "CONTRACT_NOT_FOUND"
                      ],
                      "type": "string"
                    },
                    "status": {
                      "example": 404,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No se encontró el contrato. Verifique que:\n- El código de servicio es correcto\n- El contrato no ha sido eliminado\n- Tiene permisos para acceder a este contrato\n",
            "headers": {}
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "example": "SERVICE_UNAVAILABLE",
                      "type": "string"
                    },
                    "status": {
                      "example": 503,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Error del servicio al generar el PDF",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Download contract in PDF",
        "tags": [
          "Contracts"
        ]
      }
    },
    "/company/country/": {
      "get": {
        "deprecated": false,
        "description": "Devuelve una lista paginada de países habilitados en el sistema. Este endpoint proporciona los países disponibles para usar en formularios, filtros y selecciones geográficas.\n\n## Objetivo\nProporcionar a las aplicaciones frontend e integraciones externas una lista actualizada de países soportados por la plataforma para el registro de empresas, creación de direcciones y operaciones logísticas.\n\n## Casos de uso\n- Rellenar selectores de país en formularios de registro\n- Mostrar regiones de servicio disponibles a los usuarios\n- Filtrar subastas o entregas por área geográfica\n- Validar códigos de país proporcionados por los usuarios\n\n## Autenticación\nEste es un endpoint **PÚBLICO**. No requiere autenticación.\n\n## Notas importantes\n- Solo devuelve países con `enabled: true`\n- Los países con `deleted: true` o `enabled: false` quedan excluidos\n- Los campos `_id`, `deleted`, `enabled`, `createdAt`, `updatedAt`, `__v` se filtran de la respuesta\n- Solo el campo `code` está garantizado en todos los registros\n- Los campos opcionales `name` e `iso` pueden estar presentes en algunos países\n- Paginación implementada con mongoose-paginate-v2\n- El tamaño de página por defecto proviene de `process.env.ITEMS_PAGE` (normalmente 25)\n\n**Ejemplo de solicitud**:\n```http\n# Obtener la primera página con el límite por defecto (25)\nGET /company/country/\n\n# Obtener la segunda página con 10 resultados\nGET /company/country/?page=2&limit=10\n\n# Obtener todos los países disponibles (si son menos de 50)\nGET /company/country/?limit=50\n```\n\n**Ejemplo de respuesta**:\n```json\n{\n  \"status\": 200,\n  \"data\": {\n    \"docs\": [\n      {\"code\": \"es\"},\n      {\"code\": \"pt\"},\n      {\"code\": \"fr\"},\n      {\"code\": \"de\", \"name\": \"Germany\", \"iso\": \"DEU\"}\n    ],\n    \"totalDocs\": 22,\n    \"limit\": 25,\n    \"page\": 1,\n    \"totalPages\": 1,\n    \"pagingCounter\": 1,\n    \"hasPrevPage\": false,\n    \"hasNextPage\": false,\n    \"prevPage\": null,\n    \"nextPage\": null\n  }\n}\n```\n",
        "parameters": [
          {
            "description": "Número de página a recuperar (indexado desde 1).\n\n- Valor mínimo: 1\n- Por defecto: 1\n- Los valores inválidos (< 1, NaN) pueden ser gestionados por mongoose-paginate\n",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "example": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Número máximo de resultados por página.\n\n- Mínimo: 1\n- Por defecto: 25 (desde process.env.ITEMS_PAGE)\n- Nota: Actualmente el backend no aplica un límite máximo\n",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 25,
              "example": 10,
              "minimum": 1,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "docs": [
                      {
                        "code": "es"
                      },
                      {
                        "code": "pt"
                      },
                      {
                        "code": "fr"
                      },
                      {
                        "code": "de",
                        "iso": "DEU",
                        "name": "Germany"
                      },
                      {
                        "code": "it",
                        "iso": "ITA",
                        "name": "Italy"
                      }
                    ],
                    "hasNextPage": false,
                    "hasPrevPage": false,
                    "limit": 25,
                    "nextPage": null,
                    "page": 1,
                    "pagingCounter": 1,
                    "prevPage": null,
                    "totalDocs": 22,
                    "totalPages": 1
                  },
                  "status": 200
                },
                "schema": {
                  "$ref": "#/components/schemas/CountriesResponse"
                }
              }
            },
            "description": "Operación exitosa. Devuelve la lista paginada de países habilitados.\n\nLa respuesta incluye los metadatos completos de paginación de mongoose-paginate-v2.\n",
            "headers": {}
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "message": "INTERNAL_ERROR",
                  "status": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error interno del servidor"
          }
        },
        "summary": "Get public list of enabled countries",
        "tags": [
          "countries"
        ]
      }
    },
    "/company/country/all": {
      "get": {
        "deprecated": false,
        "description": "## Propósito\nDevuelve una lista paginada de TODOS los países del sistema, incluidos los que tienen `enabled: false` o `deleted: true`. Este endpoint está destinado a la gestión administrativa del catálogo de países.\n\n## Objetivo\nProporcionar a los administradores del sistema visibilidad completa de todos los países para fines de gestión, incluyendo la capacidad de ver registros deshabilitados o eliminados lógicamente.\n\n## Casos de uso\n- Interfaz de gestión de países en el panel de administración\n- Revisar países deshabilitados antes de volver a habilitarlos\n- Auditar cambios en el catálogo de países a lo largo del tiempo\n- Restaurar países eliminados lógicamente\n\n## Autenticación\n**Requiere**: Rol de administrador (admin o dev)\n**Middleware**: `m.isAdmin`\n\n## Diferencias con el endpoint público\n- `GET /company/country/`: Solo devuelve países con `enabled: true` (público)\n- `GET /company/country/all`: Devuelve TODOS los países incluyendo los deshabilitados (solo admin)\n\n**Ejemplo de solicitud**:\n```http\nGET /company/country/all?page=1&limit=50\nAuthorization: Bearer {admin_jwt_token}\n```\n\n**Ejemplo de respuesta**:\n```json\n{\n  \"status\": 200,\n  \"data\": {\n    \"docs\": [\n      {\"code\": \"es\"},\n      {\"code\": \"pt\"},\n      {\"code\": \"xx\"}\n    ],\n    \"totalDocs\": 30,\n    \"limit\": 50,\n    \"page\": 1,\n    \"totalPages\": 1,\n    \"pagingCounter\": 1,\n    \"hasPrevPage\": false,\n    \"hasNextPage\": false,\n    \"prevPage\": null,\n    \"nextPage\": null\n  }\n}\n```\n",
        "parameters": [
          {
            "description": "Número de página (indexado desde 1)",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "example": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Resultados por página",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 25,
              "example": 50,
              "minimum": 1,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CountriesResponse"
                }
              }
            },
            "description": "Operación exitosa",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "invalidToken": {
                    "summary": "Invalid or expired token",
                    "value": {
                      "message": "TOKEN_NOT_VALID",
                      "status": 401
                    }
                  },
                  "noToken": {
                    "summary": "No token provided",
                    "value": {
                      "message": "NO_TOKEN",
                      "status": 401
                    }
                  },
                  "notAdmin": {
                    "summary": "User is not admin",
                    "value": {
                      "message": "NO_ADMIN_ROLE",
                      "status": 401
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado - Se requiere rol de administrador"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error interno del servidor"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Get all countries including disabled ones (Admin only)",
        "tags": [
          "countries-admin"
        ]
      },
      "post": {
        "deprecated": false,
        "description": "## Propósito\nCrea un nuevo registro de país en el sistema. Este endpoint permite a los administradores añadir soporte para nuevos países en la plataforma.\n\n## Objetivo\nPermitir la expansión de la plataforma a nuevas regiones geográficas añadiendo definiciones de países que se usarán en todo el sistema para validación, filtrado y localización.\n\n## Casos de uso\n- Añadir un nuevo país a las regiones soportadas\n- Crear países de prueba en entornos de desarrollo\n- Ampliar la disponibilidad de la plataforma a nuevos mercados\n\n## Flujo de validación\n```mermaid\nflowchart TD\n  A[Recibir solicitud] --> B{¿Admin autenticado?}\n  B -->|No| C[401 No autorizado]\n  B -->|Sí| D{¿Timestamp UTC válido?}\n  D -->|No| E[400 Validación UTC fallida]\n  D -->|Sí| F{¿Código de país único?}\n  F -->|No| G[401 ALREADY_EXIST]\n  F -->|Sí| H{¿Código de 2 letras?}\n  H -->|No| I[400 Código inválido]\n  H -->|Sí| J[Crear país - 200]\n```\n\n## Autenticación\n**Requiere**: Rol de administrador (admin o dev)\n**Middleware**: `m.isAdmin`, `mTools.checkUTC`\n\n## Notas importantes\n- El `code` del país debe ser único y exactamente 2 letras minúsculas\n- El código se convierte automáticamente a minúsculas por el modelo\n- Si no se proporciona `enabled`, por defecto es `true`\n- Campos adicionales como `name`, `iso`, `phoneCode`, `currency` son opcionales\n- El modelo usa `strict: false`, permitiendo campos dinámicos\n- Se requiere validación de timestamp UTC (middleware checkUTC)\n\n**Ejemplo de solicitud**:\n```http\nPOST /company/country/\nAuthorization: Bearer {admin_jwt_token}\nContent-Type: application/json\n\n{\n  \"code\": \"it\",\n  \"enabled\": true,\n  \"name\": \"Italy\",\n  \"iso\": \"ITA\",\n  \"phoneCode\": \"+39\",\n  \"currency\": \"EUR\"\n}\n```\n",
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "createMinimal": {
                  "summary": "Create country with minimal data",
                  "value": {
                    "code": "xx",
                    "enabled": true
                  }
                },
                "createWithAllFields": {
                  "summary": "Create country with all fields",
                  "value": {
                    "code": "it",
                    "currency": "EUR",
                    "enabled": true,
                    "iso": "ITA",
                    "name": "Italy",
                    "phoneCode": "+39"
                  }
                }
              },
              "schema": {
                "properties": {
                  "code": {
                    "description": "Código de país ISO de 2 letras (minúsculas).\nDebe ser único. Se convertirá automáticamente a minúsculas.\n",
                    "example": "it",
                    "maxLength": 2,
                    "minLength": 2,
                    "pattern": "^[a-z]{2}$",
                    "type": "string"
                  },
                  "currency": {
                    "description": "Código ISO de moneda (opcional)",
                    "example": "EUR",
                    "maxLength": 3,
                    "minLength": 3,
                    "pattern": "^[A-Z]{3}$",
                    "type": "string"
                  },
                  "enabled": {
                    "default": true,
                    "description": "Indica si el país está habilitado para uso en la plataforma",
                    "example": true,
                    "type": "boolean"
                  },
                  "iso": {
                    "description": "Código ISO de 3 letras del país (opcional)",
                    "example": "ITA",
                    "maxLength": 3,
                    "minLength": 3,
                    "pattern": "^[A-Z]{3}$",
                    "type": "string"
                  },
                  "name": {
                    "description": "Nombre del país en inglés (opcional)",
                    "example": "Italy",
                    "type": "string"
                  },
                  "phoneCode": {
                    "description": "Prefijo telefónico internacional (opcional)",
                    "example": "+39",
                    "pattern": "^\\\\+[0-9]{1,4}$",
                    "type": "string"
                  }
                },
                "required": [
                  "code"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "code": "it",
                    "currency": "EUR",
                    "enabled": true,
                    "iso": "ITA",
                    "name": "Italy",
                    "phoneCode": "+39"
                  },
                  "status": 200
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Country"
                    },
                    "status": {
                      "example": 200,
                      "type": "integer"
                    }
                  },
                  "required": [
                    "status",
                    "data"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "País creado correctamente"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "message": "UTC_VALIDATION_FAILED",
                  "status": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Solicitud incorrecta - Validación UTC fallida o parámetros inválidos"
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "alreadyExists": {
                    "summary": "Country code already exists",
                    "value": {
                      "message": "ALREADY_EXIST",
                      "status": 401
                    }
                  },
                  "noToken": {
                    "summary": "No token provided",
                    "value": {
                      "message": "NO_TOKEN",
                      "status": 401
                    }
                  },
                  "notAdmin": {
                    "summary": "User is not admin",
                    "value": {
                      "message": "NO_ADMIN_ROLE",
                      "status": 401
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado o el país ya existe"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error interno del servidor"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Create new country (Admin only)",
        "tags": [
          "countries-admin"
        ]
      },
      "put": {
        "deprecated": false,
        "description": "## Propósito\nActualiza un registro de país existente en el sistema. Permite a los administradores modificar propiedades del país como habilitarlo/deshabilitarlo, actualizar nombres o cambiar metadatos.\n\n## Objetivo\nMantener la información de países actualizada y controlar qué países están disponibles para los usuarios sin eliminar datos históricos.\n\n## Casos de uso\n- Deshabilitar un país temporalmente (establecer `enabled: false`)\n- Actualizar el nombre del país o el código ISO\n- Cambiar el prefijo telefónico o la información de moneda\n- Volver a habilitar un país previamente deshabilitado\n\n## Flujo de validación\n```mermaid\nflowchart TD\n  A[Recibir solicitud] --> B{¿Admin autenticado?}\n  B -->|No| C[401 No autorizado]\n  B -->|Sí| D{¿Timestamp UTC válido?}\n  D -->|No| E[400 Validación UTC fallida]\n  D -->|Sí| F{¿País existe?}\n  F -->|No| G[404 NOT_FOUND]\n  F -->|Sí| H[Actualizar campos]\n  H --> I[Guardar en base de datos]\n  I --> J[200 OK con datos actualizados]\n```\n\n## Autenticación\n**Requiere**: Rol de administrador (admin o dev)\n**Middleware**: `m.isAdmin`, `mTools.checkUTC`\n\n## Notas importantes\n- El campo `code` en el cuerpo de la solicitud identifica qué país actualizar\n- El `code` en sí no puede modificarse (es el identificador)\n- El resto de campos pueden actualizarse\n- Solo los campos proporcionados serán actualizados (actualización parcial)\n- Se requiere validación de timestamp UTC\n\n**Ejemplo de solicitud**:\n```http\nPUT /company/country/\nAuthorization: Bearer {admin_jwt_token}\nContent-Type: application/json\n\n{\n  \"code\": \"it\",\n  \"enabled\": false,\n  \"name\": \"Italy (Updated)\"\n}\n```\n",
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "disableCountry": {
                  "summary": "Disable a country",
                  "value": {
                    "code": "it",
                    "enabled": false
                  }
                },
                "updateMetadata": {
                  "summary": "Update country metadata",
                  "value": {
                    "code": "it",
                    "currency": "EUR",
                    "name": "Italy (Updated)",
                    "phoneCode": "+39"
                  }
                }
              },
              "schema": {
                "properties": {
                  "code": {
                    "description": "Código del país a actualizar (identificador, no puede modificarse).\nDebe coincidir con un país existente.\n",
                    "example": "it",
                    "type": "string"
                  },
                  "currency": {
                    "description": "Actualizar el código de moneda",
                    "example": "EUR",
                    "type": "string"
                  },
                  "enabled": {
                    "description": "Habilitar o deshabilitar el país",
                    "example": false,
                    "type": "boolean"
                  },
                  "iso": {
                    "description": "Actualizar el código ISO de 3 letras",
                    "example": "ITA",
                    "type": "string"
                  },
                  "name": {
                    "description": "Actualizar el nombre del país",
                    "example": "Italy",
                    "type": "string"
                  },
                  "phoneCode": {
                    "description": "Actualizar el prefijo telefónico",
                    "example": "+39",
                    "type": "string"
                  }
                },
                "required": [
                  "code"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "code": "it",
                    "enabled": false,
                    "iso": "ITA",
                    "name": "Italy"
                  },
                  "status": 200
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Country"
                    },
                    "status": {
                      "example": 200,
                      "type": "integer"
                    }
                  },
                  "required": [
                    "status",
                    "data"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "País actualizado correctamente"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "message": "UTC_VALIDATION_FAILED",
                  "status": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Solicitud incorrecta - Validación UTC fallida"
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "noToken": {
                    "summary": "No token provided",
                    "value": {
                      "message": "NO_TOKEN",
                      "status": 401
                    }
                  },
                  "notAdmin": {
                    "summary": "User is not admin",
                    "value": {
                      "message": "NO_ADMIN_ROLE",
                      "status": 401
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado - Se requiere rol de administrador"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NOT_FOUND",
                  "status": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "País no encontrado"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error interno del servidor"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Update existing country (Admin only)",
        "tags": [
          "countries-admin"
        ]
      }
    },
    "/company/country/regex/{code}": {
      "get": {
        "deprecated": false,
        "description": "Devuelve patrones de expresión regular organizados por código de país para validar formatos específicos de campos como identificaciones fiscales, números de teléfono, matrículas de vehículos y direcciones de correo electrónico.\n\n## Objetivo\nPermitir la validación en el cliente y en el servidor de datos proporcionados por el usuario según los requisitos de formato específicos de cada país. Esto garantiza la calidad de los datos y el cumplimiento de los estándares regionales.\n\n## Casos de uso\n- Validar números de identificación fiscal (NIF, CIF, VAT) durante el registro de empresas\n- Verificar formatos de número de teléfono antes de almacenar información de contacto\n- Comprobar formatos de matrícula al registrar camiones\n- Validar formatos de correo electrónico con un patrón genérico\n\n## Flujo de validación\n```mermaid\nflowchart TD\n  A[Solicitud del cliente con parámetro code] --> B{¿Tipo de código válido?}\n  B -->|taxID| C[Devolver patrones de ID fiscal para 15 países]\n  B -->|phoneNumber| D[Devolver patrones telefónicos para 46+ países]\n  B -->|plate| E[Devolver patrones de matrícula para 3 países]\n  B -->|email| F[Devolver patrón genérico de email]\n  B -->|Otro| G[Devolver objeto vacío]\n  C --> H[200 OK con mapa país:regex]\n  D --> H\n  E --> H\n  F --> H\n  G --> H\n```\n\n## Estructura de respuesta\nLa respuesta es un mapa donde las claves son códigos de país (códigos ISO de 2 letras) y los valores son cadenas regex.\n\n**Notas importantes**:\n- Los códigos de país pueden estar en MAYÚSCULAS o minúsculas según el tipo de validación\n- `taxID`: códigos en minúsculas (es, pt, fr, it, de, lu, pl, se, dk, nl, be, gb, ie, ad, ch)\n- `phoneNumber`: códigos en MAYÚSCULAS + \"ALL\" para el patrón genérico\n- `plate`: códigos en MAYÚSCULAS (ES, FR, PT)\n- `email`: solo clave \"ALL\" con regex genérica de correo electrónico\n- Los códigos inválidos devuelven un objeto de datos vacío `{status: 200, data: {}}`\n- Los patrones regex se cargan desde `src/features/common/regEx.json`\n\n## Autenticación\nEste es un endpoint **PÚBLICO**. No requiere autenticación.\n\n**Ejemplos de solicitud**:\n```http\n# Obtener patrones de validación de ID fiscal para todos los países soportados\nGET /company/country/regex/taxID\n\n# Obtener patrones de validación de número de teléfono\nGET /company/country/regex/phoneNumber\n\n# Obtener patrones de validación de matrícula de vehículo\nGET /company/country/regex/plate\n\n# Obtener patrón de validación de correo electrónico\nGET /company/country/regex/email\n```\n\n**Ejemplo de integración**:\n```javascript\n// Obtener patrones de ID fiscal\nconst response = await fetch('/company/country/regex/taxID');\nconst { data } = await response.json();\n\n// Validar NIF español\nconst esNifRegex = new RegExp(data.es);\nconst isValid = esNifRegex.test(\"B12345678\"); // true para formato válido\n\n// Validar ID fiscal portugués\nconst ptNifRegex = new RegExp(data.pt);\nconst isValidPt = ptNifRegex.test(\"123456789\"); // true para 9 dígitos\n```\n",
        "parameters": [
          {
            "description": "Tipo de patrón de validación a obtener.\n\nValores válidos:\n- `taxID`: Números de identificación fiscal (NIF, CIF, VAT, etc.)\n- `phoneNumber`: Formatos de número de teléfono internacional\n- `plate`: Formatos de matrícula de vehículo\n- `email`: Formato de dirección de correo electrónico (genérico)\n\nLos valores inválidos devolverán un objeto de datos vacío sin error.\n",
            "in": "path",
            "name": "code",
            "required": true,
            "schema": {
              "enum": [
                "taxID",
                "phoneNumber",
                "plate",
                "email"
              ],
              "example": "taxID",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "emailExample": {
                    "description": "Devuelve el patrón genérico de validación de correo electrónico",
                    "summary": "Email pattern (email)",
                    "value": {
                      "data": {
                        "ALL": "^[A-Za-z0-9_!#$%&'*+/=?`{|}~^.-]+@[A-Za-z0-9.-]+$"
                      },
                      "status": 200
                    }
                  },
                  "emptyExample": {
                    "description": "Cuando se proporciona un código inválido, devuelve un objeto de datos vacío",
                    "summary": "Invalid code returns empty data",
                    "value": {
                      "data": {},
                      "status": 200
                    }
                  },
                  "phoneNumberExample": {
                    "description": "Devuelve patrones de validación telefónica para 46+ países más el patrón genérico",
                    "summary": "Phone number patterns (phoneNumber)",
                    "value": {
                      "data": {
                        "ALL": "^(?:\\+\\d{1,3}|00\\d{1,3})?\\d{7,12}$",
                        "DE": "^(?:\\+49|0049|49)?[1-9]\\d{10}$",
                        "ES": "^(?:\\+34|0034|34)?[1-9]\\d{8}$",
                        "FR": "^(?:\\+33|0033|33)?[1-9]\\d{8}$",
                        "IT": "^(?:\\+39|0039|39)?\\d{10}$",
                        "PT": "^(?:\\+351|00351|351)?[1-9]\\d{8}$"
                      },
                      "status": 200
                    }
                  },
                  "plateExample": {
                    "description": "Devuelve patrones de validación de matrícula para España, Francia y Portugal",
                    "summary": "License plate patterns (plate)",
                    "value": {
                      "data": {
                        "ES": "^[0-9]{4}[BCDFGHJKLMNPQRSTVWXYZ]{3}$",
                        "FR": "^[A-Z]{2}-[0-9]{3}-[A-Z]{2}$",
                        "PT": "^[0-9]{2}-[0-9]{2}-[A-Z]{2}-[0-9]{1}$"
                      },
                      "status": 200
                    }
                  },
                  "taxIDExample": {
                    "description": "Devuelve patrones de validación de identificación fiscal para 15 países europeos",
                    "summary": "Tax ID patterns (taxID)",
                    "value": {
                      "data": {
                        "ad": "^[A-Z]{2}\\d{6}[A-Z]$",
                        "be": "^\\d{10}$",
                        "ch": "^CHE-\\d{3}\\.\\d{3}\\.\\d{3}(MWS)?$",
                        "de": "^\\d{9}$",
                        "dk": "^\\d{8}$",
                        "es": "^([XYZ]\\d{7}[A-Z]|\\d{8}[A-Z]|[A-Z]\\d{7}[A-Z]|[A-Z]\\d{8})$",
                        "fr": "^(FR)?[0-9A-Z]{2}\\d{9}$",
                        "gb": "^(GB)?\\d{9}$|^(GB)?\\d{12}$|^GD\\d{3}$|^HA\\d{3}$",
                        "ie": "^\\d{7}[A-Z]{1,2}$|^\\d[A-Z+*]\\d{5}[A-Z]$",
                        "it": "^\\d{11}$",
                        "lu": "^\\d{8}$",
                        "nl": "^(NL)?\\d{9}B\\d{2}$",
                        "pl": "^\\d{10}$",
                        "pt": "^[0-9]{9}$",
                        "se": "^\\d{10}$|^\\d{12}$"
                      },
                      "status": 200
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "additionalProperties": {
                        "description": "Patrón de expresión regular para el tipo de validación solicitado",
                        "pattern": "^.*$",
                        "type": "string"
                      },
                      "description": "Diccionario que mapea códigos de país a patrones regex.\nLas claves son códigos de país ISO de 2 letras o \"ALL\" para patrones genéricos.\nLos valores son cadenas regex listas para usar con `new RegExp()`.\n",
                      "example": {
                        "es": "^([XYZ]\\d{7}[A-Z]|\\d{8}[A-Z]|[A-Z]\\d{7}[A-Z]|[A-Z]\\d{8})$",
                        "fr": "^(FR)?[0-9A-Z]{2}\\d{9}$",
                        "pt": "^[0-9]{9}$"
                      },
                      "type": "object"
                    },
                    "status": {
                      "description": "Código de estado HTTP",
                      "example": 200,
                      "type": "integer"
                    }
                  },
                  "required": [
                    "status",
                    "data"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Operación exitosa. Devuelve un diccionario de patrones regex organizados por código de país.\n\nLa estructura de respuesta siempre es `{status: 200, data: {...}}` donde data contiene el mapa de regex.\n\n**Cobertura por tipo**:\n- `taxID`: 15 países europeos\n- `phoneNumber`: 46+ países incluyendo el patrón genérico \"ALL\"\n- `plate`: 3 países (ES, FR, PT)\n- `email`: Patrón genérico con clave \"ALL\"\n",
            "headers": {}
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "message": "INTERNAL_ERROR",
                  "status": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error interno del servidor"
          }
        },
        "summary": "Get regex patterns for field validation by country",
        "tags": [
          "countries"
        ]
      }
    },
    "/company/country/{code}": {
      "delete": {
        "deprecated": false,
        "description": "## Propósito\nRealiza una eliminación lógica de un registro de país usando el plugin mongoose-delete. El país no se elimina físicamente de la base de datos, sino que se marca como eliminado.\n\n## Objetivo\nEliminar países del uso activo de forma segura preservando los datos históricos y las relaciones. Esto evita problemas de integridad de datos con los registros existentes que referencian al país eliminado.\n\n## Casos de uso\n- Eliminar un país de las opciones disponibles\n- Limpiar registros de países de prueba o duplicados\n- Deprecar el soporte para una región específica\n\n## Flujo de validación\n```mermaid\nflowchart TD\n  A[Recibir solicitud] --> B{¿Admin autenticado?}\n  B -->|No| C[401 No autorizado]\n  B -->|Sí| D{¿País existe?}\n  D -->|No| E[404 NOT_FOUND]\n  D -->|Sí| F[Marcar como eliminado]\n  F --> G{¿Eliminación exitosa?}\n  G -->|No| H[404 NOT_FOUND]\n  G -->|Sí| I[200 OK con código de país]\n```\n\n## Autenticación\n**Requiere**: Rol de administrador (admin o dev)\n**Middleware**: `m.isAdmin`\n\n## Notas importantes\n- Esto es una ELIMINACIÓN LÓGICA - el registro permanece en la base de datos\n- El plugin mongoose-delete establece `deleted: true` y `deletedAt: timestamp`\n- Los países eliminados NO aparecerán en:\n  - `GET /company/country/` (endpoint público)\n  - Selectores de país en formularios del frontend\n- Los países eliminados SÍ aparecerán en:\n  - `GET /company/country/all` (endpoint de admin)\n- Los registros existentes (direcciones, empresas) que referencian este país NO se ven afectados\n- El país puede restaurarse actualizando `deleted: false` directamente en la base de datos\n\n## Efecto sobre los datos relacionados\n- Las direcciones de empresa existentes con este código de país permanecen sin cambios\n- La integridad de los datos históricos se preserva\n- Las nuevas direcciones no podrán usar el código de país eliminado\n\n**Ejemplo de solicitud**:\n```http\nDELETE /company/country/it\nAuthorization: Bearer {admin_jwt_token}\n```\n\n**Ejemplo de respuesta**:\n```json\n{\n  \"status\": 200,\n  \"data\": {\n    \"code\": \"it\"\n  }\n}\n```\n",
        "parameters": [
          {
            "description": "Código de país ISO de 2 letras a eliminar (minúsculas)",
            "in": "path",
            "name": "code",
            "required": true,
            "schema": {
              "example": "it",
              "maxLength": 2,
              "minLength": 2,
              "pattern": "^[a-z]{2}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "code": "it"
                  },
                  "status": 200
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "code": {
                          "description": "Código del país eliminado",
                          "example": "it",
                          "type": "string"
                        }
                      },
                      "required": [
                        "code"
                      ],
                      "type": "object"
                    },
                    "status": {
                      "example": 200,
                      "type": "integer"
                    }
                  },
                  "required": [
                    "status",
                    "data"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "País eliminado lógicamente con éxito"
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "noToken": {
                    "summary": "No token provided",
                    "value": {
                      "message": "NO_TOKEN",
                      "status": 401
                    }
                  },
                  "notAdmin": {
                    "summary": "User is not admin",
                    "value": {
                      "message": "NO_ADMIN_ROLE",
                      "status": 401
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado - Se requiere rol de administrador"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NOT_FOUND",
                  "status": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "País no encontrado o ya eliminado"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error interno del servidor"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Soft delete a country (Admin only)",
        "tags": [
          "countries-admin"
        ]
      }
    },
    "/company/dashboard/": {
      "get": {
        "deprecated": false,
        "description": "\nDevuelve estadísticas de subastas y entregas para el panel de control empresarial de Cargoffer.\n\n## Propósito\nProporcionar métricas agregadas de actividad operativa y financiera de subastas y entregas\npara el panel de control (dashboard) de una empresa, permitiendo análisis de rendimiento\ny seguimiento de actividad en un rango de fechas específico.\n\n## Objetivo\nPermitir a las empresas visualizar el desempeño de sus operaciones logísticas mediante\nindicadores clave de rendimiento (KPIs) basados en subastas y entregas realizadas.\n\n## Casos de uso\n- Dashboard principal: visualización de KPIs del último año (valores por defecto)\n- Informes trimestrales: GET /?minDate=2024-01-01&maxDate=2024-03-31\n- Análisis mensual: GET /?minDate=2024-05-01&maxDate=2024-05-31\n- Análisis anual: GET /?minDate=2023-01-01&maxDate=2023-12-31\n\n## Métricas de subastas\n\nPara cada grupo de subastas se devuelven los siguientes datos agregados:\n\n- **total_sale**: Monto total de ventas calculado según la lógica de pujas\n- **total_auctions**: Cantidad de subastas en el grupo\n- **total_savings**: Ahorro total vs precio inicial (total_start_price - total_sale)\n- **sumBidWinner**: Suma de montos de pujas ganadoras\n- **total_start_price**: Suma de precios iniciales de todas las subastas\n\n**Cálculo de total_sale por subasta**:\n1. Si existe puja ganadora (bidWinner): usa el monto de esa puja\n2. Si no, pero existe puja asignada (bidAsigned): usa el monto de esa puja\n3. Si no tiene ninguna puja: usa el precio inicial (start_price)\n\n**Grupos de subastas**:\n- **auctions**: Todas las subastas (estados: planned, published, awarded, approved)\n- **publishedAuctions**: Subastas publicadas (status: \"published\")\n- **awardedAuctions**: Subastas adjudicadas (status: \"awarded\")\n- **approvedAuctions**: Subastas aprobadas (status: \"approved\")\n- **plannedAuctions**: Subastas planificadas (status: \"planned\")\n\n## Métricas de entregas\n\nPara cada grupo de entregas se devuelven los siguientes datos agregados:\n\n- **total_sale**: Monto total de entregas (basado en pujas ganadoras o asignadas)\n- **total_deliveries**: Cantidad de entregas en el grupo\n- **sumBidWinner**: Suma de montos de pujas ganadoras\n\n**Grupos de entregas**:\n- **plannedDeliveries**: Entregas planificadas (status: \"planned\")\n- **collectedDeliveries**: Entregas recogidas (status: \"collected\")\n- **deliveredDeliveries**: Entregas completadas (status: \"delivered\")\n\n## Filtrado por rango de fechas\n\nLos parámetros `minDate` y `maxDate` permiten filtrar las estadísticas por período.\nEl campo de fecha usado para el filtro depende del tipo de estadística y estado:\n\n**Para subastas**:\n- Se filtra por fecha de fin de la subasta (`date_end`)\n- Estados incluidos: planned, published, awarded, approved\n\n**Para entregas, el campo de fecha depende del estado**:\n- **collected** (recogidas): fecha estimada de carga (`etl_date`)\n- **delivered** (completadas): fecha estimada de entrega (`etd_date`)\n- **planned/replanned/accepted**: fecha de creación (`createdAt`)\n\nEsta lógica permite análisis más precisos según la fase del proceso logístico.\n\n**Valores por defecto**:\n- `minDate`: 1 año antes de la fecha actual\n- `maxDate`: Fecha actual\n\n## Ejemplo de uso\n\n**Dashboard principal** (último año):\n```http\nGET /company/dashboard/\nAuthorization: Bearer {jwt_token}\n```\n\n**Informe trimestral**:\n```http\nGET /company/dashboard/?minDate=2024-01-01&maxDate=2024-03-31\nAuthorization: Bearer {jwt_token}\n```\n\n## Notas importantes\n\n- Requiere autenticación con token JWT\n- Solo devuelve estadísticas de la empresa del usuario autenticado\n- Si el usuario no tiene empresa asignada, retorna error 404\n- Los estados de subastas y entregas están predefinidos en los modelos\n- Los cálculos se realizan mediante agregaciones MongoDB para optimizar rendimiento",
        "parameters": [
          {
            "description": "Fecha de inicio para filtrar estadísticas (formato YYYY-MM-DD).\nPor defecto es 1 año antes de la fecha actual.\n\nEjemplos:\n- 2024-01-15 (15 de enero de 2024)\n- 2023-06-01 (1 de junio de 2023)",
            "in": "query",
            "name": "minDate",
            "required": false,
            "schema": {
              "example": "2024-01-01",
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Fecha de fin para filtrar estadísticas (formato YYYY-MM-DD).\nPor defecto es la fecha actual.\n\nEjemplos:\n- 2024-05-20 (20 de mayo de 2024)\n- 2023-12-31 (31 de diciembre de 2023)",
            "in": "query",
            "name": "maxDate",
            "required": false,
            "schema": {
              "example": "2024-12-31",
              "format": "date",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "approvedAuctions": {
                    "sumBidWinner": 52000,
                    "total_auctions": 20,
                    "total_sale": 60000,
                    "total_savings": 8000,
                    "total_start_price": 68000
                  },
                  "auctions": {
                    "sumBidWinner": 110000.25,
                    "total_auctions": 42,
                    "total_sale": 125000.5,
                    "total_savings": 15000.75,
                    "total_start_price": 140000.75
                  },
                  "awardedAuctions": {
                    "sumBidWinner": 70000,
                    "total_auctions": 28,
                    "total_sale": 80000.25,
                    "total_savings": 10000.25,
                    "total_start_price": 80000.25
                  },
                  "collectedDeliveries": {
                    "sumBidWinner": 55000.25,
                    "total_deliveries": 25,
                    "total_sale": 60000.75
                  },
                  "deliveredDeliveries": {
                    "sumBidWinner": 38000,
                    "total_deliveries": 15,
                    "total_sale": 40000.5
                  },
                  "plannedAuctions": {
                    "sumBidWinner": 45000,
                    "total_auctions": 15,
                    "total_sale": 50000,
                    "total_savings": 5000,
                    "total_start_price": 55000
                  },
                  "plannedDeliveries": {
                    "sumBidWinner": 80000.5,
                    "total_deliveries": 35,
                    "total_sale": 85000.3
                  },
                  "publishedAuctions": {
                    "sumBidWinner": 88000.5,
                    "total_auctions": 35,
                    "total_sale": 100000,
                    "total_savings": 12000.5,
                    "total_start_price": 112000.5
                  }
                },
                "schema": {
                  "properties": {
                    "approvedAuctions": {
                      "$ref": "#/components/schemas/AuctionStats"
                    },
                    "auctions": {
                      "$ref": "#/components/schemas/AuctionStats"
                    },
                    "awardedAuctions": {
                      "$ref": "#/components/schemas/AuctionStats"
                    },
                    "collectedDeliveries": {
                      "$ref": "#/components/schemas/DeliveryStats"
                    },
                    "deliveredDeliveries": {
                      "$ref": "#/components/schemas/DeliveryStats"
                    },
                    "plannedAuctions": {
                      "$ref": "#/components/schemas/AuctionStats"
                    },
                    "plannedDeliveries": {
                      "$ref": "#/components/schemas/DeliveryStats"
                    },
                    "publishedAuctions": {
                      "$ref": "#/components/schemas/AuctionStats"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Estadísticas recuperadas exitosamente. Contiene los siguientes grupos de datos:\n\n**Grupos de subastas** (AuctionStats):\n- auctions: Todas las subastas (planned, published, awarded, approved)\n- publishedAuctions: Subastas publicadas\n- awardedAuctions: Subastas adjudicadas\n- approvedAuctions: Subastas aprobadas\n- plannedAuctions: Subastas planificadas\n\n**Grupos de entregas** (DeliveryStats):\n- plannedDeliveries: Entregas planificadas\n- collectedDeliveries: Entregas recogidas\n- deliveredDeliveries: Entregas completadas\n\nCada grupo de subastas incluye: total_sale, total_auctions, total_savings, sumBidWinner, total_start_price\nCada grupo de entregas incluye: total_sale, total_deliveries, sumBidWinner",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN",
                  "status": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. El token JWT es inválido, ha expirado o no tiene permisos para acceder a estas estadísticas.\n\nSoluciones:\n1. Verificar que el token sea válido y no haya expirado\n2. Asegurarse que el usuario tenga rol de empresa\n3. Solicitar nuevo token de autenticación",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "companyNotFound": {
                    "summary": "Empresa no encontrada",
                    "value": {
                      "message": "CIA_NOT_FOUND",
                      "status": 404
                    }
                  },
                  "userNotFound": {
                    "summary": "Usuario no encontrado",
                    "value": {
                      "message": "USER_NOT_FOUND",
                      "status": 404
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario o empresa no encontrada. Ocurre cuando:\n- El ID de empresa asociado al usuario no existe\n- El usuario no tiene empresa asignada\n- La empresa fue desactivada\n\nSoluciones:\n1. Verificar que el usuario tenga una empresa válida asignada\n2. Contactar al administrador si la empresa aparece como desactivada",
            "headers": {}
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "message": "INTERNAL_ERROR",
                  "status": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error interno del servidor"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Get dashboard statistics",
        "tags": [
          "Dashboard"
        ]
      }
    },
    "/company/deliveries/": {
      "get": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "GET /company/deliveries/",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/deliveries/:service_code": {
      "get": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "GET /company/deliveries/:service_code",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/deliveries/active": {
      "get": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "GET /company/deliveries/active",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/deliveries/delete/:id": {
      "delete": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "DELETE /company/deliveries/delete/:id",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/deliveries/msg/:id": {
      "post": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "POST /company/deliveries/msg/:id",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/deliveries/notes/:id": {
      "put": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "PUT /company/deliveries/notes/:id",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/deliveries/route/:service_code": {
      "get": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "GET /company/deliveries/route/:service_code",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/deliveries/search-location": {
      "get": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "GET /company/deliveries/search-location",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/deliveries/trackable": {
      "get": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "GET /company/deliveries/trackable",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/deliveries/tracking/:service_code": {
      "post": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "POST /company/deliveries/tracking/:service_code",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/delivery/": {
      "get": {
        "deprecated": false,
        "description": "## Purpose\nObtiene una lista paginada de todos los envíos de la empresa con múltiples opciones de filtrado.\n\n## Objective\nPermitir a las empresas consultar y filtrar su historial completo de envíos para análisis,\nreportes y gestión operativa.\n\n## Use Cases\n- Visualizar el historial completo de envíos de la empresa\n- Filtrar envíos por fechas (creación, subasta, inicio, fin, ETL, ETD) para reportes periódicos\n- Buscar envíos específicos por código, referencia personalizada o tipo de carga\n- Excluir envíos cancelados o reclamados de las listas por defecto\n- Analizar envíos activos vs. históricos\n\n## Filtering Logic\n```mermaid\nflowchart TD\n  A[Receive Request] --> B{Has minDate?}\n  B -->|Yes| C[Filter date_start OR etl_date >= minDate]\n  B -->|No| D{Has maxDate?}\n  D -->|Yes| E[Filter date_end OR etd_date <= maxDate]\n  C --> F{Has Status Filter?}\n  E --> F\n  F -->|Yes| G[Apply exact status match]\n  F -->|No| H{Exclude Canceled?}\n  H -->|show_canceled=false| I[Remove status=canceled]\n  H -->|show_canceled=true| J{Exclude Claimed?}\n  J -->|show_claimed=false| K[Remove status=claimed]\n  J -->|show_claimed=true| L[Apply filters]\n  I --> L\n  K --> L\n  G --> L\n  L --> M[Return Paginated Results]\n```\n",
        "parameters": [
          {
            "description": "Número de página para paginación. Por defecto 1.",
            "example": 1,
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Límite de resultados por página. Por defecto 20, máximo 100.",
            "example": 20,
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Fecha mínima de creación (YYYY-MM-DD) para filtrar envíos.",
            "example": "2025-01-01T00:00:00.000Z",
            "in": "query",
            "name": "minCreate",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Fecha máxima de creación (YYYY-MM-DD) para filtrar envíos.",
            "example": "2025-12-31T23:59:59.999Z",
            "in": "query",
            "name": "maxCreate",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Fecha mínima de subasta (date_start) para filtrar envíos.",
            "example": "2025-01-01T00:00:00.000Z",
            "in": "query",
            "name": "minAuction",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Fecha máxima de subasta (date_end) para filtrar envíos.",
            "example": "2025-12-31T23:59:59.999Z",
            "in": "query",
            "name": "maxAuction",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Fecha mínima de inicio (date_start) para filtrar envíos.",
            "example": "2025-01-01T00:00:00.000Z",
            "in": "query",
            "name": "minStart",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Fecha máxima de inicio (date_start) para filtrar envíos.",
            "example": "2025-12-31T23:59:59.999Z",
            "in": "query",
            "name": "maxStart",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Fecha mínima de fin (date_end) para filtrar envíos.",
            "example": "2025-01-01T00:00:00.000Z",
            "in": "query",
            "name": "minEnd",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Fecha máxima de fin (date_end) para filtrar envíos.",
            "example": "2025-12-31T23:59:59.999Z",
            "in": "query",
            "name": "maxEnd",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Fecha mínima de carga (etl_date) para filtrar envíos.",
            "example": "2025-01-01T00:00:00.000Z",
            "in": "query",
            "name": "minETL",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Fecha máxima de carga (etl_date) para filtrar envíos.",
            "example": "2025-12-31T23:59:59.999Z",
            "in": "query",
            "name": "maxETL",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Fecha mínima de descarga (etd_date) para filtrar envíos.",
            "example": "2025-01-01T00:00:00.000Z",
            "in": "query",
            "name": "minETD",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Fecha máxima de descarga (etd_date) para filtrar envíos.",
            "example": "2025-12-31T23:59:59.999Z",
            "in": "query",
            "name": "maxETD",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Fecha mínima general (YYYY-MM-DD). Filtra envíos donde date_start >= minDate\nO etl_date >= minDate (condición OR).\n",
            "example": "2025-01-01T00:00:00.000Z",
            "in": "query",
            "name": "minDate",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Fecha máxima general (YYYY-MM-DD). Filtra envíos donde date_end <= maxDate\nO etd_date <= maxDate (condición OR).\n",
            "example": "2025-12-31T23:59:59.999Z",
            "in": "query",
            "name": "maxDate",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Fecha mínima estimada de llegada (date_eta) para filtrar envíos.",
            "example": "2025-08-01T00:00:00.000Z",
            "in": "query",
            "name": "minETA",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Fecha máxima estimada de llegada (date_eta) para filtrar envíos.",
            "example": "2025-08-31T23:59:59.999Z",
            "in": "query",
            "name": "maxETA",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Estado del envío para filtrar. Valores posibles:\n- accepted: Aceptado por transportista\n- planned: Planificado\n- replanned: Replanificado (cambios pendientes de aprobación)\n- collected: Carga recogida\n- issue: Incidente en curso\n- delivered: Entregado\n- paid: Pagado\n- claimed: Reclamado\n- canceled: Cancelado\n",
            "example": "delivered",
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "enum": [
                "accepted",
                "planned",
                "replanned",
                "collected",
                "issue",
                "delivered",
                "paid",
                "claimed",
                "canceled"
              ],
              "type": "string"
            }
          },
          {
            "description": "Tipo de carga para filtrar. Valores posibles:\n- pallets: Palets\n- full: Carga completa\n- package: Paquete\n- trailer: Trailer completo\n",
            "example": "pallets",
            "in": "query",
            "name": "cargo_type",
            "required": false,
            "schema": {
              "enum": [
                "pallets",
                "full",
                "package",
                "trailer"
              ],
              "type": "string"
            }
          },
          {
            "description": "Término de búsqueda para filtrar envíos. Busca en:\n- service_code: Código único del envío (búsqueda parcial, case-insensitive)\n- custom_code: Código de referencia personalizado\n- etl_cargo_method: Método de carga\n- pallets_type: Tipo de palets\n- description: Descripción de la carga\n",
            "example": "DEL-12345",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "maxLength": 200,
              "minLength": 1,
              "type": "string"
            }
          },
          {
            "description": "Incluir envíos cancelados en los resultados.\n- false (default): Excluye envíos con status=canceled\n- true: Incluye envíos cancelados\n",
            "example": false,
            "in": "query",
            "name": "show_canceled",
            "required": false,
            "schema": {
              "default": false,
              "type": "boolean"
            }
          },
          {
            "description": "Incluir envíos reclamados en los resultados.\n- false (default): Excluye envíos con status=claimed\n- true: Incluye envíos reclamados\n",
            "example": false,
            "in": "query",
            "name": "show_claimed",
            "required": false,
            "schema": {
              "default": false,
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "docs": [
                    {
                      "cargo_type": "pallets",
                      "custom_code": "PED-2025-001",
                      "etd_date": "2025-08-02T12:00:00.000Z",
                      "etl_date": "2025-08-01T08:00:00.000Z",
                      "service_code": "DEL-12345",
                      "status": "completed"
                    }
                  ],
                  "limit": 20,
                  "page": 1,
                  "totalDocs": 100,
                  "totalPages": 5
                },
                "schema": {
                  "$ref": "#/components/schemas/DeliveryListResponse"
                }
              }
            },
            "description": "Lista de envíos paginada con éxito"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Token de autenticación no proporcionado o inválido",
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. El token JWT es inválido o ha expirado.\nSe debe autenticar nuevamente.\n"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Empresa no encontrada",
                  "message": "CIA_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Empresa no encontrada. La compañía asociada al token no existe\no no tiene permisos para acceder a estos datos.\n"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "List all shipments",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/delivery/active": {
      "get": {
        "deprecated": false,
        "description": "## Purpose\nObtiene una lista paginada de los envíos activos de la empresa (aquellos con\netd_date >= fecha actual).\n\n## Objective\nPermitir a las empresas monitorear rápidamente sus envíos pendientes de ejecución\no en curso para gestión operativa del día a día.\n\n## Use Cases\n- Monitorear envíos en curso o pendientes\n- Visualizar envíos que requieren atención inmediata\n- Gestionar la logística de operaciones activas del día\n- Preparar recursos para próximos recogidas/entregas\n\n## Filtering\nEste endpoint acepta todos los mismos parámetros de filtrado que GET /company/delivery/\n(minCreate, maxCreate, minAuction, maxAuction, minStart, maxStart, minEnd, maxEnd,\nminETL, maxETL, minETD, maxETD, minDate, maxDate, minETA, maxETA, status,\ncargo_type, search, show_canceled, show_claimed).\n\nAdicionalmente, aplica automáticamente un filtro:\n- etd_date >= new Date() (solo envíos con fecha de descarga futura)\n",
        "parameters": [
          {
            "description": "Número de página para paginación. Por defecto 1.",
            "example": 1,
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Límite de resultados por página. Por defecto 10, máximo 50.",
            "example": 10,
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 10,
              "maximum": 50,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Estado del envío para filtrar. Ver lista completa en GET /company/delivery/\n",
            "example": "planned",
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "enum": [
                "accepted",
                "planned",
                "replanned",
                "collected",
                "issue",
                "delivered",
                "paid",
                "claimed",
                "canceled"
              ],
              "type": "string"
            }
          },
          {
            "description": "Tipo de carga (pallets, full, package, trailer)",
            "example": "pallets",
            "in": "query",
            "name": "cargo_type",
            "required": false,
            "schema": {
              "enum": [
                "pallets",
                "full",
                "package",
                "trailer"
              ],
              "type": "string"
            }
          },
          {
            "description": "Término de búsqueda (service_code, custom_code, etc.)",
            "example": "DEL-67890",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "maxLength": 200,
              "minLength": 1,
              "type": "string"
            }
          },
          {
            "description": "Incluir envíos cancelados (default: false)",
            "example": false,
            "in": "query",
            "name": "show_canceled",
            "required": false,
            "schema": {
              "default": false,
              "type": "boolean"
            }
          },
          {
            "description": "Incluir envíos reclamados (default: false)",
            "example": false,
            "in": "query",
            "name": "show_claimed",
            "required": false,
            "schema": {
              "default": false,
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "docs": [
                    {
                      "cargo_type": "pallets",
                      "date_eta": "2025-08-10T17:30:00.000Z",
                      "etd_date": "2025-08-10T18:00:00.000Z",
                      "etl_date": "2025-08-08T10:00:00.000Z",
                      "pallets_num": 24,
                      "service_code": "DEL-67890",
                      "status": "planned"
                    }
                  ],
                  "limit": 10,
                  "page": 1,
                  "totalDocs": 5
                },
                "schema": {
                  "$ref": "#/components/schemas/DeliveryListResponse"
                }
              }
            },
            "description": "Lista de envíos activos obtenida con éxito"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Token de autenticación no proporcionado o inválido",
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. El token JWT es inválido o ha expirado.\nSe debe autenticar nuevamente.\n"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Empresa no encontrada",
                  "message": "CIA_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Empresa no encontrada. La compañía asociada al token no existe\no no tiene permisos para acceder a estos datos.\n"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get active shipments",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/delivery/aprove/{service_code}": {
      "put": {
        "deprecated": false,
        "description": "## Purpose\nAprueba los cambios realizados por el transportista en un envío replanificado.\nEl estado debe ser 'replanned' con replannedBy === 'trucker'.\n\n## Objective\nPermitir a las empresas aceptar modificaciones solicitadas por el transportista,\nfinalizando el proceso de negociación de cambios.\n\n## Use Cases\n- Confirmar cambios de fechas solicitados por el transportista\n- Aprobar modificaciones en la carga (peso, palets, temperatura)\n- Aceptar cambios en metros lineales o altura de carga\n- Finalizar el proceso de negociación de cambios\n\n## Approval Flow\n```mermaid\nflowchart LR\n  A[Trucker Edits] -->|replannedBy=trucker| B[Status: replanned]\n  B --> C{Company Reviews}\n  C -->|Approve| D[PUT /aprove]\n  D -->|replannedBy=aproved| E[Status: planned]\n  C -->|Reject| F[Manual Revert]\n```\n\n## Validation Logic\n```mermaid\nflowchart TD\n  A[Receive Approval] --> B{Delivery Exists?}\n  B -->|No| C[404 NOT_FOUND]\n  B -->|Yes| D{Company Owns Delivery?}\n  D -->|No| E[404 NOT_ALLOWED]\n  D -->|Yes| F{Is Final Status?}\n  F -->|Yes| G[401 NOT_ALLOWED]\n  F -->|No| H{Already Approved?}\n  H -->|replannedBy=aproved| I[400 ALREADY_APROVED]\n  H -->|No| J{Must Approve Trucker?}\n  J -->|replannedBy≠trucker| K[401 MUST_APROVE_TRUCKER]\n  J -->|Yes| L[Set status=planned]\n  L --> M[Set replannedBy=aproved]\n  M --> N[Save - 200]\n```\n\n## Notes\n- El envío debe estar en estado 'replanned'\n- replannedBy debe ser 'trucker' (no puede ser 'aproved' ni vacío)\n- No puede ser estado final (delivered, claimed, canceled)\n- Solo la empresa propietaria puede aprobar\n- status cambia a 'planned' (no a 'approved')\n- replannedBy cambia a 'aproved'\n",
        "parameters": [
          {
            "description": "Código único del envío a aprobar",
            "example": "DEL-67890",
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "maxLength": 50,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "date_eta": "2025-08-11T17:30:00.000Z",
                  "etd_date": "2025-08-11T18:00:00.000Z",
                  "etl_date": "2025-08-10T10:00:00.000Z",
                  "replannedBy": "aproved",
                  "service_code": "DEL-67890",
                  "status": "planned",
                  "updated_at": "2025-08-08T15:45:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/DeliveryResponse"
                }
              }
            },
            "description": "Cambios aprobados con éxito"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Los cambios ya fueron aprobados anteriormente",
                  "message": "ALREADY_APROVED"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No se puede aprobar. Ya fue aprobado anteriormente.\n"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Solo se pueden aprobar cambios realizados por el transportista",
                  "message": "MUST_APROVE_TRUCKER"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No se puede aprobar. Debe aprobar cambios del transportista.\n"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": "El envío está en un estado final que no permite aprobación",
                  "message": "NOT_ALLOWED"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No permitido. Estado final no permite aprobación.\n"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Envío no encontrado",
                  "message": "NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Envío no encontrado. El service_code proporcionado no existe\no no pertenece a la empresa del usuario autenticado.\n"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Approve delivery changes",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/delivery/customcode/{id}": {
      "put": {
        "deprecated": false,
        "description": "## Purpose\nActualiza el código de referencia personalizado de un envío.\n\n## Objective\nPermitir a las empresas asignar sus propios códigos de referencia a los envíos\npara integración con sistemas internos y mejor trazabilidad.\n\n## Use Cases\n- Asignar un código de pedido interno al envío\n- Vincular el envío con un sistema externo ERP/WMS\n- Agregar una referencia fácilmente reconocible para el equipo\n- Sincronizar códigos con sistemas de gestión empresarial\n\n## Notes\n- El parámetro {id} debe ser MongoDB _id (no service_code)\n- custom_code es opcional, acepta string vacío\n- Longitud máxima: 50 caracteres\n- Puede actualizarse múltiples veces sin restricciones de estado\n",
        "parameters": [
          {
            "description": "MongoDB _id del envío a actualizar.\nNOTA: Requiere MongoDB _id, no service_code.\n",
            "example": "507f1f77bcf86cd799439012",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "maxLength": 24,
              "minLength": 24,
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "clear-code": {
                  "summary": "Eliminar código personalizado",
                  "value": {
                    "custom_code": ""
                  }
                },
                "with-code": {
                  "summary": "Asignar código personalizado",
                  "value": {
                    "custom_code": "PED-2025-1234"
                  }
                }
              },
              "schema": {
                "properties": {
                  "custom_code": {
                    "description": "Código de referencia personalizado (0-50 caracteres).\nPuede ser string vacío para eliminar el código actual.\n",
                    "example": "PED-2025-1234",
                    "maxLength": 50,
                    "minLength": 0,
                    "type": "string"
                  }
                },
                "required": [
                  "custom_code"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "507f1f77bcf86cd799439012"
                },
                "schema": {
                  "properties": {
                    "_id": {
                      "description": "MongoDB _id del envío actualizado",
                      "example": "507f1f77bcf86cd799439012",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Código personalizado guardado con éxito"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Envío no encontrado",
                  "message": "NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Envío no encontrado. El ID proporcionado no existe\no no pertenece a la empresa del usuario autenticado.\n"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Save custom code",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/delivery/documents/{service_code}": {
      "get": {
        "deprecated": false,
        "description": "## Purpose\nLista todos los documentos asociados a un envío.\n\n## Objective\nPermitir a las empresas consultar la documentación adjunta a un envío\npara su gestión y descarga individual.\n\n## Use Cases\n- Verificar qué documentos están adjuntos al envío\n- Obtener lista de documentos para descarga selectiva\n- Revisar documentación subida por la empresa o el transportista\n\n## Notes\n- El nombre del documento se extrae del path (split por '--')\n- Solo se retorna el _id y el nombre legible del documento\n- No incluye el contenido del archivo (usar GET /documents/{service_code}/{document_id})\n",
        "parameters": [
          {
            "description": "Código único del envío",
            "example": "DEL-12345",
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "maxLength": 50,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "_id": "507f1f77bcf86cd799439070",
                    "name": "factura.pdf"
                  },
                  {
                    "_id": "507f1f77bcf86cd799439071",
                    "name": "albaran.pdf"
                  },
                  {
                    "_id": "507f1f77bcf86cd799439072",
                    "name": "fotos-carga.zip"
                  }
                ],
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/DeliveryDocument"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Lista de documentos obtenida con éxito"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Empresa no encontrada",
                  "message": "CIA_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Envío no encontrado",
                  "message": "DELIVERY_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Envío no encontrado."
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "List delivery documents",
        "tags": [
          "Delivery"
        ]
      },
      "post": {
        "deprecated": false,
        "description": "## Purpose\nSube nuevos documentos a un envío, guardándolos en S3.\n\n## Objective\nPermitir a las empresas adjuntar documentación adicional a sus envíos\npara complementar la información del transporte.\n\n## Use Cases\n- Adjuntar facturas o albaranes al envío\n- Subir fotos de la carga antes del transporte\n- Agregar documentos aduaneros o certificados\n- Compartir documentación relevante con el transportista\n\n## Notes\n- Acepta hasta 4 archivos por petición (multipart/form-data)\n- Los archivos se guardan en S3 bajo la clave del archivo\n- Cada documento se registra con owner_company (ID del usuario)\n- Retorna el delivery completo con el array documents actualizado\n- Aplicable tanto para empresa como para transportista\n",
        "parameters": [
          {
            "description": "Código único del envío",
            "example": "DEL-12345",
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "maxLength": 50,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "examples": {
                "multiple-files": {
                  "summary": "Múltiples documentos",
                  "value": {
                    "files": [
                      "factura.pdf",
                      "albaran.pdf",
                      "fotos.zip"
                    ]
                  }
                },
                "single-file": {
                  "summary": "Un solo documento",
                  "value": {
                    "files": [
                      "factura.pdf"
                    ]
                  }
                }
              },
              "schema": {
                "properties": {
                  "files": {
                    "description": "Archivos a subir (máximo 4)",
                    "items": {
                      "format": "binary",
                      "type": "string"
                    },
                    "maxItems": 4,
                    "type": "array"
                  }
                },
                "required": [
                  "files"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "507f1f77bcf86cd799439060",
                  "documents": [
                    {
                      "_id": "507f1f77bcf86cd799439070",
                      "owner_company": "507f1f77bcf86cd799439080",
                      "path": "uploads/deliveries/DEL-12345--factura.pdf"
                    },
                    {
                      "_id": "507f1f77bcf86cd799439071",
                      "owner_company": "507f1f77bcf86cd799439080",
                      "path": "uploads/deliveries/DEL-12345--albaran.pdf"
                    }
                  ],
                  "service_code": "DEL-12345",
                  "status": "planned",
                  "updated_at": "2025-08-10T10:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/DeliveryResponse"
                }
              }
            },
            "description": "Documentos subidos con éxito"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Empresa no encontrada",
                  "message": "CIA_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Envío no encontrado",
                  "message": "DELIVERY_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Envío no encontrado."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": "No se pudieron guardar los documentos",
                  "message": "Error interno"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error al subir los documentos."
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Upload delivery documents",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/delivery/documents/{service_code}/{document_id}": {
      "delete": {
        "deprecated": false,
        "description": "## Purpose\nElimina un documento específico de un envío, borrándolo también de S3.\n\n## Objective\nPermitir a las empresas gestionar la documentación de sus envíos,\neliminando archivos que ya no sean necesarios o fueron subidos por error.\n\n## Use Cases\n- Eliminar documentos subidos por error\n- Remover documentación obsoleta\n- Gestionar el espacio de almacenamiento\n- Corregir errores en la documentación adjunta\n\n## Notes\n- Elimina el documento del array documents\n- Elimina el archivo físico de S3\n- Retorna el delivery completo con el array documents actualizado\n- Operación irreversible para el archivo en S3\n",
        "parameters": [
          {
            "description": "Código único del envío",
            "example": "DEL-12345",
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "maxLength": 50,
              "minLength": 1,
              "type": "string"
            }
          },
          {
            "description": "ID del documento a eliminar",
            "example": "507f1f77bcf86cd799439070",
            "in": "path",
            "name": "document_id",
            "required": true,
            "schema": {
              "maxLength": 24,
              "minLength": 24,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "507f1f77bcf86cd799439060",
                  "documents": [
                    {
                      "_id": "507f1f77bcf86cd799439071",
                      "owner_company": "507f1f77bcf86cd799439080",
                      "path": "uploads/deliveries/DEL-12345--albaran.pdf"
                    }
                  ],
                  "service_code": "DEL-12345",
                  "status": "planned",
                  "updated_at": "2025-08-10T10:05:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/DeliveryResponse"
                }
              }
            },
            "description": "Documento eliminado con éxito"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Empresa no encontrada",
                  "message": "CIA_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Envío no encontrado",
                  "message": "NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Envío no encontrado."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": "No se pudo eliminar el documento",
                  "message": "Error interno"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error al eliminar el documento."
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Delete delivery document",
        "tags": [
          "Delivery"
        ]
      },
      "get": {
        "deprecated": false,
        "description": "## Purpose\nDescarga un documento específico de un envío desde S3.\n\n## Objective\nPermitir a las empresas descargar individualmente los documentos adjuntos\na un envío según sus necesidades.\n\n## Use Cases\n- Descargar un documento específico sin descargar todo el ZIP\n- Visualizar un documento en el navegador\n- Compartir un documento concreto con terceros\n\n## Notes\n- El archivo se sirve directamente desde S3\n- Content-Type depende del tipo de archivo\n- Verifica que el usuario tenga permisos (company o trucker_cia)\n",
        "parameters": [
          {
            "description": "Código único del envío",
            "example": "DEL-12345",
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "maxLength": 50,
              "minLength": 1,
              "type": "string"
            }
          },
          {
            "description": "ID del documento a descargar",
            "example": "507f1f77bcf86cd799439070",
            "in": "path",
            "name": "document_id",
            "required": true,
            "schema": {
              "maxLength": 24,
              "minLength": 24,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/octet-stream": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "Archivo descargado con éxito",
            "headers": {
              "Content-Disposition": {
                "example": "attachment; filename=\"factura.pdf\"",
                "schema": {
                  "type": "string"
                }
              },
              "Content-Type": {
                "example": "application/pdf",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Empresa no encontrada",
                  "message": "CIA_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Documento no encontrado",
                  "message": "NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Documento no encontrado."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": "No se pudo descargar el documento",
                  "message": "Error interno"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error al descargar el documento."
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Download delivery document",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/delivery/download/{service_code}": {
      "get": {
        "deprecated": false,
        "description": "## Purpose\nDescarga un archivo ZIP con toda la documentación generada del envío.\n\n## Objective\nPermitir a las empresas descargar un paquete completo con todos los documentos\nrelacionados con un envío para archivo o compartir con terceros.\n\n## Use Cases\n- Descargar documentación completa para archivo legal\n- Compartir todos los documentos con el transportista\n- Guardar copia de seguridad de la documentación del envío\n- Generar expediente completo para auditorías\n\n## Generated Contents\nEl ZIP incluye los siguientes archivos PDF cuando la información está disponible:\n- **CMR PDF**: Carta de porte electrónico\n- **Contract PDF**: Contrato de transporte (si tiene auction)\n- **Chat PDF**: Historial de mensajes intercambiados (si hay mensajes)\n- **Issues PDF**: Reporte de incidencias (si las hay)\n- **Details PDF**: Detalles completos del envío\n\n## Notes\n- El archivo se genera dinámicamente\n- Solo se incluyen PDFs que tengan información disponible\n- El archivo se elimina del servidor temporal después de enviarse\n- Content-Type: application/zip\n",
        "parameters": [
          {
            "description": "Código único del envío a descargar",
            "example": "DEL-12345",
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "maxLength": 50,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/zip": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "Archivo ZIP generado con éxito",
            "headers": {
              "Content-Disposition": {
                "description": "Nombre del archivo ZIP",
                "schema": {
                  "example": "attachment; filename=\"info_DEL-12345.zip\"",
                  "type": "string"
                }
              },
              "Content-Type": {
                "schema": {
                  "example": "application/zip",
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Envío no encontrado",
                  "message": "NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Envío no encontrado."
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Error al generar la documentación",
                  "message": "SOMETHING_WENT_WRONG"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error al generar el ZIP."
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Download delivery documentation ZIP",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/delivery/msg/{id}": {
      "get": {
        "deprecated": false,
        "description": "## Purpose\nObtiene el historial completo de mensajes de un envío.\n\n## Objective\nPermitir a las empresas consultar toda la conversación mantenida con\nel transportista sobre un envío específico.\n\n## Use Cases\n- Revisar el historial de comunicaciones\n- Verificar instrucciones dadas al transportista\n- Consultar acuerdos verbales por escrito\n- Revisar archivos compartidos previamente\n\n## Notes\n- El parámetro {id} puede ser service_code o MongoDB _id\n- Los mensajes se ordenan por createdAt DESC (más recientes primero)\n- Las URLs de archivos incluyen el prefijo /images?file=\n",
        "parameters": [
          {
            "description": "Identificador del envío. Puede ser service_code o MongoDB _id.\n",
            "example": "DEL-12345",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "maxLength": 100,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "authorId": "507f1f77bcf86cd799439050",
                    "authorName": "Juan García",
                    "createdAt": "2025-08-10T08:30:00.000Z",
                    "files": [
                      "/images?file=uploads/photo1.jpg"
                    ],
                    "message": "Estoy llegando al punto de recogida"
                  },
                  {
                    "authorId": "507f1f77bcf86cd799439051",
                    "authorName": "María López",
                    "createdAt": "2025-08-10T08:32:00.000Z",
                    "files": [],
                    "message": "Perfecto, te espero en la puerta 3"
                  }
                ],
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/DeliveryMessage"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Mensajes obtenidos con éxito"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Envío no encontrado",
                  "message": "NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Envío no encontrado."
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get delivery messages",
        "tags": [
          "Delivery"
        ]
      },
      "post": {
        "deprecated": false,
        "description": "## Purpose\nEnvía un mensaje al chat de un envío, con opción de adjuntar archivos.\n\n## Objective\nFacilitar la comunicación directa entre empresa y transportista dentro del\ncontexto de un envío específico.\n\n## Use Cases\n- Coordinar detalles de la carga/descarga\n- Notificar cambios urgentes en la operación\n- Compartir fotos o documentos relevantes\n- Resolver incidencias durante el transporte\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Message] --> B{Has Message Text?}\n  B -->|No/Empty| C[400 NOT_MESSAGE]\n  B -->|Yes| D{Delivery is Active?}\n  D -->|No| E[403 NOT_ALLOWED]\n  D -->|Yes| F{User Authenticated?}\n  F -->|No| G[404 USER_NOT_FOUND]\n  F -->|Yes| H[Create Message Object]\n  H --> I{Has Files?}\n  I -->|Yes| J[Add S3 Keys to Message]\n  I -->|No| K[Continue]\n  J --> K\n  K --> L[Push to messages Array]\n  L --> M[Save Delivery]\n  M -->|Error| N[503 Error]\n  M -->|Success| O[Return All Messages DESC]\n```\n\n## Notes\n- El parámetro {id} puede ser service_code o MongoDB _id\n- Acepta hasta 6 archivos (multipart/form-data)\n- Los archivos se guardan en S3 con la clave de S3\n- Solo envíos activos pueden recibir mensajes (isActive check)\n- Retorna todos los mensajes ordenados por createdAt DESC\n- Los archivos se devuelven con la ruta completa /images?file=\n",
        "parameters": [
          {
            "description": "Identificador del envío. Puede ser service_code o MongoDB _id.\n",
            "example": "DEL-12345",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "maxLength": 100,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "encoding": {
                "files": {
                  "contentType": "application/octet-stream"
                }
              },
              "examples": {
                "text-only": {
                  "summary": "Mensaje solo texto",
                  "value": {
                    "message": "Hola, ¿puedes confirmar la hora de recogida?"
                  }
                },
                "with-files": {
                  "summary": "Mensaje con archivos",
                  "value": {
                    "files": [
                      "photo1.jpg",
                      "photo2.jpg"
                    ],
                    "message": "Adjunto fotos de la carga"
                  }
                }
              },
              "schema": {
                "properties": {
                  "files": {
                    "description": "Archivos adjuntos (máximo 6)",
                    "example": [],
                    "items": {
                      "format": "binary",
                      "type": "string"
                    },
                    "maxItems": 6,
                    "type": "array"
                  },
                  "message": {
                    "description": "Texto del mensaje (1-2000 caracteres)",
                    "example": "Estoy llegando al punto de recogida en 15 minutos",
                    "maxLength": 2000,
                    "minLength": 1,
                    "type": "string"
                  }
                },
                "required": [
                  "message"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "authorId": "507f1f77bcf86cd799439050",
                    "authorName": "Juan García",
                    "createdAt": "2025-08-10T08:30:00.000Z",
                    "files": [
                      "/images?file=uploads/photo1.jpg",
                      "/images?file=uploads/photo2.jpg"
                    ],
                    "message": "Estoy llegando al punto de recogida"
                  }
                ],
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/DeliveryMessage"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Mensaje enviado con éxito"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "error": "El mensaje no puede estar vacío",
                  "message": "NOT_MESSAGE"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Mensaje vacío o inválido."
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": "El envío no está activo para recibir mensajes",
                  "message": "NOT_ALLOWED"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "El envío no está activo. No se pueden enviar mensajes.\n"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Usuario no encontrado",
                  "message": "USER_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario o envío no encontrado."
          },
          "503": {
            "content": {
              "application/json": {
                "example": {
                  "error": "No se pudo guardar el mensaje",
                  "message": "Error interno"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error al guardar el mensaje."
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Send message to delivery",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/delivery/route/{service_code}": {
      "get": {
        "deprecated": false,
        "description": "## Purpose\nObtiene la ruta y datos de geolocalización de un envío específico.\n\n## Objective\nProporcionar información detallada de tracking y ruta para seguimiento\nvisual en mapas y herramientas de monitoreo.\n\n## Use Cases\n- Visualizar la ruta completa del envío en un mapa\n- Obtener puntos de geolocalización de recogida y entrega\n- Recuperar la polyline de la ruta para renderizado\n- Consultar firmas y fechas de entrega/recogida\n\n## Notes\n- Retorna la polyline codificada de la ruta\n- Incluye coordenadas de pickup y delivery con fechas\n- Útil para integración con mapas interactivos\n",
        "parameters": [
          {
            "description": "Código único del envío",
            "example": "DEL-12345",
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "maxLength": 50,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "geolocationDelivery": {
                    "coordinates": [
                      2.173403,
                      41.385063
                    ],
                    "type": "Point"
                  },
                  "geolocationDeliveryDate": "2025-08-11T17:00:00.000Z",
                  "geolocationPickup": {
                    "coordinates": [
                      -3.70379,
                      40.416775
                    ],
                    "type": "Point"
                  },
                  "geolocationPickupDate": "2025-08-10T08:00:00.000Z",
                  "position": [
                    {
                      "location": {
                        "accuracy": 10,
                        "altitude": 667,
                        "latitude": 40.416775,
                        "longitude": -3.70379,
                        "speed": 60,
                        "time": 1691745600000
                      },
                      "processId": "track_12345"
                    }
                  ],
                  "route": "encoded_polyline_string_for_map_rendering",
                  "service_code": "DEL-12345",
                  "sign_delivery_date": "2025-08-11T17:30:00.000Z",
                  "sign_pickup_date": "2025-08-10T08:30:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/DeliveryRouteResponse"
                }
              }
            },
            "description": "Ruta y tracking obtenidos con éxito"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "error": "El código de servicio es obligatorio",
                  "message": "MISSING_ID"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Falta el service_code en la petición."
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Empresa no encontrada",
                  "message": "CIA_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Empresa no encontrada."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Envío no encontrado",
                  "message": "NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Envío no encontrado."
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get delivery route and tracking",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/delivery/trackable": {
      "get": {
        "deprecated": false,
        "description": "## Purpose\nObtiene la lista de transportistas de la empresa que pueden ser rastreados.\n\n## Objective\nProporcionar a las empresas un listado de sus transportistas asociados\ncon información de posición para seguimiento en tiempo real.\n\n## Use Cases\n- Ver qué transportistas están disponibles para seguimiento GPS\n- Consultar información de contacto de transportistas\n- Obtener lista de opciones para asignación de envíos\n- Monitorear flota de transportistas asociados\n\n## Notes\n- Retorna truckers asociados a la empresa (cia.truckers)\n- Incluye información de posición actual si está disponible\n- No requiere parámetros adicionales\n",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "_id": "507f1f77bcf86cd799439020",
                    "email": "trucker1@example.com",
                    "lastname": "Rodríguez",
                    "name": "Carlos",
                    "position": {
                      "accuracy": 10,
                      "altitude": 667,
                      "latitude": 40.416775,
                      "longitude": -3.70379,
                      "speed": 0,
                      "time": 1691745600000
                    },
                    "taxid": "B12345678"
                  },
                  {
                    "_id": "507f1f77bcf86cd799439021",
                    "email": "trucker2@example.com",
                    "lastname": "González",
                    "name": "María",
                    "position": null,
                    "taxid": "B87654321"
                  }
                ],
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/TruckerTrackable"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Lista de transportistas rastreables obtenida con éxito"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Empresa no encontrada",
                  "message": "CIA_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. Token JWT inválido o empresa no encontrada."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Usuario no encontrado",
                  "message": "USER_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado."
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get trackable truckers",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/delivery/{service_code}": {
      "delete": {
        "deprecated": false,
        "description": "## Purpose\nCancela un envío existente cambiando su estado a 'canceled' y rejectedBy a 'cia'.\nSolo se pueden cancelar envíos en estados 'planned' o 'replanned'.\n\n## Objective\nPermitir a las empresas cancelar envíos planificados cuando ya no se realizarán,\nprocesando automáticamente las acciones necesarias en Stripe y auctions.\n\n## Use Cases\n- Cancelar un envío planificado que ya no se realizará\n- Detener un envío replanificado por problemas logísticos o de negocio\n- Revertir un envío mal planificado antes de que inicie\n- Cancelar por cambios en la carga o destino\n\n## Side Effects\n- Cambia status a 'canceled'\n- Establece rejectedBy='cia'\n- Cancela el payment_intent en Stripe (procesa reembolso si corresponde)\n- Cancela la subasta asociada si existe\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Cancel Request] --> B{Delivery Exists?}\n  B -->|No| C[404 NOT_FOUND]\n  B -->|Yes| D{Company Owns Delivery?}\n  D -->|No| E[403 NOT_ALLOWED]\n  D -->|Yes| F{Is Final Status?}\n  F -->|Yes| G[401 NOT_ALLOWED]\n  F -->|No| H{Status is Cancelable?}\n  H -->|No| I[401 NOT_ALLOWED]\n  H -->|Yes| J[Set status=canceled]\n  J --> K[Set rejectedBy=cia]\n  K --> L{Cancel Stripe Payment}\n  L -->|Error| M[403 CANT_CANCEL_PAYMENT]\n  L -->|Success| N{Cancel Auction}\n  N -->|Success| O[Save - 200]\n```\n\n## Notes\n- Solo se pueden cancelar envíos en estados 'planned' o 'replanned'\n- No se puede cancelar un envío en estado final (delivered, claimed, canceled)\n- Si falla la cancelación del pago en Stripe, toda la operación se cancela\n- La subasta asociada también se cancela automáticamente\n",
        "parameters": [
          {
            "description": "Código único del envío a cancelar",
            "example": "DEL-12345",
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "maxLength": 50,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "etd_date": "2025-08-11T12:00:00.000Z",
                  "etl_date": "2025-08-10T08:00:00.000Z",
                  "rejectedBy": "cia",
                  "service_code": "DEL-12345",
                  "status": "canceled",
                  "updated_at": "2025-08-08T15:30:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/DeliveryResponse"
                }
              }
            },
            "description": "Envío cancelado con éxito"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "El envío está en un estado que no permite cancelación",
                  "message": "NOT_ALLOWED"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. El estado del envío no permite cancelación.\n"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": "No se pudo cancelar el payment_intent en Stripe",
                  "message": "CANT_CANCEL_PAYMENT"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No se puede cancelar el pago en Stripe.\n"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Envío no encontrado",
                  "message": "NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Envío no encontrado. El service_code proporcionado no existe\no no pertenece a la empresa del usuario autenticado.\n"
          },
          "503": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Error al guardar el envío cancelado",
                  "message": "NOT_SAVED"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error al guardar los cambios.\n"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Cancel delivery service",
        "tags": [
          "Delivery"
        ]
      },
      "get": {
        "deprecated": false,
        "description": "## Purpose\nObtiene los detalles completos de un envío específico, incluyendo toda la información\nrelevante para su gestión y seguimiento.\n\n## Objective\nProporcionar a las empresas una vista completa y detallada de un envío individual,\nincluyendo información de direcciones, vehículo, mensajes, estado y capacidades de mensajería.\n\n## Use Cases\n- Consultar detalles completos de un envío antes de realizar cambios\n- Verificar información completa de carga y direcciones\n- Revisar mensajes intercambiados con el transportista\n- Comprobar si el envío permite mensajería activa (can_message)\n\n## Notes\n- El parámetro {id} puede ser un service_code (ej: DEL-12345) o un MongoDB _id\n- Incluye información de direcciones, vehículo, auction y mensajes\n- El campo can_message indica si se pueden enviar mensajes (isActive)\n- Los mensajes se ordenan por createdAt DESC (más recientes primero)\n- Las URLs de archivos en mensajes incluyen el prefijo /images?file=\n",
        "parameters": [
          {
            "description": "Identificador del envío. Puede ser:\n- service_code (ej: DEL-12345)\n- MongoDB _id (ej: 507f1f77bcf86cd799439011)\n",
            "example": "DEL-12345",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "maxLength": 100,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "507f1f77bcf86cd799439060",
                  "auction": {
                    "service_code": "AUCT-2025-001"
                  },
                  "can_message": true,
                  "cargo_height": 180,
                  "cargo_weight": 1500,
                  "custom_code": "PED-2025-001",
                  "date_eta": "2025-08-11T17:30:00.000Z",
                  "description": "Carga electrónica frágil, manejar con cuidado",
                  "ecmr": null,
                  "eta_updated_at": "2025-08-10T09:00:00.000Z",
                  "etd_address": {
                    "_id": "507f1f77bcf86cd799439012",
                    "city": "Barcelona",
                    "country": "Spain",
                    "name": "Centro Logístico Mediterráneo",
                    "zipcode": "08001"
                  },
                  "etd_cargo_method": "lateral",
                  "etd_comment": "",
                  "etd_date": "2025-08-11T17:00:00.000Z",
                  "etd_extra_time": 60,
                  "etd_photos": [],
                  "etl_address": {
                    "_id": "507f1f77bcf86cd799439011",
                    "city": "Madrid",
                    "country": "Spain",
                    "name": "Centro de Distribución Norte",
                    "zipcode": "28001"
                  },
                  "etl_cargo_method": "back",
                  "etl_comment": "",
                  "etl_date": "2025-08-10T08:00:00.000Z",
                  "etl_extra_time": 30,
                  "etl_photos": [],
                  "fresh_cargo_temp": 0,
                  "geolocationDelivery": null,
                  "geolocationPickup": null,
                  "is_fresh": false,
                  "is_imperial_measure": false,
                  "linear_meters": 12.5,
                  "messages": [
                    {
                      "authorId": "507f1f77bcf86cd799439050",
                      "authorName": "Juan García",
                      "createdAt": "2025-08-10T08:30:00.000Z",
                      "files": [
                        "/images?file=uploads/photo1.jpg"
                      ],
                      "message": "Estoy en camino al punto de recogida"
                    }
                  ],
                  "pallets_num": 12,
                  "pallets_type": "european",
                  "plate_full_trailer": "ABC-1234",
                  "position": [],
                  "replannedBy": "",
                  "service_code": "DEL-12345",
                  "status": "planned",
                  "trucker_cia": {
                    "_id": "507f1f77bcf86cd799439020",
                    "name": "Transportes Rápidos SA",
                    "taxid": "B12345678"
                  },
                  "trucker_user": {
                    "_id": "507f1f77bcf86cd799439040",
                    "lastname": "García",
                    "name": "Juan"
                  },
                  "trucker_vehicle": {
                    "_id": "507f1f77bcf86cd799439030",
                    "plate": "ABC-1234"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/DeliveryDetailsResponse"
                }
              }
            },
            "description": "Detalles del envío obtenidos con éxito"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Token de autenticación no proporcionado o inválido",
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. Token JWT inválido o expirado."
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Envío no encontrado",
                  "message": "NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Envío no encontrado. El ID proporcionado no existe\no no pertenece a la empresa del usuario autenticado.\n"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get delivery details",
        "tags": [
          "Delivery"
        ]
      },
      "put": {
        "deprecated": false,
        "description": "## Purpose\nActualiza los detalles de un envío existente. Los campos editables dependen\ndel estado actual del envío.\n\n## Objective\nPermitir a las empresas modificar ciertos aspectos de sus envíos según el estado\nen que se encuentren, manteniendo un control de cambios mediante replannedBy.\n\n## Use Cases\n- Modificar fechas de carga/descarga en envíos planificados o aceptados\n- Actualizar detalles de la carga (peso, altura, palets, temperatura)\n- Corregir metros lineales de carga\n- Iniciar un proceso de re-planificación que requiere aprobación\n\n## Editable Fields by Status\n\n**Estado 'planned' o 'replanned':**\nPueden editar: etl_date, etd_date, pallets_num, fresh_cargo_temp,\ncargo_weight, cargo_height, linear_meters\n\n**Estado 'accepted':**\nPueden editar: etl_date, etd_date\n\n**Cualquier otra edición:**\n- Cambia status a 'replanned'\n- Establece replannedBy a 'cia' (empresa)\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Edit Request] --> B{Delivery Exists?}\n  B -->|No| C[404 NOT_FOUND]\n  B -->|Yes| D{Company Owns Delivery?}\n  D -->|No| E[404 NOT_ALLOWED]\n  D -->|Yes| F{Is Final Status?}\n  F -->|Yes| G[401 NOT_ALLOWED]\n  F -->|No| H[Get Editable Keys]\n  H --> I{Any Valid Keys?}\n  I -->|No| J[401 NOT_ALLOWED]\n  I -->|Yes| K[Update Fields]\n  K --> L[Set status=replanned]\n  L --> M[Set replannedBy=cia]\n  M --> N[Save Changes - 200]\n```\n\n## Notes\n- No se pueden editar envíos en estado final (delivered, claimed, canceled)\n- Cualquier edición cambia automáticamente el estado a 'replanned'\n- Si el trucker debe aprobar los cambios, replannedBy='trucker'\n- La empresa aprueba los cambios del trucker con PUT /aprove/{service_code}\n",
        "parameters": [
          {
            "description": "Código único del envío a modificar",
            "example": "DEL-12345",
            "in": "path",
            "name": "service_code",
            "required": true,
            "schema": {
              "maxLength": 50,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "accepted": {
                  "summary": "Campos editables en accepted",
                  "value": {
                    "etd_date": "2025-08-11T13:00:00.000Z",
                    "etl_date": "2025-08-10T09:00:00.000Z"
                  }
                },
                "planned-replanned": {
                  "summary": "Campos editables en planned/replanned",
                  "value": {
                    "cargo_height": 190,
                    "cargo_weight": 1600,
                    "etd_date": "2025-08-11T12:00:00.000Z",
                    "etl_date": "2025-08-10T08:00:00.000Z",
                    "fresh_cargo_temp": 5,
                    "linear_meters": 13.5,
                    "pallets_num": 15
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/DeliveryUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "cargo_weight": 1600,
                  "etd_date": "2025-08-11T12:00:00.000Z",
                  "etl_date": "2025-08-10T08:00:00.000Z",
                  "pallets_num": 15,
                  "replannedBy": "cia",
                  "service_code": "DEL-12345",
                  "status": "replanned",
                  "updated_at": "2025-08-08T15:25:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/DeliveryResponse"
                }
              }
            },
            "description": "Envío actualizado con éxito"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Datos de actualización inválidos",
                  "message": "INVALID_DATA"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Operación no permitida. Ocurre cuando:\n- Los datos proporcionados son inválidos\n"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "No se permite editar estos campos en el estado actual",
                  "message": "NOT_ALLOWED"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. El token JWT es inválido o ha expirado,\no no se pueden editar los campos solicitados para el estado actual.\n"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Envío no encontrado",
                  "message": "NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Envío no encontrado. El service_code proporcionado no existe\no no pertenece a la empresa del usuario autenticado.\n"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Edit delivery",
        "tags": [
          "Delivery"
        ]
      }
    },
    "/company/dgt/": {
      "post": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "POST /company/dgt/",
        "tags": [
          "DGT"
        ]
      }
    },
    "/company/dgt/:id": {
      "put": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "PUT /company/dgt/:id",
        "tags": [
          "DGT"
        ]
      }
    },
    "/company/dgt/email": {
      "post": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "POST /company/dgt/email",
        "tags": [
          "DGT"
        ]
      }
    },
    "/company/documents": {
      "get": {
        "deprecated": false,
        "description": "Obtiene una lista paginada de todos los documentos asociados a la compañía del usuario autenticado.\n\n### Flujo de operación:\n1. Autenticación mediante JWT válido con rol gestor o superior\n2. Validación de parámetros de paginación\n3. Filtrado opcional por tipo de documento\n4. Consulta paginada a base de datos (solo última versión de cada documento)\n5. Retorno de documentos con metadatos y rutas de archivos\n\n### Requisitos de acceso:\n- **Autenticación**: Token JWT válido (Bearer token)\n- **Rol mínimo**: Gestor de compañía (isGestor)\n- **Permisos**: Acceso solo a documentos de la propia compañía\n\n### Casos de uso típicos:\n- Consultar facturas y contratos de la empresa\n- Filtrar documentos por categoría (documentType)\n- Revisar estado de aprobación de documentos pendientes\n- Acceder a historial de versiones recientes\n\n### Estados de documento:\n- `pending`: Pendiente de revisión por administración\n- `approved`: Aprobado y disponible para uso\n- `rejected`: Rechazado con motivo especificado\n\n### Consideraciones importantes:\n- **Solo devuelve la última versión** de cada documento (excluye versiones obsoletas con `newVersion: null`)\n- Todos los timestamps están en formato UTC\n- Máximo 100 documentos por página\n- Ordenación por fecha de creación descendente (más recientes primero)\n- Los archivos se almacenan en S3/MinIO con rutas relativas en `filePaths`\n\n### Ejemplo de solicitud:\n```http\nGET /company/documents?page=1&limit=25&documentType=648ac82b769e704acd2c73f5\nAuthorization: Bearer {token}\n```\n\n### Ejemplo de respuesta exitosa:\n```json\n{\n  \"docs\": [\n    {\n      \"_id\": \"5f8d3b7a9c2d1e0f4a6b5c4d\",\n      \"name\": \"Factura Q1 2023\",\n      \"status\": \"approved\",\n      \"documentType\": \"5f8d3b7a9c2d1e0f4a6b5c4e\",\n      \"version\": 1,\n      \"filePaths\": [\"documents/company1/factura_q1.pdf\"],\n      \"createdAt\": \"2023-04-15T09:30:00.000Z\",\n      \"updatedAt\": \"2023-04-20T14:15:00.000Z\"\n    }\n  ],\n  \"totalDocs\": 15,\n  \"limit\": 10,\n  \"page\": 1,\n  \"totalPages\": 2\n}\n```\n",
        "parameters": [
          {
            "description": "Número de página para navegación en resultados paginados.\nÚtil para dividir la visualización cuando hay muchos documentos.\n",
            "example": 1,
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Cantidad máxima de documentos a devolver por página.\nPermite balancear rendimiento (valores bajos) vs comodidad (valores altos).\n",
            "example": 25,
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 10,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Filtra documentos por tipo específico usando su ID de MongoDB.\nLos tipos disponibles se obtienen del endpoint /documents/types.\nÚtil para mostrar solo facturas, contratos u otras categorías.\n",
            "example": "648ac82b769e704acd2c73f5",
            "in": "query",
            "name": "documentType",
            "required": false,
            "schema": {
              "format": "ObjectId",
              "pattern": "^[a-f0-9]{24}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "docs": [
                    {
                      "_id": "5f8d3b7a9c2d1e0f4a6b5c4d",
                      "createdAt": "2023-04-15T09:30:00.000Z",
                      "documentType": "5f8d3b7a9c2d1e0f4a6b5c4e",
                      "filePaths": [
                        "documents/company1/factura_q1.pdf"
                      ],
                      "name": "Factura Q1 2023",
                      "status": "approved",
                      "updatedAt": "2023-04-20T14:15:00.000Z",
                      "version": 1
                    },
                    {
                      "_id": "5f8d3b7a9c2d1e0f4a6b5c4f",
                      "createdAt": "2023-05-10T11:20:00.000Z",
                      "documentType": "5f8d3b7a9c2d1e0f4a6b5c50",
                      "filePaths": [
                        "documents/company1/contrato_v2.pdf",
                        "documents/company1/anexo.pdf"
                      ],
                      "name": "Contrato de Servicios",
                      "status": "pending",
                      "updatedAt": "2023-05-12T16:45:00.000Z",
                      "version": 2
                    }
                  ],
                  "limit": 10,
                  "page": 1,
                  "totalDocs": 15,
                  "totalPages": 2
                },
                "schema": {
                  "$ref": "#/components/schemas/DocumentsPaginatedResponse"
                }
              }
            },
            "description": "Lista de documentos obtenida exitosamente"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "NO_TOKEN"
                },
                "schema": {
                  "properties": {
                    "error": {
                      "enum": [
                        "NO_TOKEN",
                        "TOKEN_NOT_VALID"
                      ],
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No autorizado - Token JWT inválido o faltante"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": "NOT_ALLOWED"
                },
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Prohibido - Permisos insuficientes"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "COMPANY_NOT_FOUND"
                },
                "schema": {
                  "properties": {
                    "error": {
                      "enum": [
                        "COMPANY_NOT_FOUND"
                      ],
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Compañía no encontrada"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "List company documents",
        "tags": [
          "Documents"
        ]
      },
      "post": {
        "deprecated": false,
        "description": "Endpoint para crear nuevos documentos corporativos con sus archivos asociados en Amazon S3/MinIO.\n\n### Flujo de operación:\n1. Autenticación mediante JWT válido con rol gestor o superior\n2. Validación de metadatos (nombre, tipo de documento)\n3. Validación de archivos: formato, tamaño y cantidad\n4. Subida paralela de archivos a S3/MinIO\n5. Generación automática de rutas de archivos (filePaths)\n6. Creación de registro en base de datos asociado a la compañía\n7. Estado inicial: `pending` (pendiente de aprobación)\n\n### Requisitos de acceso:\n- **Autenticación**: Token JWT válido (Bearer token)\n- **Rol mínimo**: Gestor de compañía (isGestor)\n- **Compañía**: Debe existir y estar activa\n\n### Configuración de archivos:\n- **Cantidad máxima**: 6 archivos por documento\n- **Tamaño máximo**: 10MB por archivo (10,485,760 bytes)\n- **Formatos soportados**:\n  - Documentos: PDF, DOCX, XLSX\n  - Imágenes: JPG, PNG\n- **Almacenamiento**: S3/MinIO con estructura: `documents/{companyId}/{timestamp}_{filename.ext}`\n- **filePaths**: Se genera automáticamente a partir de los archivos subidos. No es necesario enviarlo manualmente.\n\n### Validaciones:\n- Todos los timestamps deben estar en UTC\n- El tipo de documento debe existir en `/documents/types`\n- El nombre debe tener entre 3-100 caracteres\n- Los archivos deben cumplir con formatos y tamaños permitidos\n\n### Casos de uso típicos:\n- Subir facturas y contratos para aprobación\n- Registrar documentación legal de la empresa\n- Adjuntar documentos de seguros y permisos\n- Cargar certificados y acreditaciones\n\n### Ejemplo de solicitud:\n```bash\ncurl -X POST https://api.pro.cargoffer.com/company/documents \\\n  -H \"Authorization: Bearer {token}\" \\\n  -F \"name=Contrato de Servicios\" \\\n  -F \"documentType=5f8d3b7a9c2d1e0f4a6b5c4f\" \\\n  -F \"files=@contrato.pdf\" \\\n  -F \"files=@anexo.pdf\"\n```\n\n### Ejemplo de respuesta exitosa:\n```json\n{\n  \"_id\": \"5f8d3b7a9c2d1e0f4a6b5c4d\",\n  \"name\": \"Contrato de transporte\",\n  \"status\": \"pending\",\n  \"documentType\": \"5f8d3b7a9c2d1e0f4a6b5c4f\",\n  \"version\": 1,\n  \"filePaths\": [\n    \"documents/company1/1701234567890_contrato.pdf\",\n    \"documents/company1/1701234567891_anexo.pdf\"\n  ],\n  \"createdAt\": \"2023-07-15T14:30:00.000Z\",\n  \"updatedAt\": \"2023-07-15T14:30:00.000Z\"\n}\n```\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "encoding": {
                "files": {
                  "contentType": "application/pdf, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, image/jpeg, image/png"
                }
              },
              "schema": {
                "properties": {
                  "documentType": {
                    "description": "ID del tipo de documento (se obtiene de /documents/types)",
                    "example": "5f8d3b7a9c2d1e0f4a6b5c4f",
                    "format": "ObjectId",
                    "pattern": "^[a-f0-9]{24}$",
                    "type": "string"
                  },
                  "files": {
                    "description": "Archivos a subir (máximo 6).\nFormatos soportados: PDF, DOCX, XLSX, JPG, PNG.\nTamaño máximo: 10MB por archivo.\n**Nota**: filePaths se genera automáticamente, no enviar manualmente.\n",
                    "example": [],
                    "items": {
                      "format": "binary",
                      "type": "string"
                    },
                    "maxItems": 6,
                    "type": "array"
                  },
                  "name": {
                    "description": "Nombre descriptivo del documento (3-100 caracteres)",
                    "example": "Contrato de transporte",
                    "maxLength": 100,
                    "minLength": 3,
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "documentType"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "5f8d3b7a9c2d1e0f4a6b5c4d",
                  "createdAt": "2023-07-15T14:30:00.000Z",
                  "documentType": "5f8d3b7a9c2d1e0f4a6b5c4f",
                  "filePaths": [
                    "documents/company1/1701234567890_contrato.pdf",
                    "documents/company1/1701234567891_anexo.pdf"
                  ],
                  "name": "Contrato de transporte",
                  "status": "pending",
                  "updatedAt": "2023-07-15T14:30:00.000Z",
                  "version": 1
                },
                "schema": {
                  "$ref": "#/components/schemas/DocumentResponse"
                }
              }
            },
            "description": "Documento creado exitosamente"
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "file_too_large": {
                    "summary": "Archivo excede 10MB",
                    "value": {
                      "error": "FILE_TOO_LARGE"
                    }
                  },
                  "invalid_file": {
                    "summary": "Formato de archivo no soportado",
                    "value": {
                      "error": "INVALID_FILE_TYPE"
                    }
                  },
                  "missing_fields": {
                    "summary": "Campos faltantes",
                    "value": {
                      "error": "FORM_DATA_NOT_VALID"
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "error": {
                      "enum": [
                        "FORM_DATA_NOT_VALID",
                        "INVALID_FILE_TYPE",
                        "FILE_TOO_LARGE"
                      ],
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Solicitud inválida - Validación fallida"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "NO_TOKEN"
                },
                "schema": {
                  "properties": {
                    "error": {
                      "enum": [
                        "NO_TOKEN",
                        "TOKEN_NOT_VALID"
                      ],
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No autorizado - Token JWT inválido o faltante"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "COMPANY_NOT_FOUND"
                },
                "schema": {
                  "properties": {
                    "error": {
                      "enum": [
                        "COMPANY_NOT_FOUND"
                      ],
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Compañía no encontrada"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Create new document",
        "tags": [
          "Documents"
        ]
      }
    },
    "/company/documents/files": {
      "get": {
        "deprecated": false,
        "description": "Descarga un archivo almacenado en S3/MinIO a partir de su ruta (key) en el bucket configurado.\nEl archivo se sirve como stream binario con el tipo MIME y nombre de archivo correctos.\n\n### Flujo de operación:\n1. Autenticación mediante JWT válido (cualquier rol de usuario autenticado)\n2. Extracción del parámetro `file` de la query string\n3. Resolución del key en el bucket S3/MinIO\n4. Obtención del objeto desde S3/MinIO via GetObjectCommand\n5. Configuración de cabeceras `Content-Type`, `Content-Length` y `Content-Disposition`\n6. Stream del contenido binario al cliente como descarga adjunta\n\n### Requisitos de acceso:\n- **Autenticación**: Token JWT válido (Bearer token)\n- **Rol mínimo**: Cualquier usuario autenticado (isLoged)\n- **Permisos**: El archivo debe ser accesible en el bucket configurado\n\n### Comportamiento de descarga:\n- La respuesta incluye cabecera `Content-Disposition: attachment; filename=file.{ext}`\n- El tipo MIME se detecta automáticamente desde los metadatos del objeto S3\n- La extensión del archivo se deriva del Content-Type devuelto por S3\n- Si el parámetro `file` no se proporciona, se usa el valor `'-'` (resultará en error S3)\n\n### Formatos de ruta soportados:\n- **Key relativo**: `documents/company1/factura.pdf`\n- **URL completa de S3**: `https://bucket.s3.amazonaws.com/documents/company1/factura.pdf`\n  (el sistema extrae automáticamente el key de la URL)\n\n### Casos de uso típicos:\n- Descargar facturas y contratos subidos por la empresa\n- Visualizar documentos adjuntos a registros de la plataforma\n- Acceder a archivos de CMR, permisos y certificados\n- Exportar documentos para revisión o firma\n\n### Ejemplo de solicitud:\n```http\nGET /company/documents/files?file=documents%2Fcompany1%2Ffactura.pdf\nAuthorization: Bearer {token}\n```\n\n### Ejemplo de respuesta exitosa:\n```\nHTTP/1.1 200 OK\nContent-Type: application/pdf\nContent-Length: 204800\nContent-Disposition: attachment; filename=file.pdf\n[Binary stream...]\n```\n\n### Diagrama de flujo:\n```mermaid\nflowchart TD\n  A[GET /company/documents/files?file=...] --> B{Token JWT válido?}\n  B -->|No| C[401 NO_TOKEN / TOKEN_NOT_VALID]\n  B -->|Sí| D{Parámetro file presente?}\n  D -->|No| E[Usar key '-' → error S3]\n  D -->|Sí| F{¿URL completa de S3?}\n  F -->|Sí| G[Extraer key de la URL]\n  F -->|No| H[Usar path como key directo]\n  G --> I[GetObjectCommand S3]\n  H --> I\n  I --> J{Objeto existe en bucket?}\n  J -->|No| K[404 NoSuchKey]\n  J -->|Sí| L[Stream binario con Content-Type y Content-Disposition]\n```\n",
        "parameters": [
          {
            "description": "Ruta (key) del archivo en el bucket S3/MinIO, o URL completa del objeto S3.\nSe obtiene del campo `filePaths` de un documento existente.\nEjemplos:\n- `documents/company1/1701234567890_factura.pdf`\n- `https://bucket.s3.eu-west-2.amazonaws.com/documents/company1/factura.pdf`\n",
            "in": "query",
            "name": "file",
            "required": true,
            "schema": {
              "example": "documents/company1/1701234567890_factura.pdf",
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/pdf": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              },
              "application/vnd.openxmlformats-officedocument.wordprocessingml.document": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              },
              "image/jpeg": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              },
              "image/png": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "Archivo descargado exitosamente como stream binario",
            "headers": {
              "Content-Disposition": {
                "description": "Indica que el archivo debe descargarse como adjunto con extensión detectada",
                "schema": {
                  "example": "attachment; filename=file.pdf",
                  "type": "string"
                }
              },
              "Content-Length": {
                "description": "Tamaño del archivo en bytes",
                "schema": {
                  "example": 204800,
                  "type": "integer"
                }
              },
              "Content-Type": {
                "description": "Tipo MIME del archivo detectado desde los metadatos de S3",
                "schema": {
                  "example": "application/pdf",
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "NO_TOKEN"
                },
                "schema": {
                  "properties": {
                    "error": {
                      "enum": [
                        "NO_TOKEN",
                        "TOKEN_NOT_VALID"
                      ],
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No autorizado - Token JWT inválido o faltante"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": "AccessDenied"
                },
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Prohibido - Sin acceso al archivo en S3 (AccessDenied)"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "NoSuchKey"
                },
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Archivo no encontrado en el bucket S3/MinIO"
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": "InternalError"
                },
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Error interno al acceder al almacenamiento S3/MinIO"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Download a file from S3/MinIO storage",
        "tags": [
          "Documents"
        ]
      }
    },
    "/company/documents/types": {
      "get": {
        "deprecated": false,
        "description": "Devuelve la lista de tipos de documento habilitados y aplicables al origen del usuario autenticado\n(company, trucker o staff). Los tipos de documento determinan las categorías disponibles\nal crear o clasificar documentos corporativos.\n\n### Flujo de operación:\n1. Autenticación mediante JWT válido (cualquier rol de usuario autenticado)\n2. Validación de parámetros UTC mediante `checkUTC`\n3. Determinación del origen del usuario (`company`, `trucker` o `staff`)\n4. Consulta de tipos de documento activos (`isEnabled: true`) filtrados por `requiredBy` igual al origen\n5. Parseo de resultados (excluye: `updatedAt`, `__v`, `isEnabled`, `createdBy`, `createdAt`)\n6. Retorno de la lista de tipos aplicables\n\n### Requisitos de acceso:\n- **Autenticación**: Token JWT válido (Bearer token)\n- **Rol mínimo**: Cualquier usuario autenticado (isLoged)\n- **Filtrado automático**: Solo devuelve tipos relevantes para el tipo de usuario (company, trucker, staff)\n\n### Campos devueltos por tipo de documento:\n- `_id`: Identificador MongoDB del tipo (usar como `documentType` al crear documentos)\n- `name`: Nombre descriptivo del tipo (ej: \"Factura\", \"Contrato de transporte\")\n- `required`: Si el tipo de documento es obligatorio para el usuario\n- `requiredBy`: Array de orígenes que deben presentar este tipo (`[\"company\"]`, `[\"trucker\"]`, etc.)\n- `expireTime`: Fecha de vencimiento o renovación del tipo de documento\n- `code`: Código interno identificador del tipo (lowercase)\n\n### Casos de uso típicos:\n- Poblar un selector de tipo al crear un nuevo documento corporativo\n- Determinar qué documentos son obligatorios para completar el perfil de empresa\n- Mostrar al usuario qué tipos de documento debe subir para cumplir con los requisitos de la plataforma\n- Obtener el ID de tipo necesario para el campo `documentType` al llamar a `POST /company/documents`\n\n### Ejemplo de solicitud:\n```http\nGET /company/documents/types\nAuthorization: Bearer {token}\n```\n\n### Ejemplo de respuesta exitosa:\n```json\n[\n  {\n    \"_id\": \"648ac82b769e704acd2c73f5\",\n    \"name\": \"Factura\",\n    \"required\": true,\n    \"requiredBy\": [\"company\"],\n    \"expireTime\": \"2025-01-01T00:00:00.000Z\",\n    \"code\": \"invoice\"\n  },\n  {\n    \"_id\": \"648ac82b769e704acd2c73f6\",\n    \"name\": \"Contrato de transporte\",\n    \"required\": false,\n    \"requiredBy\": [\"company\", \"trucker\"],\n    \"expireTime\": \"2025-06-30T00:00:00.000Z\",\n    \"code\": \"transport_contract\"\n  }\n]\n```\n\n### Diagrama de flujo:\n```mermaid\nflowchart TD\n  A[GET /company/documents/types] --> B{Token JWT válido?}\n  B -->|No| C[401 NO_TOKEN / TOKEN_NOT_VALID]\n  B -->|Sí| D[Determinar origen del usuario]\n  D --> E{¿Es staff?}\n  E -->|Sí| F[origin = 'staff']\n  E -->|No| G{¿Es trucker?}\n  G -->|Sí| H[origin = 'trucker']\n  G -->|No| I{¿Es company?}\n  I -->|Sí| J[origin = 'company']\n  I -->|No| K[404 USER_NOT_FOUND]\n  F --> L[Consultar DocTypes con requiredBy=origin y isEnabled=true]\n  H --> L\n  J --> L\n  L --> M[Parsear y retornar lista]\n```\n",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "_id": "648ac82b769e704acd2c73f5",
                    "code": "invoice",
                    "expireTime": "2025-01-01T00:00:00.000Z",
                    "name": "Factura",
                    "required": true,
                    "requiredBy": [
                      "company"
                    ]
                  },
                  {
                    "_id": "648ac82b769e704acd2c73f6",
                    "code": "transport_contract",
                    "expireTime": "2025-06-30T00:00:00.000Z",
                    "name": "Contrato de transporte",
                    "required": false,
                    "requiredBy": [
                      "company",
                      "trucker"
                    ]
                  }
                ],
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/DocumentType"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Lista de tipos de documento disponibles para el usuario autenticado"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "NO_TOKEN"
                },
                "schema": {
                  "properties": {
                    "error": {
                      "enum": [
                        "NO_TOKEN",
                        "TOKEN_NOT_VALID"
                      ],
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No autorizado - Token JWT inválido o faltante"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "USER_NOT_FOUND"
                },
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Usuario no encontrado - No se pudo determinar el origen del usuario"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "List document types available for the authenticated user",
        "tags": [
          "Documents"
        ]
      }
    },
    "/company/documents/{id}": {
      "delete": {
        "deprecated": false,
        "description": "Marca un documento como eliminado mediante soft delete (mongoose-delete).\nLos archivos en S3/MinIO NO se eliminan permanentemente.\n\n### Flujo de operación:\n1. Autenticación mediante JWT válido con rol administrador\n2. Validación de permisos de administración\n3. Verificación de existencia del documento\n4. Verificación de que el documento puede ser eliminado\n5. Marca `deleted: true` en base de datos (soft delete)\n6. Los archivos en S3/MinIO permanecen almacenados\n\n### Requisitos de acceso:\n- **Autenticación**: Token JWT válido (Bearer token)\n- **Rol mínimo**: Administrador (isAdmin)\n- **Documento**: Debe existir y no estar protegido contra eliminación\n\n### Política de eliminación:\n- **Soft delete**: Marca `deleted: true` en BD, el documento puede ser restaurado\n- **Archivos S3/MinIO**: Los archivos **NO** se eliminan del almacenamiento (persisten)\n- **Versiones**: Marca como eliminada solo la versión especificada\n- **Reversibilidad**: La operación es reversible mediante restauración\n\n### Alternativas recomendadas:\n- **Para documentos incorrectos**: Cambiar estado a `rejected`\n- **Para documentos obsoletos**: Crear nueva versión\n- **Para eliminación permanente**: Contactar a soporte técnico\n\n### Casos de uso típicos:\n- Eliminar documentos duplicados por error\n- Remover borradores de documentos no deseados\n- Limpiar documentos de prueba\n- Ocultar documentos confidenciales temporalmente\n\n### Ejemplo de solicitud:\n```bash\ncurl -X DELETE https://api.pro.cargoffer.com/company/documents/5f8d3b7a9c2d1e0f4a6b5c4d \\\n  -H \"Authorization: Bearer {token}\"\n```\n\n### Ejemplo de respuesta exitosa:\n```json\n{\n  \"_id\": \"5f8d3b7a9c2d1e0f4a6b5c4d\",\n  \"message\": \"DELETED\",\n  \"name\": \"Documento obsoleto\",\n  \"deletedFiles\": 3,\n  \"deletedVersions\": 2,\n  \"timestamp\": \"2023-07-20T10:30:00.000Z\"\n}\n```\n\n### Consideraciones de seguridad:\n- Operación reversible (soft delete) mediante restauración de BD\n- Registro de auditoría generado automáticamente\n- Los archivos de S3/MinIO permanecen almacenados\n- Solo administradores pueden realizar esta operación\n",
        "parameters": [
          {
            "description": "Identificador único del documento en formato MongoDB ObjectId.\nSe obtiene al crear el documento o al listar documentos.\nDebe corresponder a un documento existente y accesible para el usuario.\n",
            "example": "5f8d3b7a9c2d1e0f4a6b5c4d",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "ObjectId",
              "pattern": "^[a-f0-9]{24}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "5f8d3b7a9c2d1e0f4a6b5c4d",
                  "deletedFiles": 3,
                  "deletedVersions": 2,
                  "message": "DELETED",
                  "name": "Documento obsoleto",
                  "timestamp": "2023-07-20T10:30:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/DocumentDeleteResponse"
                }
              }
            },
            "description": "Documento marcado como eliminado exitosamente (soft delete)"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "NO_TOKEN"
                },
                "schema": {
                  "properties": {
                    "error": {
                      "enum": [
                        "NO_TOKEN",
                        "TOKEN_NOT_VALID"
                      ],
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No autorizado - Token JWT inválido o faltante"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": "NOT_ALLOWED"
                },
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Prohibido - Permisos insuficientes (se requiere admin)"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "NOT_FOUND"
                },
                "schema": {
                  "properties": {
                    "error": {
                      "enum": [
                        "NOT_FOUND"
                      ],
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Documento no encontrado"
          },
          "412": {
            "content": {
              "application/json": {
                "example": {
                  "message": "CANT_DELETE",
                  "status": false
                },
                "schema": {
                  "properties": {
                    "message": {
                      "enum": [
                        "CANT_DELETE"
                      ],
                      "type": "string"
                    },
                    "status": {
                      "example": false,
                      "type": "boolean"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No se puede eliminar el documento"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Delete document (soft delete)",
        "tags": [
          "Documents"
        ]
      },
      "post": {
        "deprecated": false,
        "description": "Crea una nueva versión de un documento existente en S3/MinIO, manteniendo el historial completo.\n\n### Flujo de operación:\n1. Autenticación mediante JWT válido con rol administrador\n2. Validación de permisos y parámetros UTC\n3. Verificación de existencia del documento\n4. Subida de nuevos archivos a S3/MinIO\n5. Creación de nueva versión en base de datos\n6. Actualización de referencias entre versiones\n7. Estado de la nueva versión: `pending`\n\n### Requisitos de acceso:\n- **Autenticación**: Token JWT válido (Bearer token)\n- **Rol mínimo**: Administrador (isAdmin)\n- **Documento**: Debe existir y pertenecer a la compañía\n\n### Comportamiento de versionado:\n- **Número de versión**: Se incrementa automáticamente (version + 1)\n- **Archivos anteriores**: ⚠️ **SE CONSERVAN Y ACUMULAN** con los nuevos\n- **Nuevos archivos**: Se añaden a los existentes (no se reemplazan)\n- **Estado**: Se reinicia a `pending` para revisión por administración\n\n### ⚠️ ADVERTENCIA CRÍTICA - ACUMULACIÓN DE ARCHIVOS:\nSi la versión 1 tiene [A.pdf, B.pdf] y subes [C.pdf],\nla versión 2 tendrá [A.pdf, B.pdf, C.pdf].\n\n**Los archivos de versiones anteriores se suman a los nuevos, no se eliminan.**\n\n### Restricciones:\n- Máximo 6 nuevos archivos por versión\n- Tamaño máximo: 10MB por archivo\n- Mismos formatos soportados que en creación: PDF, DOCX, XLSX, JPG, PNG\n- El documento debe existir y estar activo\n\n### Casos de uso típicos:\n- Actualizar contratos con nuevas cláusulas\n- Añadir páginas adicionales a documentos existentes\n- Corregir facturas manteniendo el histórico\n- Agregar anexos a documentos principales\n\n### Ejemplo de solicitud:\n```bash\ncurl -X POST https://api.pro.cargoffer.com/company/documents/5f8d3b7a9c2d1e0f4a6b5c4d \\\n  -H \"Authorization: Bearer {token}\" \\\n  -F \"name=Contrato de Servicios v2\" \\\n  -F \"files=@contrato_actualizado.pdf\"\n```\n\n### Ejemplo de respuesta exitosa:\n```json\n{\n  \"_id\": \"5f8d3b7a9c2d1e0f4a6b5c4d\",\n  \"name\": \"Contrato de transporte v2\",\n  \"status\": \"pending\",\n  \"documentType\": \"5f8d3b7a9c2d1e0f4a6b5c4f\",\n  \"version\": 2,\n  \"previousVersion\": \"5f8d3b7a9c2d1e0f4a6b5c4c\",\n  \"filePaths\": [\n    \"documents/5f8d3b7a9c2d1e0f4a6b5c4d/1689345600_contrato_v1.pdf\",\n    \"documents/5f8d3b7a9c2d1e0f4a6b5c4d/1689345600_anexo_v1.pdf\",\n    \"documents/5f8d3b7a9c2d1e0f4a6b5c4d/1689456700_contrato_v2.pdf\"\n  ],\n  \"createdAt\": \"2023-07-15T14:30:00.000Z\",\n  \"updatedAt\": \"2023-07-20T09:15:00.000Z\"\n}\n```\n",
        "parameters": [
          {
            "description": "Identificador único del documento en formato MongoDB ObjectId.\nSe obtiene al crear el documento o al listar documentos.\nDebe corresponder a un documento existente y accesible para el usuario.\n",
            "example": "5f8d3b7a9c2d1e0f4a6b5c4d",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "ObjectId",
              "pattern": "^[a-f0-9]{24}$",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "properties": {
                  "files": {
                    "description": "Archivos adicionales a subir (máximo 6).\nEstos archivos se acumularán con los de versiones anteriores.\nFormatos: PDF, DOCX, XLSX, JPG, PNG (máx 10MB c/u).\n",
                    "example": [],
                    "items": {
                      "format": "binary",
                      "type": "string"
                    },
                    "maxItems": 6,
                    "type": "array"
                  },
                  "name": {
                    "description": "Nuevo nombre descriptivo del documento (opcional)",
                    "example": "Contrato de transporte v2",
                    "maxLength": 100,
                    "minLength": 3,
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "5f8d3b7a9c2d1e0f4a6b5c4d",
                  "createdAt": "2023-07-15T14:30:00.000Z",
                  "documentType": "5f8d3b7a9c2d1e0f4a6b5c4f",
                  "filePaths": [
                    "documents/5f8d3b7a9c2d1e0f4a6b5c4d/1689345600_contrato_v1.pdf",
                    "documents/5f8d3b7a9c2d1e0f4a6b5c4d/1689345600_anexo_v1.pdf",
                    "documents/5f8d3b7a9c2d1e0f4a6b5c4d/1689456700_contrato_v2.pdf"
                  ],
                  "name": "Contrato de transporte v2",
                  "previousVersion": "5f8d3b7a9c2d1e0f4a6b5c4c",
                  "status": "pending",
                  "updatedAt": "2023-07-20T09:15:00.000Z",
                  "version": 2
                },
                "schema": {
                  "$ref": "#/components/schemas/DocumentResponse"
                }
              }
            },
            "description": "Documento actualizado exitosamente (nueva versión creada)"
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "file_too_large": {
                    "summary": "Archivo excede 10MB",
                    "value": {
                      "error": "FILE_TOO_LARGE"
                    }
                  },
                  "invalid_file": {
                    "summary": "Formato de archivo no soportado",
                    "value": {
                      "error": "INVALID_FILE_TYPE"
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "error": {
                      "enum": [
                        "FORM_DATA_NOT_VALID",
                        "INVALID_FILE_TYPE",
                        "FILE_TOO_LARGE"
                      ],
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Solicitud inválida - Validación fallida"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "NO_TOKEN"
                },
                "schema": {
                  "properties": {
                    "error": {
                      "enum": [
                        "NO_TOKEN",
                        "TOKEN_NOT_VALID"
                      ],
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No autorizado - Token JWT inválido o faltante"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": "NOT_ALLOWED"
                },
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Prohibido - Permisos insuficientes (se requiere admin)"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "NOT_FOUND"
                },
                "schema": {
                  "properties": {
                    "error": {
                      "enum": [
                        "NOT_FOUND"
                      ],
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Documento no encontrado"
          },
          "412": {
            "content": {
              "application/json": {
                "example": {
                  "error": "BROKEN_VERSION"
                },
                "schema": {
                  "properties": {
                    "error": {
                      "enum": [
                        "BROKEN_VERSION"
                      ],
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Error en el versionado del documento"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Update/Version document",
        "tags": [
          "Documents"
        ]
      }
    },
    "/company/fee/": {
      "get": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "GET /company/fee/",
        "tags": [
          "Fee"
        ]
      }
    },
    "/company/invoices/": {
      "get": {
        "deprecated": false,
        "description": "## Purpose\nRecupera un listado paginado de todas las facturas asociadas a los usuarios de la compañía autenticada. Este endpoint permite navegar el historial completo de facturación.\n\n## Objective\nProporcionar acceso al historial de facturas con soporte para paginación, permitiendo a las empresas visualizar, auditar y gestionar su documentación fiscal de manera eficiente.\n\n## Use Cases\n- Visualizar el historial completo de facturación de la empresa\n- Integración con sistemas contables externos\n- Auditoría de pagos y estados de facturación\n- Exportación de datos fiscales a terceros\n\n## Notes\n- Las facturas se filtran automáticamente por los usuarios de la compañía autenticada\n- El parámetro `sortBy` permite ordenar por cualquier campo del modelo (formato: campo_sentido, ej: createdAt_-1)\n- Usa mongoose-paginate-v2 para la paginación\n",
        "parameters": [
          {
            "description": "Número de página a recuperar (basado en 1, primera página = 1)",
            "example": 1,
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Cantidad máxima de facturas por página (máximo permitido: 100)",
            "example": 20,
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Campo y sentido para ordenar resultados. Formato: campo_sentido\nSentido: 1 (ascendente), -1 (descendente)\nEjemplos: createdAt_-1, serial_number_1, paid_1\n",
            "example": "createdAt_-1",
            "in": "query",
            "name": "sortBy",
            "required": false,
            "schema": {
              "example": "createdAt_-1",
              "pattern": "^[a-zA-Z_]+_(1|-1)$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "docs": [
                    {
                      "_id": "507f191e810c19729de860ea",
                      "bank_account": "ES00 0000 0000 0000 0000",
                      "client": "507f1f77bcf86cd799439011",
                      "createdAt": "2024-02-12T10:30:00.000Z",
                      "delivery": "507f1f77bcf86cd799439012",
                      "expiration_date": "2024-03-12T23:59:59.000Z",
                      "from_address": "Calle Principal 123, 28001 Madrid, España",
                      "from_email": "facturacion@transportes.com",
                      "from_nif": "B12345678",
                      "from_social": "Transportes Logísticos SL",
                      "invoice_date": "2024-02-12T10:30:00.000Z",
                      "irpf": 0,
                      "is_bot": false,
                      "iva": 21,
                      "paid": true,
                      "paid_date": "2024-02-15T14:20:00.000Z",
                      "payment_method": "transferencia",
                      "serial_number": 240212001,
                      "tax_base": 1000,
                      "to_address": "Avenida Comercial 456, 08001 Barcelona, España",
                      "to_email": "clientes@distribuidor.com",
                      "to_nif": "B87654321",
                      "to_social": "Cliente Distribuidor SA",
                      "updatedAt": "2024-02-15T14:20:00.000Z"
                    }
                  ],
                  "hasNextPage": true,
                  "hasPrevPage": false,
                  "limit": 20,
                  "nextPage": 2,
                  "page": 1,
                  "pagingCounter": 1,
                  "prevPage": null,
                  "totalDocs": 45,
                  "totalPages": 3
                },
                "schema": {
                  "properties": {
                    "docs": {
                      "description": "Array de facturas de la página actual",
                      "items": {
                        "$ref": "#/components/schemas/Invoice"
                      },
                      "type": "array"
                    },
                    "hasNextPage": {
                      "description": "Indica si existe una página siguiente",
                      "example": true,
                      "type": "boolean"
                    },
                    "hasPrevPage": {
                      "description": "Indica si existe una página anterior",
                      "example": false,
                      "type": "boolean"
                    },
                    "limit": {
                      "description": "Límite de facturas por página",
                      "example": 20,
                      "type": "integer"
                    },
                    "nextPage": {
                      "description": "Número de página siguiente (null si no existe)",
                      "example": 2,
                      "nullable": true,
                      "type": "integer"
                    },
                    "page": {
                      "description": "Página actual",
                      "example": 1,
                      "type": "integer"
                    },
                    "pagingCounter": {
                      "description": "Contador de paginación",
                      "example": 1,
                      "type": "integer"
                    },
                    "prevPage": {
                      "description": "Número de página anterior (null si no existe)",
                      "example": null,
                      "nullable": true,
                      "type": "integer"
                    },
                    "totalDocs": {
                      "description": "Total de facturas encontradas",
                      "example": 45,
                      "type": "integer"
                    },
                    "totalPages": {
                      "description": "Total de páginas disponibles",
                      "example": 3,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Listado de facturas recuperado exitosamente"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "CIA_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Compañía no encontrada (CIA_NOT_FOUND).\nEl token JWT no corresponde a ninguna compañía registrada.\n"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado (USER_NOT_FOUND).\nEl token JWT no corresponde a ningún usuario válido.\n"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get paginated list of invoices",
        "tags": [
          "invoices"
        ]
      },
      "post": {
        "deprecated": false,
        "description": "## Purpose\nCrea una nueva factura en el sistema con los datos fiscales del emisor y receptor. Este endpoint es el punto principal para generar documentación fiscal de operaciones de transporte.\n\n## Objective\nPermitir a las empresas generar facturas fiscales válidas con toda la información requerida por la legislación vigente, incluyendo datos del emisor, receptor, impuestos y métodos de pago.\n\n## Use Cases\n- Emitir factura por servicios de transporte realizados\n- Registrar facturas de clientes para control fiscal\n- Generar documentación para integraciones contables\n- Crear facturas recurrentes para operaciones periódicas\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request] --> B{User Authenticated?}\n  B -->|No| C[404 USER_NOT_FOUND]\n  B -->|Yes| D[Validate Required Fields]\n  D -->|Missing| E[400 Validation Error]\n  D -->|Valid| F[Generate Auto Fields]\n  F --> G[serial_number = generateInvoiceCode]\n  F --> H[expiration_date = now + INVOICE_EXPIRATION_DAYS]\n  F --> I[invoice_date = now]\n  G --> J[Create Invoice - 200]\n  H --> J\n  I --> J\n```\n\n## Auto-Generated Fields\n- **serial_number**: Generado automáticamente por `tools.generateInvoiceCode()` en formato AAMMDDNNN (ej: 240212001)\n- **expiration_date**: Calculada automáticamente como fecha actual + INVOICE_EXPIRATION_DAYS (variable de entorno)\n- **invoice_date**: Establecida automáticamente a la fecha y hora actual\n\n## Required Fields\n- **client**: ObjectId del usuario trucker (cliente de la factura)\n- **from_social, from_address, from_email, from_nif**: Datos fiscales del emisor (requeridos)\n- **to_social, to_address, to_email, to_nif**: Datos fiscales del receptor (requeridos)\n\n## Notes\n- Todos los timestamps son convertidos a UTC por el middleware checkUTC\n- Los impuestos (tax_base, irpf, iva) se utilizan para cálculos fiscales\n- El campo delivery permite asociar la factura a un delivery específico\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "bank_account": "ES00 0000 0000 0000 0000",
                "client": "507f1f77bcf86cd799439011",
                "delivery": "507f1f77bcf86cd799439012",
                "from_address": "Calle Principal 123, 28001 Madrid, España",
                "from_email": "facturacion@transportes.com",
                "from_nif": "B12345678",
                "from_social": "Transportes Logísticos SL",
                "irpf": 0,
                "is_bot": false,
                "iva": 21,
                "tax_base": 1000,
                "to_address": "Avenida Comercial 456, 08001 Barcelona, España",
                "to_email": "clientes@distribuidor.com",
                "to_nif": "B87654321",
                "to_social": "Cliente Distribuidor SA"
              },
              "schema": {
                "$ref": "#/components/schemas/CreateInvoiceRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "507f191e810c19729de860ea",
                  "bank_account": "ES00 0000 0000 0000 0000",
                  "client": "507f1f77bcf86cd799439011",
                  "createdAt": "2024-02-12T15:30:00.000Z",
                  "delivery": "507f1f77bcf86cd799439012",
                  "expiration_date": "2024-03-12T23:59:59.000Z",
                  "from_address": "Calle Principal 123, 28001 Madrid, España",
                  "from_email": "facturacion@transportes.com",
                  "from_nif": "B12345678",
                  "from_social": "Transportes Logísticos SL",
                  "invoice_date": "2024-02-12T15:30:00.000Z",
                  "irpf": 0,
                  "is_bot": false,
                  "iva": 21,
                  "paid": false,
                  "paid_date": null,
                  "payment_method": null,
                  "serial_number": 240212001,
                  "tax_base": 1000,
                  "to_address": "Avenida Comercial 456, 08001 Barcelona, España",
                  "to_email": "clientes@distribuidor.com",
                  "to_nif": "B87654321",
                  "to_social": "Cliente Distribuidor SA",
                  "updatedAt": "2024-02-12T15:30:00.000Z"
                },
                "schema": {
                  "properties": {
                    "_id": {
                      "description": "ID único de la factura creada",
                      "pattern": "^[a-f0-9]{24}$",
                      "type": "string"
                    },
                    "bank_account": {
                      "description": "Cuenta bancaria",
                      "type": "string"
                    },
                    "client": {
                      "description": "ID del cliente (trucker_user)",
                      "type": "string"
                    },
                    "createdAt": {
                      "description": "Fecha de creación",
                      "format": "date-time",
                      "type": "string"
                    },
                    "delivery": {
                      "description": "ID del delivery asociado",
                      "nullable": true,
                      "type": "string"
                    },
                    "expiration_date": {
                      "description": "Fecha de vencimiento autogenerada",
                      "format": "date-time",
                      "type": "string"
                    },
                    "from_address": {
                      "description": "Dirección del emisor",
                      "type": "string"
                    },
                    "from_email": {
                      "description": "Email del emisor",
                      "type": "string"
                    },
                    "from_nif": {
                      "description": "NIF/CIF del emisor",
                      "type": "string"
                    },
                    "from_social": {
                      "description": "Razón social del emisor",
                      "type": "string"
                    },
                    "invoice_date": {
                      "description": "Fecha de la factura autogenerada",
                      "format": "date-time",
                      "type": "string"
                    },
                    "irpf": {
                      "description": "IRPF",
                      "type": "number"
                    },
                    "is_bot": {
                      "description": "Indica si es generada por bot",
                      "type": "boolean"
                    },
                    "iva": {
                      "description": "IVA",
                      "type": "number"
                    },
                    "paid": {
                      "description": "Estado de pago",
                      "type": "boolean"
                    },
                    "paid_date": {
                      "description": "Fecha de pago",
                      "format": "date-time",
                      "nullable": true,
                      "type": "string"
                    },
                    "payment_method": {
                      "description": "Método de pago",
                      "nullable": true,
                      "type": "string"
                    },
                    "serial_number": {
                      "description": "Número de serie autogenerado",
                      "type": "integer"
                    },
                    "tax_base": {
                      "description": "Base imponible",
                      "type": "number"
                    },
                    "to_address": {
                      "description": "Dirección del receptor",
                      "type": "string"
                    },
                    "to_email": {
                      "description": "Email del receptor",
                      "type": "string"
                    },
                    "to_nif": {
                      "description": "NIF/CIF del receptor",
                      "type": "string"
                    },
                    "to_social": {
                      "description": "Razón social del receptor",
                      "type": "string"
                    },
                    "updatedAt": {
                      "description": "Fecha de última actualización",
                      "format": "date-time",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Factura creada exitosamente"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado (USER_NOT_FOUND).\nEl token JWT no corresponde a ningún usuario válido.\n"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Create new invoice",
        "tags": [
          "invoices"
        ]
      }
    },
    "/company/invoices/paid/{_id}": {
      "put": {
        "deprecated": false,
        "description": "## Purpose\nMarca una factura como pagada o no pagada, actualizando automáticamente el estado y la fecha de pago según corresponda.\n\n## Objective\nPermitir a las empresas gestionar el estado de pago de sus facturas de manera sencilla, con manejo automático de la fecha de pago.\n\n## Use Cases\n- Marcar una factura como pagada tras recibir confirmación del banco\n- Desmarcar una factura pagada por error\n- Actualizar el estado de facturas para conciliación bancaria\n- Corregir estados de pago en procesos de auditoría\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request] --> B{User Authenticated?}\n  B -->|No| C[404 USER_NOT_FOUND]\n  B -->|Yes| D{Invoice Exists?}\n  D -->|No| E[404 NOT_FOUND]\n  D -->|Yes| F{paid === true?}\n  F -->|Yes| G[Set paid_date = now]\n  F -->|No| H[Set paid_date = null]\n  G --> I[Save Invoice - 200]\n  H --> I\n```\n\n## Important Notes\n- Cuando `paid = true`: el campo `paid_date` se asigna automáticamente a `new Date()` (fecha/hora actual)\n- Cuando `paid = false`: el campo `paid_date` se establece en `null`\n- Este endpoint SOLO actualiza el estado de pago, no el método de pago\n- No se puede especificar una fecha de pago manualmente (usa fecha actual del servidor)\n",
        "parameters": [
          {
            "description": "ID único de la factura a actualizar (24 caracteres hexadecimales)",
            "example": "507f191e810c19729de860ea",
            "in": "path",
            "name": "_id",
            "required": true,
            "schema": {
              "maxLength": 24,
              "minLength": 24,
              "pattern": "^[a-f0-9]{24}$",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "paid": true
              },
              "schema": {
                "properties": {
                  "paid": {
                    "description": "Nuevo estado de pago (true = pagada, false = no pagada)",
                    "example": true,
                    "type": "boolean"
                  }
                },
                "required": [
                  "paid"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "507f191e810c19729de860ea",
                  "bank_account": "ES00 0000 0000 0000 0000",
                  "client": "507f1f77bcf86cd799439011",
                  "createdAt": "2024-02-12T10:30:00.000Z",
                  "delivery": "507f1f77bcf86cd799439012",
                  "expiration_date": "2024-03-12T23:59:59.000Z",
                  "from_address": "Calle Principal 123, 28001 Madrid, España",
                  "from_email": "facturacion@transportes.com",
                  "from_nif": "B12345678",
                  "from_social": "Transportes Logísticos SL",
                  "invoice_date": "2024-02-12T10:30:00.000Z",
                  "irpf": 0,
                  "is_bot": false,
                  "iva": 21,
                  "paid": true,
                  "paid_date": "2024-02-15T16:45:30.000Z",
                  "payment_method": null,
                  "serial_number": 240212001,
                  "tax_base": 1000,
                  "to_address": "Avenida Comercial 456, 08001 Barcelona, España",
                  "to_email": "clientes@distribuidor.com",
                  "to_nif": "B87654321",
                  "to_social": "Cliente Distribuidor SA",
                  "updatedAt": "2024-02-15T16:45:30.000Z"
                },
                "schema": {
                  "properties": {
                    "_id": {
                      "type": "string"
                    },
                    "createdAt": {
                      "format": "date-time",
                      "type": "string"
                    },
                    "expiration_date": {
                      "format": "date-time",
                      "type": "string"
                    },
                    "from_nif": {
                      "type": "string"
                    },
                    "from_social": {
                      "type": "string"
                    },
                    "invoice_date": {
                      "format": "date-time",
                      "type": "string"
                    },
                    "paid": {
                      "description": "Estado de pago actualizado",
                      "type": "boolean"
                    },
                    "paid_date": {
                      "description": "Fecha de pago (automática).\n- Si paid=true: fecha actual del servidor\n- Si paid=false: null\n",
                      "format": "date-time",
                      "nullable": true,
                      "type": "string"
                    },
                    "serial_number": {
                      "type": "integer"
                    },
                    "to_nif": {
                      "type": "string"
                    },
                    "to_social": {
                      "type": "string"
                    },
                    "updatedAt": {
                      "format": "date-time",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Estado de pago actualizado exitosamente"
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "invoiceNotFound": {
                    "value": {
                      "message": "NOT_FOUND:{\"_id\":\"507f191e810c19729de860ea\"}"
                    }
                  },
                  "userNotFound": {
                    "value": {
                      "message": "USER_NOT_FOUND"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Factura no encontrada o usuario no encontrado.\n\nPosibles casos:\n- `NOT_FOUND`: El ID proporcionado no existe en la base de datos.\n- `USER_NOT_FOUND`: El token JWT no corresponde a ningún usuario válido.\n"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Update invoice payment status",
        "tags": [
          "invoices"
        ]
      }
    },
    "/company/invoices/payment/{_id}": {
      "put": {
        "deprecated": false,
        "description": "## Purpose\nActualiza los detalles completos del pago de una factura, incluyendo estado de pago, método de pago y fecha de pago. Permite un control más granular que el endpoint /paid/.\n\n## Objective\nPermitir a las empresas registrar información completa de pagos, incluyendo el método utilizado y la fecha específica de la transacción.\n\n## Use Cases\n- Registrar pago con método específico (transferencia, tarjeta, efectivo)\n- Actualizar fecha de pago cuando difiere de la fecha de registro\n- Corregir detalles de pago erroneamente ingresados\n- Desmarcar factura como pagada (limpia todos los datos de pago)\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request] --> B{User Authenticated?}\n  B -->|No| C[404 USER_NOT_FOUND]\n  B -->|Yes| D{Invoice Exists?}\n  D -->|No| E[404 NOT_FOUND]\n  D -->|Yes| F{paid === true?}\n  F -->|Yes| G[Update payment_method, paid_date, paid]\n  F -->|No| H[Clear payment_method, paid_date, paid]\n  G --> I[Save Invoice - 200]\n  H --> I\n```\n\n## Important Notes\n- Cuando `paid = true`: actualiza `payment_method`, `paid_date` (del body) y `paid`\n- Cuando `paid = false`: limpia `payment_method` (cadena vacía), `paid_date` (null) y `paid` (false)\n- A diferencia del endpoint /paid/, este permite especificar la fecha de pago manualmente\n- No hay validación de métodos de pago específicos en el código (cualquier string es aceptado)\n",
        "parameters": [
          {
            "description": "ID único de la factura a actualizar (24 caracteres hexadecimales)",
            "example": "507f191e810c19729de860ea",
            "in": "path",
            "name": "_id",
            "required": true,
            "schema": {
              "maxLength": 24,
              "minLength": 24,
              "pattern": "^[a-f0-9]{24}$",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "paid": true,
                "paid_date": "2024-02-15T14:30:00.000Z",
                "payment_method": "transferencia"
              },
              "schema": {
                "properties": {
                  "paid": {
                    "description": "Nuevo estado de pago (true = pagada, false = no pagada)",
                    "example": true,
                    "type": "boolean"
                  },
                  "paid_date": {
                    "description": "Fecha/hora del pago en formato UTC (opcional, requerido solo si paid=true).\nDebe ser una fecha válida en formato ISO 8601.\n",
                    "example": "2024-02-15T14:30:00.000Z",
                    "format": "date-time",
                    "type": "string"
                  },
                  "payment_method": {
                    "description": "Método de pago utilizado (opcional, requerido solo si paid=true).\nNo hay validación de valores específicos - cualquier string es aceptado.\nEjemplos comunes: transferencia, tarjeta, efectivo, cheque, PayPal\n",
                    "example": "transferencia",
                    "type": "string"
                  }
                },
                "required": [
                  "paid"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "507f191e810c19729de860ea",
                  "bank_account": "ES00 0000 0000 0000 0000",
                  "client": "507f1f77bcf86cd799439011",
                  "createdAt": "2024-02-12T10:30:00.000Z",
                  "delivery": "507f1f77bcf86cd799439012",
                  "expiration_date": "2024-03-12T23:59:59.000Z",
                  "from_address": "Calle Principal 123, 28001 Madrid, España",
                  "from_email": "facturacion@transportes.com",
                  "from_nif": "B12345678",
                  "from_social": "Transportes Logísticos SL",
                  "invoice_date": "2024-02-12T10:30:00.000Z",
                  "irpf": 0,
                  "is_bot": false,
                  "iva": 21,
                  "paid": true,
                  "paid_date": "2024-02-15T14:30:00.000Z",
                  "payment_method": "transferencia",
                  "serial_number": 240212001,
                  "tax_base": 1000,
                  "to_address": "Avenida Comercial 456, 08001 Barcelona, España",
                  "to_email": "clientes@distribuidor.com",
                  "to_nif": "B87654321",
                  "to_social": "Cliente Distribuidor SA",
                  "updatedAt": "2024-02-15T14:35:00.000Z"
                },
                "schema": {
                  "properties": {
                    "_id": {
                      "type": "string"
                    },
                    "createdAt": {
                      "format": "date-time",
                      "type": "string"
                    },
                    "expiration_date": {
                      "format": "date-time",
                      "type": "string"
                    },
                    "from_nif": {
                      "type": "string"
                    },
                    "from_social": {
                      "type": "string"
                    },
                    "invoice_date": {
                      "format": "date-time",
                      "type": "string"
                    },
                    "paid": {
                      "description": "Estado de pago actualizado",
                      "type": "boolean"
                    },
                    "paid_date": {
                      "description": "Fecha de pago.\n- Si paid=true: fecha del request body\n- Si paid=false: null\n",
                      "format": "date-time",
                      "nullable": true,
                      "type": "string"
                    },
                    "payment_method": {
                      "description": "Método de pago.\n- Si paid=true: valor del request body\n- Si paid=false: cadena vacía\n",
                      "nullable": true,
                      "type": "string"
                    },
                    "serial_number": {
                      "type": "integer"
                    },
                    "to_nif": {
                      "type": "string"
                    },
                    "to_social": {
                      "type": "string"
                    },
                    "updatedAt": {
                      "format": "date-time",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Detalles de pago actualizados exitosamente"
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "invoiceNotFound": {
                    "value": {
                      "message": "NOT_FOUND:{\"_id\":\"507f191e810c19729de860ea\"}"
                    }
                  },
                  "userNotFound": {
                    "value": {
                      "message": "USER_NOT_FOUND"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Factura no encontrada o usuario no encontrado.\n\nPosibles casos:\n- `NOT_FOUND`: El ID proporcionado no existe en la base de datos.\n- `USER_NOT_FOUND`: El token JWT no corresponde a ningún usuario válido.\n"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Update invoice payment details",
        "tags": [
          "invoices"
        ]
      }
    },
    "/company/invoices/{id}": {
      "get": {
        "deprecated": false,
        "description": "## Purpose\nRecupera los detalles completos de una factura específica por su ID. Este endpoint proporciona toda la información fiscal y de estado de una factura individual.\n\n## Objective\nPermitir la consulta detallada de facturas para visualización, verificación de estados de pago, y auditoría de transacciones específicas.\n\n## Use Cases\n- Visualizar detalles completos de una factura antes del pago\n- Verificar el estado actual de pago de una factura\n- Auditoría de transacciones específicas\n- Obtener datos para generar documentación adicional\n\n## Notes\n- El ID puede venir en el path parameter, query parameter o body (prioridad: body > query > params)\n- Solo se pueden consultar facturas propias\n",
        "parameters": [
          {
            "description": "ID único de la factura a consultar (24 caracteres hexadecimales)",
            "example": "507f191e810c19729de860ea",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "maxLength": 24,
              "minLength": 24,
              "pattern": "^[a-f0-9]{24}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "507f191e810c19729de860ea",
                  "bank_account": "ES00 0000 0000 0000 0000",
                  "client": "507f1f77bcf86cd799439011",
                  "createdAt": "2024-02-12T10:30:00.000Z",
                  "delivery": "507f1f77bcf86cd799439012",
                  "expiration_date": "2024-03-12T23:59:59.000Z",
                  "from_address": "Calle Principal 123, 28001 Madrid, España",
                  "from_email": "facturacion@transportes.com",
                  "from_nif": "B12345678",
                  "from_social": "Transportes Logísticos SL",
                  "invoice_date": "2024-02-12T10:30:00.000Z",
                  "irpf": 0,
                  "is_bot": false,
                  "iva": 21,
                  "paid": true,
                  "paid_date": "2024-02-15T14:20:00.000Z",
                  "payment_method": "transferencia",
                  "serial_number": 240212001,
                  "tax_base": 1000,
                  "to_address": "Avenida Comercial 456, 08001 Barcelona, España",
                  "to_email": "clientes@distribuidor.com",
                  "to_nif": "B87654321",
                  "to_social": "Cliente Distribuidor SA",
                  "updatedAt": "2024-02-15T14:20:00.000Z"
                },
                "schema": {
                  "properties": {
                    "_id": {
                      "description": "ID único de la factura",
                      "type": "string"
                    },
                    "bank_account": {
                      "description": "Cuenta bancaria",
                      "type": "string"
                    },
                    "client": {
                      "description": "ID del cliente (trucker_user)",
                      "type": "string"
                    },
                    "createdAt": {
                      "description": "Fecha de creación",
                      "format": "date-time",
                      "type": "string"
                    },
                    "delivery": {
                      "description": "ID del delivery asociado",
                      "nullable": true,
                      "type": "string"
                    },
                    "expiration_date": {
                      "description": "Fecha de vencimiento",
                      "format": "date-time",
                      "type": "string"
                    },
                    "from_address": {
                      "description": "Dirección del emisor",
                      "type": "string"
                    },
                    "from_email": {
                      "description": "Email del emisor",
                      "type": "string"
                    },
                    "from_nif": {
                      "description": "NIF/CIF del emisor",
                      "type": "string"
                    },
                    "from_social": {
                      "description": "Razón social del emisor",
                      "type": "string"
                    },
                    "invoice_date": {
                      "description": "Fecha de la factura",
                      "format": "date-time",
                      "type": "string"
                    },
                    "irpf": {
                      "description": "IRPF",
                      "type": "number"
                    },
                    "is_bot": {
                      "description": "Indica si es generada por bot",
                      "type": "boolean"
                    },
                    "iva": {
                      "description": "IVA",
                      "type": "number"
                    },
                    "paid": {
                      "description": "Estado de pago",
                      "type": "boolean"
                    },
                    "paid_date": {
                      "description": "Fecha de pago",
                      "format": "date-time",
                      "nullable": true,
                      "type": "string"
                    },
                    "payment_method": {
                      "description": "Método de pago",
                      "nullable": true,
                      "type": "string"
                    },
                    "serial_number": {
                      "description": "Número de serie",
                      "type": "integer"
                    },
                    "tax_base": {
                      "description": "Base imponible",
                      "type": "number"
                    },
                    "to_address": {
                      "description": "Dirección del receptor",
                      "type": "string"
                    },
                    "to_email": {
                      "description": "Email del receptor",
                      "type": "string"
                    },
                    "to_nif": {
                      "description": "NIF/CIF del receptor",
                      "type": "string"
                    },
                    "to_social": {
                      "description": "Razón social del receptor",
                      "type": "string"
                    },
                    "updatedAt": {
                      "description": "Fecha de última actualización",
                      "format": "date-time",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Detalles de factura recuperados exitosamente"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "CIA_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Compañía no encontrada (CIA_NOT_FOUND).\nEl token JWT no corresponde a ninguna compañía registrada.\n"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NOT_FOUND:{\"_id\":\"507f191e810c19729de860ea\"}"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Factura no encontrada (NOT_FOUND).\nPosibles causas:\n- El ID no existe en la base de datos\n- La factura pertenece a otra compañía\n"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get details of a specific invoice",
        "tags": [
          "invoices"
        ]
      }
    },
    "/company/issues/": {
      "get": {
        "deprecated": false,
        "description": "Obtiene un listado paginado de todas las incidencias registradas por la empresa.\nPermite filtrar por término de búsqueda y controlar la paginación.\n\n**Casos de uso:**\n- Visualizar el historial de incidencias\n- Buscar incidencias específicas por texto\n- Implementar paginación en el frontend\n\n**Lógica relacionada:** `ctrl.get()` en routes.js\n",
        "parameters": [
          {
            "description": "Número de página a recuperar. \nEjemplo: `1` para la primera página.\nPor defecto es 1.\n",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Cantidad máxima de incidencias por página.\nEjemplo: `10` para mostrar 10 incidencias por página.\nPor defecto es 20, máximo permitido es 100.\n",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Término de búsqueda para filtrar incidencias.\nBusca en los campos: código de servicio, código interno y mensajes.\nEjemplo: `\"problema entrega\"` buscará incidencias que contengan ese texto.\n",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "docs": [
                    {
                      "createdAt": "2023-06-25T10:30:00.000Z",
                      "files": [],
                      "id": "60d5ec9a3a5f8e001f3e4a1c",
                      "messages": [
                        {
                          "authorId": "60d5ec9a3a5f8e001f3e4a1b",
                          "authorName": "Juan Pérez",
                          "createdAt": "2023-06-25T10:30:00.000Z",
                          "files": [],
                          "message": "Problema con la entrega en almacén"
                        }
                      ],
                      "service_code": "DEL-123",
                      "status": "open",
                      "updatedAt": "2023-06-25T10:30:00.000Z"
                    }
                  ],
                  "limit": 20,
                  "page": 1,
                  "totalDocs": 15,
                  "totalPages": 1
                },
                "schema": {
                  "$ref": "#/components/schemas/IssueListResponse"
                }
              }
            },
            "description": "Listado paginado de incidencias.\nIncluye metadatos de paginación y array de incidencias.\n",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. El token JWT es inválido o ha expirado.\nIncluir token válido en el header Authorization.\n",
            "headers": {}
          },
          "404": {
            "description": "Empresa no encontrada. \nVerificar que el token JWT corresponde a una empresa registrada.\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "List support incidents/tickets",
        "tags": [
          "Issues"
        ]
      },
      "post": {
        "deprecated": false,
        "description": "Crea una nueva incidencia con posibilidad de adjuntar archivos.\nPermite hasta 6 archivos adjuntos (ver implementación en routes.js).\n\n**Casos de uso:**\n- Reportar problemas con entregas, subastas u otros servicios\n- Solicitar soporte técnico\n- Notificar incidencias en operaciones logísticas\n\n**Lógica relacionada:** `ctrl.create()` en routes.js con middleware multerS3\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "properties": {
                  "files": {
                    "description": "Archivos adjuntos (opcional).\nMáximo 6 archivos (configuración en routes.js).\nFormatos soportados: PDF, JPG, PNG, DOCX.\nTamaño máximo por archivo: 5MB.\n",
                    "items": {
                      "format": "binary",
                      "type": "string"
                    },
                    "type": "array"
                  },
                  "internal_code": {
                    "description": "Código interno de referencia (opcional).\nEjemplo: \"FACT-456\" para referenciar una factura.\n",
                    "example": "",
                    "type": "string"
                  },
                  "message": {
                    "description": "Mensaje descriptivo del problema (obligatorio).\nMínimo 20 caracteres, máximo 1000.\nEjemplo: \"El transportista no se presentó en el punto de recogida\"\n",
                    "example": "Esto es un problema",
                    "type": "string"
                  },
                  "relatedTo": {
                    "description": "Entidad relacionada (opcional).\nEjemplos: \"delivery\", \"auction\", \"invoice\".\n",
                    "example": "",
                    "type": "string"
                  },
                  "service_code": {
                    "description": "Código del servicio relacionado (obligatorio).\nEjemplo: \"DEL-123\" para una entrega.\nSe pueden obtener códigos válidos con GET /service_codes\n",
                    "example": "ACOPOLF9d9F",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "createdAt": "2023-06-25T10:30:00.000Z",
                  "files": [
                    "file1.pdf",
                    "file2.jpg"
                  ],
                  "id": "60d5ec9a3a5f8e001f3e4a1c",
                  "messages": [
                    {
                      "authorId": "60d5ec9a3a5f8e001f3e4a1b",
                      "authorName": "Juan Pérez",
                      "createdAt": "2023-06-25T10:30:00.000Z",
                      "files": [
                        "file1.pdf",
                        "file2.jpg"
                      ],
                      "message": "El transportista no se presentó en el punto de recogida"
                    }
                  ],
                  "service_code": "DEL-123",
                  "status": "open",
                  "updatedAt": "2023-06-25T10:30:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/IssueResponse"
                }
              }
            },
            "description": "Incidencia creada exitosamente.\nDevuelve los detalles de la incidencia recién creada.\n",
            "headers": {}
          },
          "400": {
            "description": "Datos inválidos. Posibles causas:\n- Faltan campos obligatorios (service_code, message)\n- Mensaje demasiado corto/largo\n- Demasiados archivos adjuntos (>6)\n- Tipo/tamaño de archivo no permitido\n",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. El usuario autenticado no tiene una empresa asociada.\n\n**CIA_NOT_FOUND**:\n- El usuario autenticado no tiene una empresa asignada\n- La empresa fue desactivada o eliminada\n",
            "headers": {}
          },
          "404": {
            "description": "Usuario no encontrado. El token JWT no corresponde a un usuario válido en el sistema.\n\n**USER_NOT_FOUND**:\n- El token contiene un ID de usuario que no existe\n- El usuario fue eliminado después de generar el token\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Create new support incident/ticket",
        "tags": [
          "Issues"
        ]
      }
    },
    "/company/issues/motives": {
      "get": {
        "deprecated": false,
        "description": "Devuelve la lista de motivos predefinidos para clasificar incidencias.\nEstos motivos ayudan a categorizar y priorizar los tickets de soporte.\n\n**Casos de uso:**\n- Mostrar dropdown de selección al crear/modificar incidencias\n- Filtrar incidencias por motivo\n- Generar reportes estadísticos por tipo de problema\n\n**Lógica relacionada:** `cInfo.getReasons()` en routes.js\n",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "motives": [
                    "Problema con transportista",
                    "Retraso en entrega",
                    "Daño en mercancía",
                    "Error en facturación",
                    "Problema técnico"
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/MotivesResponse"
                }
              }
            },
            "description": "Lista de motivos disponibles.\nArray de strings donde cada elemento es un motivo válido.\n",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. El token JWT es inválido o ha expirado.\nIncluir token válido en el header Authorization.\n",
            "headers": {}
          },
          "404": {
            "description": "Usuario no encontrado. El token JWT no corresponde a un usuario válido en el sistema.\n\n**USER_NOT_FOUND**:\n- El token contiene un ID de usuario que no existe\n- El usuario fue eliminado después de generar el token\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get reasons for incidents",
        "tags": [
          "Issues"
        ]
      }
    },
    "/company/issues/service_codes": {
      "get": {
        "deprecated": false,
        "description": "Devuelve la lista de códigos de servicio válidos para crear incidencias.\nEstos códigos identifican los diferentes tipos de servicios logísticos.\n\n**Casos de uso:**\n- Mostrar dropdown de selección al crear incidencias\n- Validar códigos antes de enviar formularios\n\n**Lógica relacionada:** `cInfo.getCodes()` en routes.js\n",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "codes": [
                    "DEL-001",
                    "DEL-002",
                    "AUC-100",
                    "INV-500"
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/ServiceCodesResponse"
                }
              }
            },
            "description": "Lista de códigos de servicio disponibles.\nArray de strings donde cada elemento es un código válido.\n",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. El token JWT es inválido o ha expirado.\nIncluir token válido en el header Authorization.\n",
            "headers": {}
          },
          "404": {
            "description": "Usuario no encontrado. El token JWT no corresponde a un usuario válido en el sistema.\n\n**USER_NOT_FOUND**:\n- El token contiene un ID de usuario que no existe\n- El usuario fue eliminado después de generar el token\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get available service codes",
        "tags": [
          "Issues"
        ]
      }
    },
    "/company/issues/{id}": {
      "delete": {
        "deprecated": false,
        "description": "Elimina permanentemente una incidencia y todos sus datos asociados.\nEsta acción es irreversible y debe usarse con precaución.\n\n**Casos de uso:**\n- Eliminar tickets creados por error\n- Limpieza de incidencias resueltas (según políticas de retención)\n\n**Lógica relacionada:** `ctrl.delete()` en routes.js\n",
        "parameters": [
          {
            "description": "ID único de la incidencia a eliminar.\nEjemplo: \"60d5ec9a3a5f8e001f3e4a1c\"\n",
            "example": "68f7718862055e63347a58ee",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "60d5ec9a3a5f8e001f3e4a1c"
                },
                "schema": {
                  "properties": {
                    "_id": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Incidencia eliminada exitosamente.\nDevuelve el ID de la incidencia eliminada.\n",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. El token JWT es inválido o ha expirado.\nIncluir token válido en el header Authorization.\n",
            "headers": {}
          },
          "404": {
            "description": "Incidencia no encontrada.\nVerificar que el ID proporcionado es correcto y pertenece a la empresa.\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Delete an incident",
        "tags": [
          "Issues"
        ]
      },
      "get": {
        "deprecated": false,
        "description": "Devuelve todos los detalles de una incidencia específica, incluyendo:\n- Información básica (códigos, estado)\n- Historial completo de mensajes\n- Archivos adjuntos\n\n**Casos de uso:**\n- Visualizar el detalle completo de un ticket\n- Mostrar el historial de conversaciones\n- Acceder a archivos adjuntos\n\n**Lógica relacionada:** `ctrl.getIssue()` en routes.js\n",
        "parameters": [
          {
            "description": "ID único de la incidencia.\nEjemplo: \"60d5ec9a3a5f8e001f3e4a1c\"\n",
            "example": "68f765ce12479dbd7a4e9712",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "createdAt": "2023-06-25T10:30:00.000Z",
                  "files": [
                    "informe.pdf"
                  ],
                  "id": "60d5ec9a3a5f8e001f3e4a1c",
                  "messages": [
                    {
                      "authorId": "60d5ec9a3a5f8e001f3e4a1b",
                      "authorName": "Juan Pérez",
                      "createdAt": "2023-06-25T10:30:00.000Z",
                      "files": [],
                      "message": "Problema con la entrega en almacén"
                    },
                    {
                      "authorId": "60d5ec9a3a5f8e001f3e4a1a",
                      "authorName": "Soporte Cargoffer",
                      "createdAt": "2023-06-25T11:15:00.000Z",
                      "files": [
                        "informe.pdf"
                      ],
                      "message": "Hemos contactado al transportista"
                    }
                  ],
                  "service_code": "DEL-123",
                  "status": "in_progress",
                  "updatedAt": "2023-06-25T11:15:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/IssueResponse"
                }
              }
            },
            "description": "Detalles completos de la incidencia solicitada.\nIncluye información básica, mensajes y archivos.\n",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. El token JWT es inválido o ha expirado.\nIncluir token válido en el header Authorization.\n",
            "headers": {}
          },
          "404": {
            "description": "Incidencia no encontrada.\nVerificar que el ID proporcionado es correcto y pertenece a la empresa.\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get incident details",
        "tags": [
          "Issues"
        ]
      },
      "post": {
        "deprecated": false,
        "description": "Añade un nuevo mensaje y/o archivos a una incidencia existente.\nPermite hasta 2 archivos adjuntos (ver implementación en routes.js).\n\n**Casos de uso:**\n- Responder a un ticket de soporte\n- Proporcionar información adicional sobre una incidencia\n- Adjuntar documentos relevantes (facturas, fotos, etc.)\n\n**Lógica relacionada:** `ctrl.saveMessage()` en routes.js con middleware multerS3\n",
        "parameters": [
          {
            "description": "ID único de la incidencia a actualizar.\nEjemplo: \"60d5ec9a3a5f8e001f3e4a1c\"\n",
            "example": "68f7718862055e63347a58ee",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "properties": {
                  "files": {
                    "description": "Archivos adjuntos (opcional).\nMáximo 2 archivos (configuración en routes.js).\nFormatos soportados: PDF, JPG, PNG, DOCX.\nTamaño máximo por archivo: 5MB.\n",
                    "items": {
                      "format": "binary",
                      "type": "string"
                    },
                    "type": "array"
                  },
                  "message": {
                    "description": "Contenido del mensaje (obligatorio).\nMínimo 10 caracteres, máximo 1000.\nEjemplo: \"Adjunto fotos del daño reportado\"\n",
                    "example": "Mensaje",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "createdAt": "2023-06-25T10:30:00.000Z",
                  "files": [
                    "foto1.jpg",
                    "foto2.jpg"
                  ],
                  "id": "60d5ec9a3a5f8e001f3e4a1c",
                  "messages": [
                    {
                      "authorId": "60d5ec9a3a5f8e001f3e4a1b",
                      "authorName": "Juan Pérez",
                      "createdAt": "2023-06-25T10:30:00.000Z",
                      "files": [],
                      "message": "Problema con la entrega en almacén"
                    },
                    {
                      "authorId": "60d5ec9a3a5f8e001f3e4a1a",
                      "authorName": "Soporte Cargoffer",
                      "createdAt": "2023-06-25T11:45:00.000Z",
                      "files": [
                        "foto1.jpg",
                        "foto2.jpg"
                      ],
                      "message": "Adjunto fotos del daño reportado"
                    }
                  ],
                  "service_code": "DEL-123",
                  "status": "in_progress",
                  "updatedAt": "2023-06-25T11:45:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/IssueResponse"
                }
              }
            },
            "description": "Incidencia actualizada exitosamente.\nDevuelve los detalles completos de la incidencia con el nuevo mensaje.\n",
            "headers": {}
          },
          "400": {
            "description": "Datos inválidos. Posibles causas:\n- Mensaje faltante o demasiado corto/largo\n- Demasiados archivos adjuntos (>2)\n- Tipo/tamaño de archivo no permitido\n",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. El token JWT es inválido o ha expirado.\nIncluir token válido en el header Authorization.\n",
            "headers": {}
          },
          "404": {
            "description": "Incidencia no encontrada.\nVerificar que el ID proporcionado es correcto y pertenece a la empresa.\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Add message to an issue",
        "tags": [
          "Issues"
        ]
      }
    },
    "/company/issues/{id}/resolve": {
      "post": {
        "deprecated": false,
        "description": "Cambia el estado de una incidencia a \"resuelta\".\nEsta acción registra la fecha/hora UTC actual como momento de resolución.\n\n**Casos de uso:**\n- Cerrar tickets cuando se ha solucionado el problema\n- Actualizar el estado de incidencias en flujos de trabajo\n- Generar métricas de tiempo de resolución\n\n**Lógica relacionada:** `ctrl.setResolved()` en routes.js con middleware checkUTC\n",
        "parameters": [
          {
            "description": "ID único de la incidencia a marcar como resuelta.\nEjemplo: \"60d5ec9a3a5f8e001f3e4a1c\"\n",
            "example": "68f7737a62055e63347a5d4b",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "createdAt": "2023-06-25T10:30:00.000Z",
                  "files": [],
                  "id": "60d5ec9a3a5f8e001f3e4a1c",
                  "messages": [
                    {
                      "authorId": "60d5ec9a3a5f8e001f3e4a1b",
                      "authorName": "Juan Pérez",
                      "createdAt": "2023-06-25T10:30:00.000Z",
                      "files": [],
                      "message": "Problema con la entrega en almacén"
                    },
                    {
                      "authorId": "60d5ec9a3a5f8e001f3e4a1a",
                      "authorName": "Soporte Cargoffer",
                      "createdAt": "2023-06-25T12:00:00.000Z",
                      "files": [],
                      "message": "Incidencia resuelta"
                    }
                  ],
                  "service_code": "DEL-123",
                  "status": "resolved",
                  "updatedAt": "2023-06-25T12:00:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/IssueResponse"
                }
              }
            },
            "description": "Incidencia marcada como resuelta exitosamente.\nDevuelve los detalles actualizados de la incidencia.\n",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. El token JWT es inválido o ha expirado.\nIncluir token válido en el header Authorization.\n",
            "headers": {}
          },
          "404": {
            "description": "Incidencia no encontrada.\nVerificar que el ID proporcionado es correcto y pertenece a la empresa.\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Mark incident as resolved",
        "tags": [
          "Issues"
        ]
      }
    },
    "/company/minimal": {
      "get": {
        "description": "## Propósito\nCalcula el precio mínimo viable para el transporte de mercancías entre dos puntos,\nincorporando todos los costes operativos, comisiones de plataforma y márgenes de beneficio.\nEs el endpoint de pricing principal utilizado en la plataforma.\n\n## Objetivo\nProporcionar un precio mínimo preciso que cubra todos los costes operativos manteniendo\ntarifas competitivas para los servicios de transporte de mercancías.\n\n## Casos de Uso\n- Establecer importes mínimos de puja en la creación de subastas\n- Validar si el precio de una puja es económicamente viable\n- Calcular precios suelo para contratos de transporte\n- Soporte para algoritmos de precios dinámicos\n- Generar estimaciones de precio para clientes\n- Evitar precios por debajo del coste en pujas competitivas\n\n## Algoritmo de Precio\nEl cálculo del precio mínimo sigue esta fórmula:\n1. **Coste Base**: Coste bruto de la API de enrutamiento externo (combustible, mantenimiento, tiempo)\n2. **Factor Aleatorio**: `settings.pricing.today_random` % de variación sobre el coste base\n3. **Margen de Ganancia**: `settings.pricing.general_gain` % añadido sobre el coste\n4. **Comisión de Plataforma**: `settings.pricing.fee` % (normalmente 3%)\n5. **Margen de Seguridad**: 5% adicional sobre el total\n6. **Precio por Volumen**: `(volumen / 30) * costeTotal` — proporcional al volumen de carga\n7. **Precio por Peso**: `(peso / 44000) * costeTotal` — solo se aplica cuando peso >= 22.000 kg (50%)\n\nPrecio final = `max(precioPorVolumen, precioPorPeso, costeTotal/2)` con un mínimo de 100€\nConsumo medio de combustible utilizado: 32L/100km\n\n## Parámetros de Ubicación Requeridos\n**Es obligatorio proporcionar al menos uno de estos pares de ubicación:**\n\n| Par | Parámetros | Prioridad |\n|-----|-----------|-----------|\n| IDs de dirección | `idEtl` + `idEtd` | 1ª (máxima) |\n| Códigos postales | `zipcodeStart` + `zipcodeEnd` | 2ª |\n| Coordenadas GPS | `coordsStart` + `coordsEnd` | 3ª |\n\nSi no se proporciona ningún par, la respuesta será `400 INVALID_PARAMETERS`.\n\n## Reglas de Validación\n- Volumen máximo: 30 m³ (se limita si se supera)\n- Peso máximo: 44.000 kg (se limita si se supera)\n- Precio mínimo: 100€ (evita precios cero o negativos)\n- Mismo origen y destino devuelve valores mínimos (1 km, 0€)\n- Si `volume` no se proporciona, se aplica el factor de precio al 100%\n\n## Respuestas de Error\n- 400: No se proporcionó ningún identificador de ubicación (`INVALID_PARAMETERS`)\n- 404: Direcciones no encontradas\n- 500: Error del servicio externo o configuración de precios no encontrada\n",
        "parameters": [
          {
            "description": "ID de la dirección de origen en la base de datos (MongoDB ObjectId, 24 hex).\nPrioridad máxima. **Debe usarse junto con `idEtd`.**\nSi se proporciona, `zipcodeStart` y `coordsStart` son ignorados.\n",
            "in": "query",
            "name": "idEtl",
            "required": false,
            "schema": {
              "example": "507f1f77bcf86cd799439012",
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "ID de la dirección de destino en la base de datos (MongoDB ObjectId, 24 hex).\n**Debe usarse junto con `idEtl`.**\n",
            "in": "query",
            "name": "idEtd",
            "required": false,
            "schema": {
              "example": "507f1f77bcf86cd799439013",
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "Código postal de origen. **Debe usarse junto con `zipcodeEnd`.**\nSe usa cuando no se proporciona `idEtl`.\n",
            "in": "query",
            "name": "zipcodeStart",
            "required": false,
            "schema": {
              "example": "28001",
              "maxLength": 10,
              "minLength": 3,
              "type": "string"
            }
          },
          {
            "description": "Código postal de destino. **Debe usarse junto con `zipcodeStart`.**\nSe usa cuando no se proporciona `idEtd`.\n",
            "in": "query",
            "name": "zipcodeEnd",
            "required": false,
            "schema": {
              "example": "08001",
              "maxLength": 10,
              "minLength": 3,
              "type": "string"
            }
          },
          {
            "description": "Coordenadas GPS de origen en formato `\"latitud,longitud\"`.\n**Debe usarse junto con `coordsEnd`.**\nBusca la dirección más cercana en un radio de 100 km.\nSe usa cuando no se proporcionan `idEtl` ni `zipcodeStart`.\n",
            "in": "query",
            "name": "coordsStart",
            "required": false,
            "schema": {
              "example": "40.41678,-3.70379",
              "pattern": "^-?\\d+\\.?\\d+,-?\\d+\\.?\\d+$",
              "type": "string"
            }
          },
          {
            "description": "Coordenadas GPS de destino en formato `\"latitud,longitud\"`.\n**Debe usarse junto con `coordsStart`.**\nBusca la dirección más cercana en un radio de 100 km.\n",
            "in": "query",
            "name": "coordsEnd",
            "required": false,
            "schema": {
              "example": "41.38506,2.17340",
              "pattern": "^-?\\d+\\.?\\d+,-?\\d+\\.?\\d+$",
              "type": "string"
            }
          },
          {
            "description": "Volumen de la carga en metros cúbicos (m³). Entero positivo.\n- Máximo: 30 m³ (valores superiores se limitan a 30).\n- Si no se proporciona, se aplica el factor de precio al 100% (precio máximo).\n- El precio se calcula proporcionalmente: `(volume / 30) * totalCost`.\n",
            "in": "query",
            "name": "volume",
            "required": false,
            "schema": {
              "example": 15,
              "maximum": 30,
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "description": "Peso de la carga en kilogramos (kg). Entero positivo.\n- Máximo: 44.000 kg (valores superiores se limitan a 44.000).\n- El precio por peso **solo se aplica cuando el peso >= 22.000 kg** (50% del máximo).\n- Si no se proporciona o es 0, no se aplica precio por peso.\n",
            "in": "query",
            "name": "weight",
            "required": false,
            "schema": {
              "example": 5000,
              "maximum": 44000,
              "minimum": 0,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "arrival": "2026-02-12T14:22:00Z",
                  "costs": {
                    "aire": 10.4,
                    "clutch": 1.3,
                    "correa": 8.3,
                    "fuel": {
                      "fuel": 0.0035,
                      "monetary": 187.5
                    },
                    "monetaryCost": 231.1,
                    "oil": 3.12,
                    "toll": 15.5,
                    "total_cost": 231.1,
                    "volumePrice": 348.25,
                    "weightPrice": 0,
                    "wheels": 4.98
                  },
                  "departure": "2026-02-12T08:00:00Z",
                  "distance": 623500,
                  "fuelCost": 187.5,
                  "fuelPrice": 1.65,
                  "monetary": 450.75,
                  "otherCosts": {
                    "aire": 10.4,
                    "clutch": 1.3,
                    "correa": 8.3,
                    "fuel": {
                      "fuel": 0.0035,
                      "monetary": 187.5
                    },
                    "monetaryCost": 231.1,
                    "oil": 3.12,
                    "toll": 15.5,
                    "total_cost": 231.1,
                    "volumePrice": 348.25,
                    "weightPrice": 0,
                    "wheels": 4.98
                  },
                  "timeCost": 382,
                  "tolls": [
                    {
                      "cost": 15.5,
                      "location": "41.385,2.174",
                      "name": "AP-7"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/MinimalPrice"
                }
              }
            },
            "description": "Precio mínimo calculado correctamente"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "message": "INVALID_PARAMETERS",
                  "status": 400
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No se proporcionó ningún identificador de ubicación.\nOcurre cuando la petición no incluye ninguno de: `idEtl`, `idEtd`,\n`zipcodeStart`, `zipcodeEnd`, `coordsStart`, `coordsEnd`.\n"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NOT_FOUND",
                  "status": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Direcciones no encontradas"
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "message": "INTERNAL_SERVER_ERROR",
                  "status": 500
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error del servicio externo o configuración de precios no encontrada"
          }
        },
        "security": [],
        "summary": "Calculate minimum price for freight transport",
        "tags": [
          "minimal",
          "pricing"
        ]
      }
    },
    "/company/minimal/bid-auction": {
      "get": {
        "description": "## Propósito\nObtiene la información de peajes de una subasta existente usando su MongoDB ObjectId (`_id`).\nProporciona datos detallados de peajes para la ruta específica asociada a la subasta.\n\n## Objetivo\nPermitir el acceso rápido a la información de peajes de mercancías subastadas, facilitando\nel desglose transparente de costes y la validación de pujas.\n\n## Casos de Uso\n- Mostrar los costes de peajes en el detalle de subastas para transportistas\n- Validar importes de pujas frente a los costes de peajes\n- Proporcionar desglose de costes en resúmenes de subastas\n- Soporte para cálculos de coste total en decisiones de puja\n- Generar informes de costes para subastas finalizadas\n\n## Flujo del Proceso\n1. Consultar la subasta por MongoDB `_id` (ObjectId)\n2. Poblar las direcciones de recogida (etl_address) y entrega (etd_address)\n3. Extraer las coordenadas GPS del campo de ubicación de la dirección\n4. Consultar la API externa de peajes para la ruta\n5. Devolver los datos de peajes con coordenadas de inicio/fin y costes\n\n## Información de la Respuesta\n- **start**: Coordenadas GPS de origen (lat, lng)\n- **end**: Coordenadas GPS de destino (lat, lng)\n- **tolls**: Array con la información de peajes de la ruta\n- **tollCost**: Coste total de todos los peajes en EUR\n\n## Respuestas de Error\n- 400: ID de subasta faltante o inválido\n- 404: Subasta con el ID indicado no encontrada\n- 500: Error del servicio de peajes externo o coordenadas de dirección faltantes\n",
        "parameters": [
          {
            "description": "MongoDB ObjectId de la subasta (`_id`).\nSe usa para buscar la subasta y obtener sus direcciones de recogida y entrega.\nDebe ser un ObjectId hexadecimal válido de 24 caracteres.\n",
            "in": "query",
            "name": "id",
            "required": true,
            "schema": {
              "example": "507f1f77bcf86cd799439011",
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "end": {
                    "lat": 41.38506,
                    "lng": 2.1734
                  },
                  "start": {
                    "lat": 40.41678,
                    "lng": -3.70379
                  },
                  "tollCost": 15.5,
                  "tolls": [
                    {
                      "cost": 15.5,
                      "entry": "28 Tarragona",
                      "exit": "31 Barcelona",
                      "location": {
                        "lat": 41.385,
                        "lng": 2.174
                      },
                      "name": "AP-7"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/BidAuctionTolls"
                }
              }
            },
            "description": "Costes de peajes obtenidos correctamente"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Auction ID is required",
                  "message": "INVALID_PARAMETERS"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Parámetro ID de subasta faltante o inválido"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Auction not found",
                  "message": "NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Subasta no encontrada"
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Toll service unavailable",
                  "message": "INTERNAL_SERVER_ERROR"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error del servicio de peajes externo"
          }
        },
        "security": [],
        "summary": "Get toll costs for an auction by MongoDB ObjectId",
        "tags": [
          "minimal",
          "tolls",
          "auctions"
        ]
      }
    },
    "/company/minimal/co2": {
      "get": {
        "description": "## Propósito\nCalcula las emisiones de dióxido de carbono (CO2) para el transporte de mercancías\nentre un punto de origen y un punto de destino. Permite evaluar el impacto medioambiental\nde las operaciones logísticas.\n\n## Objetivo\nProporcionar cálculos precisos de emisiones de CO2 para apoyar iniciativas de sostenibilidad,\ninformes medioambientales y optimización de rutas ecológicas en el transporte de mercancías.\n\n## Casos de Uso\n- Generar informes de impacto medioambiental para operaciones de transporte\n- Comparar emisiones de CO2 entre distintas opciones de ruta\n- Seguimiento de la huella de carbono en dashboards de sostenibilidad corporativa\n- Optimizar rutas para minimizar el impacto ambiental\n- Cumplir con normativas medioambientales y requisitos de reporte\n\n## Método de Cálculo\nEl cálculo de CO2 tiene en cuenta:\n- Distancia total de la ruta (en kilómetros)\n- Peso y volumen de la carga\n- Tipo de vehículo y consumo medio de combustible (32L/100km — fijo para el cálculo de CO2)\n- Tipo de combustible y factores de emisión\n- Nota: los peajes NO están incluidos en el cálculo de CO2\n\n## Prioridad de Parámetros\nEl endpoint acepta identificadores de ubicación en múltiples formatos. Orden de prioridad:\n1. **id** (auction_id): Usa las direcciones de recogida/entrega de la subasta\n2. **idEtl/idEtd**: Usa direcciones por ID de base de datos\n3. **zipcodeStart/zipcodeEnd**: Usa códigos postales para encontrar la dirección más cercana\n4. **coordsStart/coordsEnd**: Usa coordenadas GPS (formato lat,lng)\n5. **countryOrigin/countryDestiny**: Códigos de país para el enrutamiento\n\nSe debe proporcionar al menos un par de identificadores de ubicación.\n\n## Respuestas de Error\n- 400: Parámetros inválidos, campos requeridos faltantes o combinaciones de identificadores incompatibles\n- 404: Subasta o direcciones no encontradas\n- 500: Error del servicio de enrutamiento externo\n",
        "parameters": [
          {
            "description": "ID de la subasta (ObjectId). Cuando se proporciona, usa las direcciones de recogida\ny entrega asociadas a la subasta. Tiene mayor prioridad sobre el resto de parámetros\nde ubicación.\n",
            "in": "query",
            "name": "id",
            "required": false,
            "schema": {
              "example": "507f1f77bcf86cd799439011",
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "ID de la dirección de origen en la base de datos. Se usa para obtener las coordenadas\ny el país del punto de recogida. Debe usarse junto con idEtd.\n",
            "in": "query",
            "name": "idEtl",
            "required": false,
            "schema": {
              "example": "507f1f77bcf86cd799439012",
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "ID de la dirección de destino en la base de datos. Se usa para obtener las coordenadas\ny el país del punto de entrega. Debe usarse junto con idEtl.\n",
            "in": "query",
            "name": "idEtd",
            "required": false,
            "schema": {
              "example": "507f1f77bcf86cd799439013",
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "Código postal de origen. El sistema busca direcciones que coincidan con este código postal.\nAlternativa a idEtl/coordsStart.\n",
            "in": "query",
            "name": "zipcodeStart",
            "required": false,
            "schema": {
              "example": "28001",
              "maxLength": 10,
              "minLength": 3,
              "type": "string"
            }
          },
          {
            "description": "Código postal de destino. El sistema busca direcciones que coincidan con este código postal.\nAlternativa a idEtd/coordsEnd.\n",
            "in": "query",
            "name": "zipcodeEnd",
            "required": false,
            "schema": {
              "example": "08001",
              "maxLength": 10,
              "minLength": 3,
              "type": "string"
            }
          },
          {
            "description": "Coordenadas GPS de origen en formato \"latitud,longitud\".\nBusca la dirección más cercana en un radio de 100 km.\nAlternativa a idEtl/zipcodeStart.\n",
            "in": "query",
            "name": "coordsStart",
            "required": false,
            "schema": {
              "example": "40.41678,-3.70379",
              "pattern": "^-?\\d+\\.?\\d+,-?\\d+\\.?\\d+$",
              "type": "string"
            }
          },
          {
            "description": "Coordenadas GPS de destino en formato \"latitud,longitud\".\nBusca la dirección más cercana en un radio de 100 km.\nAlternativa a idEtd/zipcodeEnd.\n",
            "in": "query",
            "name": "coordsEnd",
            "required": false,
            "schema": {
              "example": "41.38506,2.17340",
              "pattern": "^-?\\d+\\.?\\d+,-?\\d+\\.?\\d+$",
              "type": "string"
            }
          },
          {
            "description": "Código de país ISO de origen (ej: 'es', 'fr', 'pt').\nSe usa cuando la dirección no tiene información de país.\n",
            "in": "query",
            "name": "countryOrigin",
            "required": false,
            "schema": {
              "example": "es",
              "maxLength": 2,
              "minLength": 2,
              "pattern": "^[a-z]{2}$",
              "type": "string"
            }
          },
          {
            "description": "Código de país ISO de destino (ej: 'es', 'fr', 'pt').\nSe usa cuando la dirección no tiene información de país.\n",
            "in": "query",
            "name": "countryDestiny",
            "required": false,
            "schema": {
              "example": "es",
              "maxLength": 2,
              "minLength": 2,
              "pattern": "^[a-z]{2}$",
              "type": "string"
            }
          },
          {
            "description": "Volumen de la carga en metros cúbicos (m³). Afecta al cálculo de CO2.\nValor máximo: 30 m³. Los valores superiores se limitan a 30.\n",
            "in": "query",
            "name": "volume",
            "required": false,
            "schema": {
              "example": 12.5,
              "maximum": 30,
              "minimum": 0,
              "type": "number"
            }
          },
          {
            "description": "Peso de la carga en kilogramos (kg). Factor clave en el cálculo de emisiones de CO2.\nValor máximo: 44.000 kg. Los valores superiores se limitan a 44.000.\n",
            "in": "query",
            "name": "weight",
            "required": false,
            "schema": {
              "example": 1500,
              "maximum": 44000,
              "minimum": 0,
              "type": "number"
            }
          },
          {
            "description": "Incluir las coordenadas de los puntos intermedios de la ruta en la respuesta\npara su visualización en mapa.\n",
            "in": "query",
            "name": "with_points",
            "required": false,
            "schema": {
              "example": false,
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "co2": 125.7,
                  "distance": 623.5,
                  "unit": "kg"
                },
                "schema": {
                  "$ref": "#/components/schemas/RouteCO2"
                }
              }
            },
            "description": "Cálculo de CO2 completado correctamente"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Missing required location identifiers",
                  "message": "INVALID_PARAMETERS"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Parámetros de la petición inválidos"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Auction not found",
                  "message": "NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Subasta o direcciones no encontradas"
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Routing service unavailable",
                  "message": "INTERNAL_SERVER_ERROR"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error del servicio de enrutamiento externo"
          }
        },
        "security": [],
        "summary": "Calculate CO2 emissions for a transport route",
        "tags": [
          "minimal",
          "environment"
        ]
      }
    },
    "/company/minimal/costs": {
      "get": {
        "description": "## Propósito\nCalcula los costes brutos del transporte de mercancías entre origen y destino\nutilizando la API externa de enrutamiento CargoRutas. Devuelve un desglose detallado\nde costes sin aplicar márgenes de plataforma ni ajustes de precio.\n\n## Objetivo\nProporcionar datos de coste bruto precisos para el transporte de mercancías, útiles\npara análisis de costes internos, cálculo de márgenes y comparación de rutas.\n\n## Casos de Uso\n- Analizar componentes de coste bruto sin comisiones de plataforma\n- Comparar costes entre distintas opciones de ruta\n- Análisis de costes internos y cálculo de margen\n- Soporte para decisiones de precios basadas en costes\n\n## Componentes de Coste Devueltos\n- **Combustible**: Basado en distancia y consumo medio (35L/100km por defecto)\n- **Peajes**: Cuando `with_tolls=true`, incluye los costes de peajes de autopista\n- **Mantenimiento**: Aceite, ruedas/neumáticos, embrague, filtro de aire, correa\n- **Tiempo**: Duración estimada del trayecto en minutos\n- **Monetario**: Coste total bruto en EUR\n\n## Nota\nEste endpoint devuelve costes brutos de la API de enrutamiento sin aplicar los márgenes\nde la plataforma (ganancia, comisión, factor aleatorio). Usa `GET /company/minimal`\npara obtener el precio mínimo de subasta con todos los márgenes aplicados.\n\n## Prioridad de Parámetros\nPrioridad de identificadores de ubicación:\n1. **id** (auction_id): Usa las direcciones de recogida/entrega de la subasta\n2. **idEtl/idEtd**: IDs de direcciones en base de datos\n3. **zipcodeStart/zipcodeEnd**: Búsqueda por código postal\n4. **coordsStart/coordsEnd**: Coordenadas GPS\n5. **countryOrigin/countryDestiny**: Códigos de país (fallback)\n\n## Respuestas de Error\n- 400: Parámetros inválidos o identificadores de ubicación faltantes\n- 404: Subasta o direcciones no encontradas\n- 500: Error del servicio de enrutamiento externo\n",
        "parameters": [
          {
            "description": "ID de la subasta (ObjectId). Usa las direcciones de recogida/entrega de la subasta.\nOpcional — omitir para usar otros parámetros de ubicación.\n",
            "in": "query",
            "name": "id",
            "required": false,
            "schema": {
              "example": "507f1f77bcf86cd799439011",
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "ID de la dirección de origen en la base de datos. Usar junto con idEtd.\n",
            "in": "query",
            "name": "idEtl",
            "required": false,
            "schema": {
              "example": "507f1f77bcf86cd799439012",
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "ID de la dirección de destino en la base de datos. Usar junto con idEtl.\n",
            "in": "query",
            "name": "idEtd",
            "required": false,
            "schema": {
              "example": "507f1f77bcf86cd799439013",
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "Código postal de origen. Alternativa a idEtl/coordsStart.\n",
            "in": "query",
            "name": "zipcodeStart",
            "required": false,
            "schema": {
              "example": "28001",
              "maxLength": 10,
              "minLength": 3,
              "type": "string"
            }
          },
          {
            "description": "Código postal de destino. Alternativa a idEtd/coordsEnd.\n",
            "in": "query",
            "name": "zipcodeEnd",
            "required": false,
            "schema": {
              "example": "08001",
              "maxLength": 10,
              "minLength": 3,
              "type": "string"
            }
          },
          {
            "description": "Coordenadas GPS de origen (lat,lng). Alternativa a idEtl/zipcodeStart.\n",
            "in": "query",
            "name": "coordsStart",
            "required": false,
            "schema": {
              "example": "40.41678,-3.70379",
              "pattern": "^-?\\d+\\.?\\d+,-?\\d+\\.?\\d+$",
              "type": "string"
            }
          },
          {
            "description": "Coordenadas GPS de destino (lat,lng). Alternativa a idEtd/zipcodeEnd.\n",
            "in": "query",
            "name": "coordsEnd",
            "required": false,
            "schema": {
              "example": "41.38506,2.17340",
              "pattern": "^-?\\d+\\.?\\d+,-?\\d+\\.?\\d+$",
              "type": "string"
            }
          },
          {
            "description": "Código de país ISO de origen (ej: 'es', 'fr', 'de').\n",
            "in": "query",
            "name": "countryOrigin",
            "required": false,
            "schema": {
              "example": "es",
              "maxLength": 2,
              "minLength": 2,
              "pattern": "^[a-z]{2}$",
              "type": "string"
            }
          },
          {
            "description": "Código de país ISO de destino (ej: 'es', 'fr', 'de').\n",
            "in": "query",
            "name": "countryDestiny",
            "required": false,
            "schema": {
              "example": "es",
              "maxLength": 2,
              "minLength": 2,
              "pattern": "^[a-z]{2}$",
              "type": "string"
            }
          },
          {
            "description": "Volumen de la carga en metros cúbicos (m³). Máximo: 30 m³.\nAfecta al precio de forma proporcional.\n",
            "in": "query",
            "name": "volume",
            "required": false,
            "schema": {
              "example": 15,
              "maximum": 30,
              "minimum": 0,
              "type": "number"
            }
          },
          {
            "description": "Peso de la carga en kilogramos (kg). Máximo: 44.000 kg.\nEl precio por peso se aplica cuando el peso es >= 50% del máximo.\n",
            "in": "query",
            "name": "weight",
            "required": false,
            "schema": {
              "example": 5000,
              "maximum": 44000,
              "minimum": 0,
              "type": "number"
            }
          },
          {
            "description": "Incluir los costes de peajes en el cálculo. Cuando es `true`, consulta los datos\nde peajes de la ruta e incluye `tollCost` en la respuesta.\n",
            "in": "query",
            "name": "with_tolls",
            "required": false,
            "schema": {
              "example": true,
              "type": "boolean"
            }
          },
          {
            "description": "Incluir las coordenadas de los puntos intermedios en la respuesta para\nsu visualización en mapa.\n",
            "in": "query",
            "name": "with_points",
            "required": false,
            "schema": {
              "example": false,
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "arrival": "2026-02-12T14:22:00Z",
                  "costs": {
                    "aire": 10.4,
                    "clutch": 1.3,
                    "correa": 8.3,
                    "fuel": {
                      "fuel": 0.0035,
                      "monetary": 187.5
                    },
                    "oil": 3.12,
                    "toll": 15.5,
                    "total_cost": 231.1,
                    "wheels": 4.98
                  },
                  "departure": "2026-02-12T08:00:00Z",
                  "distance": 623500,
                  "fuelCost": 187.5,
                  "fuel_price": 1.5,
                  "monetary": 245.7,
                  "timeCost": 382,
                  "tolls": [
                    {
                      "cost": 15.5,
                      "location": "41.385,2.174",
                      "name": "AP-7"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/RouteCosts"
                }
              }
            },
            "description": "Cálculo de costes completado correctamente"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Missing required location identifiers",
                  "message": "INVALID_PARAMETERS"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Parámetros de la petición inválidos"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Auction not found",
                  "message": "NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Subasta o direcciones no encontradas"
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Cost calculation failed",
                  "message": "INTERNAL_SERVER_ERROR"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error de cálculo o del servicio externo"
          }
        },
        "security": [],
        "summary": "Calculate raw transportation costs (fuel, tolls, maintenance)",
        "tags": [
          "minimal",
          "pricing"
        ]
      }
    },
    "/company/minimal/costs-routes": {
      "post": {
        "description": "## Propósito\nCalcula los costes de transporte para un par origen-destino utilizando nombres de ciudad\ncomo identificadores de ubicación en lugar de coordenadas o IDs de base de datos.\nEs la variante POST del cálculo de costes, que acepta los datos en el cuerpo de la petición.\n\n## Objetivo\nProporcionar un endpoint de cálculo de costes fácil de usar mediante nombres de ciudad\nlegibles por humanos, útil para integraciones donde las coordenadas no están disponibles.\n\n## Casos de Uso\n- Calcular costes usando nombres de ciudad en lugar de coordenadas\n- Integración desde sistemas externos que envían datos basados en ciudad\n- Estimación rápida de costes para herramientas de planificación\n- Comparación de tarifas de transportistas por nombre de ruta\n\n## Formato de la Petición\nEspera un objeto JSON con:\n- **origin** (requerido): Nombre de la ciudad de origen (ej: \"Madrid\", \"Barcelona\")\n- **destination** (requerido): Nombre de la ciudad de destino (ej: \"Vigo\", \"Bilbao\")\n- **with_tolls** (opcional): Incluir costes de peajes en el cálculo (por defecto: false)\n\n## Método de Cálculo\nUsa el mismo motor de cálculo que `GET /company/minimal/costs`:\n- La API de enrutamiento externo proporciona los costes base\n- Coste de combustible basado en distancia y consumo medio\n- Costes de peajes cuando `with_tolls=true`\n- Costes de mantenimiento (aceite, ruedas, embrague, etc.)\n- Costes de tiempo/duración\n\n## Integración Externa\nEste endpoint se integra con el servicio de enrutamiento Transcend para proporcionar\ndatos precisos de rutas y peajes en rutas europeas.\n\n## Respuestas de Error\n- 400: Cuerpo de la petición inválido o campos requeridos faltantes\n- 404: Ubicación de origen o destino no encontrada\n- 500: Error del servicio de enrutamiento externo\n",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "destination": "Vigo",
                "origin": "Madrid",
                "with_tolls": true
              },
              "schema": {
                "properties": {
                  "destination": {
                    "description": "Nombre de la ciudad de destino o identificador de ubicación.\nEl sistema busca las ubicaciones que coincidan.\n",
                    "example": "Vigo",
                    "type": "string"
                  },
                  "origin": {
                    "description": "Nombre de la ciudad de origen o identificador de ubicación.\nEl sistema busca las ubicaciones que coincidan.\n",
                    "example": "Madrid",
                    "type": "string"
                  },
                  "with_tolls": {
                    "description": "Incluir los costes de peajes en el cálculo.\n",
                    "example": true,
                    "type": "boolean"
                  }
                },
                "required": [
                  "origin",
                  "destination"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "arrival": "2026-02-12T14:45:00Z",
                  "costs": {
                    "aire": 8.1,
                    "clutch": 1.01,
                    "correa": 6.46,
                    "fuel": {
                      "fuel": 0.0035,
                      "monetary": 145.5
                    },
                    "oil": 2.42,
                    "toll": 12.8,
                    "total_cost": 180.17,
                    "wheels": 3.88
                  },
                  "departure": "2026-02-12T09:00:00Z",
                  "distance": 485000,
                  "fuelCost": 145.5,
                  "fuel_price": 1.5,
                  "monetary": 198.3,
                  "timeCost": 285,
                  "tolls": [
                    {
                      "cost": 12.8,
                      "location": "40.312,-3.415",
                      "name": "AP-6"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/RouteCosts"
                }
              }
            },
            "description": "Cálculo de costes completado correctamente"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Missing required field: origin",
                  "message": "INVALID_REQUEST"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Cuerpo de la petición inválido"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Origin location not found",
                  "message": "NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Ubicación no encontrada"
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Routing service unavailable",
                  "message": "INTERNAL_SERVER_ERROR"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error del servicio externo"
          }
        },
        "security": [],
        "summary": "Calculate transportation costs from city names (POST variant)",
        "tags": [
          "minimal",
          "pricing",
          "batch"
        ]
      }
    },
    "/company/minimal/route": {
      "get": {
        "description": "## Propósito\nObtiene información geográfica y temporal detallada de una ruta de transporte de mercancías,\nincluyendo los puntos intermedios para su visualización en mapa y planificación del trayecto.\n\n## Objetivo\nProporcionar datos completos de la ruta para navegación GPS, renderizado en mapas\ny sistemas de planificación logística.\n\n## Casos de Uso\n- Mostrar rutas en interfaces de mapa para conductores y gestores\n- Calcular el ETA (tiempo estimado de llegada) con precisión\n- Planificar paradas de descanso y puntos de repostaje en la ruta\n- Visualizar alternativas de ruta en herramientas de planificación logística\n- Generar instrucciones de navegación giro a giro\n- Seguimiento del progreso del conductor respecto a la ruta planificada\n\n## Información de la Respuesta\n- **distance**: Distancia total de la ruta en metros\n- **duration**: Tiempo estimado de viaje en segundos\n- **points**: Array de coordenadas GPS que definen el trazado de la ruta\n- **tolls**: Información de peajes cuando `with_tolls=true`\n- **departure/arrival**: Timestamps de salida y llegada estimados\n\n## Prioridad de Parámetros\nPrioridad de identificadores de ubicación (igual que en /co2):\n1. **id** (auction_id): Usa las direcciones de la subasta\n2. **idEtl/idEtd**: IDs de direcciones en base de datos\n3. **zipcodeStart/zipcodeEnd**: Búsqueda por código postal\n4. **coordsStart/coordsEnd**: Coordenadas GPS\n5. **countryOrigin/countryDestiny**: Códigos de país\n\n## Respuestas de Error\n- 400: Parámetros inválidos o identificadores de ubicación faltantes\n- 404: Direcciones o subasta no encontradas\n- 500: Error del servicio de enrutamiento externo\n",
        "parameters": [
          {
            "description": "ID de la subasta (ObjectId). Usa las direcciones de recogida/entrega de la subasta.\nOpcional — omitir para usar otros parámetros de ubicación.\n",
            "in": "query",
            "name": "id",
            "required": false,
            "schema": {
              "example": "507f1f77bcf86cd799439011",
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "ID de la dirección de origen en la base de datos. Usar junto con idEtd.\n",
            "in": "query",
            "name": "idEtl",
            "required": false,
            "schema": {
              "example": "507f1f77bcf86cd799439012",
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "ID de la dirección de destino en la base de datos. Usar junto con idEtl.\n",
            "in": "query",
            "name": "idEtd",
            "required": false,
            "schema": {
              "example": "507f1f77bcf86cd799439013",
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "Código postal de origen. Alternativa a idEtl/coordsStart.\n",
            "in": "query",
            "name": "zipcodeStart",
            "required": false,
            "schema": {
              "example": "28001",
              "maxLength": 10,
              "minLength": 3,
              "type": "string"
            }
          },
          {
            "description": "Código postal de destino. Alternativa a idEtd/coordsEnd.\n",
            "in": "query",
            "name": "zipcodeEnd",
            "required": false,
            "schema": {
              "example": "08001",
              "maxLength": 10,
              "minLength": 3,
              "type": "string"
            }
          },
          {
            "description": "Coordenadas GPS de origen (lat,lng). Alternativa a idEtl/zipcodeStart.\n",
            "in": "query",
            "name": "coordsStart",
            "required": false,
            "schema": {
              "example": "40.41678,-3.70379",
              "pattern": "^-?\\d+\\.?\\d+,-?\\d+\\.?\\d+$",
              "type": "string"
            }
          },
          {
            "description": "Coordenadas GPS de destino (lat,lng). Alternativa a idEtd/zipcodeEnd.\n",
            "in": "query",
            "name": "coordsEnd",
            "required": false,
            "schema": {
              "example": "41.38506,2.17340",
              "pattern": "^-?\\d+\\.?\\d+,-?\\d+\\.?\\d+$",
              "type": "string"
            }
          },
          {
            "description": "Código de país ISO de origen (ej: 'es', 'fr', 'de').\n",
            "in": "query",
            "name": "countryOrigin",
            "required": false,
            "schema": {
              "example": "es",
              "maxLength": 2,
              "minLength": 2,
              "pattern": "^[a-z]{2}$",
              "type": "string"
            }
          },
          {
            "description": "Código de país ISO de destino (ej: 'es', 'fr', 'de').\n",
            "in": "query",
            "name": "countryDestiny",
            "required": false,
            "schema": {
              "example": "es",
              "maxLength": 2,
              "minLength": 2,
              "pattern": "^[a-z]{2}$",
              "type": "string"
            }
          },
          {
            "description": "Incluir información de peajes en la respuesta de la ruta.\n**Por defecto: `true`** — los datos de peajes se incluyen salvo que se indique `false` explícitamente.\n",
            "in": "query",
            "name": "with_tolls",
            "required": false,
            "schema": {
              "default": true,
              "example": true,
              "type": "boolean"
            }
          },
          {
            "description": "Incluir las coordenadas detalladas de los puntos intermedios en la respuesta\npara su visualización en mapa.\n",
            "in": "query",
            "name": "with_points",
            "required": false,
            "schema": {
              "example": true,
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "arrival": "2026-02-12T11:49:00Z",
                  "departure": "2026-02-12T08:00:00Z",
                  "distance": 623500,
                  "duration": 13740,
                  "points": [
                    {
                      "lat": 40.41678,
                      "lng": -3.70379
                    },
                    {
                      "lat": 40.4255,
                      "lng": -3.7096
                    },
                    {
                      "lat": 41.38506,
                      "lng": 2.1734
                    }
                  ],
                  "tolls": [
                    {
                      "cost": 15.5,
                      "location": "41.385,2.174",
                      "name": "AP-7"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/RouteInfo"
                }
              }
            },
            "description": "Información de la ruta obtenida correctamente"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Missing required location identifiers",
                  "message": "INVALID_ROUTE"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Parámetros de ruta inválidos"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Address not found",
                  "message": "NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Direcciones o subasta no encontradas"
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Routing service unavailable",
                  "message": "INTERNAL_SERVER_ERROR"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error del servicio de enrutamiento externo"
          }
        },
        "security": [],
        "summary": "Get detailed route information with waypoints",
        "tags": [
          "minimal",
          "routing"
        ]
      }
    },
    "/company/my_carriers/": {
      "get": {
        "deprecated": false,
        "description": "Devuelve un listado paginado de transportistas de la compañía con filtros.\nDiferente de /all porque incluye paginación y filtros avanzados.\nRequiere autenticación mediante JWT.\n\nCasos de uso:\n- Consultar transportistas con paginación para grandes volúmenes\n- Filtrar transportistas por tax ID (búsqueda parcial)\n- Filtrar transportistas por estado habilitado\n- Implementar interfaces con listado paginado\n\nNotas:\n- La búsqueda por taxId es case insensitive\n- Los resultados se ordenan por nombre alfabéticamente\n- Incluye metadatos de paginación (total, página, límite, totalPages)\n- El límite máximo documentado (100) NO está validado en el backend actualmente\n",
        "parameters": [
          {
            "description": "Filtro por tax ID (búsqueda parcial, case insensitive)",
            "in": "query",
            "name": "taxId",
            "required": false,
            "schema": {
              "example": "B12345678",
              "type": "string"
            }
          },
          {
            "description": "Filtrar carriers por estado habilitado",
            "in": "query",
            "name": "enabled",
            "required": false,
            "schema": {
              "example": true,
              "type": "boolean"
            }
          },
          {
            "description": "Número de página",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "example": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Items por página",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 10,
              "example": 10,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "docs": [
                    {
                      "_id": "60d5ec9f8f8b8a001f3e5a6b",
                      "email": "contacto@transporte-ejemplo.com",
                      "enabled": true,
                      "name": "Transportes Ejemplo S.L.",
                      "phone": "+34600123456",
                      "taxid": "B12345678"
                    },
                    {
                      "_id": "60d5ec9f8f8b8a001f3e5a6c",
                      "email": "info@logisticarapida.com",
                      "enabled": false,
                      "name": "Logística Rápida S.A.",
                      "phone": "+34600234567",
                      "taxid": "A87654321"
                    }
                  ],
                  "pagination": {
                    "limit": 10,
                    "page": 1,
                    "total": 10,
                    "totalPages": 1
                  }
                },
                "schema": {
                  "properties": {
                    "docs": {
                      "items": {
                        "properties": {
                          "_id": {
                            "description": "ID único del carrier",
                            "example": "60d5ec9f8f8b8a001f3e5a6b",
                            "type": "string"
                          },
                          "email": {
                            "description": "Email de contacto",
                            "example": "contacto@transporte-ejemplo.com",
                            "type": "string"
                          },
                          "enabled": {
                            "description": "Estado habilitado del transportista",
                            "example": true,
                            "type": "boolean"
                          },
                          "name": {
                            "description": "Nombre del transportista",
                            "example": "Transportes Ejemplo S.L.",
                            "type": "string"
                          },
                          "phone": {
                            "description": "Teléfono de contacto",
                            "example": "+34600123456",
                            "type": "string"
                          },
                          "taxid": {
                            "description": "NIF/CIF del transportista",
                            "example": "B12345678",
                            "type": "string"
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "pagination": {
                      "properties": {
                        "limit": {
                          "description": "Items por página",
                          "example": 10,
                          "type": "integer"
                        },
                        "page": {
                          "description": "Página actual",
                          "example": 1,
                          "type": "integer"
                        },
                        "total": {
                          "description": "Total de carriers que coinciden con los filtros",
                          "example": 25,
                          "type": "integer"
                        },
                        "totalPages": {
                          "description": "Total de páginas disponibles",
                          "example": 3,
                          "type": "integer"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Lista de carriers con paginación",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. Token JWT inválido o no proporcionado",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get my carriers with pagination",
        "tags": [
          "Carriers"
        ]
      },
      "post": {
        "deprecated": false,
        "description": "Permite registrar un nuevo transportista asociado a la compañía.\nRequiere autenticación mediante JWT.\n\nCasos de uso:\n- Registrar nuevos transportistas para gestionar envíos\n- Mantener un directorio de transportistas disponibles\n- Asociar transportistas a operaciones logísticas\n\nEjemplo de flujo:\n1. Obtener token de autenticación\n2. Registrar transportista con datos básicos\n3. Habilitar transportista cuando esté listo para operar\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "email": "contacto@transporte-ejemplo.com",
                "enabled": false,
                "name": "Transportes Ejemplo S.L.",
                "phone": "+34600123456",
                "taxid": "B12345678"
              },
              "schema": {
                "properties": {
                  "email": {
                    "description": "Email de contacto principal",
                    "example": "contacto@transporte-ejemplo.com",
                    "format": "email",
                    "type": "string"
                  },
                  "enabled": {
                    "default": false,
                    "description": "Indica si el transportista está activo y disponible para operaciones",
                    "type": "boolean"
                  },
                  "name": {
                    "description": "Nombre completo o razón social del transportista",
                    "example": "Transportes Ejemplo S.L.",
                    "type": "string"
                  },
                  "phone": {
                    "description": "Teléfono de contacto del transportista",
                    "example": "+34600123456",
                    "type": "string"
                  },
                  "taxid": {
                    "description": "NIF/CIF del transportista",
                    "example": "B12345678",
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "taxid"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "6731cf0d1576811151027813",
                  "address": {},
                  "invoice_data": {},
                  "my_carriers": [
                    "698c9351dd1255eb0b780d05",
                    "698c9351dd1255eb0b780d06"
                  ],
                  "name": "BotCIA",
                  "payment_settings": {}
                },
                "schema": {
                  "description": "Objeto Company completo (no solo el carrier creado)",
                  "properties": {
                    "_id": {
                      "description": "ID de la compañía",
                      "example": "6731cf0d1576811151027813",
                      "type": "string"
                    },
                    "address": {
                      "description": "Dirección de la compañía",
                      "type": "object"
                    },
                    "invoice_data": {
                      "description": "Datos de facturación",
                      "type": "object"
                    },
                    "my_carriers": {
                      "description": "Array de IDs de carriers (el último es el recién creado)",
                      "example": [
                        "698c9351dd1255eb0b780d05",
                        "698c9351dd1255eb0b780d06"
                      ],
                      "items": {
                        "type": "string"
                      },
                      "type": "array"
                    },
                    "name": {
                      "description": "Nombre de la compañía",
                      "example": "BotCIA",
                      "type": "string"
                    },
                    "payment_settings": {
                      "description": "Configuración de pagos",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Transportista creado exitosamente.\n\n**NOTA IMPORTANTE**: Actualmente el endpoint devuelve el objeto Company completo en lugar del Carrier creado.\nEsto es un comportamiento conocido del sistema. El ID del carrier creado estará en el array `my_carriers` de la respuesta.\n",
            "headers": {}
          },
          "400": {
            "description": "Datos de entrada inválidos. Posibles causas:\n- Nombre vacío o muy corto\n- NIF/CIF con formato incorrecto\n- Email inválido\n",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. Token JWT inválido o no proporcionado",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Add a new carrier",
        "tags": [
          "Carriers"
        ]
      }
    },
    "/company/my_carriers/all": {
      "get": {
        "deprecated": false,
        "description": "Devuelve un listado completo de todos los transportistas asociados a la compañía.\nRequiere autenticación mediante JWT.\n\nCasos de uso:\n- Consultar el directorio completo de transportistas\n- Filtrar transportistas activos/inactivos en el cliente\n- Obtener datos básicos para seleccionar transportistas\n\nNotas:\n- No incluye paginación, devuelve todos los resultados\n- Permite filtrar por estado habilitado mediante query parameter\n- Los resultados NO están ordenados por defecto (se devuelven en orden de inserción en MongoDB)\n",
        "parameters": [
          {
            "description": "Filtrar carriers por estado habilitado",
            "in": "query",
            "name": "enabled",
            "required": false,
            "schema": {
              "example": true,
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "docs": [
                    {
                      "_id": "60d5ec9f8f8b8a001f3e5a6b",
                      "email": "contacto@transporte-ejemplo.com",
                      "enabled": true,
                      "name": "Transportes Ejemplo S.L.",
                      "phone": "+34600123456",
                      "taxid": "B12345678"
                    },
                    {
                      "_id": "60d5ec9f8f8b8a001f3e5a6c",
                      "email": "info@logisticarapida.com",
                      "enabled": false,
                      "name": "Logística Rápida S.A.",
                      "phone": "+34600234567",
                      "taxid": "A87654321"
                    }
                  ]
                },
                "schema": {
                  "properties": {
                    "docs": {
                      "items": {
                        "properties": {
                          "_id": {
                            "description": "ID único del carrier",
                            "example": "60d5ec9f8f8b8a001f3e5a6b",
                            "type": "string"
                          },
                          "email": {
                            "description": "Email de contacto",
                            "example": "contacto@transporte-ejemplo.com",
                            "type": "string"
                          },
                          "enabled": {
                            "description": "Estado habilitado del transportista",
                            "example": true,
                            "type": "boolean"
                          },
                          "name": {
                            "description": "Nombre del transportista",
                            "example": "Transportes Ejemplo S.L.",
                            "type": "string"
                          },
                          "phone": {
                            "description": "Teléfono de contacto",
                            "example": "+34600123456",
                            "type": "string"
                          },
                          "taxid": {
                            "description": "NIF/CIF del transportista",
                            "example": "B12345678",
                            "type": "string"
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Listado de transportistas obtenido correctamente",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. Token JWT inválido o no proporcionado",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get list of all carriers",
        "tags": [
          "Carriers"
        ]
      }
    },
    "/company/my_carriers/driver/{carrierId}": {
      "get": {
        "deprecated": false,
        "description": "Devuelve un listado completo de todos los conductores asociados a un transportista específico.\nRequiere autenticación mediante JWT.\n\nCasos de uso:\n- Consultar el equipo de conductores de un transportista\n- Filtrar conductores activos/inactivos\n- Obtener datos básicos para asignar conductores a operaciones\n\nNotas:\n- El transportista debe existir previamente\n- La respuesta incluye metadatos como el conteo total\n",
        "parameters": [
          {
            "description": "ID único del transportista cuyos conductores se quieren listar",
            "example": "68fa21f246c1e920eac914fa",
            "in": "path",
            "name": "carrierId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "_id": "698c9382dd1255eb0b780d31",
                    "associated": "698c9382dd1255eb0b780d2f",
                    "email": "j.perez@transporte-ejemplo.com",
                    "enabled": true,
                    "name": "Juan Pérez López",
                    "phone": "+34600123456",
                    "taxid": "12345678A"
                  },
                  {
                    "_id": "698c9382dd1255eb0b780d32",
                    "associated": "698c9382dd1255eb0b780d30",
                    "email": "m.garcia@transporte-ejemplo.com",
                    "enabled": false,
                    "name": "María García Ruiz",
                    "phone": "+34600567890",
                    "taxid": "87654321B"
                  }
                ],
                "schema": {
                  "description": "Array de conductores del transportista",
                  "items": {
                    "$ref": "#/components/schemas/Driver"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Listado de conductores obtenido correctamente.\nRetorna un array directo de conductores (NO envuelto en objeto con propiedad \"drivers\").\n\n**Estructura de IDs en cada driver**:\n- `_id`: ID del subdocumento dentro del array `drivers` del carrier\n- `associated`: ID del usuario `trucker_user` en la colección `truckers_users` (referencia al usuario real)\n",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "No se proporcionó token de autenticación",
                  "message": "NO_TOKEN"
                }
              }
            },
            "description": "No autorizado. Token JWT inválido o no proporcionado",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "Carrier not found"
                }
              }
            },
            "description": "Transportista no encontrado. El ID proporcionado no existe o no está asociado a la compañía",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "List all drivers of a carrier",
        "tags": [
          "Drivers"
        ]
      },
      "post": {
        "deprecated": false,
        "description": "Registra un nuevo conductor asociado a un transportista específico.\nRequiere autenticación mediante JWT.\n\nCasos de uso:\n- Registrar conductores para transportistas existentes\n- Mantener un directorio de conductores disponibles\n- Asociar conductores a operaciones logísticas\n\nNotas:\n- El transportista debe existir previamente\n- Se requieren datos básicos del conductor\n- **Reutilización de conductores**: Si ya existe un conductor con el mismo `taxid`,\n  el endpoint reutilizará el conductor existente en lugar de crear uno duplicado\n- **Password automático**: Si no se proporciona password, se genera automáticamente\n  con \"123456\" y se hashea con bcrypt antes de guardar\n",
        "parameters": [
          {
            "description": "ID único del transportista al que se asociará el conductor",
            "example": "68fa21f246c1e920eac914fa",
            "in": "path",
            "name": "carrierId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "email": "j.perez@transporte-ejemplo.com",
                "enabled": false,
                "name": "Juan Pérez López",
                "password": "123456",
                "phone": "+34600123456",
                "taxid": "12345678A"
              },
              "schema": {
                "properties": {
                  "email": {
                    "description": "Email de contacto del conductor",
                    "example": "j.perez@transporte-ejemplo.com",
                    "format": "email",
                    "type": "string"
                  },
                  "enabled": {
                    "default": false,
                    "description": "Indica si el conductor está activo y disponible para operaciones",
                    "type": "boolean"
                  },
                  "name": {
                    "description": "Nombre completo del conductor",
                    "example": "Juan Pérez López",
                    "type": "string"
                  },
                  "password": {
                    "default": "123456",
                    "description": "Password para el usuario del conductor (se hashea con bcrypt antes de guardar).\nSi no se proporciona, se usará \"123456\" por defecto.\n",
                    "example": "123456",
                    "minLength": 6,
                    "type": "string"
                  },
                  "phone": {
                    "description": "Teléfono de contacto del conductor",
                    "example": "+34600123456",
                    "type": "string"
                  },
                  "taxid": {
                    "description": "DNI/NIE del conductor",
                    "example": "12345678A",
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "taxid"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "698c9351dd1255eb0b780d05",
                  "drivers": [
                    {
                      "_id": "698c9382dd1255eb0b780d31",
                      "associated": "698c9382dd1255eb0b780d2f",
                      "email": "conductor@test.com",
                      "enabled": true,
                      "name": "Conductor Test",
                      "phone": "+34600555666",
                      "taxid": "12345678B"
                    }
                  ],
                  "email": "test@carrier.com",
                  "enabled": true,
                  "name": "Carrier Test Actualizado",
                  "phone": "+34600999888",
                  "taxid": "B11112222"
                },
                "schema": {
                  "description": "Objeto Carrier completo con drivers actualizado",
                  "properties": {
                    "_id": {
                      "description": "ID del transportista",
                      "example": "698c9351dd1255eb0b780d05",
                      "type": "string"
                    },
                    "drivers": {
                      "description": "Array de conductores (incluye el recién creado)",
                      "items": {
                        "$ref": "#/components/schemas/Driver"
                      },
                      "type": "array"
                    },
                    "name": {
                      "description": "Nombre del transportista",
                      "example": "Carrier Test Actualizado",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Conductor agregado correctamente.\n\n**NOTA IMPORTANTE**: El endpoint retorna el objeto Carrier completo con el array de drivers actualizado,\nNO solo el driver creado. El nuevo driver estará en el último elemento del array `drivers`.\n\n**Estructura de IDs en drivers**:\n- `_id`: ID del subdocumento dentro del array `drivers` del carrier\n- `associated`: ID del usuario `trucker_user` en la colección `truckers_users` (referencia al usuario real)\n",
            "headers": {}
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "details": "El nombre es obligatorio y debe tener al menos 1 carácter",
                  "message": "Error de validación"
                }
              }
            },
            "description": "Datos de entrada inválidos. Posibles causas:\n- Nombre vacío o muy corto\n- DNI/NIE con formato incorrecto\n- Email inválido\n- Teléfono mal formado\n",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "No se proporcionó token de autenticación",
                  "message": "NO_TOKEN"
                }
              }
            },
            "description": "No autorizado. Token JWT inválido o no proporcionado",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "Carrier not found"
                }
              }
            },
            "description": "Transportista no encontrado. El ID proporcionado no existe o no está asociado a la compañía",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Add a driver to a carrier",
        "tags": [
          "Drivers"
        ]
      }
    },
    "/company/my_carriers/driver/{carrierId}/{driverId}": {
      "delete": {
        "deprecated": false,
        "description": "Elimina permanentemente un conductor del sistema.\nRequiere autenticación mediante JWT.\n\nCasos de uso:\n- Eliminar conductores que ya no trabajan con el transportista\n- Limpiar registros duplicados o incorrectos\n\nNotas:\n- Esta acción es irreversible\n- El transportista y conductor deben existir previamente\n- Solo se pueden eliminar conductores asociados al transportista especificado\n- El endpoint valida que el conductor pertenezca al transportista (usando el campo `associated`)\n",
        "parameters": [
          {
            "description": "ID único del transportista al que pertenece el conductor",
            "example": "68fa21f246c1e920eac914fa",
            "in": "path",
            "name": "carrierId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "ID único del conductor a eliminar",
            "example": "68fa225746c1e920eac91521",
            "in": "path",
            "name": "driverId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "success": true
                },
                "schema": {
                  "properties": {
                    "success": {
                      "description": "Indica si la operación fue exitosa",
                      "example": true,
                      "type": "boolean"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Conductor eliminado correctamente",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "No se proporcionó token de autenticación",
                  "message": "NO_TOKEN"
                }
              }
            },
            "description": "No autorizado. Token JWT inválido o no proporcionado",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "Driver not found"
                }
              }
            },
            "description": "No encontrado. Posibles causas:\n- Transportista no existe\n- Conductor no existe o no pertenece al transportista especificado\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Remove a driver",
        "tags": [
          "Drivers"
        ]
      },
      "put": {
        "deprecated": false,
        "description": "Permite modificar los datos de un conductor existente.\nRequiere autenticación mediante JWT.\n\nCasos de uso:\n- Actualizar datos de contacto del conductor\n- Cambiar el estado activo/inactivo del conductor\n- Corregir información errónea\n\nNotas:\n- Todos los campos son opcionales (solo se actualizarán los proporcionados)\n- El transportista y conductor deben existir previamente\n- El endpoint valida que el conductor pertenezca al transportista especificado\n- **ADVERTENCIA**: Editar el campo `taxid` puede causar inconsistencia\n  con el usuario `trucker_user` asociado (campo `associated`). Se recomienda\n  NO modificar este campo una vez creado el conductor.\n",
        "parameters": [
          {
            "description": "ID único del transportista al que pertenece el conductor",
            "example": "68fa21f246c1e920eac914fa",
            "in": "path",
            "name": "carrierId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "ID único del conductor a actualizar",
            "example": "68fa225746c1e920eac91521",
            "in": "path",
            "name": "driverId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "email": "j.perez.nuevo@transporte-ejemplo.com",
                "enabled": true,
                "name": "Juan Pérez López",
                "phone": "+34600123456",
                "taxid": "12345678A"
              },
              "schema": {
                "properties": {
                  "email": {
                    "description": "Nuevo email de contacto",
                    "example": "j.perez.nuevo@transporte-ejemplo.com",
                    "format": "email",
                    "maxLength": 255,
                    "type": "string"
                  },
                  "enabled": {
                    "description": "Nuevo estado activo/inactivo",
                    "example": true,
                    "type": "boolean"
                  },
                  "name": {
                    "description": "Nuevo nombre completo del conductor",
                    "example": "Juan Pérez López",
                    "maxLength": 255,
                    "minLength": 1,
                    "type": "string"
                  },
                  "phone": {
                    "description": "Nuevo teléfono de contacto",
                    "example": "+34600123456",
                    "maxLength": 15,
                    "minLength": 9,
                    "pattern": "^\\+?[0-9]{9,15}$",
                    "type": "string"
                  },
                  "taxid": {
                    "description": "⚠️ ADVERTENCIA: Editar el taxid puede causar inconsistencia\ncon el usuario trucker_user asociado (campo 'associated').\nSe recomienda NO modificar este campo una vez creado.\n",
                    "example": "12345678A",
                    "maxLength": 15,
                    "minLength": 8,
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "success": true
                },
                "schema": {
                  "properties": {
                    "success": {
                      "description": "Indica si la operación fue exitosa",
                      "example": true,
                      "type": "boolean"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Conductor actualizado correctamente",
            "headers": {}
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "details": "El nombre es obligatorio y debe tener al menos 1 carácter",
                  "message": "Error de validación"
                }
              }
            },
            "description": "Datos de entrada inválidos. Posibles causas:\n- Nombre vacío o muy corto\n- Email inválido\n- Teléfono mal formado\n",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "No se proporcionó token de autenticación",
                  "message": "NO_TOKEN"
                }
              }
            },
            "description": "No autorizado. Token JWT inválido o no proporcionado",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "Driver not found"
                }
              }
            },
            "description": "No encontrado. Posibles causas:\n- Transportista no existe\n- Conductor no existe en la base de datos\n- Conductor no pertenece al transportista especificado\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Update driver information",
        "tags": [
          "Drivers"
        ]
      }
    },
    "/company/my_carriers/mydriver/{carrierId}": {
      "get": {
        "deprecated": false,
        "description": "Devuelve un listado paginado de conductores asociados a un transportista con filtros.\nRequiere autenticación mediante JWT.\n\nCasos de uso:\n- Consultar conductores con paginación para grandes volúmenes\n- Filtrar conductores por tax ID (búsqueda parcial)\n- Implementar interfaces con listado paginado\n\nNotas:\n- La búsqueda por taxid es case insensitive\n- Incluye metadatos de paginación (total, página, límite, totalPages)\n- El límite se toma de la variable de entorno ITEMS_PAGE si no se especifica\n",
        "parameters": [
          {
            "description": "ID único del transportista",
            "example": "68fa21f246c1e920eac914fa",
            "in": "path",
            "name": "carrierId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          },
          {
            "description": "Filtro por tax ID (búsqueda parcial, case insensitive)",
            "in": "query",
            "name": "taxid",
            "required": false,
            "schema": {
              "example": "12345678",
              "type": "string"
            }
          },
          {
            "description": "Número de página",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "example": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Items por página",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 10,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "docs": [
                    {
                      "_id": "60d5ec9f8f8b8a001f3e5a7c",
                      "associated": "60d5ec9f8f8b8a001f3e5a7c",
                      "email": "juan.garcia@transporte-ejemplo.com",
                      "enabled": true,
                      "name": "Juan García López",
                      "phone": "+34600111222",
                      "taxid": "12345678A"
                    }
                  ],
                  "pagination": {
                    "limit": 10,
                    "page": 1,
                    "total": 1,
                    "totalPages": 1
                  }
                },
                "schema": {
                  "properties": {
                    "docs": {
                      "items": {
                        "properties": {
                          "_id": {
                            "description": "ID único del conductor",
                            "example": "60d5ec9f8f8b8a001f3e5a7c",
                            "type": "string"
                          },
                          "associated": {
                            "description": "ID del usuario asociado",
                            "example": "60d5ec9f8f8b8a001f3e5a7c",
                            "type": "string"
                          },
                          "email": {
                            "description": "Email del conductor",
                            "example": "juan.garcia@transporte-ejemplo.com",
                            "type": "string"
                          },
                          "enabled": {
                            "description": "Estado habilitado del conductor",
                            "example": true,
                            "type": "boolean"
                          },
                          "name": {
                            "description": "Nombre del conductor",
                            "example": "Juan García López",
                            "type": "string"
                          },
                          "phone": {
                            "description": "Teléfono del conductor",
                            "example": "+34600111222",
                            "type": "string"
                          },
                          "taxid": {
                            "description": "NIF/DNI del conductor",
                            "example": "12345678A",
                            "type": "string"
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "pagination": {
                      "properties": {
                        "limit": {
                          "description": "Items por página",
                          "example": 10,
                          "type": "integer"
                        },
                        "page": {
                          "description": "Página actual",
                          "example": 1,
                          "type": "integer"
                        },
                        "total": {
                          "description": "Total de conductores que coinciden con los filtros",
                          "example": 5,
                          "type": "integer"
                        },
                        "totalPages": {
                          "description": "Total de páginas disponibles",
                          "example": 1,
                          "type": "integer"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Lista de conductores con paginación",
            "headers": {}
          },
          "400": {
            "description": "Carrier no encontrado",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. Token JWT inválido o no proporcionado",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get drivers with pagination",
        "tags": [
          "Carriers"
        ]
      }
    },
    "/company/my_carriers/selected/{auctionCode}": {
      "get": {
        "deprecated": false,
        "description": "Devuelve los transportistas seleccionados para una subasta específica.\nRequiere autenticación mediante JWT.\n\nCasos de uso:\n- Obtener los transportistas asignados a una subasta específica\n- Verificar qué carriers participarán en una operación\n- Preparar información para contacto con transportistas\n\nNotas:\n- Si la subasta no es para proveedores (for_providers=false), devuelve array vacío\n- Si la subasta no tiene proveedores asignados, devuelve array vacío\n- Los carriers se identifican por su taxid en la lista de providers de la subasta\n",
        "parameters": [
          {
            "description": "Código de servicio de la subasta (service_code)",
            "example": "SRV-2025-001",
            "in": "path",
            "name": "auctionCode",
            "required": true,
            "schema": {
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "docs": [
                    {
                      "_id": "60d5ec9f8f8b8a001f3e5a6b",
                      "email": "contacto@transporte-ejemplo.com",
                      "enabled": true,
                      "name": "Transportes Ejemplo S.L.",
                      "phone": "+34600123456",
                      "taxid": "B12345678"
                    },
                    {
                      "_id": "60d5ec9f8f8b8a001f3e5a6c",
                      "email": "info@logisticarapida.com",
                      "enabled": true,
                      "name": "Logística Rápida S.A.",
                      "phone": "+34600234567",
                      "taxid": "A87654321"
                    }
                  ]
                },
                "schema": {
                  "properties": {
                    "docs": {
                      "items": {
                        "properties": {
                          "_id": {
                            "description": "ID único del carrier",
                            "example": "60d5ec9f8f8b8a001f3e5a6b",
                            "type": "string"
                          },
                          "email": {
                            "description": "Email de contacto",
                            "example": "contacto@transporte-ejemplo.com",
                            "type": "string"
                          },
                          "enabled": {
                            "description": "Estado habilitado del transportista",
                            "example": true,
                            "type": "boolean"
                          },
                          "name": {
                            "description": "Nombre del transportista",
                            "example": "Transportes Ejemplo S.L.",
                            "type": "string"
                          },
                          "phone": {
                            "description": "Teléfono de contacto",
                            "example": "+34600123456",
                            "type": "string"
                          },
                          "taxid": {
                            "description": "NIF/CIF del transportista",
                            "example": "B12345678",
                            "type": "string"
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Lista de carriers de la subasta",
            "headers": {}
          },
          "400": {
            "description": "Subasta no encontrada o parámetros inválidos",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. Token JWT inválido o no proporcionado",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get carriers for an auction",
        "tags": [
          "Carriers"
        ]
      }
    },
    "/company/my_carriers/{carrierId}": {
      "delete": {
        "deprecated": false,
        "description": "Elimina permanentemente un transportista del sistema.\nRequiere autenticación mediante JWT.\n\nCasos de uso:\n- Eliminar transportistas que ya no trabajan con la compañía\n- Limpiar registros duplicados o incorrectos\n\nNotas:\n- Esta acción es irreversible\n- Se eliminarán también todos los conductores asociados\n- El ID del transportista debe ser válido y existente\n- El transportista debe estar asociado a la compañía autenticada\n",
        "parameters": [
          {
            "description": "ID único del transportista a eliminar",
            "example": "68fa21f246c1e920eac914fa",
            "in": "path",
            "name": "carrierId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "message": "Transportista eliminado exitosamente"
                },
                "schema": {
                  "properties": {},
                  "type": "object"
                }
              }
            },
            "description": "Transportista eliminado correctamente",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. Token JWT inválido o no proporcionado",
            "headers": {}
          },
          "404": {
            "description": "Transportista no encontrado. El ID proporcionado no existe o no está asociado a la compañía",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Delete a carrier",
        "tags": [
          "Carriers"
        ]
      },
      "get": {
        "deprecated": false,
        "description": "Devuelve la información detallada de un transportista específico.\nRequiere autenticación mediante JWT.\n\nCasos de uso:\n- Verificar datos completos de un transportista\n- Mostrar información detallada en interfaces de usuario\n- Validar estado y configuración de un transportista\n\nNotas:\n- El ID del transportista debe ser válido y existente\n- Solo se puede acceder a transportistas asociados a la compañía\n- Incluye información de conductores asociados si existen\n",
        "parameters": [
          {
            "description": "ID único del transportista",
            "example": "68fa21f246c1e920eac914fa",
            "in": "path",
            "name": "carrierId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "60d5ec9f8f8b8a001f3e5a6b",
                  "drivers": [],
                  "email": "contacto@transporte-ejemplo.com",
                  "enabled": true,
                  "name": "Transportes Ejemplo S.L.",
                  "phone": "+34600123456",
                  "taxid": "B12345678"
                },
                "schema": {
                  "properties": {
                    "_id": {
                      "description": "ID único del transportista",
                      "example": "60d5ec9f8f8b8a001f3e5a6b",
                      "type": "string"
                    },
                    "drivers": {
                      "description": "Lista de conductores asociados (puede estar vacío o con datos parciales)",
                      "example": [],
                      "items": {
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "email": {
                      "description": "Email de contacto",
                      "example": "contacto@transporte-ejemplo.com",
                      "type": "string"
                    },
                    "enabled": {
                      "description": "Estado habilitado del transportista",
                      "example": true,
                      "type": "boolean"
                    },
                    "name": {
                      "description": "Nombre del transportista",
                      "example": "Transportes Ejemplo S.L.",
                      "type": "string"
                    },
                    "phone": {
                      "description": "Teléfono de contacto",
                      "example": "+34600123456",
                      "type": "string"
                    },
                    "taxid": {
                      "description": "NIF/CIF del transportista",
                      "example": "B12345678",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Detalles del transportista obtenidos correctamente",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. Token JWT inválido o no proporcionado",
            "headers": {}
          },
          "404": {
            "description": "Transportista no encontrado. El ID proporcionado no existe o no está asociado a la compañía",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get carrier details",
        "tags": [
          "Carriers"
        ]
      },
      "put": {
        "deprecated": false,
        "description": "Permite actualizar cualquier campo de un transportista existente.\nRequiere autenticación mediante JWT.\n\nCasos de uso:\n- Actualizar información de contacto (email, phone)\n- Modificar razón social o NIF/CIF\n- Activar/desactivar transportistas temporalmente\n- Gestionar conductores asociados al transportista\n\nNotas:\n- Se pueden actualizar todos los campos: name, email, phone, taxid, enabled, drivers\n- Los campos no incluidos en el request mantienen su valor actual\n- El ID del transportista debe ser válido y existente\n- El transportista debe estar asociado a la compañía autenticada\n",
        "parameters": [
          {
            "description": "ID único del transportista a actualizar",
            "example": "68fa21f246c1e920eac914fa",
            "in": "path",
            "name": "carrierId",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "email": "nuevo-email@transporte-ejemplo.com",
                "enabled": true,
                "name": "Transportes Actualizados S.L.",
                "phone": "+34600999888"
              },
              "schema": {
                "properties": {
                  "drivers": {
                    "description": "Lista de conductores asociados al transportista",
                    "items": {
                      "properties": {
                        "driver": {
                          "description": "ID del conductor (user_trucker)",
                          "example": "60d5ec9f8f8b8a001f3e5a7c",
                          "type": "string"
                        },
                        "enabled": {
                          "description": "Estado del conductor",
                          "example": true,
                          "type": "boolean"
                        }
                      },
                      "type": "object"
                    },
                    "type": "array"
                  },
                  "email": {
                    "description": "Email de contacto principal",
                    "example": "nuevo-email@transporte-ejemplo.com",
                    "format": "email",
                    "type": "string"
                  },
                  "enabled": {
                    "description": "Indica si el transportista está activo y disponible para operaciones",
                    "example": true,
                    "type": "boolean"
                  },
                  "name": {
                    "description": "Nombre completo o razón social del transportista",
                    "example": "Transportes Actualizados S.L.",
                    "type": "string"
                  },
                  "phone": {
                    "description": "Teléfono de contacto del transportista",
                    "example": "+34600999888",
                    "type": "string"
                  },
                  "taxid": {
                    "description": "NIF/CIF del transportista",
                    "example": "B87654321",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "60d5ec9f8f8b8a001f3e5a6b",
                  "createdAt": "2025-08-08T18:26:41.000Z",
                  "drivers": [],
                  "email": "nuevo-email@transporte-ejemplo.com",
                  "enabled": true,
                  "name": "Transportes Actualizados S.L.",
                  "phone": "+34600999888",
                  "taxid": "B12345678",
                  "updatedAt": "2025-08-08T18:29:10.000Z"
                },
                "schema": {
                  "properties": {
                    "_id": {
                      "description": "ID único del transportista",
                      "example": "60d5ec9f8f8b8a001f3e5a6b",
                      "type": "string"
                    },
                    "createdAt": {
                      "description": "Fecha de creación",
                      "example": "2025-08-08T18:26:41.000Z",
                      "format": "date-time",
                      "type": "string"
                    },
                    "drivers": {
                      "description": "Lista de conductores asociados",
                      "example": [],
                      "items": {
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "email": {
                      "description": "Email de contacto",
                      "example": "nuevo-email@transporte-ejemplo.com",
                      "type": "string"
                    },
                    "enabled": {
                      "description": "Estado habilitado del transportista",
                      "example": true,
                      "type": "boolean"
                    },
                    "name": {
                      "description": "Nombre del transportista",
                      "example": "Transportes Actualizados S.L.",
                      "type": "string"
                    },
                    "phone": {
                      "description": "Teléfono de contacto",
                      "example": "+34600999888",
                      "type": "string"
                    },
                    "taxid": {
                      "description": "NIF/CIF del transportista",
                      "example": "B12345678",
                      "type": "string"
                    },
                    "updatedAt": {
                      "description": "Fecha de última actualización",
                      "example": "2025-08-08T18:29:10.000Z",
                      "format": "date-time",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Transportista actualizado correctamente",
            "headers": {}
          },
          "400": {
            "description": "Datos de entrada inválidos. Posibles causas:\n- Email con formato inválido\n- Cuerpo de la solicitud mal formado\n- Tax ID duplicado\n",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. Token JWT inválido o no proporcionado",
            "headers": {}
          },
          "404": {
            "description": "Transportista no encontrado. El ID proporcionado no existe o no está asociado a la compañía",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Update carrier",
        "tags": [
          "Carriers"
        ]
      }
    },
    "/company/notifications/dashboard": {
      "get": {
        "deprecated": false,
        "description": "## Propósito\nRecupera información consolidada para el dashboard de notificaciones de la empresa, incluyendo\nnotificaciones no leídas y alertas de acciones pendientes como subastas sin firma.\n\n## Objetivo\nPermitir a las empresas visualizar en un solo endpoint todas las alertas críticas y\nnotificaciones pendientes, optimizando la experiencia de usuario en el dashboard principal.\n\n## Casos de uso\n- **Dashboard principal**: Carga inicial del dashboard empresarial\n- **Actualización en tiempo real**: Refresco periódico de notificaciones\n- **Alertas de firma**: Recordatorio de subastas pendientes de firma por parte de la empresa\n- **Monitor de actividad**: Seguimiento de notificaciones no leídas\n\n## Flujo de datos\n\nEl endpoint implementa un sistema de priorización con slots limitados (máximo 6 items):\n\n```mermaid\nflowchart TD\n    A[Request Dashboard] --> B{User Authenticated?}\n    B -->|No| C[404 USER_NOT_FOUND]\n    B -->|Yes| D{Company Exists?}\n    D -->|No| E[401 CIA_NOT_FOUND]\n    D -->|Yes| F[Get Company Data]\n    F --> G{Missing Signature?}\n    G -->|Yes| H[companyMissingSignature: true]\n    G -->|No| I[companyMissingSignature: false]\n    H --> J[Find Auctions Without Signature]\n    I --> J\n    J --> K{Count > 6?}\n    K -->|Yes| L[Limit: 6 Auctions]\n    K -->|No| M[Use All Auctions]\n    L --> N[Calculate Available Slots]\n    M --> N\n    N --> O{Slots > 0?}\n    O -->|No| P[Return Dashboard]\n    O -->|Yes| Q[Get Unread Notifications]\n    Q --> R[Fill Available Slots]\n    R --> S[Return Dashboard - 200]\n```\n\n## Campos de respuesta\n\n**companyMissingSignature** (boolean):\n- `true`: La empresa no ha completado su proceso de firma\n- `false`: La empresa tiene firma registrada\n\n**notSignedAuctionsCount** (number, rango 0-6):\n- Conteo de subastas adjudicadas que la empresa no ha firmado\n- Valor máximo: 6 (límite del sistema)\n- Cada subasta sin firma ocupa un slot en el dashboard\n\n**notSignedAuctions** (array):\n- Lista de subastas pendientes de firma\n- Cada elemento contiene únicamente el campo `service_code`\n- Formato: `[{\"service_code\": \"AU12345\"}, {\"service_code\": \"AU12346\"}]`\n\n**notifications** (array):\n- Lista de notificaciones no leídas\n- La cantidad de notificaciones varía según los slots disponibles\n- Slots disponibles = 6 - notSignedAuctionsCount\n- Si hay 6 subastas sin firma, no se incluyen notificaciones\n- Si hay 0 subastas sin firma, se incluyen hasta 6 notificaciones\n\n## Lógica de priorización\n\n1. Las subastas sin firma tienen **prioridad máxima**\n2. Las notificaciones llenan los **slots restantes**\n3. Total de items nunca excede 6 elementos\n\n## Notas importantes\n\n- Requiere autenticación con token JWT de empresa\n- Solo retorna datos de la empresa del usuario autenticado\n- Las subastas sin firma se limitan a máximo 6\n- Las notificaciones se ordenan por `createdAt` descendente (más recientes primero)\n- Solo se incluyen notificaciones con `isRead: false`\n- El campo `ref` en notificaciones viene poblado con el `service_code` correspondiente\n\n## Errores\n\n- **401 CIA_NOT_FOUND**: El usuario no tiene una empresa asociada\n- **404 USER_NOT_FOUND**: El token no corresponde a un usuario válido",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "companyMissingSignature": false,
                  "notSignedAuctions": [
                    {
                      "service_code": "AU12345"
                    },
                    {
                      "service_code": "AU12346"
                    }
                  ],
                  "notSignedAuctionsCount": 2,
                  "notifications": [
                    {
                      "_id": "507f1f77bcf86cd799439011",
                      "createdAt": "2025-02-12T10:30:00.000Z",
                      "isRead": false,
                      "ref": {
                        "service_code": "AU12350"
                      },
                      "service_code": "AU12350",
                      "subType": "newBidWinner",
                      "type": "auction"
                    },
                    {
                      "_id": "507f1f77bcf86cd799439012",
                      "createdAt": "2025-02-12T09:15:00.000Z",
                      "isRead": false,
                      "ref": {
                        "service_code": "DL67890"
                      },
                      "service_code": "DL67890",
                      "subType": "eta",
                      "type": "delivery"
                    },
                    {
                      "_id": "507f1f77bcf86cd799439013",
                      "createdAt": "2025-02-11T16:45:00.000Z",
                      "isRead": false,
                      "ref": null,
                      "service_code": null,
                      "subType": "sign",
                      "type": "account"
                    },
                    {
                      "_id": "507f1f77bcf86cd799439014",
                      "createdAt": "2025-02-11T14:20:00.000Z",
                      "isRead": false,
                      "ref": {
                        "service_code": "AU12351"
                      },
                      "service_code": "AU12351",
                      "subType": "statusToPublished",
                      "type": "auction"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/DashboardNotificationsResponse"
                }
              }
            },
            "description": "Dashboard de notificaciones recuperado exitosamente.\n\nEl objeto de respuesta contiene hasta 6 elementos distribuidos entre:\n- Subastas pendientes de firma (prioridad máxima)\n- Notificaciones no leídas (llenan los slots restantes)"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "CIA_NOT_FOUND",
                  "status": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. El usuario no tiene una empresa asociada o el token es inválido.\n\n**CIA_NOT_FOUND**:\n- El usuario autenticado no tiene una empresa asignada\n- La empresa fue desactivada o eliminada\n- El campo `users` en la colección de empresas no coincide con el usuario"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND",
                  "status": 404
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado. El token JWT no corresponde a un usuario válido en el sistema.\n\n**USER_NOT_FOUND**:\n- El token contiene un ID de usuario que no existe\n- El usuario fue eliminado después de generar el token"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Get dashboard notifications and pending actions",
        "tags": [
          "Notifications"
        ]
      }
    },
    "/company/notifications/read/{id}": {
      "put": {
        "deprecated": false,
        "description": "## Propósito\nActualiza el estado de una notificación para marcarla como leída, registrando la fecha\ny hora actual de la lectura.\n\n## Objetivo\nPermitir a las empresas marcar notificaciones como leídas para mantener un seguimiento\npreciso de las alertas pendientes y mejorar la experiencia de usuario.\n\n## Casos de uso\n- **Interacción del usuario**: Cuando el usuario hace clic en una notificación\n- **Sincronización multi-dispositivo**: Marcar como leída en todos los dispositivos\n- **Lectura masiva**: Marcar múltiples notificaciones en lote\n- **Gestión de notificaciones**: Actualización del estado desde el panel de notificaciones\n\n## Flujo de actualización\n\n```mermaid\nflowchart TD\n    A[PUT /read/{id}] --> B{ID Provided?}\n    B -->|No| C[Check Body]\n    B -->|Yes| D[Use Path ID]\n    C -->|Found| E[Use Body ID]\n    C -->|Not Found| F[Check Query]\n    F -->|Found| G[Use Query ID]\n    F -->|Not Found| H[400 Bad Request]\n    D --> I{User Authenticated?}\n    E --> I\n    G --> I\n    I -->|No| J[404 USER_NOT_FOUND]\n    I -->|Yes| K{Company Exists?}\n    K -->|No| L[401 CIA_NOT_FOUND]\n    K -->|Yes| M{Notification Exists?}\n    M -->|No| N[404 NOTIFICATION_NOT_FOUND]\n    M -->|Yes| O[Update Notification]\n    O --> P[Set isRead: true]\n    P --> Q[Set readedAt: now]\n    Q --> R[Save to Database]\n    R --> S[Return Notification - 200]\n```\n\n## Flexibilidad de parámetros\n\nEl ID de la notificación puede enviarse de **tres formas** (en orden de prioridad):\n\n1. **Path parameter** (recomendado): `/company/notifications/read/{id}`\n2. **Query parameter**: `/company/notifications/read?id={id}`\n3. **Request body**: `{ \"id\": \"{id}\" }`\n\nEl sistema verifica en este orden: body → query → params\n\n## Campos actualizados\n\n**isRead**: Se establece en `true`\n**readedAt**: Se establece con la fecha y hora actual del servidor\n\n## Notas importantes\n\n- La operación es **idempotente**: llamar al endpoint múltiples veces con el mismo ID no causa errores\n- Si la notificación ya estaba marcada como leída, se actualiza `readedAt` nuevamente\n- Solo se pueden marcar notificaciones que existen en el sistema\n- No se valida que la notificación pertenezca a la empresa del usuario (por diseño actual)\n- La fecha `readedAt` se establece en formato UTC del servidor\n\n## Errores\n\n- **401 CIA_NOT_FOUND**: El usuario no tiene una empresa asociada\n- **404 USER_NOT_FOUND**: El token no corresponde a un usuario válido\n- **404 NOTIFICATION_NOT_FOUND**: El ID no corresponde a una notificación existente",
        "parameters": [
          {
            "description": "ID único de la notificación a marcar como leída.\n\n**Alternativas**: El ID también puede enviarse vía query parameter (?id=) o en el body ({\"id\": \"...\"}).\n\nFormato: String hexadecimal de 24 caracteres (MongoDB ObjectId)",
            "example": "507f1f77bcf86cd799439011",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "maxLength": 24,
              "minLength": 24,
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "507f1f77bcf86cd799439011",
                  "createdAt": "2025-02-12T10:00:00.000Z",
                  "isRead": true,
                  "readedAt": "2025-02-12T14:30:00.000Z",
                  "ref": {
                    "_id": "507f1f77bcf86cd799499999",
                    "service_code": "AU12345"
                  },
                  "service_code": "AU12345",
                  "subType": "newBidWinner",
                  "type": "auction",
                  "updatedAt": "2025-02-12T14:30:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Notification"
                }
              }
            },
            "description": "Notificación marcada como leída exitosamente"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "CIA_NOT_FOUND",
                  "status": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. El usuario no tiene una empresa asociada.\n\n**CIA_NOT_FOUND**:\n- El usuario autenticado no tiene una empresa asignada\n- La empresa fue desactivada o eliminada"
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "notificationNotFound": {
                    "summary": "Notificación no encontrada",
                    "value": {
                      "message": "NOTIFICATION_NOT_FOUND",
                      "status": 404
                    }
                  },
                  "userNotFound": {
                    "summary": "Usuario no encontrado",
                    "value": {
                      "message": "USER_NOT_FOUND",
                      "status": 404
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Recurso no encontrado. Puede ser por dos razones:\n\n**USER_NOT_FOUND**:\n- El token contiene un ID de usuario que no existe\n\n**NOTIFICATION_NOT_FOUND**:\n- El ID proporcionado no corresponde a ninguna notificación\n- La notificación fue eliminada previamente"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Mark notification as read",
        "tags": [
          "Notifications"
        ]
      }
    },
    "/company/notifications/{id}": {
      "delete": {
        "deprecated": false,
        "description": "## Propósito\nElimina permanentemente una notificación del sistema. Esta acción es irreversible y\nlibera espacio en la base de datos.\n\n## Objetivo\nPermitir a las empresas gestionar sus notificaciones eliminando aquellas que no necesitan\nmantener, permitiendo una limpieza controlada del histórico de alertas.\n\n## Casos de uso\n- **Limpieza de notificaciones antiguas**: Eliminar notificaciones históricas no relevantes\n- **Eliminación individual**: Cuando el usuario decide borrar una notificación específica\n- **Gestión de almacenamiento**: Reducir el tamaño de la base de datos eliminando registros innecesarios\n- **Eliminación después de acción**: Borrar notificaciones después de completar la acción asociada\n\n## Flujo de eliminación\n\n```mermaid\nflowchart TD\n    A[DELETE /{id}] --> B{ID Provided?}\n    B -->|No| C[Check Body]\n    B -->|Yes| D[Use Path ID]\n    C -->|Found| E[Use Body ID]\n    C -->|Not Found| F[Check Query]\n    F -->|Found| G[Use Query ID]\n    F -->|Not Found| H[400 Bad Request]\n    D --> I{User Authenticated?}\n    E --> I\n    G --> I\n    I -->|No| J[404 USER_NOT_FOUND]\n    I -->|Yes| K{Company Exists?}\n    K -->|No| L[401 CIA_NOT_FOUND]\n    K -->|Yes| M{Notification Exists?}\n    M -->|No| N[404 NOTIFICATION_NOT_FOUND]\n    M -->|Yes| O[Delete Notification]\n    O --> P[Permanent Deletion]\n    P --> Q[Return Deleted Notification - 200]\n```\n\n## Flexibilidad de parámetros\n\nEl ID de la notificación puede enviarse de **tres formas** (en orden de prioridad):\n\n1. **Path parameter** (recomendado): `/company/notifications/{id}`\n2. **Query parameter**: `/company/notifications/{id}?id={id}` (no recomendado, redundante)\n3. **Request body**: `{ \"id\": \"{id}\" }` (alternativa para某些 clientes)\n\nEl sistema verifica en este orden: body → query → params\n\n## Impacto de la eliminación\n\n- **Permanente**: La notificación no se puede recuperar\n- **Inmediata**: La eliminación se efectúa al momento de la llamada\n- **Sin rollback**: No hay papelera de reciclaje ni restauración\n- **Desindexación**: La notificación desaparece de todas las consultas\n\n## Consideraciones\n\n- Se recomienda implementar una confirmación en el cliente antes de llamar al endpoint\n- La eliminación no afecta al objeto relacionado (subasta, entrega, etc.)\n- Solo se elimina el registro de notificación\n- Para eliminaciones masivas, considerar implementar un endpoint batch\n- Esta operación está destinada a mantenimiento de notificaciones, no a operaciones frecuentes\n\n## Notas importantes\n\n- Solo se pueden eliminar notificaciones que existen en el sistema\n- No se valida que la notificación pertenezca a la empresa del usuario (por diseño actual)\n- La eliminación es permanente e irreversible\n- No hay límite de cuántas notificaciones se pueden eliminar\n- La operación es atómica: o se elimina completamente o falla\n\n## Errores\n\n- **401 CIA_NOT_FOUND**: El usuario no tiene una empresa asociada\n- **404 USER_NOT_FOUND**: El token no corresponde a un usuario válido\n- **404 NOTIFICATION_NOT_FOUND**: El ID no corresponde a una notificación existente",
        "parameters": [
          {
            "description": "ID único de la notificación a eliminar permanentemente.\n\n**Alternativas**: El ID también puede enviarse vía query parameter (?id=) o en el body ({\"id\": \"...\"}).\n\nFormato: String hexadecimal de 24 caracteres (MongoDB ObjectId)",
            "example": "507f1f77bcf86cd799439011",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "maxLength": 24,
              "minLength": 24,
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "507f1f77bcf86cd799439011",
                  "createdAt": "2025-02-12T10:00:00.000Z",
                  "isRead": true,
                  "readedAt": "2025-02-12T14:30:00.000Z",
                  "ref": {
                    "_id": "507f1f77bcf86cd799499999",
                    "service_code": "AU12345"
                  },
                  "service_code": "AU12345",
                  "subType": "newBidWinner",
                  "type": "auction",
                  "updatedAt": "2025-02-12T14:30:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Notification"
                }
              }
            },
            "description": "Notificación eliminada exitosamente.\n\nLa respuesta contiene la notificación tal como existed antes de ser eliminada.\nEsta información puede ser útil para:\n- Confirmar qué se eliminó\n- Actualizar la UI localmente\n- Logging/auditoría"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "CIA_NOT_FOUND",
                  "status": 401
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. El usuario no tiene una empresa asociada.\n\n**CIA_NOT_FOUND**:\n- El usuario autenticado no tiene una empresa asignada\n- La empresa fue desactivada o eliminada"
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "notificationNotFound": {
                    "summary": "Notificación no encontrada",
                    "value": {
                      "message": "NOTIFICATION_NOT_FOUND",
                      "status": 404
                    }
                  },
                  "userNotFound": {
                    "summary": "Usuario no encontrado",
                    "value": {
                      "message": "USER_NOT_FOUND",
                      "status": 404
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Recurso no encontrado. Puede ser por dos razones:\n\n**USER_NOT_FOUND**:\n- El token contiene un ID de usuario que no existe\n\n**NOTIFICATION_NOT_FOUND**:\n- El ID proporcionado no corresponde a ninguna notificación\n- La notificación ya fue eliminada previamente"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Delete notification",
        "tags": [
          "Notifications"
        ]
      }
    },
    "/company/payment/bank_account_link": {
      "get": {
        "summary": "Obtener enlace de onboarding para cuenta bancaria",
        "deprecated": false,
        "description": "## Purpose\nGenera un enlace de Stripe Account Link para que la compañía complete\no retome el proceso de vinculación de su cuenta bancaria.\n\n## Objective\nIniciar o continuar el flujo de onboarding de Stripe Connect para\nconfigurar la cuenta bancaria donde se recibirán las transferencias.\n\n## Use Cases\n- Iniciar la vinculación de cuenta bancaria por primera vez\n- Retomar un proceso de onboarding interrumpido o expirado\n- Actualizar datos bancarios existentes\n- Completar requisitos pendientes de Stripe\n\n## Notes\n- Requiere autenticación JWT (bearerAuth)\n- Genera un `account_link` de tipo `account_onboarding`\n- El enlace expira en ~5 minutos (por defecto de Stripe)\n- Requiere que `stripe_account` esté configurado en `payment_settings`\n- Stripe redirige a `/onboarding/return` al completar y a `/onboarding/refresh` si expira\n",
        "tags": [
          "stripe-connect"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Enlace de Account Link generado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "boolean",
                      "example": true
                    },
                    "data": {
                      "type": "object",
                      "description": "Objeto AccountLink de Stripe",
                      "properties": {
                        "object": {
                          "type": "string",
                          "example": "account_link"
                        },
                        "created": {
                          "type": "integer",
                          "description": "Timestamp Unix de creación",
                          "example": 1640000000
                        },
                        "expires_at": {
                          "type": "integer",
                          "description": "Timestamp Unix de expiración",
                          "example": 1640000300
                        },
                        "url": {
                          "type": "string",
                          "description": "URL para completar el onboarding",
                          "example": "https://connect.stripe.com/setup/s/AbCdEfGhIjKl"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "status": true,
                  "data": {
                    "object": "account_link",
                    "created": 1640000000,
                    "expires_at": 1640000300,
                    "url": "https://connect.stripe.com/setup/s/AbCdEfGhIjKl"
                  }
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Cuenta Stripe Connect o compañía no encontrada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "cia_not_found": {
                    "value": {
                      "message": "CIA_NOT_FOUND"
                    }
                  },
                  "account_not_found": {
                    "value": {
                      "message": "STRIPE_ACCOUNT_NOT_FOUND"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Usuario no encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "USER_NOT_FOUND"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "summary": "Generar enlace de onboarding para cuenta bancaria (POST)",
        "deprecated": false,
        "description": "## Purpose\nFuncionalidad idéntica al `GET /bank_account_link`. Disponible como POST\npara clientes que requieran enviar datos adicionales en el body.\n\n## Objective\nGenerar un nuevo Account Link de Stripe para el proceso de onboarding\nde cuenta bancaria mediante método POST.\n\n## Use Cases\n- Alternativa POST para clientes que prefieren este método\n- Reiniciar el proceso de vinculación bancaria enviando parámetros\n- Integración con flujos que usan exclusivamente POST\n\n## Notes\n- Requiere autenticación JWT (bearerAuth)\n- Misma lógica que `GET /bank_account_link`\n- Requiere `stripe_account` configurado en `payment_settings`\n",
        "tags": [
          "stripe-connect"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Sin campos requeridos. El enlace se genera con la cuenta Stripe del usuario autenticado.",
                "properties": {
                  "refresh": {
                    "type": "boolean",
                    "description": "Indica si se desea generar un enlace nuevo aunque ya exista uno activo",
                    "example": false
                  }
                },
                "example": {
                  "refresh": false
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Enlace de Account Link generado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "boolean",
                      "example": true
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "object": {
                          "type": "string",
                          "example": "account_link"
                        },
                        "url": {
                          "type": "string",
                          "example": "https://connect.stripe.com/setup/s/AbCdEfGhIjKl"
                        },
                        "expires_at": {
                          "type": "integer",
                          "example": 1640000300
                        }
                      }
                    }
                  }
                },
                "example": {
                  "status": true,
                  "data": {
                    "object": "account_link",
                    "url": "https://connect.stripe.com/setup/s/AbCdEfGhIjKl",
                    "expires_at": 1640000300
                  }
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Cuenta Stripe Connect o compañía no encontrada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "cia_not_found": {
                    "value": {
                      "message": "CIA_NOT_FOUND"
                    }
                  },
                  "account_not_found": {
                    "value": {
                      "message": "STRIPE_ACCOUNT_NOT_FOUND"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Usuario no encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "USER_NOT_FOUND"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/company/payment/changePlans": {
      "post": {
        "deprecated": false,
        "description": "## Purpose\nGenera una sesión del portal de cliente de Stripe donde la compañía puede\ngestionar su suscripción, cambiar de plan, actualizar tarjeta o ver facturas.\n\n## Objective\nProporcionar acceso al portal self-service de Stripe con validación de\nque la cuenta esté activa y no expirada.\n\n## Use Cases\n- Cambiar de plan Basic a Professional o viceversa\n- Cancelar suscripción activa\n- Actualizar método de pago de la suscripción\n- Ver historial de facturas de Stripe\n- Descargar facturas en PDF\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request - returnUrl] --> B{returnUrl provided?}\n  B -->|No| C[400 MISSING_RETURN_URL]\n  B -->|Yes| D{User authenticated?}\n  D -->|No| E[404 USER_NOT_FOUND]\n  D -->|Yes| F{Company found?}\n  F -->|No| G[401 CIA_NOT_FOUND]\n  F -->|Yes| H{UserActivity exists?}\n  H -->|No| I[404 USER_NOT_FOUND]\n  H -->|Yes| J{Account not expired?}\n  J -->|Expired| K[404 USER_EXPIRED]\n  J -->|Active| L[Create Customer Portal session]\n  L --> M[200 Portal URL]\n```\n\n## Notes\n- Requiere autenticación JWT (bearerAuth)\n- A diferencia del módulo truckers, **valida que la cuenta no esté expirada**\n- Verifica `userActivity.dateEnd > now` antes de crear la sesión\n- La URL de retorno (`returnUrl`) es obligatoria en el body\n- La sesión del portal expira en ~5 minutos (comportamiento de Stripe)\n- Requiere `stripe_customer` configurado en la compañía\n",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "returnUrl": "https://app.cargoffer.com/admin/config"
              },
              "schema": {
                "properties": {
                  "returnUrl": {
                    "description": "URL donde Stripe redirige al salir del portal",
                    "example": "https://app.cargoffer.com/admin/config",
                    "format": "uri",
                    "maxLength": 2048,
                    "minLength": 10,
                    "type": "string"
                  }
                },
                "required": [
                  "returnUrl"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": "https://billing.stripe.com/p/session/test_AbCd...",
                  "status": true
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "URL de la sesión del portal de Stripe",
                      "example": "https://billing.stripe.com/p/session/test_...",
                      "type": "string"
                    },
                    "status": {
                      "example": true,
                      "type": "boolean"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "URL del portal de cliente de Stripe",
            "headers": {}
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "message": "MISSING_RETURN_URL"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "URL de retorno no proporcionada",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "CIA_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Compañía no encontrada",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "user_expired": {
                    "summary": "Cuenta de usuario expirada",
                    "value": {
                      "message": "USER_EXPIRED"
                    }
                  },
                  "user_not_found": {
                    "value": {
                      "message": "USER_NOT_FOUND"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado o cuenta expirada",
            "headers": {}
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error al crear sesión del portal",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Acceder al portal de cliente de Stripe para gestionar suscripción",
        "tags": [
          "payment-processing"
        ]
      }
    },
    "/company/payment/default_payment_method/{id}": {
      "post": {
        "summary": "Establecer método de pago por defecto",
        "deprecated": false,
        "description": "## Purpose\nDesigna un método de pago existente como el predeterminado para todas\nlas operaciones de pago futuras de la compañía.\n\n## Objective\nActualizar `payment_settings.default_payment_method` en la compañía\npara que los pagos de entregas se procesen con esa tarjeta.\n\n## Use Cases\n- Seleccionar la tarjeta principal tras añadir una nueva\n- Cambiar el método predeterminado tras renovar una tarjeta\n- Designar método activo antes de procesar una entrega\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request - id] --> B{User authenticated?}\n  B -->|No| C[404 USER_NOT_FOUND]\n  B -->|Yes| D{Company found?}\n  D -->|No| E[401 CIA_NOT_FOUND]\n  D -->|Yes| F{stripe_customer exists?}\n  F -->|No| G[404 STRIPE_CUSTOMER_NOT_FOUND]\n  F -->|Yes| H{PM belongs to customer?}\n  H -->|No| I[401 PAYMENT_METHOD_NOT_FOUND]\n  H -->|Yes| J[Set as default_payment_method]\n  J --> K[Save company]\n  K --> L[200 success: true]\n```\n\n## Notes\n- Requiere autenticación JWT (bearerAuth)\n- Solo un método puede ser predeterminado a la vez\n- La validación de propiedad verifica que el PM pertenece al `stripe_customer` de la compañía\n- El cambio es inmediato y aplica a los próximos pagos de entrega\n",
        "tags": [
          "payment-methods"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "ID del método de pago en Stripe (formato `pm_...`)",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^pm_[a-zA-Z0-9]+"
            },
            "example": "pm_1AbCdEfGhIjKlMnOp"
          }
        ],
        "responses": {
          "200": {
            "description": "Método de pago establecido como predeterminado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                },
                "example": {
                  "success": true
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Compañía no encontrada o método no pertenece a la compañía",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "cia_not_found": {
                    "value": {
                      "message": "CIA_NOT_FOUND"
                    }
                  },
                  "pm_not_found": {
                    "value": {
                      "message": "PAYMENT_METHOD_NOT_FOUND"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Usuario o cliente Stripe no encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "user_not_found": {
                    "value": {
                      "message": "USER_NOT_FOUND"
                    }
                  },
                  "stripe_not_found": {
                    "value": {
                      "message": "STRIPE_CUSTOMER_NOT_FOUND"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "500": {
            "description": "Error al guardar",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/company/payment/onboarding/refresh": {
      "get": {
        "summary": "Callback de refresh del onboarding de Stripe Connect",
        "deprecated": false,
        "description": "## Purpose\nEndpoint de callback al que Stripe redirige cuando el enlace de onboarding\nha expirado o cuando el proceso necesita ser retomado con información adicional.\n\n## Objective\nActualizar los requisitos pendientes del onboarding en la base de datos\ny mostrar una página con los campos que la compañía debe completar.\n\n## Use Cases\n- Enlace de onboarding expirado (>5 minutos)\n- Compañía que interrumpió el proceso y necesita retomarlo\n- Stripe detecta información incompleta o incorrecta\n- Mostrar lista de campos pendientes (`currently_due`, `past_due`)\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request - account_id] --> B{account_id valid?}\n  B -->|No| C[400 STRIPE_ACCOUNT_ID_INVALID]\n  B -->|Yes| D[Fetch Stripe account]\n  D --> E{Account found?}\n  E -->|No| F[404 STRIPE_ACCOUNT_NOT_FOUND]\n  E -->|Yes| G{Company found?}\n  G -->|No| H[404 CIA_NOT_FOUND]\n  G -->|Yes| I[Set status = requirements_needed]\n  I --> J[Save to DB]\n  J --> K[Render HTML with requirements list]\n```\n\n## Notes\n- **No requiere autenticación** (es callback público de Stripe)\n- Siempre establece `onboarding_status = 'requirements_needed'`\n- Para generar un nuevo enlace: llamar a `GET /bank_account_link`\n- Incluye `currently_due`, `past_due` y `eventually_due` en la plantilla\n- Retorna HTML, no JSON\n",
        "tags": [
          "stripe-onboarding"
        ],
        "parameters": [
          {
            "name": "account_id",
            "in": "query",
            "description": "ID de la cuenta Stripe Connect. Debe comenzar con `acct_`.\nAñadido automáticamente por Stripe en la URL de refresh.\n",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^acct_[a-zA-Z0-9]+"
            },
            "example": "acct_AbCdEfGhIjKlMnOp"
          }
        ],
        "responses": {
          "200": {
            "description": "Página HTML con los requisitos pendientes del onboarding",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string",
                  "description": "Plantilla `stripe_onboarding` con lista de requisitos pendientes"
                }
              }
            },
            "headers": {}
          },
          "400": {
            "description": "account_id con formato inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "STRIPE_ACCOUNT_ID_INVALID"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Cuenta Stripe o compañía no encontrada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "account_not_found": {
                    "value": {
                      "message": "STRIPE_ACCOUNT_NOT_FOUND"
                    }
                  },
                  "cia_not_found": {
                    "value": {
                      "message": "CIA_NOT_FOUND"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "500": {
            "description": "Error al procesar o renderizar la plantilla",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "RENDER_TEMPLATE_ERROR"
                }
              }
            },
            "headers": {}
          }
        }
      }
    },
    "/company/payment/onboarding/return": {
      "get": {
        "summary": "Callback de retorno del onboarding de Stripe Connect",
        "deprecated": false,
        "description": "## Purpose\nEndpoint de callback al que Stripe redirige a la compañía tras completar\n(o intentar completar) el proceso de onboarding de Stripe Connect.\n\n## Objective\nActualizar el estado del onboarding en la base de datos, registrar la\ncuenta bancaria vinculada si existe, y mostrar una página HTML de confirmación.\n\n## Use Cases\n- Confirmación tras completar el onboarding de Stripe Connect\n- Detección de requisitos pendientes tras retornar de Stripe\n- Actualización automática de `onboarding_status` en la compañía\n- Registro de `stripe_bankaccount` si se vinculó una cuenta bancaria\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request - account_id, lang] --> B{account_id valid format?}\n  B -->|No - not acct_...| C[400 STRIPE_ACCOUNT_ID_INVALID]\n  B -->|Yes| D[Fetch Stripe account]\n  D --> E{Account found?}\n  E -->|No| F[404 STRIPE_ACCOUNT_NOT_FOUND]\n  E -->|Yes| G{Company found by stripe_account?}\n  G -->|No| H[404 CIA_NOT_FOUND]\n  G -->|Yes| I{Bank account in Stripe?}\n  I -->|Yes| J[Save stripe_bankaccount ID]\n  I -->|No| K[Continue]\n  J --> K\n  K --> L{requirements.currently_due empty?}\n  L -->|Yes| M[status = completed + redirect_url]\n  L -->|No| N[status = requirements_needed]\n  M --> O[Render HTML template]\n  N --> O\n```\n\n## Notes\n- **No requiere autenticación** (es callback público de Stripe)\n- El `account_id` debe comenzar con `acct_`\n- `lang` controla el idioma de la página HTML renderizada (default: `es`)\n- Actualiza `payment_settings.onboarding_status` y `payment_settings.onboarding_last_update`\n- Si hay cuenta bancaria, actualiza `payment_settings.stripe_bankaccount`\n- Retorna HTML, no JSON\n",
        "tags": [
          "stripe-onboarding"
        ],
        "parameters": [
          {
            "name": "account_id",
            "in": "query",
            "description": "ID de la cuenta Stripe Connect. Debe comenzar con `acct_`.\nAñadido automáticamente por Stripe en la URL de retorno.\n",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^acct_[a-zA-Z0-9]+"
            },
            "example": "acct_AbCdEfGhIjKlMnOp"
          },
          {
            "name": "lang",
            "in": "query",
            "description": "Idioma de la página de confirmación renderizada",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "es",
                "en"
              ],
              "default": "es"
            },
            "example": "es"
          }
        ],
        "responses": {
          "200": {
            "description": "Página HTML con el resultado del onboarding",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string",
                  "description": "Plantilla `stripe_onboarding` renderizada con estado del proceso"
                }
              }
            },
            "headers": {}
          },
          "400": {
            "description": "account_id con formato inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "STRIPE_ACCOUNT_ID_INVALID"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Cuenta Stripe o compañía no encontrada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "account_not_found": {
                    "value": {
                      "message": "STRIPE_ACCOUNT_NOT_FOUND"
                    }
                  },
                  "cia_not_found": {
                    "value": {
                      "message": "CIA_NOT_FOUND"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "500": {
            "description": "Error al procesar o renderizar la plantilla",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "RENDER_TEMPLATE_ERROR"
                }
              }
            },
            "headers": {}
          }
        }
      }
    },
    "/company/payment/pay": {
      "post": {
        "deprecated": false,
        "description": "## Purpose\nGenera un Payment Intent de Stripe para pagar el importe de una entrega\nespecífica usando el método de pago por defecto de la compañía.\n\n## Objective\nIniciar el cobro a la compañía por una entrega adjudicada, incluyendo la\ncomisión de la plataforma, y notificar a ambas partes por email.\n\n## Use Cases\n- Compañía paga por una entrega adjudicada mediante subasta\n- Liquidación automática de entregas completadas\n- Procesamiento de pagos entre compañías y transportistas\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request - serviceCode] --> B{User authenticated?}\n  B -->|No| C[404 USER_NOT_FOUND]\n  B -->|Yes| D{Company found?}\n  D -->|No| E[401 CIA_NOT_FOUND]\n  D -->|Yes| F{withStripe enabled?}\n  F -->|No| G[403 STRIPE_PAYMENTS_DISABLED]\n  F -->|Yes| H{default_payment_method set?}\n  H -->|No| I[401 PAYMENT_METHOD_NOT_FOUND]\n  H -->|Yes| J{Delivery found?}\n  J -->|No| K[401 DELIVERY_NOT_FOUND]\n  J -->|Yes| L[Calculate amount + platform fee]\n  L --> M[Generate Payment Intent in Stripe]\n  M --> N[Save payment_intent to delivery]\n  N --> O[Send emails to both parties]\n  O --> P[200 success: true]\n```\n\n## Notes\n- Requiere autenticación JWT (bearerAuth)\n- Requiere `withStripe=true` en `payment_settings` de la compañía\n- Requiere método de pago por defecto (`default_payment_method`) configurado\n- El fee de plataforma se obtiene de `Settings` (default 3%)\n- Envía emails a la compañía y al transportista (trucker_cia)\n- El `payment_intent` ID se guarda en el documento `delivery`\n",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "serviceCode": "CARGO-2024-000123"
              },
              "schema": {
                "properties": {
                  "serviceCode": {
                    "description": "Código único de la entrega a pagar",
                    "example": "CARGO-2024-000123",
                    "maxLength": 50,
                    "minLength": 1,
                    "type": "string"
                  }
                },
                "required": [
                  "serviceCode"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "success": true
                },
                "schema": {
                  "properties": {
                    "success": {
                      "example": true,
                      "type": "boolean"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Payment Intent creado y pago iniciado",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "cia_not_found": {
                    "value": {
                      "message": "CIA_NOT_FOUND"
                    }
                  },
                  "delivery_not_found": {
                    "value": {
                      "message": "DELIVERY_NOT_FOUND"
                    }
                  },
                  "no_payment_method": {
                    "value": {
                      "message": "PAYMENT_METHOD_NOT_FOUND"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Compañía, entrega o método de pago no encontrado",
            "headers": {}
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "message": "STRIPE_PAYMENTS_DISABLED"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Pagos con Stripe no habilitados para esta compañía",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado",
            "headers": {}
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error al procesar el pago en Stripe",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Procesar pago de una entrega",
        "tags": [
          "payment-processing"
        ]
      }
    },
    "/company/payment/payment_method": {
      "get": {
        "summary": "Obtener lista de métodos de pago",
        "deprecated": false,
        "description": "## Purpose\nRecupera todos los métodos de pago (tarjetas) registrados para la\ncompañía del usuario autenticado.\n\n## Objective\nMostrar las tarjetas disponibles indicando cuál es la predeterminada\ny cuáles pueden eliminarse de forma segura.\n\n## Use Cases\n- Listar tarjetas en la sección de configuración de pagos\n- Identificar la tarjeta predeterminada para futuros pagos\n- Determinar qué tarjetas pueden eliminarse (no están en deliveries activos)\n\n## Notes\n- Requiere autenticación JWT (bearerAuth)\n- `is_default`: coincide con `payment_settings.default_payment_method`\n- `can_delete: false` si el método está en uso en deliveries activos o es el predeterminado\n- Solo devuelve datos parciales de tarjeta (PCI compliant: sin número completo)\n",
        "tags": [
          "payment-methods"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Lista de métodos de pago de la compañía",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "boolean",
                      "example": true
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "ID del método de pago en Stripe",
                            "example": "pm_AbCdEfGhIjKlMnOp"
                          },
                          "billing_details": {
                            "type": "object",
                            "properties": {
                              "name": {
                                "type": "string",
                                "description": "Nombre del titular",
                                "example": "Juan García López"
                              }
                            }
                          },
                          "card": {
                            "type": "object",
                            "properties": {
                              "country": {
                                "type": "string",
                                "description": "País emisor",
                                "example": "ES"
                              },
                              "brand": {
                                "type": "string",
                                "enum": [
                                  "visa",
                                  "mastercard",
                                  "amex",
                                  "discover",
                                  "jcb",
                                  "unionpay",
                                  "unknown"
                                ],
                                "example": "visa"
                              },
                              "exp_month": {
                                "type": "integer",
                                "description": "Mes de expiración",
                                "example": 12
                              },
                              "exp_year": {
                                "type": "integer",
                                "description": "Año de expiración",
                                "example": 2026
                              },
                              "last4": {
                                "type": "string",
                                "description": "Últimos 4 dígitos",
                                "example": "4242"
                              }
                            }
                          },
                          "is_default": {
                            "type": "boolean",
                            "description": "Si es el método predeterminado",
                            "example": true
                          },
                          "can_delete": {
                            "type": "boolean",
                            "description": "Si se puede eliminar (no en uso, no predeterminado)",
                            "example": false
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "status": true,
                  "data": [
                    {
                      "id": "pm_1AbCdEfGhIjKlMnOp",
                      "billing_details": {
                        "name": "Juan García López"
                      },
                      "card": {
                        "country": "ES",
                        "brand": "visa",
                        "exp_month": 12,
                        "exp_year": 2026,
                        "last4": "4242"
                      },
                      "is_default": true,
                      "can_delete": false
                    },
                    {
                      "id": "pm_2AbCdEfGhIjKlMnOp",
                      "billing_details": {
                        "name": "Juan García López"
                      },
                      "card": {
                        "country": "ES",
                        "brand": "mastercard",
                        "exp_month": 6,
                        "exp_year": 2025,
                        "last4": "5555"
                      },
                      "is_default": false,
                      "can_delete": true
                    }
                  ]
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Compañía no encontrada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "CIA_NOT_FOUND"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Usuario no encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "USER_NOT_FOUND"
                }
              }
            },
            "headers": {}
          },
          "500": {
            "description": "Error al obtener métodos de pago de Stripe",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/company/payment/payment_method/{id}": {
      "delete": {
        "summary": "Eliminar método de pago",
        "deprecated": false,
        "description": "## Purpose\nElimina permanentemente un método de pago (tarjeta) de la cuenta Stripe\nde la compañía.\n\n## Objective\nPermitir la depuración del portafolio de métodos de pago eliminando\ntarjetas obsoletas que no estén actualmente en uso.\n\n## Use Cases\n- Eliminar una tarjeta caducada\n- Remover una tarjeta añadida por error\n- Depurar la lista de métodos de pago (máx. 10)\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request - id] --> B{User authenticated?}\n  B -->|No| C[404 USER_NOT_FOUND]\n  B -->|Yes| D{Company found?}\n  D -->|No| E[401 CIA_NOT_FOUND]\n  D -->|Yes| F{stripe_customer exists?}\n  F -->|No| G[404 STRIPE_CUSTOMER_NOT_FOUND]\n  F -->|Yes| H{PM belongs to customer?}\n  H -->|No| I[401 PAYMENT_METHOD_NOT_FOUND]\n  H -->|Yes| J{PM active in deliveries?}\n  J -->|Yes| K[401 PAYMENT_METHOD_IN_USE]\n  J -->|No| L[Delete from Stripe]\n  L --> M[200 success: true]\n```\n\n## Notes\n- Requiere autenticación JWT (bearerAuth)\n- No se puede eliminar si está activo en deliveries con `payment_intent` pagados\n- No se puede eliminar si es el método predeterminado (`is_default: true`)\n- La eliminación en Stripe es permanente (no recuperable)\n",
        "tags": [
          "payment-methods"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "ID del método de pago en Stripe (formato `pm_...`)",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^pm_[a-zA-Z0-9]+"
            },
            "example": "pm_1AbCdEfGhIjKlMnOp"
          }
        ],
        "responses": {
          "200": {
            "description": "Método de pago eliminado exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                },
                "example": {
                  "success": true
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Compañía no encontrada, método no existente o en uso activo",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "cia_not_found": {
                    "value": {
                      "message": "CIA_NOT_FOUND"
                    }
                  },
                  "pm_not_found": {
                    "value": {
                      "message": "PAYMENT_METHOD_NOT_FOUND"
                    }
                  },
                  "pm_in_use": {
                    "value": {
                      "message": "PAYMENT_METHOD_IN_USE"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Usuario o cliente Stripe no encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "user_not_found": {
                    "value": {
                      "message": "USER_NOT_FOUND"
                    }
                  },
                  "stripe_not_found": {
                    "value": {
                      "message": "STRIPE_CUSTOMER_NOT_FOUND"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "500": {
            "description": "Error al eliminar en Stripe",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/company/payment/register_stripe": {
      "post": {
        "summary": "Registrar compañía como cliente en Stripe",
        "deprecated": false,
        "description": "## Purpose\nCrea un cliente en Stripe para el usuario autenticado si aún no tiene\nuno registrado, y retorna el ID del cliente.\n\n## Objective\nInicializar la relación entre el usuario de la compañía y Stripe,\nobteniendo el `stripe_customer` ID necesario para todas las operaciones\nde pago posteriores.\n\n## Use Cases\n- Primer registro antes de configurar métodos de pago\n- Verificar si el usuario ya está registrado en Stripe\n- Prerequisito para suscripciones y pagos\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request] --> B{User authenticated?}\n  B -->|No| C[404 USER_NOT_FOUND]\n  B -->|Yes| D{User in DB?}\n  D -->|No| E[401 USER_NOT_FOUND]\n  D -->|Yes| F{stripe_customer exists?}\n  F -->|Yes| G[Return existing ID]\n  F -->|No| H[Create Stripe customer]\n  H --> I{Created OK?}\n  I -->|No| J[401 STRIPE_ACCOUNT_NOT_CREATED]\n  I -->|Yes| K[Save ID to user.payment_settings]\n  K --> L[200 success + stripe_customer]\n  G --> L\n```\n\n## Notes\n- Requiere autenticación JWT (bearerAuth)\n- Idempotente: si ya existe `stripe_customer`, retorna el ID existente\n- Opera sobre el `company_user`, no sobre la compañía directamente\n- Sin body requerido\n",
        "tags": [
          "payment-setup"
        ],
        "parameters": [],
        "requestBody": {
          "required": false,
          "description": "Sin campos requeridos. El registro usa los datos del usuario autenticado (email, nombre).",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Email de contacto (opcional, se usa el del usuario autenticado si no se envía)",
                    "example": "empresa@cargoffer.com"
                  },
                  "name": {
                    "type": "string",
                    "description": "Nombre del cliente (opcional, se usa el de la compañía si no se envía)",
                    "example": "Cargoffer S.L."
                  }
                },
                "example": {
                  "email": "empresa@cargoffer.com",
                  "name": "Cargoffer S.L."
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cliente Stripe registrado exitosamente o ya existente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "stripe_customer": {
                      "type": "string",
                      "description": "ID del cliente en Stripe (formato `cus_...`)",
                      "pattern": "^cus_[a-zA-Z0-9]+",
                      "example": "cus_AbCdEfGhIjKlMn"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "stripe_customer": "cus_AbCdEfGhIjKlMn"
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Usuario no encontrado en BD o error al crear cuenta en Stripe",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "user_not_found": {
                    "value": {
                      "message": "USER_NOT_FOUND"
                    }
                  },
                  "stripe_not_created": {
                    "value": {
                      "message": "STRIPE_ACCOUNT_NOT_CREATED"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Usuario autenticado no encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "USER_NOT_FOUND"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/company/payment/setup_payment_method": {
      "post": {
        "deprecated": false,
        "description": "## Purpose\nGenera una URL del portal de Stripe para que la compañía pueda agregar\nun nuevo método de pago (tarjeta de crédito/débito) de forma segura.\n\n## Objective\nRedirigir al usuario al portal de Stripe donde puede registrar su\ninformación de tarjeta sin exponer datos sensibles al backend.\n\n## Use Cases\n- Agregar la primera tarjeta de crédito/débito\n- Añadir métodos de pago adicionales (máx. 10)\n- Reemplazar tarjetas caducadas con nuevas\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request - returnUrl] --> B{returnUrl provided?}\n  B -->|No| C[400 MISSING_RETURN_URL]\n  B -->|Yes| D{User authenticated?}\n  D -->|No| E[404 USER_NOT_FOUND]\n  D -->|Yes| F{Company found?}\n  F -->|No| G[401 CIA_NOT_FOUND]\n  F -->|Yes| H[Get payment methods from Stripe]\n  H --> I{Count >= 10?}\n  I -->|Yes| J[200 - Empty string]\n  I -->|No| K[Create payment methods portal]\n  K --> L[200 - Portal URL]\n```\n\n## Notes\n- Requiere autenticación JWT (bearerAuth)\n- Límite de 10 métodos de pago por compañía; si se alcanza retorna `\"\"` (string vacío)\n- Requiere que la compañía tenga `stripe_customer` configurado (usar `/register_stripe` primero)\n- La `returnUrl` es donde Stripe redirige al usuario tras añadir el método\n- El código no valida explícitamente si `stripe_customer` existe antes de llamar a Stripe\n",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "returnUrl": "https://app.cargoffer.com/admin/payment-methods"
              },
              "schema": {
                "properties": {
                  "returnUrl": {
                    "description": "URL a la que redirige Stripe al completar la configuración",
                    "example": "https://app.cargoffer.com/admin/payment-methods",
                    "format": "uri",
                    "type": "string"
                  }
                },
                "required": [
                  "returnUrl"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "con_url": {
                    "summary": "URL del portal generada",
                    "value": {
                      "data": "https://billing.stripe.com/p/session/test_AbCd...",
                      "status": true
                    }
                  },
                  "limite_alcanzado": {
                    "summary": "Límite de 10 métodos alcanzado",
                    "value": {
                      "data": "",
                      "status": true
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "URL del portal de Stripe o string vacío",
                      "example": "https://billing.stripe.com/p/session/...",
                      "type": "string"
                    },
                    "status": {
                      "example": true,
                      "type": "boolean"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "URL del portal o string vacío si se alcanzó el límite de 10",
            "headers": {}
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "message": "MISSING_RETURN_URL"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "URL de retorno no proporcionada",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "CIA_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Compañía no encontrada",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado",
            "headers": {}
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error al crear sesión del portal",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Configurar nuevo método de pago",
        "tags": [
          "payment-methods"
        ]
      }
    },
    "/company/payment/stripe_account": {
      "get": {
        "summary": "Obtener detalles de la cuenta Stripe Connect",
        "deprecated": false,
        "description": "## Purpose\nRecupera la información completa de la cuenta Stripe Connect asociada\na la compañía, necesaria para recibir transferencias de pago.\n\n## Objective\nMostrar el estado de la cuenta Stripe Connect incluyendo requisitos\npendientes de verificación y cuentas bancarias vinculadas.\n\n## Use Cases\n- Verificar el estado del onboarding de Stripe Connect\n- Detectar requisitos pendientes (`requirements.currently_due`)\n- Mostrar información de la cuenta en el dashboard de pagos\n- Comprobar si la compañía puede recibir pagos (`payouts_enabled`)\n\n## Notes\n- Requiere autenticación JWT (bearerAuth)\n- Busca `stripe_account` en `cia.payment_settings` o `currentUser.payment_settings`\n- Retorna el objeto completo de la cuenta Stripe (todos los campos de Stripe)\n- Requiere que `stripe_account` esté configurado previamente\n",
        "tags": [
          "stripe-connect"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Datos completos de la cuenta Stripe Connect",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "boolean",
                      "example": true
                    },
                    "data": {
                      "type": "object",
                      "description": "Objeto Account de Stripe",
                      "properties": {
                        "id": {
                          "type": "string",
                          "description": "ID de la cuenta Stripe Connect",
                          "example": "acct_AbCdEfGhIjKlMnOp"
                        },
                        "charges_enabled": {
                          "type": "boolean",
                          "description": "Si la cuenta puede generar cargos",
                          "example": true
                        },
                        "payouts_enabled": {
                          "type": "boolean",
                          "description": "Si la cuenta puede recibir pagos",
                          "example": true
                        },
                        "country": {
                          "type": "string",
                          "example": "ES"
                        },
                        "default_currency": {
                          "type": "string",
                          "example": "eur"
                        },
                        "requirements": {
                          "type": "object",
                          "properties": {
                            "currently_due": {
                              "type": "array",
                              "description": "Requisitos pendientes inmediatos",
                              "items": {
                                "type": "string"
                              },
                              "example": []
                            },
                            "past_due": {
                              "type": "array",
                              "description": "Requisitos vencidos",
                              "items": {
                                "type": "string"
                              },
                              "example": []
                            },
                            "eventually_due": {
                              "type": "array",
                              "description": "Requisitos futuros",
                              "items": {
                                "type": "string"
                              },
                              "example": []
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "status": true,
                  "data": {
                    "id": "acct_AbCdEfGhIjKlMnOp",
                    "charges_enabled": true,
                    "payouts_enabled": true,
                    "country": "ES",
                    "default_currency": "eur",
                    "requirements": {
                      "currently_due": [],
                      "past_due": [],
                      "eventually_due": []
                    },
                    "external_accounts": {
                      "data": [
                        {
                          "id": "ba_AbCdEfGh",
                          "object": "bank_account",
                          "last4": "6789",
                          "country": "ES",
                          "currency": "eur"
                        }
                      ]
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Cuenta Stripe Connect o compañía no encontrada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "cia_not_found": {
                    "value": {
                      "message": "CIA_NOT_FOUND"
                    }
                  },
                  "account_not_found": {
                    "value": {
                      "message": "STRIPE_ACCOUNT_NOT_FOUND"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Usuario no encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "USER_NOT_FOUND"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "summary": "Actualizar datos de la cuenta Stripe Connect",
        "deprecated": false,
        "description": "## Purpose\nActualiza la información de la cuenta Stripe Connect de la compañía,\nincluyendo datos del representante legal y perfil de negocio.\n\n## Objective\nCompletar o actualizar el onboarding de Stripe Connect para cumplir\ncon los requisitos KYC/AML de Stripe y poder recibir pagos.\n\n## Use Cases\n- Completar el perfil de negocio durante el onboarding inicial\n- Actualizar información del representante legal (director)\n- Resolver campos en `requirements.currently_due`\n- Proporcionar documentación adicional requerida por Stripe\n\n## Notes\n- Requiere autenticación JWT (bearerAuth)\n- Crea o actualiza la `person` (representante legal) en Stripe Connect\n- Guarda el ID de la persona en `cia.stripe_director`\n- La actualización de datos de cuenta es asíncrona en Stripe\n- Requiere que `stripe_account` esté configurado en `payment_settings`\n",
        "tags": [
          "stripe-connect"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Campos a actualizar en la cuenta Stripe Connect",
                "properties": {
                  "business_profile": {
                    "type": "object",
                    "description": "Perfil público del negocio",
                    "properties": {
                      "name": {
                        "type": "string",
                        "description": "Nombre del negocio",
                        "example": "Transportes García S.L."
                      },
                      "url": {
                        "type": "string",
                        "format": "uri",
                        "description": "URL del sitio web del negocio",
                        "example": "https://transportesgarcia.com"
                      }
                    }
                  },
                  "individual": {
                    "type": "object",
                    "description": "Datos del representante legal (persona física)",
                    "properties": {
                      "first_name": {
                        "type": "string",
                        "example": "Juan"
                      },
                      "last_name": {
                        "type": "string",
                        "example": "García López"
                      },
                      "dob": {
                        "type": "object",
                        "description": "Fecha de nacimiento",
                        "properties": {
                          "day": {
                            "type": "integer",
                            "example": 15
                          },
                          "month": {
                            "type": "integer",
                            "example": 6
                          },
                          "year": {
                            "type": "integer",
                            "example": 1980
                          }
                        }
                      }
                    }
                  },
                  "business_type": {
                    "type": "string",
                    "enum": [
                      "individual",
                      "company",
                      "non_profit",
                      "government_entity"
                    ],
                    "description": "Tipo de entidad del negocio",
                    "example": "company"
                  }
                }
              },
              "example": {
                "business_profile": {
                  "name": "Transportes García S.L.",
                  "url": "https://transportesgarcia.com"
                },
                "business_type": "company"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cuenta Stripe Connect actualizada",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "account": {
                      "type": "object",
                      "description": "Objeto Account de Stripe actualizado"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "account": {
                    "id": "acct_AbCdEfGhIjKlMnOp",
                    "business_profile": {
                      "name": "Transportes García S.L."
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Cuenta Stripe Connect o compañía no encontrada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "cia_not_found": {
                    "value": {
                      "message": "CIA_NOT_FOUND"
                    }
                  },
                  "account_not_found": {
                    "value": {
                      "message": "STRIPE_ACCOUNT_NOT_FOUND"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Usuario no encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "USER_NOT_FOUND"
                }
              }
            },
            "headers": {}
          },
          "500": {
            "description": "Error al actualizar la cuenta en Stripe",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/company/payment/terms": {
      "get": {
        "summary": "Verificar aceptación de términos de pago",
        "deprecated": false,
        "description": "## Purpose\nConsulta si la compañía autenticada ha aceptado los términos y condiciones\nde pago de Stripe.\n\n## Objective\nDeterminar si se debe mostrar el modal de aceptación de términos antes\nde permitir operaciones de pago, comprobando el campo\n`payment_settings.tos_acceptance.accepted` de la compañía.\n\n## Use Cases\n- Verificar al cargar la sección de configuración de pagos\n- Determinar si mostrar u ocultar el banner de aceptación de términos\n- Prerequisito antes de habilitar Stripe o agregar métodos de pago\n\n## Notes\n- Requiere autenticación JWT (bearerAuth)\n- Retorna `success: true` si ya fueron aceptados, `success: false` si no\n- No lanza error cuando los términos no están aceptados (responde 200)\n",
        "tags": [
          "payment-terms"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Estado de aceptación de términos de Stripe",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "true si los términos han sido aceptados, false si no",
                      "example": true
                    }
                  }
                },
                "examples": {
                  "aceptado": {
                    "summary": "Términos ya aceptados",
                    "value": {
                      "success": true
                    }
                  },
                  "no_aceptado": {
                    "summary": "Términos pendientes de aceptación",
                    "value": {
                      "success": false
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Compañía no encontrada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "CIA_NOT_FOUND"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Usuario no encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "USER_NOT_FOUND"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "summary": "Aceptar términos y condiciones de pago",
        "deprecated": false,
        "description": "## Purpose\nRegistra la aceptación de los términos y condiciones de Stripe por parte\nde la compañía, habilitando así el procesamiento de pagos.\n\n## Objective\nActualizar `payment_settings.tos_acceptance` en la compañía con la fecha,\nIP y estado de aceptación, y sincronizar con la cuenta Stripe si existe.\n\n## Use Cases\n- Usuario acepta los términos desde el modal de configuración de pagos\n- Primer paso del flujo de onboarding de pagos\n- Requisito de compliance antes de procesar pagos reales con Stripe\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request] --> B{User authenticated?}\n  B -->|No| C[404 USER_NOT_FOUND]\n  B -->|Yes| D{Company found?}\n  D -->|No| E[401 CIA_NOT_FOUND]\n  D -->|Yes| F{Stripe customer exists?}\n  F -->|No| G[Create Stripe customer]\n  G --> H[Update tos_acceptance in Stripe]\n  F -->|Yes| H\n  H --> I[Save to company]\n  I --> J[200 success: true]\n```\n\n## Notes\n- Requiere autenticación JWT (bearerAuth)\n- Sin body requerido (la aceptación es implícita por la llamada)\n- Si no existe `stripe_customer`, lo crea automáticamente\n- Registra timestamp e IP del servidor para auditoría\n- Guarda `tos_acceptance.date`, `tos_acceptance.ip` y `tos_acceptance.accepted=true`\n",
        "tags": [
          "payment-terms"
        ],
        "parameters": [],
        "requestBody": {
          "required": false,
          "description": "Sin campos requeridos. La aceptación es implícita por el hecho de realizar la petición.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "accepted"
                ],
                "properties": {
                  "accepted": {
                    "type": "boolean",
                    "description": "Aceptar (true) o rechazar (false)",
                    "example": true
                  }
                }
              },
              "example": {
                "accepted": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Términos aceptados exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                },
                "example": {
                  "success": true
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Compañía no encontrada o error al crear cuenta en Stripe",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "CIA_NOT_FOUND"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Usuario no encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "USER_NOT_FOUND"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/company/payment/urls": {
      "get": {
        "deprecated": false,
        "description": "## Purpose\nGenera URLs de sesión de checkout de Stripe para cada plan de suscripción\ndisponible, listas para redirigir al usuario.\n\n## Objective\nProporcionar al frontend los enlaces directos a Stripe Checkout para que\nla compañía pueda seleccionar y pagar un plan de suscripción.\n\n## Use Cases\n- Mostrar tarjetas de planes con botones \"Suscribirse\"\n- Redirigir al usuario a Stripe Checkout para completar el pago\n- Presentar comparativa de planes disponibles con sus precios\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request] --> B{User authenticated?}\n  B -->|No| C[404 USER_NOT_FOUND]\n  B -->|Yes| D{Company found?}\n  D -->|No| E[401 CIA_NOT_FOUND]\n  D -->|Yes| F[Fetch plans from DB]\n  F --> G[Fetch Stripe prices]\n  G --> H[Create Checkout sessions per plan]\n  H --> I[200 OK - Plans + URLs]\n```\n\n## Notes\n- Requiere autenticación JWT (bearerAuth)\n- Si la compañía tiene `stripe_customer`, lo asocia a la sesión\n- Si no tiene `stripe_customer`, usa el email de `invoice_data.email`\n- La URL de retorno es fija: `${HOST_FRONT}/admin/config`\n- Modo de suscripción: `subscription` (no pago único)\n- Incluye `client_reference_id` con referencia de la compañía\n",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "description": "Para pequeñas empresas",
                      "id_plan": "prod_BasicPlanId",
                      "name": "Plan Básico",
                      "price_monthly": 29.99,
                      "truckers": 10,
                      "url": "https://checkout.stripe.com/c/pay/cs_test_basic...",
                      "users": 5,
                      "vehicles": 3
                    },
                    {
                      "description": "Para empresas en crecimiento",
                      "id_plan": "prod_ProPlanId",
                      "name": "Plan Profesional",
                      "price_monthly": 49.99,
                      "truckers": 25,
                      "url": "https://checkout.stripe.com/c/pay/cs_test_pro...",
                      "users": 10,
                      "vehicles": 10
                    }
                  ],
                  "status": true
                },
                "schema": {
                  "properties": {
                    "data": {
                      "items": {
                        "properties": {
                          "description": {
                            "description": "Descripción del plan",
                            "example": "Para pequeñas empresas",
                            "type": "string"
                          },
                          "id_plan": {
                            "description": "ID del producto en Stripe",
                            "example": "prod_AbCdEfGhIjKl",
                            "type": "string"
                          },
                          "name": {
                            "description": "Nombre del plan",
                            "example": "Plan Básico",
                            "type": "string"
                          },
                          "price_monthly": {
                            "description": "Precio mensual del plan",
                            "example": 29.99,
                            "type": "number"
                          },
                          "truckers": {
                            "description": "Límite de transportistas incluidos",
                            "example": 10,
                            "type": "integer"
                          },
                          "url": {
                            "description": "URL de sesión de Stripe Checkout",
                            "example": "https://checkout.stripe.com/c/pay/cs_test_...",
                            "type": "string"
                          },
                          "users": {
                            "description": "Límite de usuarios incluidos",
                            "example": 5,
                            "type": "integer"
                          },
                          "vehicles": {
                            "description": "Límite de vehículos incluidos",
                            "example": 3,
                            "type": "integer"
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "status": {
                      "example": true,
                      "type": "boolean"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Lista de planes con URLs de checkout de Stripe",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "CIA_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Compañía no encontrada",
            "headers": {}
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Obtener URLs de checkout para planes de suscripción",
        "tags": [
          "payment-processing"
        ]
      }
    },
    "/company/payment/withStripe": {
      "get": {
        "summary": "Obtener estado de pagos con Stripe",
        "deprecated": false,
        "description": "## Purpose\nConsulta si los pagos con Stripe están habilitados para la compañía\ndel usuario autenticado.\n\n## Objective\nPermitir al frontend conocer el estado actual de la integración con Stripe\npara mostrar u ocultar funcionalidades de pago.\n\n## Use Cases\n- Verificar si mostrar el toggle de pagos en el dashboard\n- Comprobar configuración antes de intentar procesar una entrega\n- Estado del switch de activación en la UI de configuración\n\n## Notes\n- Requiere autenticación JWT (bearerAuth)\n- Retorna solo el campo `withStripe` del objeto `payment_settings`\n- `withStripe: true` indica que los pagos están activos y configurados\n",
        "tags": [
          "payment-settings"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Estado de pagos Stripe",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "boolean",
                      "example": true
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "withStripe": {
                          "type": "boolean",
                          "description": "Indica si los pagos con Stripe están habilitados",
                          "example": true
                        }
                      }
                    }
                  }
                },
                "examples": {
                  "habilitado": {
                    "summary": "Stripe habilitado",
                    "value": {
                      "status": true,
                      "data": {
                        "withStripe": true
                      }
                    }
                  },
                  "deshabilitado": {
                    "summary": "Stripe deshabilitado",
                    "value": {
                      "status": true,
                      "data": {
                        "withStripe": false
                      }
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Compañía no encontrada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "CIA_NOT_FOUND"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Usuario no encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "USER_NOT_FOUND"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "summary": "Activar o desactivar pagos con Stripe",
        "deprecated": false,
        "description": "## Purpose\nHabilita o deshabilita la integración de pagos con Stripe para la\ncompañía del usuario autenticado.\n\n## Objective\nDar control total al administrador de la compañía sobre si los pagos\nelectrónicos están activos, con creación automática del cliente Stripe\nsi se activa por primera vez.\n\n## Use Cases\n- Activar Stripe por primera vez para comenzar a recibir pagos\n- Deshabilitar temporalmente los pagos electrónicos\n- Reactivar pagos después de un periodo de inactividad\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request - enabled] --> B{User authenticated?}\n  B -->|No| C[404 USER_NOT_FOUND]\n  B -->|Yes| D{Company found?}\n  D -->|No| E[401 CIA_NOT_FOUND]\n  D -->|Yes| F[Set withStripe = enabled]\n  F --> G{enabled=true AND no stripe_customer?}\n  G -->|Yes| H[Auto-create Stripe customer]\n  H --> I{Created OK?}\n  I -->|No| J[500 Error]\n  I -->|Yes| K[Save company]\n  G -->|No| K\n  K --> L[200 success: true]\n```\n\n## Notes\n- Requiere autenticación JWT (bearerAuth)\n- Si `enabled=true` y no existe `stripe_customer`, lo crea automáticamente usando `invoice_data.email`\n- Si `enabled=false`, solo deshabilita sin eliminar el cliente Stripe ni los métodos de pago\n",
        "tags": [
          "payment-settings"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "enabled"
                ],
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "description": "Activar (`true`) o desactivar (`false`) los pagos con Stripe",
                    "example": true
                  }
                }
              },
              "example": {
                "enabled": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Configuración de Stripe actualizada",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                },
                "example": {
                  "success": true
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Compañía no encontrada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "CIA_NOT_FOUND"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Usuario no encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "USER_NOT_FOUND"
                }
              }
            },
            "headers": {}
          },
          "500": {
            "description": "Error al crear cliente Stripe o guardar configuración",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/company/qr/confirm": {
      "put": {
        "deprecated": false,
        "description": "Confirma la finalización de una entrega. Al confirmar:\n- El estado del envío pasa a `delivered`\n- Se registra la firma digital del receptor\n- Se guardan fotos de prueba de entrega (si se adjuntan)\n- Se registra la geolocalización de la confirmación\n- Se invalida el `confirm_token`\n- Se procesa el pago del servicio (Stripe)\n- Se firma el eCMR digitalmente\n- Se envían emails de confirmación a todas las partes\n\n**Este endpoint es PÚBLICO — no requiere autenticación.**\n\n## Flujo de uso\n1. Obtener el `confirm_token` del endpoint `GET /company/qr/{token}`\n2. Recoger la firma digital del receptor (canvas base64)\n3. Opcionalmente, tomar fotos de la entrega\n4. Llamar a este endpoint con todos los datos\n\n## Campo `data`\nEl campo `data` debe enviarse como **string JSON** en el formulario multipart.\nContiene los datos del firmante y su firma digital:\n```json\n{\n  \"name\": \"Juan\",\n  \"surname\": \"Pérez\",\n  \"taxid\": \"12345678A\",\n  \"email\": \"juan@example.com\",\n  \"image\": \"data:image/png;base64,iVBORw0KG...\"\n}\n```\n\n## Notas técnicas\n- Código fuente: `src/features/company/qr_delivery/controller.js` → `confirmDelivery`\n- Acepta hasta 6 imágenes adjuntas (campo `images`, subidas a S3)\n- La geolocalización es opcional pero recomendada para auditoría\n- La acción es **irreversible**: un envío confirmado no puede revertirse a estado anterior\n",
        "operationId": "confirmDelivery",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "properties": {
                  "comment": {
                    "description": "Comentario opcional sobre la entrega",
                    "example": "Entrega sin novedades, mercancía en perfecto estado",
                    "type": "string"
                  },
                  "data": {
                    "description": "Objeto JSON serializado como string con los datos del firmante y su firma.\nCampos requeridos: `name`, `surname`, `taxid`, `email`, `image`.\nEl campo `image` es la firma digital en base64 con prefijo data URI.\n",
                    "example": "{\"name\":\"Juan\",\"surname\":\"Pérez\",\"taxid\":\"12345678A\",\"email\":\"juan@example.com\",\"image\":\"data:image/png;base64,iVBORw0KG...\"}",
                    "format": "json",
                    "type": "string"
                  },
                  "geolocation": {
                    "description": "Objeto JSON con coordenadas GPS del punto de confirmación.\nEstructura: `{ \"coords\": { \"latitude\": 40.4168, \"longitude\": -3.7038 } }`\n",
                    "example": "{\"coords\":{\"latitude\":40.4168,\"longitude\":-3.7038}}",
                    "format": "json",
                    "type": "string"
                  },
                  "images": {
                    "description": "Fotos de prueba de entrega (opcional).\nMáximo 6 imágenes. Formatos aceptados: JPG, PNG.\nTamaño máximo por archivo: 5 MB. Se almacenan en AWS S3.\n",
                    "items": {
                      "format": "binary",
                      "type": "string"
                    },
                    "type": "array"
                  },
                  "service_code": {
                    "description": "Código único del servicio asociado a la entrega",
                    "example": "SRV-2024-001234",
                    "type": "string"
                  },
                  "token": {
                    "description": "Token de confirmación obtenido al consultar `GET /company/qr/{token}`.\nSe invalida tras la confirmación.\n",
                    "example": "qr_5f8d3a1b2c3d4e5f6a7b8c9d",
                    "type": "string"
                  }
                },
                "required": [
                  "service_code",
                  "token",
                  "data"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "507f1f77bcf86cd799439011",
                  "confirmed": true,
                  "etd_comment": "Entrega sin novedades",
                  "etd_photos": [
                    "s3_key_photo1.jpg",
                    "s3_key_photo2.jpg"
                  ],
                  "service_code": "SRV-2024-001234",
                  "sign_delivery": "data:image/png;base64,iVBORw0KG...",
                  "sign_delivery_date": "2024-10-24T18:05:00.000Z",
                  "status": "delivered"
                },
                "schema": {
                  "$ref": "#/components/schemas/Delivery"
                }
              }
            },
            "description": "Entrega confirmada correctamente"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "message": "MISSING_PARAMETERS",
                  "status": false
                }
              }
            },
            "description": "Datos inválidos. Ocurre cuando:\n- Faltan parámetros requeridos (`service_code`, `token`, `data`)\n- Formato incorrecto del JSON en el campo `data`\n"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NOT_ALLOWED",
                  "status": false
                }
              }
            },
            "description": "Operación no permitida. Ocurre cuando:\n- La entrega ya está en estado final (`delivered`, `canceled`)\n- La firma (`image`) no está presente en el campo `data`\n"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "DELIVERY_NOT_FOUND",
                  "status": false
                }
              }
            },
            "description": "Entrega no encontrada. Posibles causas:\n- El `token` o `service_code` son incorrectos o no coinciden\n- La entrega fue eliminada\n"
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "message": "INTERNAL_SERVER_ERROR",
                  "status": false
                }
              }
            },
            "description": "Error interno del servidor al guardar la confirmación"
          }
        },
        "security": [],
        "summary": "Confirmar entrega mediante token QR",
        "tags": [
          "QR Delivery"
        ]
      }
    },
    "/company/qr/issue": {
      "post": {
        "deprecated": false,
        "description": "Registra una incidencia o problema relacionado con una entrega o el sistema.\n\nLas incidencias pueden ser:\n- Retrasos o problemas durante el transporte\n- Daños en la mercancía\n- Problemas con el vehículo\n- Cualquier otra incidencia relevante\n\n**Este endpoint es PÚBLICO — no requiere autenticación.**\n\n## Casos de uso\n- El conductor reporta una avería o accidente\n- El receptor reporta mercancía dañada\n- Se registra cualquier incidente durante la entrega\n\n## Notas técnicas\n- Código fuente: `src/features/company/qr_delivery/controller.js` → `createIsuue`\n- Las incidencias se almacenan en el modelo `Issue`\n- El campo `relatedTo: \"delivery\"` requiere `deliveryId`\n",
        "operationId": "createDeliveryIssue",
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "incidencia_entrega": {
                  "summary": "Incidencia relacionada con una entrega",
                  "value": {
                    "deliveryId": "507f1f77bcf86cd799439011",
                    "message": "El vehículo sufrió una avería en la A-6 km 42",
                    "relatedTo": "delivery"
                  }
                },
                "incidencia_general": {
                  "summary": "Incidencia general del sistema",
                  "value": {
                    "message": "Problema con la aplicación al confirmar entrega",
                    "relatedTo": "system"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/Issue"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "createdAt": "2024-10-24T14:30:00.000Z",
                  "id": "507f1f77bcf86cd799439099",
                  "message": "El vehículo sufrió una avería en la A-6 km 42",
                  "relatedTo": "delivery",
                  "status": "open"
                },
                "schema": {
                  "$ref": "#/components/schemas/Issue"
                }
              }
            },
            "description": "Incidencia creada correctamente"
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "message": "CAN_NOT_CREATE",
                  "status": false
                }
              }
            },
            "description": "Error interno del servidor al crear la incidencia"
          }
        },
        "security": [],
        "summary": "Reportar incidencia en una entrega",
        "tags": [
          "QR Delivery"
        ]
      }
    },
    "/company/qr/{token}": {
      "get": {
        "deprecated": false,
        "description": "Recupera toda la información de una entrega a partir de su token QR único.\n\nEl token QR se genera al crear la entrega. Al escanear el QR con la app móvil,\neste endpoint devuelve los detalles de la entrega y en el proceso **rota** el token:\n- Elimina el `qr_token` de la entrega\n- Genera un nuevo `confirm_token` (usado en `PUT /confirm`)\n\n**Este endpoint es PÚBLICO — no requiere autenticación.**\n\n## Flujo típico de uso\n1. El transportista llega al destino y escanea el QR\n2. La app llama a este endpoint con el token del QR\n3. Se muestran los detalles de la entrega al firmante\n4. El firmante confirma mediante `PUT /company/qr/confirm`\n\n## Campos devueltos\n- Datos del envío (`service_code`, `status`, `cargo_type`, etc.)\n- Dirección de carga y descarga (`etl_address`, `etd_address`)\n- Vehículo y conductor asignado\n- Fechas estimadas y reales\n- `confirm_token` para usar en la confirmación\n\n## Notas técnicas\n- Código fuente: `src/features/company/qr_delivery/controller.js` → `getDeliveryFromToken`\n- El `qr_token` original queda invalidado tras la primera consulta\n- El `confirm_token` generado es necesario para completar la entrega\n",
        "operationId": "getDeliveryFromQr",
        "parameters": [
          {
            "description": "Token QR único asociado a la entrega.\nSe obtiene del código QR generado por el sistema.\n",
            "example": "qr_5f8d3a1b2c3d4e5f6a7b8c9d",
            "in": "path",
            "name": "token",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "507f1f77bcf86cd799439011",
                  "cargo_type": "pallets",
                  "cargo_weight": 12500,
                  "confirm_token": "qr_5f8d3a1b2c3d4e5f6a7b8c9d",
                  "description": "Carga refrigerada",
                  "etd_address": {
                    "_id": "507f1f77bcf86cd799439013",
                    "city": "Barcelona",
                    "country": "ESP",
                    "name": "Almacén Barcelona",
                    "zipcode": "08001"
                  },
                  "etd_date": "2024-10-24T18:00:00.000Z",
                  "etl_address": {
                    "_id": "507f1f77bcf86cd799439012",
                    "city": "Madrid",
                    "country": "ESP",
                    "name": "Almacén Central Madrid",
                    "zipcode": "28001"
                  },
                  "etl_date": "2024-10-24T08:00:00.000Z",
                  "pallets_num": 20,
                  "service_code": "SRV-2024-001234",
                  "status": "in_progress"
                },
                "schema": {
                  "$ref": "#/components/schemas/Delivery"
                }
              }
            },
            "description": "Información de la entrega obtenida correctamente"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "DELIVERY_NOT_FOUND",
                  "status": false
                }
              }
            },
            "description": "Entrega no encontrada. Puede ocurrir cuando:\n- El token QR no existe, ya ha sido usado o ha expirado\n- La entrega fue eliminada del sistema\n- El formato del token es incorrecto\n"
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "message": "INTERNAL_SERVER_ERROR",
                  "status": false
                }
              }
            },
            "description": "Error interno del servidor al consultar la base de datos"
          }
        },
        "security": [],
        "summary": "Obtener información de entrega por token QR",
        "tags": [
          "QR Delivery"
        ]
      }
    },
    "/company/settings/legal": {
      "get": {
        "deprecated": false,
        "description": "Devuelve los documentos legales disponibles en el idioma especificado.\n\n**Propósito:**\n- Proporcionar acceso centralizado a los documentos legales de la plataforma\n- Soporte para múltiples idiomas (español e inglés)\n- Garantizar que los usuarios accedan a la versión correcta de los documentos\n\n**Casos de uso:**\n- Mostrar términos y condiciones durante el registro\n- Mostrar política de privacidad en el área de configuración\n- Actualización centralizada de documentos legales\n- Cumplimiento con regulaciones de transparencia\n\n**Flujo típico:**\n1. El cliente llama al endpoint especificando el idioma preferido\n2. El servidor busca los documentos legales en el idioma solicitado\n3. Retorna los documentos activos (visible=true)\n4. El cliente muestra los documentos al usuario\n\n**Consideraciones:**\n- Por defecto retorna documentos en español (es)\n- Solo retorna documentos marcados como visibles (visible=true)\n- Los documentos incluyen:\n  - Términos y condiciones\n  - Política de privacidad\n  - Política de cookies\n- Este endpoint es público y no requiere autenticación\n\n**Ejemplo de implementación:**\n```javascript\n// Ejemplo de consumo desde frontend\nasync function getLegalDocuments(lang = 'es') {\n  try {\n    const response = await fetch(`/company/settings/legal?lang=${lang}`);\n    const documents = await response.json();\n    renderLegalDocuments(documents);\n  } catch (error) {\n    console.error('Error fetching legal documents:', error);\n  }\n}\n```\n",
        "parameters": [
          {
            "description": "Idioma preferido (es/en)",
            "example": "es",
            "in": "query",
            "name": "lang",
            "required": false,
            "schema": {
              "default": "es",
              "enum": [
                "es",
                "en"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "cookies": {
                    "code": "cookies",
                    "lang": "es",
                    "name": "Política de Cookies",
                    "text": "Texto completo de política de cookies...",
                    "visible": true
                  },
                  "privacy": {
                    "code": "privacy",
                    "lang": "es",
                    "name": "Política de Privacidad",
                    "text": "Texto completo de política de privacidad...",
                    "visible": true
                  },
                  "terms": {
                    "code": "terms",
                    "lang": "es",
                    "name": "Términos y Condiciones",
                    "text": "Texto completo de términos y condiciones...",
                    "visible": true
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/LegalDocuments"
                }
              }
            },
            "description": "Documentos legales obtenidos",
            "headers": {}
          },
          "404": {
            "description": "No se encontraron documentos legales para el idioma especificado",
            "headers": {}
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "code": "INTERNAL_SERVER_ERROR",
                  "details": {},
                  "error": "Error al obtener documentos legales"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error interno del servidor",
            "headers": {}
          }
        },
        "summary": "Obtain legal documents",
        "tags": [
          "Legal"
        ]
      },
      "post": {
        "deprecated": false,
        "description": "Actualiza los documentos legales de la plataforma (términos, privacidad, cookies).\n\n**Propósito:**\n- Permitir a administradores actualizar documentos legales\n- Mantener actualizada la información legal de la plataforma\n- Soportar múltiples idiomas\n\n**Requisitos:**\n- Requiere autenticación JWT con rol 'admin' o 'dev'\n- El middleware checkUTC valida timestamps UTC\n- Los códigos numéricos son ignorados automáticamente\n\n**Estructura del payload:**\n- Objeto anidado por código de documento (terms, privacy, cookies)\n- Cada código contiene objetos por idioma (es, en)\n- Cada idioma tiene: name, text (HTML), visible\n\n**Comportamiento:**\n- Sobrescribe completamente los documentos existentes\n- No es un merge parcial\n- Los documentos con visible=false no se retornan en endpoints públicos\n",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "cookies": {
                  "en": {
                    "name": "Cookie Policy",
                    "text": "<h1>Cookie Policy</h1><p>We use cookies to...</p>",
                    "visible": true
                  },
                  "es": {
                    "name": "Política de Cookies",
                    "text": "<h1>Política de Cookies</h1><p>Utilizamos cookies...</p>",
                    "visible": true
                  }
                },
                "privacy": {
                  "en": {
                    "name": "Privacy Policy",
                    "text": "<h1>Privacy Policy</h1><p>At CargOffer we protect...</p>",
                    "visible": true
                  },
                  "es": {
                    "name": "Política de Privacidad",
                    "text": "<h1>Política de Privacidad</h1><p>En CargOffer protegemos...</p>",
                    "visible": true
                  }
                },
                "terms": {
                  "en": {
                    "name": "Terms and Conditions",
                    "text": "<h1>Terms and Conditions</h1><p>Welcome to CargOffer...</p>",
                    "visible": true
                  },
                  "es": {
                    "name": "Términos y Condiciones de Uso",
                    "text": "<h1>Términos y Condiciones</h1><p>Bienvenido a CargOffer...</p>",
                    "visible": true
                  }
                }
              },
              "schema": {
                "$ref": "../_components.yaml#/components/schemas/LegalUpdateRequest"
              }
            }
          },
          "description": "Documentos legales organizados por código e idioma",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "terms": {
                    "en": {
                      "name": "Terms and Conditions",
                      "text": "<h1>Terms and Conditions</h1>...",
                      "visible": true
                    },
                    "es": {
                      "name": "Términos y Condiciones de Uso",
                      "text": "<h1>Términos y Condiciones</h1>...",
                      "visible": true
                    }
                  }
                },
                "schema": {
                  "$ref": "../_components.yaml#/components/schemas/LegalDocumentsResponse"
                }
              }
            },
            "description": "Documentos legales actualizados exitosamente"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "message": "BAD_REQUEST",
                  "status": false
                },
                "schema": {
                  "$ref": "../_components.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Solicitud inválida - Error en el cuerpo de la solicitud"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN",
                  "status": false
                },
                "schema": {
                  "$ref": "../_components.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado - Token JWT inválido o no proporcionado"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "message": "FORBIDDEN",
                  "status": false
                },
                "schema": {
                  "$ref": "../_components.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Prohibido - Usuario no tiene rol de administrador"
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "message": "INTERNAL_SERVER_ERROR",
                  "status": false
                },
                "schema": {
                  "$ref": "../_components.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error interno del servidor"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Update legal documents",
        "tags": [
          "Legal",
          "Admin"
        ]
      }
    },
    "/company/trailers/": {
      "post": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "POST /company/trailers/",
        "tags": [
          "Trailer"
        ]
      }
    },
    "/company/trailers/:id": {
      "delete": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "DELETE /company/trailers/:id",
        "tags": [
          "Trailer"
        ]
      }
    },
    "/company/trucker_cia/": {
      "put": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "PUT /company/trucker_cia/",
        "tags": [
          "TruckerCia"
        ]
      }
    },
    "/company/trucker_cia/:id": {
      "put": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "PUT /company/trucker_cia/:id",
        "tags": [
          "TruckerCia"
        ]
      }
    },
    "/company/trucker_cia/edit": {
      "post": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "POST /company/trucker_cia/edit",
        "tags": [
          "TruckerCia"
        ]
      }
    },
    "/company/trucker_cia/edit/:id": {
      "post": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "POST /company/trucker_cia/edit/:id",
        "tags": [
          "TruckerCia"
        ]
      }
    },
    "/company/trucker_group/": {
      "post": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "POST /company/trucker_group/",
        "tags": [
          "TruckerGroup"
        ]
      }
    },
    "/company/trucker_group/:code": {
      "delete": {
        "responses": {
          "200": {
            "description": "OK"
          }
        },
        "summary": "DELETE /company/trucker_group/:code",
        "tags": [
          "TruckerGroup"
        ]
      }
    },
    "/company/truckers/": {
      "get": {
        "deprecated": false,
        "description": "Devuelve un listado paginado de todos los transportistas asociados a la compañía del usuario autenticado.\n\n**Funcionalidades de búsqueda:**\n- Filtrado por texto (nombre, apellido, email, taxid)\n- Autocompletado para selectores\n- Paginación configurable\n\n**Modos de respuesta:**\n- **Modo básico**: Solo _id, name, lastname (para transportistas)\n- **Modo completo**: Todos los campos relevantes (para empresas)\n- **Modo con vehículos**: Incluye populate del vehículo por defecto\n\n**Parámetros de búsqueda:**\n- `page`: Número de página (default: 1)\n- `limit`: Elementos por página (default: 10)\n- `search`: Búsqueda de texto libre\n- `autocomplete`: Filtrado para autocompletar\n- `extra=vehicles`: Incluye información de vehículos\n\n**Campos devueltos (modo empresa):**\n- _id, name, lastname, email, phone, taxid\n- address, allowSearch, accountType\n- default_vehicle (si extra=vehicles)\n- emailVerified\n\n**Respuestas:**\n- 200 OK: Listado paginado de transportistas\n- 401 Unauthorized: Token inválido o compañía no encontrada\n- 404 Not Found: Usuario no encontrado\n\n**Ejemplo de uso:**\n```bash\nGET /company/truckers/?page=1&limit=20&search=juan&extra=vehicles\n```\n",
        "parameters": [
          {
            "description": "Número de página para paginación",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Cantidad de elementos por página",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 10,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Texto de búsqueda libre en todos los campos",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Filtrado rápido para selectores autocomplete",
            "in": "query",
            "name": "autocomplete",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Información adicional a incluir (ej. 'vehicles')",
            "in": "query",
            "name": "extra",
            "required": false,
            "schema": {
              "enum": [
                "vehicles"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TruckersListResponse"
                }
              }
            },
            "description": "Listado de transportistas obtenido correctamente"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado - Token inválido o compañía no encontrada"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get list of truckers",
        "tags": [
          "Truckers"
        ]
      },
      "post": {
        "deprecated": false,
        "description": "Crea un nuevo transportista asociado a la compañía del usuario autenticado.\n\n**Proceso de creación:**\n1. Validación de datos obligatorios (email, taxid)\n2. Verificación de unicidad (email y taxid únicos)\n3. Generación automática de contraseña (si no se proporciona)\n4. Hash seguro de contraseña\n5. Subida de imagen de perfil a S3 (opcional)\n6. Envío de email con credenciales\n7. Asociación automática a la compañía\n\n**Validaciones automáticas:**\n- Email único en el sistema\n- Taxid (DNI/NIE/CIF) único y formato válido\n- Teléfono con formato válido\n- Rol válido según modelo\n\n**Gestión de contraseñas:**\n- Si se envía: Se almacena hash seguro\n- Si no se envía: Se genera automáticamente y se envía por email\n\n**Gestión de imágenes:**\n- Soporte para subida multipart/form-data\n- Almacenamiento en AWS S3\n- Campo `image` contiene la key de S3\n\n**Email de bienvenida:**\n- Se envía automáticamente al transportista\n- Incluye credenciales de acceso\n- Personalizado con nombre de la compañía\n- Multiidioma según i18n del usuario\n\n**Límites:**\n- Verificación de plan de suscripción (middleware canCreateUser)\n- Requiere permisos multitenant\n\n**Respuestas:**\n- 200 OK: Transportista creado correctamente\n- 400 Bad Request: Datos inválidos o falta información\n- 401 Unauthorized: Compañía no encontrada\n- 406 Not Acceptable: Email o taxid ya existen\n- 503 Service Unavailable: Error al guardar en BD\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "ejemplo_basico": {
                  "description": "Mínimo requerido para crear un transportista. La contraseña se genera automáticamente.",
                  "summary": "Ejemplo básico (campos obligatorios)",
                  "value": {
                    "email": "juan.garcia@example.com",
                    "lastname": "García López",
                    "name": "Juan",
                    "phone": "+34666777888",
                    "taxid": "12345678A"
                  }
                },
                "ejemplo_completo": {
                  "description": "Incluye vehículo por defecto, rol y contraseña personalizada",
                  "summary": "Ejemplo completo con todos los campos",
                  "value": {
                    "allowSearch": true,
                    "default_vehicle": "507f1f77bcf86cd799439011",
                    "email": "maria.rodriguez@transport.com",
                    "lastname": "Rodríguez Pérez",
                    "name": "María",
                    "password": "MiPass123!Segura",
                    "phone": "+34655443322",
                    "role": "driver",
                    "taxid": "X9876543B"
                  }
                },
                "ejemplo_con_direccion": {
                  "description": "Transportista con datos de dirección incluidos",
                  "summary": "Ejemplo con dirección completa",
                  "value": {
                    "address": {
                      "city": "Madrid",
                      "country": "esp",
                      "street_address": "Calle Mayor",
                      "zipcode": "28013"
                    },
                    "email": "carlos.fernandez@logistica.es",
                    "lastname": "Fernández Sánchez",
                    "name": "Carlos",
                    "phone": "+34722334455",
                    "role": "admin",
                    "taxid": "B12345678"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/CreateTruckerRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "properties": {
                  "allowSearch": {
                    "default": true,
                    "description": "Permitir búsqueda pública del transportista",
                    "type": "boolean"
                  },
                  "default_vehicle": {
                    "description": "ID del vehículo asignado por defecto",
                    "type": "string"
                  },
                  "email": {
                    "description": "Email único del transportista",
                    "example": "juan.garcia@example.com",
                    "format": "email",
                    "type": "string"
                  },
                  "image": {
                    "description": "Imagen de perfil del transportista",
                    "format": "binary",
                    "type": "string"
                  },
                  "lastname": {
                    "description": "Apellidos del transportista",
                    "example": "García López",
                    "type": "string"
                  },
                  "name": {
                    "description": "Nombre del transportista",
                    "example": "Juan",
                    "type": "string"
                  },
                  "password": {
                    "description": "Contraseña (opcional, se genera automáticamente)",
                    "format": "password",
                    "type": "string"
                  },
                  "phone": {
                    "description": "Teléfono con prefijo (opcional)",
                    "example": "+34666777888",
                    "type": "string"
                  },
                  "role": {
                    "description": "Rol del transportista",
                    "enum": [
                      "driver",
                      "admin",
                      "owner"
                    ],
                    "type": "string"
                  },
                  "taxid": {
                    "description": "DNI/NIE/CIF único del transportista",
                    "example": "12345678A",
                    "maxLength": 9,
                    "minLength": 6,
                    "type": "string"
                  }
                },
                "required": [
                  "email",
                  "taxid",
                  "name",
                  "lastname"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TruckerResponse"
                }
              }
            },
            "description": "Transportista creado correctamente"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Datos inválidos"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Compañía no encontrada"
          },
          "406": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Email o taxid ya existen"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error al guardar"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Create new trucker",
        "tags": [
          "Truckers"
        ]
      }
    },
    "/company/truckers/bulk/": {
      "post": {
        "deprecated": false,
        "description": "Crea múltiples transportistas de forma masiva mediante importación de archivo CSV.\n\n**Flujo de importación:**\n1. Usuario sube archivo CSV con datos de transportistas\n2. Sistema valida headers obligatorios\n3. Procesa cada línea validando datos\n4. Verifica unicidad de email, taxid y teléfono\n5. Genera contraseñas automáticas si no se proporcionan\n6. Crea transportistas exitosos\n7. Devuelve reporte con éxitos y errores\n\n**Validaciones por línea:**\n- Email: Único en sistema y formato válido\n- Taxid: Único y formato válido según país\n- Phone: Formato válido según regex\n- Default_vehicle: Existe en la compañía (por matrícula)\n- Role: Valor válido (se normaliza automáticamente)\n\n**Headers obligatorios CSV:**\n- name, lastname, email, phone, taxid\n- Opcional: default_vehicle (matrícula del vehículo)\n\n**Generación de credenciales:**\n- Si no se proporciona password: Se genera automáticamente\n- Se envía email de bienvenida con credenciales\n- Email personalizado con nombre de la compañía\n\n**Formato del CSV:**\n- Separador: coma (,)\n- Codificación: UTF-8\n- Primera línea: headers\n- Ejemplo: name,lastname,email,phone,taxid,default_vehicle\n\n**Respuesta detallada:**\n- `ok`: Array de transportistas creados exitosamente\n- `ko`: Array de errores con línea, razón y datos\n\n**Estructura de errores:**\n```json\n{\n  \"line\": 5,\n  \"reason\": \"EMAIL_ALREADY_IN_USE\",\n  \"data\": {...},\n  \"details\": \"john@example.com\"\n}\n```\n\n**Códigos de error comunes:**\n- MIN_HEADERS: Faltan headers obligatorios\n- EMAIL_ALREADY_IN_USE: Email duplicado\n- TAXID_ALREADY_IN_USE: Taxid duplicado\n- EMAIL_NOT_VALID: Formato de email inválido\n- TAXID_NOT_VALID: Formato de taxid inválido\n- PHONE_NOT_VALID: Formato de teléfono inválido\n- CANNOT_SAVE_TRUCKER: Error al guardar en BD\n\n**Límites:**\n- Verificación de plan de suscripción activo\n- Límite de transportistas según plan\n\n**Respuestas HTTP:**\n- 200 OK: Proceso completado (revisa ok/ko en body)\n- 400 Bad Request: Headers inválidos o datos vacíos\n- 401 Unauthorized: Compañía no encontrada\n- 404 Not Found: Archivo CSV no encontrado\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "properties": {
                  "data": {
                    "description": "Archivo CSV con datos de transportistas",
                    "format": "binary",
                    "type": "string"
                  }
                },
                "required": [
                  "data"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "ko": {
                      "description": "Errores encontrados durante la importación",
                      "items": {
                        "properties": {
                          "data": {
                            "description": "Datos de la línea con error",
                            "type": "object"
                          },
                          "details": {
                            "description": "Información adicional del error",
                            "type": "string"
                          },
                          "line": {
                            "description": "Número de línea con error",
                            "type": "integer"
                          },
                          "reason": {
                            "description": "Código del error",
                            "type": "string"
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "ok": {
                      "description": "Transportistas creados exitosamente",
                      "items": {
                        "$ref": "#/components/schemas/TruckerResponse"
                      },
                      "type": "array"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Proceso de importación completado"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Headers inválidos o datos vacíos"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Compañía no encontrada"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Archivo CSV no encontrado"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Bulk create truckers from CSV",
        "tags": [
          "Truckers Bulk"
        ]
      }
    },
    "/company/truckers/bulk/notes/{lang}": {
      "get": {
        "deprecated": false,
        "description": "Obtiene un archivo de texto con notas e instrucciones para la importación masiva.\n\n**Idiomas soportados:**\n- `es`: Español (bulk_trucker_notes_es.txt)\n- `en`: Inglés (bulk_trucker_notes_en.txt) - por defecto\n\n**Contenido de las notas:**\n- Instrucciones paso a paso\n- Formato requerido de datos\n- Ejemplos de valores válidos\n- Códigos de error comunes\n- Tips de importación\n- Validaciones aplicadas\n\n**Ubicación de archivos:**\n- src/assets/bulk_trucker_notes_es.txt\n- src/assets/bulk_trucker_notes_en.txt\n\n**Formato de salida:**\n- Content-Type: text/plain\n- Nombre archivo: notes_{lang}.txt\n- Codificación: UTF-8\n\n**Casos de uso:**\n- Ayuda contextual en interfaz de importación\n- Documentación para usuarios finales\n- Guía de referencia rápida\n\n**Respuestas:**\n- 200 OK: Archivo de texto con notas\n- 401 Unauthorized: Compañía no encontrada\n- 404 Not Found: Usuario no encontrado\n",
        "parameters": [
          {
            "description": "Código de idioma (es/en)",
            "in": "path",
            "name": "lang",
            "required": true,
            "schema": {
              "default": "en",
              "enum": [
                "es",
                "en"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "text/plain": {
                "schema": {
                  "example": "# Guía de Importación Masiva de Transportistas\n\n## Formato de Archivo\n- Tipo: CSV (valores separados por comas)\n- Codificación: UTF-8\n...\n",
                  "type": "string"
                }
              }
            },
            "description": "Notas descargadas"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Compañía no encontrada"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get bulk import notes",
        "tags": [
          "Truckers Bulk"
        ]
      }
    },
    "/company/truckers/bulk/template": {
      "get": {
        "deprecated": false,
        "description": "Descarga una plantilla CSV de ejemplo para importación masiva de transportistas.\n\n**Contenido de la plantilla:**\n- Headers con todos los campos obligatorios\n- 3 filas de ejemplo con datos ficticios\n- Formato correcto para importación\n\n**Campos incluidos:**\n- name, lastname, email, phone, taxid, default_vehicle\n\n**Uso recomendado:**\n1. Descargar plantilla\n2. Rellenar con datos reales\n3. Subir mediante POST /bulk/\n\n**Formato de salida:**\n- Content-Type: text/csv\n- Nombre archivo: template.csv\n- Codificación: UTF-8\n\n**Ejemplo de contenido:**\n```csv\nname,lastname,email,phone,taxid,default_vehicle\nPepe,Valbuena,pvalbuena@example.com,+34818809171,60262969W,11425LKJ\nValentina,Pedreiro,vpedreiro@example.com,936247383,Z3244875V,66541HGF\nPedro,Jiménez,pjimenez@example.com,34668936908,Q6495302I,778987FFD\n```\n\n**Respuestas:**\n- 200 OK: Descarga de archivo CSV\n- 401 Unauthorized: Compañía no encontrada\n- 404 Not Found: Usuario no encontrado\n",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "text/csv": {
                "schema": {
                  "example": "name,lastname,email,phone,taxid,default_vehicle\nPepe,Valbuena,pvalbuena@example.com,+34818809171,60262969W,11425LKJ\nValentina,Pedreiro,vpedreiro@example.com,936247383,Z3244875V,66541HGF\n",
                  "type": "string"
                }
              }
            },
            "description": "Plantilla CSV descargada"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Compañía no encontrada"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Download CSV template",
        "tags": [
          "Truckers Bulk"
        ]
      }
    },
    "/company/truckers/contact/{id}": {
      "get": {
        "deprecated": false,
        "description": "## Purpose\nObtiene la información de contacto de la compañía transportista (trucker_cia) asociada a un ID específico.\n\n## Objective\nProporcionar a las empresas los datos de contacto necesarios para comunicarse directamente con las compañías transportistas, ya sea para coordinar entregas, resolver incidencias o generar documentación.\n\n## Use Cases\n- Una empresa necesita contactar a la transportista sobre una entrega específica\n- Generación de documentos CMR con datos de contacto\n- Verificación de información de contacto antes de contratar\n- Coordinación operativa para logística de carga\n\n## ⚠️ CRITICAL WARNING - ENDPOINT NON-FUNCTIONAL\n**ESTADO ACTUAL: NO OPERATIVO**\n\nEste endpoint está experimentando un error crítico que impide su funcionamiento:\n\n**Error:** `400 Bad Request - \"Unexpected token 'n', \\\"null\\\" is not valid JSON\"`\n\n**Causa probable:** El middleware `isMultitennant` está fallando al parsear JSON, intentando ejecutar `JSON.parse(null)` o similar.\n\n**Requisitos de autenticación:**\n- Token JWT válido con `accountType='multitennant'`\n- El middleware `isMultitennant` valida esta condición antes de procesar la solicitud\n\n**Nota:** Este problema debe ser corregido en el middleware chain antes de que el endpoint pueda ser utilizado.\n\n## Información devuelta (cuando funcione)\nEl endpoint devolvería los siguientes campos de la compañía transportista:\n- **_id**: ID único de la compañía (trucker_cia)\n- **name**: Nombre de la empresa\n- **email**: Email de contacto general\n- **phone**: Teléfono de contacto (formato completo)\n- **country**: Código de país (ej: \"esp\", \"fra\")\n- **timezone**: Zona horaria (ej: \"utc\", \"Europe/Madrid\")\n- **createdAt**: Fecha de registro en la plataforma\n- **deleted**: Estado de eliminación (soft delete)\n\n## Diferencia con GET /{id}\n- **GET /contact/{id}**: Devuelve información de la COMPAÑÍA del transportista (trucker_cia)\n- **GET /{id}**: Devuelve información del TRANSPORTISTA individual (usuario conductor)\n\n## Validation Flow (cuando funcione)\n```mermaid\nflowchart TD\n  A[Receive Request] --> B{JWT Valid?}\n  B -->|No| C[401 Unauthorized]\n  B -->|Yes| D{accountType multitennant?}\n  D -->|No| E[403 Forbidden]\n  D -->|Yes| F{ID Valid ObjectId?}\n  F -->|No| G[400 Invalid ID]\n  F -->|Yes| H{Company Exists?}\n  H -->|No| I[404 CIA_NOT_FOUND]\n  H -->|Yes| J[Return Contact Info - 200]\n```\n\n## Error Codes\n- **CIA_NOT_FOUND (404)**: No existe compañía con el ID proporcionado\n- **CANT_GET_CONTACT (400)**: Error al obtener información de contacto\n- **NO_TOKEN (401)**: Token no proporcionado o inválido\n- **TOKEN_NOT_VALID (401)**: Token JWT inválido o expirado\n",
        "parameters": [
          {
            "description": "ID único de la compañía transportista en la colección trucker_cia.\n\n**Formato:** ObjectId de MongoDB (24 caracteres hexadecimales)\n\n**Ejemplo:** 000000000000000000000001 (ID de prueba en base de datos testing)\n",
            "examples": {
              "productionId": {
                "summary": "ID típico de producción",
                "value": "507f1f77bcf86cd799439011"
              },
              "testingId": {
                "summary": "ID de prueba (database testing)",
                "value": "000000000000000000000001"
              }
            },
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "objectId",
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "success": {
                    "summary": "Contacto de compañía transportista",
                    "value": {
                      "_id": "507f1f77bcf86cd799439011",
                      "country": "esp",
                      "createdAt": "2024-03-15T10:30:00Z",
                      "deleted": false,
                      "email": "contacto@transportesrapidoss.com",
                      "name": "Transportes Rápidos S.L.",
                      "phone": "+34600123456",
                      "timezone": "Europe/Madrid"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/TruckerCompanyContactResponse"
                }
              }
            },
            "description": "Información de contacto de la compañía transportista obtenida exitosamente.\n\n**Nota:** Esta respuesta NO está disponible actualmente debido al error crítico del middleware.\n"
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "genericError": {
                    "summary": "Error genérico del controller",
                    "value": {
                      "error": "Error al obtener información de contacto",
                      "message": "CANT_GET_CONTACT"
                    }
                  },
                  "parsingError": {
                    "summary": "Error actual de parsing JSON",
                    "value": {
                      "error": "SyntaxError",
                      "message": "Unexpected token 'n', \"null\" is not valid JSON"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "**Error actual del endpoint** - El middleware isMultitennant está fallando con error de parsing JSON.\n\nEste es el error que actualmente recibe cualquier solicitud a este endpoint.\n\nTambién puede ocurrir por:\n- CANT_GET_CONTACT: Error al procesar la solicitud en el controller\n"
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "invalidToken": {
                    "value": {
                      "error": "Invalid or expired JWT token",
                      "message": "TOKEN_NOT_VALID"
                    }
                  },
                  "noToken": {
                    "value": {
                      "error": "Token not provided",
                      "message": "NO_TOKEN"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado - Token JWT no proporcionado, inválido o expirado.\n"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": "accountType must be 'multitennant'",
                  "message": "FORBIDDEN"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Prohibido - El token JWT no tiene el accountType requerido ('multitennant').\n\n**Nota:** Este error ocurriría si el middleware funcionara correctamente y el token no tuviera accountType='multitennant'.\n"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Trucker company not found with provided ID",
                  "message": "CIA_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Compañía no encontrada - No existe ninguna compañía transportista con el ID proporcionado.\n\n**Nota:** Actualmente no se puede llegar a esta respuesta debido al error previo del middleware.\n"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get trucker company contact info",
        "tags": [
          "Truckers"
        ]
      }
    },
    "/company/truckers/filterByTaxid/{taxid}": {
      "get": {
        "deprecated": false,
        "description": "Busca un transportista por su identificación fiscal (DNI/NIE/CIF).\n\n**Caso de uso:**\n- Búsqueda rápida por documento de identidad\n- Verificación de existencia antes de crear\n- Consulta desde sistemas externos por taxid\n\n**Formato de taxid:**\n- España: DNI (12345678A), NIE (X1234567A), CIF (B12345678)\n- Longitud: 6-9 caracteres\n- Se almacena en mayúsculas\n\n**Respuesta:**\n- Devuelve perfil completo del transportista\n- Incluye todos los detalles (parseFleetDetail)\n\n**Respuestas:**\n- 200 OK: Transportista encontrado\n- 404 Not Found: No existe transportista con ese taxid\n\n**Nota de seguridad:**\nEste endpoint requiere middleware multitenant pero no verifica\npertenencia a compañía. Considerar añadir validación adicional.\n",
        "parameters": [
          {
            "description": "Identificación fiscal del transportista (DNI/NIE/CIF)",
            "in": "path",
            "name": "taxid",
            "required": true,
            "schema": {
              "example": "12345678A",
              "maxLength": 9,
              "minLength": 6,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TruckerDetailResponse"
                }
              }
            },
            "description": "Transportista encontrado"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Transportista no encontrado"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get trucker by taxid",
        "tags": [
          "Truckers"
        ]
      }
    },
    "/company/truckers/{id}": {
      "delete": {
        "deprecated": false,
        "description": "Elimina un transportista de la compañía mediante soft delete.\n\n**Proceso de eliminación:**\n1. Verificación de pertenencia a la compañía\n2. Eliminación de imagen de perfil en S3 (si existe)\n3. Soft delete del registro (mongoose-delete)\n4. El registro se marca como deleted pero se mantiene en BD\n\n**Consideraciones importantes:**\n- Es una eliminación lógica (soft delete), no física\n- La imagen se elimina permanentemente de S3\n- El transportista puede ser restaurado si es necesario\n- Las referencias en deliveries/auctions se mantienen\n\n**Seguridad:**\n- Solo puede eliminar transportistas de su propia compañía\n- Verificación automática de pertenencia\n\n**Respuestas:**\n- 200 OK: Transportista eliminado (devuelve {_id})\n- 403 Forbidden: No pertenece a la compañía\n- 404 Not Found: Transportista no encontrado\n\n**Advertencia:**\nEl código actual marca como peligroso esta operación.\nRevisar impacto en deliveries y auctions antes de eliminar.\n",
        "parameters": [
          {
            "description": "ID único del transportista a eliminar",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "objectId",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "_id": {
                      "description": "ID del transportista eliminado",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Transportista eliminado correctamente"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No pertenece a la compañía"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Transportista no encontrado"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Delete trucker",
        "tags": [
          "Truckers"
        ]
      },
      "get": {
        "deprecated": false,
        "description": "Obtiene los detalles completos de un transportista específico por su ID.\n\n**Validaciones de seguridad:**\n- El transportista debe pertenecer a la compañía del usuario autenticado\n- Verificación automática de pertenencia\n- Control de acceso por origen (trucker/company)\n\n**Información devuelta:**\n- Perfil completo del transportista\n- Datos de contacto\n- Información fiscal\n- Vehículo asignado\n- Estado de verificación de email\n- Configuración de búsqueda pública\n\n**Respuestas:**\n- 200 OK: Detalles del transportista\n- 403 Forbidden: Transportista no pertenece a la compañía\n- 404 Not Found: Transportista no encontrado\n",
        "parameters": [
          {
            "description": "ID único del transportista",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "objectId",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TruckerDetailResponse"
                }
              }
            },
            "description": "Detalles del transportista"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado para ver este transportista"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Transportista no encontrado"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get trucker details",
        "tags": [
          "Truckers"
        ]
      },
      "put": {
        "deprecated": false,
        "description": "Actualiza los datos de un transportista existente.\n\n**Proceso de actualización:**\n1. Verificación de pertenencia a la compañía\n2. Validación de cambios de email (unicidad)\n3. Validación de datos según modelo\n4. Gestión de imagen (subida/eliminación en S3)\n5. Guardado de cambios\n\n**Validaciones especiales:**\n- Si se cambia el email, se verifica que no exista otro usuario con ese email\n- No se permite cambiar a email ya existente\n- La imagen anterior se elimina de S3 si se sube una nueva o se elimina\n\n**Gestión de imágenes:**\n- Nueva imagen: Se sube a S3 y se elimina la anterior\n- Sin imagen en body: Se elimina de S3 y se pone a null\n- Imagen existente sin cambios: Se mantiene\n\n**Campos editables:**\n- name, lastname, email, phone, taxid\n- default_vehicle, allowSearch\n- image (vía multipart)\n- address (objeto completo)\n\n**Respuestas:**\n- 200 OK: Transportista actualizado\n- 400 Bad Request: Datos inválidos\n- 403 Forbidden: No pertenece a la compañía\n- 404 Not Found: Transportista no encontrado\n- 406 Not Acceptable: Email ya existe\n",
        "parameters": [
          {
            "description": "ID único del transportista a actualizar",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "objectId",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "actualizar_completo": {
                  "description": "Actualiza múltiples campos incluyendo dirección",
                  "summary": "Actualización completa de perfil",
                  "value": {
                    "address": {
                      "city": "Barcelona",
                      "country": "esp",
                      "street_address": "Avenida de la Industria 45",
                      "zipcode": "08010"
                    },
                    "allowSearch": false,
                    "default_vehicle": "507f1f77bcf86cd799439012",
                    "email": "pedro.martinez@logistica.es",
                    "lastname": "Martínez González",
                    "name": "Pedro",
                    "phone": "+34699887766",
                    "taxid": "Y7654321C"
                  }
                },
                "actualizar_contacto": {
                  "description": "Cambia email y teléfono del transportista",
                  "summary": "Actualizar datos de contacto",
                  "value": {
                    "email": "nuevo.email@transport.com",
                    "phone": "+34611223344"
                  }
                },
                "actualizar_nombre": {
                  "description": "Actualiza únicamente los datos personales básicos",
                  "summary": "Actualizar solo nombre y apellido",
                  "value": {
                    "lastname": "García López-Ruiz",
                    "name": "Juan Carlos"
                  }
                },
                "actualizar_vehiculo": {
                  "description": "Asocia un vehículo específico al transportista",
                  "summary": "Asignar vehículo por defecto",
                  "value": {
                    "default_vehicle": "507f1f77bcf86cd799439011"
                  }
                },
                "desactivar_busqueda": {
                  "description": "Oculta el perfil del transportista de búsquedas públicas",
                  "summary": "Desactivar búsqueda pública",
                  "value": {
                    "allowSearch": false
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/UpdateTruckerRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "properties": {
                  "allowSearch": {
                    "description": "Permitir búsqueda pública",
                    "type": "boolean"
                  },
                  "default_vehicle": {
                    "description": "ID del vehículo por defecto",
                    "type": "string"
                  },
                  "email": {
                    "description": "Email (se valida unicidad si cambia)",
                    "format": "email",
                    "type": "string"
                  },
                  "image": {
                    "description": "Nueva imagen de perfil",
                    "format": "binary",
                    "type": "string"
                  },
                  "lastname": {
                    "description": "Apellidos",
                    "type": "string"
                  },
                  "name": {
                    "description": "Nombre del transportista",
                    "type": "string"
                  },
                  "phone": {
                    "description": "Teléfono con prefijo",
                    "type": "string"
                  },
                  "taxid": {
                    "description": "DNI/NIE/CIF",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TruckerResponse"
                }
              }
            },
            "description": "Transportista actualizado"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Datos inválidos"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No pertenece a la compañía"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Transportista no encontrado"
          },
          "406": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Email ya existe"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Update trucker",
        "tags": [
          "Truckers"
        ]
      }
    },
    "/company/users/": {
      "get": {
        "description": "## Purpose\nDevuelve una lista paginada de todos los usuarios pertenecientes a la compañía\ndel usuario autenticado, excluyendo usuarios con rol 'dev'.\n\n## Objective\nPermitir a administradores y gestores ver el listado completo de usuarios\nde su compañía con opciones de búsqueda y paginación.\n\n## Use Cases\n- Listar usuarios para gestión administrativa\n- Buscar usuarios por nombre, email o apellidos\n- Paginar listados grandes de usuarios\n\n## Authentication\n- Requiere JWT válido (middleware m.isLoged)\n- Usuario debe estar autenticado\n- Solo devuelve usuarios de la compañía del usuario autenticado\n\n## Filtering & Pagination\n- **search**: Busca por nombre, email o apellidos (búsqueda insensible a mayúsculas)\n- **page**: Número de página (default: 1)\n- **limit**: Resultados por página (default: ITEMS_PAGE de entorno)\n- Excluye usuarios con rol 'dev' automáticamente\n\n## Notes\n- Retorna paginación con mongoose-paginate-v2\n- Los resultados usan model.parse() para transformación\n",
        "parameters": [
          {
            "description": "Número de página (por defecto 1)",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "example": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Número de resultados por página (por defecto ITEMS_PAGE del entorno)",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Buscar por nombre, email o apellidos (búsqueda insensible a mayúsculas con regex).\nBusca en campos: name, email, lastname.\n",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "example": "juan",
              "maxLength": 100,
              "minLength": 1,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "docs": [
                    {
                      "_id": "63d7907cbe76403b35da63df",
                      "email": "admin@empresa.com",
                      "emailVerified": true,
                      "i18n": "es",
                      "lastname": "Principal",
                      "name": "Admin",
                      "role": "admin",
                      "status": true
                    },
                    {
                      "_id": "63d7907cbe76403b35da64e",
                      "email": "usuario@empresa.com",
                      "emailVerified": true,
                      "i18n": "es",
                      "lastname": "Pérez García",
                      "name": "Juan",
                      "role": "gestor",
                      "status": true
                    }
                  ],
                  "limit": 20,
                  "page": 1,
                  "pages": 3,
                  "total": 45
                },
                "schema": {
                  "properties": {
                    "docs": {
                      "description": "Array de usuarios de la página actual",
                      "items": {
                        "$ref": "#/components/schemas/User"
                      },
                      "type": "array"
                    },
                    "limit": {
                      "description": "Límite de resultados por página",
                      "example": 20,
                      "type": "integer"
                    },
                    "page": {
                      "description": "Página actual",
                      "example": 1,
                      "type": "integer"
                    },
                    "pages": {
                      "description": "Total de páginas",
                      "example": 3,
                      "type": "integer"
                    },
                    "total": {
                      "description": "Total de usuarios encontrados",
                      "example": 45,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Lista de usuarios obtenida exitosamente"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado (token inválido o permisos insuficientes)"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "CIA_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Compañía no encontrada"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "List company users with pagination",
        "tags": [
          "Users - Management"
        ]
      },
      "post": {
        "description": "## Purpose\nCrea un nuevo usuario para la compañía. Solo administradores pueden crear usuarios.\n\n## Objective\nPermitir a administradores agregar nuevos usuarios a su compañía con control\nsobre el límite de usuarios según el plan de suscripción.\n\n## Use Cases\n- Agregar un nuevo empleado a la plataforma\n- Crear cuenta para un nuevo gestor\n- Añadir usuarios administrativos\n\n## Authentication & Authorization\n- Requiere JWT válido (middleware m.isLoged)\n- Requiere rol admin o dev (middleware m.isAdmin)\n- Requiere validación UTC (mTools.checkUTC)\n- Verifica límite de usuarios del plan (mPlan.canCreateUser)\n\n## Behavior\n- Valida que el email no exista en la base de datos\n- Si no se proporciona password, genera una automáticamente con tools.generatePass()\n- Crea usuario con model.createData()\n- Añade usuario al array company.users[]\n- Registra el uso en billing service (BillingService.recordUsage)\n- Envía email con credenciales usando mail.sendNewPass()\n\n## Validations\n- Email requerido y único\n- Usuario se añade a la compañía del admin\n- Billing service verifica límite de usuarios del plan\n- Si falla la creación en compañía, elimina el usuario (rollback)\n\n## Password Handling\n- Si se proporciona password en el body, se usa esa\n- Si no se proporciona, se genera automáticamente: 8 caracteres, mayúsculas, minúsculas, números\n- La contraseña se hashea con model.getPassword()\n- Se envía por email al usuario\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Recibir POST /] --> B{Usuario Admin?}\n  B -->|No| C[403 Forbidden]\n  B -->|Sí| D{Email Proporcionado?}\n  D -->|No| E[400 FORM_DATA_NOT_VALID]\n  D -->|Sí| F{Email Existe?}\n  F -->|Sí| G[406 USER_ALREADY_EXIST]\n  F -->|No| H{Plan Permite Usuario?}\n  H -->|No| I[403 PLAN_LIMIT_REACHED]\n  H -->|Sí| J[Generar Password si no hay]\n  J --> K[Crear Usuario model.createData]\n  K --> L[Añadir a company.users]\n  L --> M{Guarda Company?}\n  M -->|No| N[Eliminar usuario y 400]\n  M -->|Sí| O[Registrar en Billing]\n  O --> P[Enviar Email con password]\n  P --> Q[Retornar 201 User]\n```\n",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "email": "nuevo_usuario@empresa.com",
                "i18n": "es",
                "lastname": "Pérez García",
                "name": "Juan",
                "password": "MiContraseña123",
                "role": "gestor"
              },
              "schema": {
                "properties": {
                  "email": {
                    "description": "Email del nuevo usuario (único en la base de datos)",
                    "example": "nuevo_usuario@empresa.com",
                    "format": "email",
                    "type": "string"
                  },
                  "i18n": {
                    "default": "es",
                    "description": "Idioma preferido del usuario",
                    "enum": [
                      "es",
                      "en",
                      "fr",
                      "de"
                    ],
                    "example": "es",
                    "type": "string"
                  },
                  "lastname": {
                    "description": "Apellidos del usuario",
                    "example": "Pérez García",
                    "maxLength": 100,
                    "minLength": 2,
                    "type": "string"
                  },
                  "name": {
                    "description": "Nombre del usuario",
                    "example": "Juan",
                    "maxLength": 50,
                    "minLength": 2,
                    "type": "string"
                  },
                  "password": {
                    "description": "Contraseña del usuario (opcional). Si no se proporciona,\nse genera una automáticamente. Requisitos: mínimo 8 caracteres,\nal menos 1 mayúscula, 1 minúscula, 1 número.\n",
                    "example": "Contraseña123",
                    "maxLength": 50,
                    "minLength": 8,
                    "nullable": true,
                    "type": "string"
                  },
                  "role": {
                    "default": "gestor",
                    "description": "Rol del usuario (default: gestor). Define permisos en la plataforma.\n- dev: Superadmin con acceso total\n- admin: Gestión completa de la compañía\n- gestor: Operaciones diarias\n",
                    "enum": [
                      "dev",
                      "admin",
                      "gestor"
                    ],
                    "example": "gestor",
                    "type": "string"
                  }
                },
                "required": [
                  "email"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "63d7907cbe76403b35da70f",
                  "createdAt": "2025-02-12T10:30:00.000Z",
                  "email": "nuevo_usuario@empresa.com",
                  "emailVerified": false,
                  "i18n": "es",
                  "lastname": "Pérez García",
                  "name": "Juan",
                  "role": "gestor",
                  "status": true
                },
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            },
            "description": "Usuario creado exitosamente"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "message": "FORM_DATA_NOT_VALID"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Solicitud inválida. Posibles causas:\n- Email no proporcionado\n- Error al guardar (rollback automático)\n"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado (se requiere rol admin o dev)"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "message": "PLAN_LIMIT_REACHED"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Prohibido. Posibles causas:\n- Usuario no es administrador\n- Plan no permite crear más usuarios\n"
          },
          "406": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_ALREADY_EXIST"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Email ya existe en la base de datos"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Create new user (Admin only)",
        "tags": [
          "Users - Management"
        ]
      }
    },
    "/company/users/access/{id}": {
      "get": {
        "description": "## Purpose\nDevuelve el historial de accesos (login) de un usuario específico con información\ndetallada de IP, navegador, sistema operativo y geolocalización.\n\n## Objective\nFacilitar la auditoría de seguridad y detección de accesos sospechosos\nmediante el análisis de patrones de acceso.\n\n## Use Cases\n- Auditoría de seguridad\n- Detección de accesos desde ubicaciones inusuales\n- Análisis de dispositivos utilizados\n- Investigación de accesos no autorizados\n\n## Authentication\n- Requiere JWT válido (middleware m.isLoged)\n\n## Response Data\nCada registro de acceso incluye:\n- IP address\n- Browser (name, version)\n- OS (name, version)\n- Device (name, version)\n- Geolocalización (country, region, city, timezone)\n\n## Geolocation\n- Usa geoip-lite para determinar ubicación desde IP\n- Puede no estar disponible para todas las IPs (privadas, VPN, etc.)\n\n## Pagination\n- Retorna resultados paginados\n- Parámetros: page, limit\n",
        "parameters": [
          {
            "description": "ID del usuario cuyo historial de accesos se quiere consultar",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "63d7907cbe76403b35da63df",
              "pattern": "^[a-f0-9]{24}$",
              "type": "string"
            }
          },
          {
            "description": "Número de página (por defecto 1)",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "example": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Resultados por página (por defecto ITEMS_PAGE)",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "docs": [
                    {
                      "_id": "60a1b2c3d4e5f67890123456",
                      "browser": {
                        "name": "Chrome",
                        "version": "120.0.6099.109"
                      },
                      "createdAt": "2025-02-12T10:15:30.000Z",
                      "device": {
                        "name": "Other",
                        "version": "0.0"
                      },
                      "geo": {
                        "city": "Madrid",
                        "country": "ES",
                        "region": "Comunidad de Madrid",
                        "timezone": "Europe/Madrid"
                      },
                      "ip": "192.168.1.100",
                      "os": {
                        "name": "Windows",
                        "version": "Windows 10"
                      },
                      "user": "63d7907cbe76403b35da63df"
                    }
                  ],
                  "limit": 20,
                  "page": 1,
                  "pages": 5,
                  "total": 85
                },
                "schema": {
                  "properties": {
                    "docs": {
                      "description": "Array de registros de acceso",
                      "items": {
                        "$ref": "#/components/schemas/UserAccess"
                      },
                      "type": "array"
                    },
                    "limit": {
                      "description": "Límite de resultados por página",
                      "example": 20,
                      "type": "integer"
                    },
                    "page": {
                      "description": "Página actual",
                      "example": 1,
                      "type": "integer"
                    },
                    "pages": {
                      "description": "Total de páginas",
                      "example": 5,
                      "type": "integer"
                    },
                    "total": {
                      "description": "Total de accesos encontrados",
                      "example": 85,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Historial de accesos obtenido exitosamente"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. Posibles causas:\n- Token JWT inválido o expirado\n"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Get user access history",
        "tags": [
          "Users - History"
        ]
      }
    },
    "/company/users/actions/{id}": {
      "get": {
        "description": "## Purpose\nDevuelve el registro de acciones realizadas por un usuario específico.\n\n## Objective\nFacilitar la auditoría y análisis de actividad de usuarios para\nseguridad y cumplimiento.\n\n## Use Cases\n- Auditoría de seguridad\n- Análisis de actividad del usuario\n- Detección de comportamientos inusuales\n- Investigación de incidencias\n\n## Authentication\n- Requiere JWT válido (middleware m.isLoged)\n\n## Action Types\n- Cambios en perfil\n- Operaciones de autenticación\n- Cambios de contraseña\n- Modificaciones de configuración\n- Creación/edición de subastas\n- Gestión de entregas\n\n## Pagination\n- Retorna resultados paginados\n- Parámetros: page, limit\n",
        "parameters": [
          {
            "description": "ID del usuario cuyo historial se quiere consultar",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "63d7907cbe76403b35da63df",
              "pattern": "^[a-f0-9]{24}$",
              "type": "string"
            }
          },
          {
            "description": "Número de página (por defecto 1)",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "example": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Resultados por página (por defecto ITEMS_PAGE)",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "example": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "docs": [
                    {
                      "_id": "60a1b2c3d4e5f67890123456",
                      "action": "Cambio de contraseña",
                      "createdAt": "2025-08-01T10:15:30.000Z",
                      "user": "63d7907cbe76403b35da63df"
                    },
                    {
                      "_id": "60a1b2c3d4e5f67890123457",
                      "action": "Actualización de perfil",
                      "createdAt": "2025-08-02T14:30:45.000Z",
                      "user": "63d7907cbe76403b35da63df"
                    }
                  ],
                  "limit": 20,
                  "page": 1,
                  "pages": 8,
                  "total": 150
                },
                "schema": {
                  "properties": {
                    "docs": {
                      "description": "Array de acciones de la página actual",
                      "items": {
                        "$ref": "#/components/schemas/UserHistory"
                      },
                      "type": "array"
                    },
                    "limit": {
                      "description": "Límite de resultados por página",
                      "example": 20,
                      "type": "integer"
                    },
                    "page": {
                      "description": "Página actual",
                      "example": 1,
                      "type": "integer"
                    },
                    "pages": {
                      "description": "Total de páginas",
                      "example": 8,
                      "type": "integer"
                    },
                    "total": {
                      "description": "Total de acciones encontradas",
                      "example": 150,
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Historial de acciones obtenido exitosamente"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. Posibles causas:\n- Token JWT inválido o expirado\n"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Get user action history",
        "tags": [
          "Users - History"
        ]
      }
    },
    "/company/users/change/{id}": {
      "post": {
        "description": "## Purpose\nPermite a un administrador resetear la contraseña de cualquier usuario\nde la compañía, generando una nueva contraseña aleatoria y enviándola por email.\n\n## Objective\nFacilitar la recuperación de acceso de usuarios por parte de administradores\ncuando olvidan su contraseña o necesitan ayuda.\n\n## Use Cases\n- Usuario olvidó su contraseña y pide ayuda al admin\n- Admin necesita restablecer acceso de un usuario\n- Reset de seguridad por solicitud del usuario\n\n## Authentication & Authorization\n- Requiere JWT válido (middleware m.isLoged)\n- Requiere rol admin o dev (middleware m.isAdmin)\n\n## Behavior\n- Si no se proporciona password, genera una automáticamente con tools.generatePass()\n- La contraseña generada tiene 8 caracteres con mayúsculas, minúsculas y números\n- Hashea la contraseña con model.getPassword()\n- Envía email con la nueva contraseña usando mail.sendNewPass()\n- Verifica que el usuario pertenezca a la compañía del admin\n\n## Validations\n- Usuario debe existir\n- Usuario debe pertenecer a la compañía del admin\n- Si se proporciona password, se usa esa en lugar de generar\n\n## Password Handling\n- Si se proporciona password en el body, se usa esa\n- Si no se proporciona, se genera automáticamente: 8 caracteres, mayúsculas, minúsculas, números\n- La contraseña se hashea con model.getPassword()\n- Se envía por email al usuario en su idioma configurado (user.i18n)\n\n## Notes\n- Diferente de POST /changePass donde el usuario cambia su propia contraseña\n- Este endpoint es para que el admin resetee la contraseña de otro usuario\n",
        "parameters": [
          {
            "description": "ID del usuario al cual se le reseteará la contraseña",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "63d7907cbe76403b35da63df",
              "pattern": "^[a-f0-9]{24}$",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "password": "NuevaContraseña123"
              },
              "schema": {
                "properties": {
                  "password": {
                    "description": "Nueva contraseña para el usuario (opcional). Si no se proporciona,\nse genera una automáticamente de 8 caracteres con mayúsculas,\nminúsculas y números.\n",
                    "example": "NuevaContraseña123",
                    "maxLength": 50,
                    "minLength": 8,
                    "nullable": true,
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "63d7907cbe76403b35da63df",
                  "email": "usuario@empresa.com",
                  "i18n": "es",
                  "lastname": "Pérez García",
                  "name": "Juan",
                  "role": "gestor",
                  "status": true
                },
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            },
            "description": "Contraseña reseteada exitosamente y enviada por email"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado (se requiere rol admin o dev)"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_ALLOWED"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Prohibido (usuario no es administrador)"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Reset user password (Admin only)",
        "tags": [
          "Users - Management"
        ]
      }
    },
    "/company/users/changePass": {
      "post": {
        "description": "## Purpose\nPermite al usuario autenticado cambiar su propia contraseña.\n\n## Objective\nFacilitar el cambio de contraseña de forma segura por el propio usuario,\nmanteniendo el historial de contraseñas para evitar repeticiones.\n\n## Use Cases\n- Usuario quiere cambiar su contraseña periódicamente\n- Usuario olvidó su contraseña y usó recuperación (ahora quiere una personal)\n- Política de seguridad requiere cambio regular\n\n## Authentication\n- Requiere JWT válido (middleware m.isLoged)\n- Usuario debe estar autenticado\n\n## Validations\n- La contraseña actual debe ser válida (model.isValidPassword)\n- La nueva contraseña debe coincidir con la confirmación\n- La nueva contraseña debe cumplir requisitos de seguridad:\n  - Mínimo 8 caracteres\n  - Al menos 1 mayúscula\n  - Al menos 1 número\n- No puede ser igual a las últimas 5 contraseñas usadas\n- Crea notificación al cambiar contraseña\n\n## Process\n1. Usuario envía contraseña actual y nueva contraseña\n2. Sistema verifica que la contraseña actual sea correcta\n3. Sistema verifica que new_pass y confirm_pass coincidan\n4. Si es válida, actualiza a la nueva contraseña hasheada\n5. Limpia recovery_token\n6. Crea notificación de cambio de contraseña\n7. Actualiza token con m.setNewToken()\n\n## Error Messages\n- INVALID_PASSWORD (403): Contraseña actual incorrecta\n- PASSWORD_NOT_MATCH (405): new_pass y confirm_pass no coinciden\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "confirm_pass": "NuevaContraseña456",
                "current": "MiContraseñaActual123",
                "new_pass": "NuevaContraseña456"
              },
              "schema": {
                "properties": {
                  "confirm_pass": {
                    "description": "Confirmación de la nueva contraseña. Debe ser idéntica a new_pass.\n",
                    "example": "NuevaContraseña456",
                    "maxLength": 50,
                    "minLength": 8,
                    "type": "string"
                  },
                  "current": {
                    "description": "Contraseña actual del usuario (debe ser válida).\nSe valida con model.isValidPassword().\n",
                    "example": "MiContraseñaActual123",
                    "minLength": 8,
                    "type": "string"
                  },
                  "new_pass": {
                    "description": "Nueva contraseña deseada. Debe cumplir con requisitos de seguridad:\n- Mínimo 8 caracteres\n- Al menos 1 mayúscula\n- Al menos 1 minúscula\n- Al menos 1 número\n- No puede ser igual a las últimas 5 contraseñas\n",
                    "example": "NuevaContraseña456",
                    "maxLength": 50,
                    "minLength": 8,
                    "type": "string"
                  }
                },
                "required": [
                  "current",
                  "new_pass",
                  "confirm_pass"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "63d7907cbe76403b35da63df",
                  "email": "usuario@empresa.com",
                  "i18n": "es",
                  "lastname": "Pérez García",
                  "name": "Juan",
                  "role": "gestor",
                  "status": true,
                  "updatedAt": "2025-02-12T14:30:00.000Z"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/User"
                }
              }
            },
            "description": "Contraseña cambiada exitosamente"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "message": "FORM_DATA_NOT_VALID"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Solicitud inválida. Posibles causas:\n- Campos faltantes en el request\n- Error al guardar\n"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. Posibles causas:\n- Token JWT inválido o expirado\n- Usuario no tiene permisos\n"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "message": "INVALID_PASSWORD"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Contraseña actual incorrecta"
          },
          "405": {
            "content": {
              "application/json": {
                "example": {
                  "message": "PASSWORD_NOT_MATCH"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "La nueva contraseña y la confirmación no coinciden"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Change user password",
        "tags": [
          "users"
        ]
      }
    },
    "/company/users/delete/{id}": {
      "delete": {
        "description": "## Purpose\nDeshabilita un usuario estableciendo status=false. El usuario permanece\nen la base de datos pero no puede iniciar sesión.\n\n## Objective\nPermitir a administradores deshabilitar usuarios temporalmente sin\neliminarlos permanentemente de la base de datos.\n\n## Use Cases\n- Deshabilitar temporalmente un usuario\n- Suspender acceso mientras se investiga un incidente\n- Bloquear usuario temporalmente por seguridad\n\n## Authentication & Authorization\n- Requiere JWT válido (middleware m.isLoged)\n- Requiere rol admin o dev (middleware m.isAdmin)\n\n## Behavior\n- Busca usuario por ID\n- Establece user.status = false\n- Retorna usuario actualizado\n- No elimina el registro de la base de datos\n\n## Notes\n- Este endpoint solo deshabilita (status=false)\n- NO elimina físicamente el registro\n- Diferente de DELETE /:id que sí hace soft delete\n- El usuario puede ser reactivado cambiando status a true\n\n## Comparison\n- **DELETE /:id**: Soft delete completo (deleted=true, deletedAt set)\n- **DELETE /delete/:id**: Solo deshabilita (status=false)\n- **PUT /disabled/disable/:id**: Soft delete con razón adicional\n",
        "parameters": [
          {
            "description": "ID del usuario a deshabilitar",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "63d7907cbe76403b35da63df",
              "pattern": "^[a-f0-9]{24}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "63d7907cbe76403b35da63df",
                  "email": "usuario@empresa.com",
                  "lastname": "Pérez García",
                  "name": "Juan",
                  "role": "gestor",
                  "status": false,
                  "updatedAt": "2025-02-12T14:30:00.000Z"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/User"
                }
              }
            },
            "description": "Usuario deshabilitado exitosamente"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado (se requiere rol admin o dev)"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_ALLOWED"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Prohibido (usuario no es administrador)"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Disable user (Admin only)",
        "tags": [
          "users"
        ]
      }
    },
    "/company/users/disabled/check": {
      "get": {
        "description": "## Purpose\nVerifica si un usuario está deshabilitado (soft deleted) buscando por email.\nÚtil para verificar si un email puede ser reutilizado.\n\n## Objective\nFacilitar la verificación del estado de usuarios deshabilitados para\ndecidir si un email está disponible para reuso o si un usuario puede ser reactivado.\n\n## Use Cases\n- Verificar si un email ya está en uso por un usuario deshabilitado\n- Decidir si se puede reactivar un usuario existente\n- Prevenir duplicados al crear nuevos usuarios\n\n## Authentication & Authorization\n- Requiere JWT válido (middleware m.isLoged)\n- Requiere rol admin o dev (middleware m.isAdmin)\n\n## Behavior\n- Usa model.findOneDeleted() para incluir usuarios eliminados\n- Busca por email (query parameter requerido)\n- Retorna información sobre el estado del usuario\n\n## Notes\n- Este endpoint busca en usuarios deshabilitados (deleted=true)\n- Útil para decidir si se debe reactivar o crear nuevo usuario\n",
        "parameters": [
          {
            "description": "Email del usuario a verificar",
            "in": "query",
            "name": "email",
            "required": true,
            "schema": {
              "example": "usuario@empresa.com",
              "format": "email",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "deletedAt": "2025-01-15T14:30:00.000Z",
                  "disabled": true,
                  "exists": true,
                  "userId": "63d7907cbe76403b35da63df"
                },
                "schema": {
                  "properties": {
                    "deletedAt": {
                      "description": "Fecha de deshabilitación (si está deshabilitado)",
                      "example": "2025-01-15T14:30:00.000Z",
                      "format": "date-time",
                      "nullable": true,
                      "type": "string"
                    },
                    "disabled": {
                      "description": "Indica si el usuario está deshabilitado (deleted=true)",
                      "example": true,
                      "type": "boolean"
                    },
                    "exists": {
                      "description": "Indica si el usuario existe (activa o deshabilitada)",
                      "example": true,
                      "type": "boolean"
                    },
                    "userId": {
                      "description": "ID del usuario (si existe)",
                      "example": "63d7907cbe76403b35da63df",
                      "nullable": true,
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Verificación completada"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "message": "EMAIL_REQUIRED"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Solicitud inválida. Posibles causas:\n- Email no proporcionado\n"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado (se requiere rol admin o dev)"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_ALLOWED"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Prohibido (usuario no es administrador)"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Check if user is disabled",
        "tags": [
          "users"
        ]
      }
    },
    "/company/users/disabled/disable/{id}": {
      "put": {
        "description": "## Purpose\nDeshabilita un usuario con soft delete (método preferido sobre DELETE simple)\npermitiendo guardar una razón y mensaje explicativo.\n\n## Objective\nPermitir a administradores deshabilitar usuarios de forma controlada con\nregistro de la razón, facilitando auditoría y posible reactivación.\n\n## Use Cases\n- Deshabilitar usuario por falta de pago\n- Suspender cuenta por incumplimiento\n- Bloquear usuario temporalmente con motivo documentado\n- Deshabilitar empleado que deja la empresa\n\n## Authentication & Authorization\n- Requiere JWT válido (middleware m.isLoged)\n- Requiere rol admin o dev (middleware m.isAdmin)\n\n## Behavior\n- Ejecuta user.delete() (soft delete de mongoose-delete)\n- Establece status = false\n- Actualiza campos de razón: reason, reasonMessage\n- Guarda fecha de deshabilitación en reasonDate\n- Establece deleted = true y deletedAt automáticamente\n\n## Request Body\n- **reason**: Razón de deshabilitación (default: \"USER_DISABLED\")\n- **message**: Mensaje explicativo opcional\n\n## Comparison with other disable methods\n- **DELETE /:id**: Soft delete simple (deleted=true)\n- **DELETE /delete/:id**: Solo deshabilita (status=false)\n- **PUT /disabled/disable/:id**: Soft delete + razón + mensaje (RECOMENDADO)\n\n## Process\n1. Busca usuario por ID\n2. Si no existe, retorna 404\n3. Ejecuta user.delete() para soft delete\n4. Actualiza reason y reasonMessage del body\n5. Establece reasonDate = now\n6. Establece status = false\n7. Guarda cambios\n8. Retorna confirmación con deletedAt\n",
        "parameters": [
          {
            "description": "ID del usuario a deshabilitar",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "63d7907cbe76403b35da63df",
              "pattern": "^[a-f0-9]{24}$",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "message": "Usuario deshabilitado por falta de pago",
                "reason": "USER_DISABLED"
              },
              "schema": {
                "properties": {
                  "message": {
                    "description": "Mensaje explicativo de la deshabilitación (opcional).\nVisible para administradores y puede incluirse en notificaciones.\n",
                    "example": "Usuario deshabilitado por falta de pago de factura",
                    "maxLength": 500,
                    "nullable": true,
                    "type": "string"
                  },
                  "reason": {
                    "default": "USER_DISABLED",
                    "description": "Razón de deshabilitación (default: \"USER_DISABLED\").\nPuede ser cualquiera de las razones válidas: NONE, BAD_USER,\nPENDING, ACTIVE, BLOCKED, o una personalizada.\n",
                    "example": "USER_DISABLED",
                    "maxLength": 50,
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "deletedAt": "2025-02-12T14:30:00.000Z",
                  "disabled": true,
                  "userId": "63d7907cbe76403b35da63df"
                },
                "schema": {
                  "properties": {
                    "deletedAt": {
                      "description": "Fecha/hora de deshabilitación",
                      "example": "2025-02-12T14:30:00.000Z",
                      "format": "date-time",
                      "type": "string"
                    },
                    "disabled": {
                      "description": "Indica que el usuario fue deshabilitado",
                      "example": true,
                      "type": "boolean"
                    },
                    "userId": {
                      "description": "ID del usuario deshabilitado",
                      "example": "63d7907cbe76403b35da63df",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Usuario deshabilitado exitosamente con soft delete"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_ID_REQUIRED"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Solicitud inválida (ID no proporcionado)"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado (se requiere rol admin o dev)"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_ALLOWED"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Prohibido (usuario no es administrador)"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado"
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "message": "DISABLE_FAILED"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error al deshabilitar usuario"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Soft disable user with reason (Admin only)",
        "tags": [
          "users"
        ]
      }
    },
    "/company/users/disabled/reactivate/{id}": {
      "post": {
        "description": "## Purpose\nReactiva un usuario deshabilitado (restaura de soft delete) permitiéndole\nacceder nuevamente a la plataforma.\n\n## Objective\nPermitir a administradores reactivar usuarios que fueron deshabilitados\nmediante soft delete, restaurando su acceso completo.\n\n## Use Cases\n- Reactivar un empleado que regresa a la empresa\n- Restaurar acceso deshabilitado por error\n- Reactivar usuario tras completar verificación\n\n## Authentication & Authorization\n- Requiere JWT válido (middleware m.isLoged)\n- Requiere rol admin o dev (middleware m.isAdmin)\n\n## Behavior\n- Busca usuario con model.findOneDeleted()\n- Restaura usando user.restore() de mongoose-delete\n- Resetea estado: status = true, reason = \"NONE\"\n- Limpia reasonDate y reasonMessage\n- Establece mensaje: \"Usuario reactivado del estado deshabilitado\"\n\n## Process Flow\n1. Busca usuario deshabilitado por ID\n2. Si no existe, retorna 404\n3. Ejecuta user.restore() para revertir soft delete\n4. Establece status = true\n5. Resetea reason = \"NONE\"\n6. Limpia reasonDate y reasonMessage\n7. Guarda cambios\n8. Retorna usuario reactivado\n\n## Notes\n- Este endpoint restaura usuarios eliminados con soft delete\n- Diferente de POST /status/:id que solo cambia status\n- Recupera el usuario completamente (elimina flag deleted)\n",
        "parameters": [
          {
            "description": "ID del usuario a reactivar",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "63d7907cbe76403b35da63df",
              "pattern": "^[a-f0-9]{24}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "reactivated": true,
                  "user": {
                    "_id": "63d7907cbe76403b35da63df",
                    "deleted": false,
                    "deletedAt": null,
                    "email": "usuario@empresa.com",
                    "lastname": "Pérez García",
                    "name": "Juan",
                    "reason": "NONE",
                    "reasonDate": null,
                    "reasonMessage": "Usuario reactivado del estado deshabilitado",
                    "status": true,
                    "updatedAt": "2025-02-12T14:30:00.000Z"
                  }
                },
                "schema": {
                  "properties": {
                    "reactivated": {
                      "description": "Indica que el usuario fue reactivado",
                      "example": true,
                      "type": "boolean"
                    },
                    "user": {
                      "$ref": "../company-docs/openapi.yaml#/components/schemas/User",
                      "description": "Usuario reactivado"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Usuario reactivado exitosamente"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_ID_REQUIRED"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Solicitud inválida (ID no proporcionado)"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado (se requiere rol admin o dev)"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_ALLOWED"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Prohibido (usuario no es administrador)"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado (no existe o no está deshabilitado)"
          },
          "500": {
            "content": {
              "application/json": {
                "example": {
                  "message": "REACTIVATION_FAILED"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error al reactivar usuario"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Reactivate disabled user (Admin only)",
        "tags": [
          "users"
        ]
      }
    },
    "/company/users/fileRequest": {
      "post": {
        "description": "## Purpose\nSolicita documentación adicional a un usuario. Esta funcionalidad está\nprevista para futura implementación en el frontend.\n\n## Objective\nPermitir a administradores solicitar documentos adicionales a los usuarios\npara verificación de identidad, compliance, etc.\n\n## Use Cases\n- Solicitar documentación fiscal adicional\n- Pedir documentos de identificación\n- Requerir contratos o permisos especiales\n\n## Authentication & Authorization\n- Requiere JWT válido (middleware m.isLoged)\n- Requiere rol admin o dev (middleware m.isAdmin)\n\n## Status\n- Actualmente el endpoint solo guarda el usuario sin lógica adicional\n- Pendiente de implementación completa en el frontend\n- Puede estar en desarrollo para futuras funcionalidades\n\n## Notes\n- Este endpoint parece incompleto según el código actual\n- Se recomienda verificar si está en uso antes de depender de él\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "description": "Cuerpo del request pendiente de definición",
                "type": "object"
              }
            }
          },
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            },
            "description": "Solicitud procesada (implementación pendiente)"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado (se requiere rol admin o dev)"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_ALLOWED"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Prohibido (usuario no es administrador)"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Request user documentation (Admin only)",
        "tags": [
          "Users - Management"
        ]
      }
    },
    "/company/users/lang": {
      "get": {
        "description": "## Purpose\nDevuelve el idioma preferido configurado por el usuario autenticado.\n\n## Objective\nPermitir a la aplicación conocer el idioma del usuario para personalizar\nla interfaz y contenido.\n\n## Use Cases\n- Mostrar interfaz en el idioma correcto\n- Cargar contenido traducido\n- Enviar emails en el idioma preferido\n\n## Authentication\n- Requiere JWT válido (middleware m.isLoged)\n\n## Supported Languages\n- es (Español)\n- en (Inglés)\n- fr (Francés)\n- de (Alemán)\n",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "i18n": "es"
                },
                "schema": {
                  "properties": {
                    "i18n": {
                      "description": "Código de idioma preferido del usuario",
                      "enum": [
                        "es",
                        "en",
                        "fr",
                        "de"
                      ],
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Idioma actual del usuario"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. Posibles causas:\n- Token JWT inválido o expirado\n- Usuario no tiene permisos\n"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Get user language preference",
        "tags": [
          "users"
        ]
      },
      "post": {
        "description": "## Purpose\nPermite al usuario configurar su idioma preferido.\n\n## Objective\nFacilitar la personalización del idioma de la interfaz y contenidos\nsegún las preferencias del usuario.\n\n## Use Cases\n- Usuario cambia el idioma de la interfaz\n- Usuario selecciona su idioma preferido en registro\n- Usuario actualiza preferencias de idioma\n\n## Authentication\n- Requiere JWT válido (middleware m.isLoged)\n\n## Persistence\n- El cambio se guarda en la base de datos\n- Afecta a todas las respuestas futuras de la API\n- Se usa para enviar emails en el idioma correcto\n\n## Supported Languages\n- es (Español)\n- en (Inglés)\n- fr (Francés)\n- de (Alemán)\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "language": "es"
              },
              "schema": {
                "properties": {
                  "language": {
                    "description": "Código de idioma a establecer",
                    "enum": [
                      "es",
                      "en",
                      "fr",
                      "de"
                    ],
                    "type": "string"
                  }
                },
                "required": [
                  "language"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "message": "Idioma actualizado correctamente"
                },
                "schema": {
                  "properties": {
                    "message": {
                      "example": "Idioma actualizado a español",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Idioma actualizado exitosamente"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "message": "FORM_DATA_NOT_VALID"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Solicitud inválida. Posibles causas:\n- Idioma no soportado\n- Campo language faltante\n"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado (token inválido o permisos insuficientes)"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Set user language preference",
        "tags": [
          "users"
        ]
      }
    },
    "/company/users/me": {
      "get": {
        "description": "## Purpose\nDevuelve el perfil completo del usuario actualmente autenticado mediante JWT.\nIncluye todos los datos personales, preferencias y configuración del usuario.\n\n## Objective\nPermitir al usuario obtener su propia información de perfil para mostrarla en la interfaz\ny verificar su estado de verificación.\n\n## Use Cases\n- Mostrar información del perfil en el frontend\n- Verificar estado de verificación de email\n- Obtener preferencias de idioma y zona horaria\n- Cargar datos del usuario en formularios de edición\n\n## Authentication\n- Requiere JWT válido (middleware m.isLoged)\n- Usuario debe estar autenticado\n\n## Notes\n- Retorna datos usando model.parseMe() con información extendida\n- Error 404 si userData no existe en el token\n",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "63d7907cbe76403b35da63df",
                  "createdAt": "2025-01-15T10:30:00.000Z",
                  "email": "usuario@empresa.com",
                  "emailVerified": true,
                  "i18n": "es",
                  "lastname": "Pérez García",
                  "name": "Juan",
                  "phone": "+34666555444",
                  "role": "gestor",
                  "status": true,
                  "timezone": "Europe/Madrid"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/User"
                }
              }
            },
            "description": "Perfil de usuario obtenido exitosamente"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. Posibles causas:\n- Token JWT inválido o expirado\n- Usuario no tiene permisos\n"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Get authenticated user profile",
        "tags": [
          "users"
        ]
      },
      "put": {
        "description": "## Purpose\nPermite al usuario autenticado actualizar su propia información de perfil.\nSolo actualiza los campos proporcionados en el request (PATCH semantics).\n\n## Objective\nFacilitar la actualización de datos personales y preferencias del usuario por sí mismo.\n\n## Use Cases\n- Actualizar datos personales desde formulario de perfil\n- Cambiar preferencias de idioma\n- Actualizar número de teléfono\n- Modificar zona horaria\n- Cambiar email (con validación de duplicados)\n\n## Authentication\n- Requiere JWT válido (middleware m.isLoged)\n- Usuario debe estar autenticado\n- Requiere validación UTC (mTools.checkUTC)\n\n## Validations\n- Fechas deben estar en formato ISO8601 (YYYY-MM-DD)\n- Teléfono debe ser válido según el país\n- Email validado para evitar duplicados\n- Datos validados con model.validateData()\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "birthDate": "1990-05-15T00:00:00.000Z",
                "email": "nuevo_email@empresa.com",
                "i18n": "es",
                "lastname": "Pérez García",
                "name": "Juan Carlos",
                "phone": "+34666777888",
                "taxid": "12345678A",
                "timezone": "Europe/Madrid"
              },
              "schema": {
                "$ref": "../company-docs/openapi.yaml#/components/schemas/UserUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "63d7907cbe76403b35da63df",
                  "birthDate": "1990-05-15T00:00:00.000Z",
                  "email": "nuevo_email@empresa.com",
                  "i18n": "es",
                  "lastname": "Pérez García",
                  "name": "Juan Carlos",
                  "phone": "+34666777888",
                  "timezone": "Europe/Madrid",
                  "updatedAt": "2025-02-12T14:30:00.000Z"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/User"
                }
              }
            },
            "description": "Perfil actualizado exitosamente"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "message": "FORM_DATA_NOT_VALID"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Solicitud inválida. Posibles causas:\n- Datos de formulario no válidos (model.validateData)\n- Email ya existe (USER_ALREADY_EXIST)\n- Formato de fecha incorrecto\n"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado (token inválido o permisos insuficientes)"
          },
          "406": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_ALREADY_EXIST"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Email ya existe en la base de datos"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Update authenticated user profile",
        "tags": [
          "users"
        ]
      }
    },
    "/company/users/send_verify": {
      "post": {
        "description": "## Purpose\nEnvía un email de verificación al usuario actual para activar su cuenta.\nÚtil cuando el email anterior expiró o no llegó.\n\n## Objective\nPermitir a los usuarios solicitar un nuevo email de verificación para\ncompletar el proceso de activación de su cuenta.\n\n## Use Cases\n- Usuario no recibió el email de verificación original\n- El enlace de verificación expiró\n- Usuario quiere cambiar su email y necesita verificar el nuevo\n\n## Authentication\n- Requiere JWT válido (middleware m.isLoged)\n\n## Process\n1. Genera token de verificación único con tools.generateToken()\n2. Guarda token en user.recovery_token\n3. Envía email usando mail.sendActive() en el idioma del usuario\n4. Retorna confirmación de envío\n\n## Email Content\n- Subject: Depende del idioma (user.i18n)\n- Body: Incluye enlace con token de verificación\n- Language: Usa el idioma configurado del usuario (user.i18n)\n\n## Notes\n- No valida si el email ya está verificado\n- Sobrescribe cualquier token anterior\n- El token se usa en el endpoint de verificación de auth\n\n## Error Messages\n- NOT_VALID (401): Email no válido o vacío\n",
        "parameters": [],
        "requestBody": {},
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "message": "EMAIL_SENT"
                  },
                  "status": true
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "message": {
                          "example": "EMAIL_SENT",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    },
                    "status": {
                      "example": true,
                      "type": "boolean"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Email de verificación enviado exitosamente"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NOT_VALID"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado o email no válido. Posibles causas:\n- Token JWT inválido o expirado\n- Email del usuario no es válido\n"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Send email verification",
        "tags": [
          "users"
        ]
      }
    },
    "/company/users/status/{id}": {
      "post": {
        "description": "## Purpose\nCambia el estado de un usuario (activar/desactivar) mediante el campo\nstatus. Solo administradores pueden cambiar el estado de usuarios.\n\n## Objective\nPermitir a administradores controlar el acceso de usuarios a la plataforma\nmediante activación/desactivación de cuentas.\n\n## Use Cases\n- Desactivar un usuario temporalmente\n- Activar un usuario que estaba desactivado\n- Suspender acceso por razones de seguridad\n- Bloquear usuario por falta de pago o incumplimiento\n\n## Authentication & Authorization\n- Requiere JWT válido (middleware m.isLoged)\n- Requiere rol admin o dev (middleware m.isAdmin)\n\n## Behavior\n- **reason = \"NONE\"**: Activa usuario (status = true), limpia reason, reasonDate, reasonMessage\n- **reason ≠ \"NONE\"**: Desactiva usuario (status = false), establece reason y reasonMessage\n- Valida razón con model.isValidReason()\n- Actualiza reasonDate si es válida (formato ISO8601)\n\n## Valid Reasons\n- **NONE**: Usuario normal activo sin incidencias (activa usuario)\n- **BAD_USER**: Bloqueado por mal comportamiento (reportes, fraude)\n- **PENDING**: Registro completado, esperando activación\n- **ACTIVE**: Usuario verificado y operativo\n- **BLOCKED**: Bloqueado administrativamente (impago, seguridad)\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Recibir POST /status/:id] --> B{Usuario Admin?}\n  B -->|No| C[403 Forbidden]\n  B -->|Sí| D{Usuario Existe?}\n  D -->|No| E[404 Not Found]\n  D -->|Sí| F{Reason === 'NONE'?}\n  F -->|Sí| G[Activar Usuario]\n  G --> H[status = true]\n  H --> I[reason = NONE]\n  I --> J[Limpiar reasonDate y reasonMessage]\n  F -->|No| K[Desactivar Usuario]\n  K --> L[status = false]\n  L --> M[Establecer reason]\n  M --> N[Establecer reasonDate]\n  N --> O[Establecer reasonMessage]\n  J --> P[Validar Rol]\n  O --> P\n  P --> Q[Guardar y Retornar 200]\n```\n\n## Notes\n- Este es el método preferido para bloquear/desbloquear usuarios temporalmente\n- Diferente de soft delete (DELETE /:id) que elimina el registro\n",
        "parameters": [
          {
            "description": "ID del usuario al cual se le cambiará el estado",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "63d7907cbe76403b35da63df",
              "pattern": "^[a-f0-9]{24}$",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "reason": "NONE",
                "reasonDate": "2025-02-12T14:30:00.000Z",
                "reasonMessage": "Usuario reactivado tras completar verificación"
              },
              "schema": {
                "properties": {
                  "reason": {
                    "description": "Razón del cambio de estado. Si es \"NONE\", el usuario se activa.\nCualquier otro valor desactiva al usuario.\n",
                    "enum": [
                      "NONE",
                      "BAD_USER",
                      "PENDING",
                      "ACTIVE",
                      "BLOCKED"
                    ],
                    "example": "NONE",
                    "type": "string"
                  },
                  "reasonDate": {
                    "description": "Fecha/hora del cambio de estado (opcional). Si no se proporciona,\nse usa la fecha actual. Formato ISO8601.\n",
                    "example": "2025-02-12T14:30:00.000Z",
                    "format": "date-time",
                    "nullable": true,
                    "type": "string"
                  },
                  "reasonMessage": {
                    "description": "Mensaje explicativo del cambio de estado (opcional).\nVisible para el usuario y administradores.\n",
                    "example": "Usuario bloqueado temporalmente por verificación de documentación",
                    "maxLength": 500,
                    "nullable": true,
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "63d7907cbe76403b35da63df",
                  "email": "usuario@empresa.com",
                  "lastname": "Pérez García",
                  "name": "Juan",
                  "reason": "NONE",
                  "reasonDate": null,
                  "reasonMessage": null,
                  "status": true,
                  "updatedAt": "2025-02-12T14:30:00.000Z"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/User"
                }
              }
            },
            "description": "Estado de usuario cambiado exitosamente"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "message": "FORM_DATA_NOT_VALID"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Solicitud inválida. Posibles causas:\n- Razón no válida\n- Error al guardar\n"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado (se requiere rol admin o dev)"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_ALLOWED"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Prohibido (usuario no es administrador)"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND"
                },
                "schema": {
                  "$ref": "../company-docs/openapi.yaml#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Change user status (Admin only)",
        "tags": [
          "users"
        ]
      }
    },
    "/company/users/{id}": {
      "delete": {
        "description": "## Purpose\nElimina un usuario de forma permanente usando soft delete (mongoose-delete).\nEl usuario ya no aparecerá en consultas normales pero puede ser recuperado.\n\n## Objective\nPermitir a administradores eliminar usuarios de forma segura con posibilidad\nde recuperación mediante soft delete.\n\n## Use Cases\n- Eliminar un usuario que ya no trabaja en la empresa\n- Eliminar cuentas duplicadas\n- Eliminar usuarios de prueba\n\n## Authentication & Authorization\n- Requiere JWT válido (middleware m.isLoged)\n- Requiere rol admin o dev (middleware m.isAdmin)\n\n## Behavior\n- Usa user.delete() de mongoose-delete (soft delete)\n- Establece deleted: true y deletedAt con la fecha actual\n- Usuario ya no aparece en queries normales\n- Recuperable vía endpoint POST /disabled/reactivate/:id\n\n## Notes\n- Diferente de DELETE /delete/:id que solo deshabilita (status=false)\n- Este endpoint elimina completamente el registro (aunque recuperable)\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Recibir DELETE /:id] --> B{Usuario Admin?}\n  B -->|No| C[403 Forbidden]\n  B -->|Sí| D{Usuario Existe?}\n  D -->|No| E[404 Not Found]\n  D -->|Sí| F[Ejecutar soft delete]\n  F --> G[Establecer deleted: true]\n  G --> H[Establecer deletedAt]\n  H --> I[Retornar 200 OK]\n```\n",
        "parameters": [
          {
            "description": "ID del usuario a eliminar (ObjectId MongoDB)",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "63d7907cbe76403b35da63df",
              "pattern": "^[a-f0-9]{24}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "63d7907cbe76403b35da63df",
                  "deleted": true
                },
                "schema": {
                  "properties": {
                    "_id": {
                      "description": "ID del usuario eliminado",
                      "example": "63d7907cbe76403b35da63df",
                      "type": "string"
                    },
                    "deleted": {
                      "description": "Indica que el usuario fue eliminado",
                      "example": true,
                      "type": "boolean"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Usuario eliminado exitosamente (soft delete)"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado (se requiere rol admin o dev)"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_ALLOWED"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Prohibido (usuario no es administrador)"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Delete user with soft delete (Admin only)",
        "tags": [
          "Users - Management"
        ]
      },
      "get": {
        "description": "## Purpose\nDevuelve la información completa de un usuario específico de la compañía.\n\n## Objective\nPermitir visualizar el perfil de cualquier usuario de la compañía,\nútil para administradores y gestores.\n\n## Use Cases\n- Visualizar perfil de otro usuario (solo admin/gestor)\n- Verificar información de contacto\n- Consultar datos de un usuario específico\n\n## Authentication\n- Requiere JWT válido (middleware m.isLoged)\n- Usuario debe estar autenticado\n- Solo devuelve datos si el usuario pertenece a la misma compañía\n\n## Notes\n- Los usuarios no administradores pueden ver su propio perfil\n- El controlador verifica que el usuario pertenezca a la compañía\n",
        "parameters": [
          {
            "description": "ID del usuario a consultar (ObjectId MongoDB)",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "63d7907cbe76403b35da63df",
              "pattern": "^[a-f0-9]{24}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "63d7907cbe76403b35da63df",
                  "company_name": "Mi Empresa SL",
                  "email": "usuario@empresa.com",
                  "emailVerified": true,
                  "i18n": "es",
                  "lastname": "Pérez García",
                  "name": "Juan",
                  "phone": "+34666555444",
                  "role": "gestor",
                  "status": true
                },
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            },
            "description": "Detalles del usuario obtenidos exitosamente"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado. Posibles causas:\n- Token JWT inválido o expirado\n- Usuario no tiene permisos para ver este perfil\n"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Get user details by ID",
        "tags": [
          "Users - Management"
        ]
      },
      "put": {
        "description": "## Purpose\nPermite a un administrador editar el perfil de cualquier usuario de la compañía.\n\n## Objective\nFacilitar la gestión de usuarios por parte de administradores, permitiendo\nmodificar datos de cualquier usuario de la compañía.\n\n## Use Cases\n- Actualizar datos de un usuario como administrador\n- Cambiar rol de usuario\n- Modificar email de usuario\n- Actualizar información de contacto\n\n## Authentication & Authorization\n- Requiere JWT válido (middleware m.isLoged)\n- Requiere rol admin o dev (middleware m.isAdmin)\n- Requiere validación UTC (mTools.checkUTC)\n\n## Validations\n- Email validado para evitar duplicados\n- Rol validado con model.getValidRole()\n- Datos validados con model.validateData()\n- Verifica que el usuario pertenezca a la compañía\n\n## Notes\n- Similar a PUT /me pero para cualquier usuario de la compañía\n- Solo administradores pueden acceder\n",
        "parameters": [
          {
            "description": "ID del usuario a editar (ObjectId MongoDB)",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "63d7907cbe76403b35da63df",
              "pattern": "^[a-f0-9]{24}$",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "email": "nuevo_email@empresa.com",
                "i18n": "es",
                "lastname": "Pérez García",
                "name": "Juan Carlos",
                "phone": "+34666777888",
                "role": "admin",
                "timezone": "Europe/Madrid"
              },
              "schema": {
                "$ref": "#/components/schemas/UserUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "63d7907cbe76403b35da63df",
                  "email": "nuevo_email@empresa.com",
                  "i18n": "es",
                  "lastname": "Pérez García",
                  "name": "Juan Carlos",
                  "phone": "+34666777888",
                  "role": "admin",
                  "timezone": "Europe/Madrid",
                  "updatedAt": "2025-02-12T14:30:00.000Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            },
            "description": "Perfil actualizado exitosamente"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "message": "FORM_DATA_NOT_VALID"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Solicitud inválida. Posibles causas:\n- Datos de formulario no válidos\n- Email ya existe (USER_ALREADY_EXIST)\n"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "message": "NO_TOKEN"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No autorizado (se requiere rol admin o dev)"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_ALLOWED"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Prohibido (usuario no es administrador)"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_NOT_FOUND"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Usuario no encontrado"
          },
          "406": {
            "content": {
              "application/json": {
                "example": {
                  "message": "USER_ALREADY_EXIST"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Email ya existe en la base de datos"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Update user profile (Admin only)",
        "tags": [
          "Users - Management"
        ]
      }
    },
    "/company/vehicles/": {
      "get": {
        "deprecated": false,
        "description": "## Purpose\nRetrieves the authenticated company's complete vehicle fleet with pagination, search, and filtering capabilities.\n\n## Objective\nEnable companies to manage their fleet by viewing, searching, and paginating through all registered vehicles.\n\n## Use Cases\n- Display paginated list of all company vehicles in fleet management interface\n- Search for specific vehicles by license plate\n- Use autocomplete for quick vehicle lookup\n- Retrieve default vehicle for digital signature operations\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request] --> B{User Authenticated?}\n  B -->|No| C[401 Unauthorized]\n  B -->|Yes| D{User Has Company?}\n  D -->|No| E[401 CIA_NOT_FOUND]\n  D -->|Yes| F[Get Company Vehicles]\n  F --> G{Apply Filters}\n  G --> H[Paginate Results]\n  H --> I{isSign = true?}\n  I -->|Yes| J[Include Default Vehicle]\n  I -->|No| K[Return Paginated List]\n  J --> K\n```\n\n## Pagination\nThe response uses custom pagination format with metadata including:\n- docs: Array of vehicle objects\n- totalDocs: Total number of matching vehicles\n- page: Current page number\n- limit: Results per page\n- totalPages: Total number of pages\n- hasNextPage/hasPrevPage: Navigation flags\n- nextPage/prevPage: Page numbers for navigation\n\n## Search Behavior\nBoth `search` and `autocomplete` parameters filter vehicles by license plate using case-insensitive regex matching.\n",
        "parameters": [
          {
            "description": "Page number for pagination",
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "default": 1,
              "example": 1,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Number of results per page",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 10,
              "example": 10,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Search vehicles by license plate (case insensitive regex)",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "example": "ABC",
              "maxLength": 50,
              "minLength": 1,
              "type": "string"
            }
          },
          {
            "description": "Additional filter for license plate autocomplete functionality",
            "in": "query",
            "name": "autocomplete",
            "required": false,
            "schema": {
              "example": "12",
              "maxLength": 50,
              "minLength": 1,
              "type": "string"
            }
          },
          {
            "description": "If 'true', includes the user's default vehicle in the response (used for digital signature operations)",
            "in": "query",
            "name": "isSign",
            "required": false,
            "schema": {
              "enum": [
                "true",
                "false"
              ],
              "example": "true",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "docs": [
                    {
                      "_id": "68fb5b7df42f3ccb7e37b62b",
                      "cargo_type": [
                        "up",
                        "lateral",
                        "back"
                      ],
                      "fresh_cargo_temp": null,
                      "image": "",
                      "itv": "",
                      "plate": "8521eee",
                      "shipping_type": "dry",
                      "updatedAt": "2025-07-22T10:15:30.000Z",
                      "vehicle_type": "tir"
                    }
                  ],
                  "hasNextPage": true,
                  "hasPrevPage": false,
                  "limit": 10,
                  "nextPage": 2,
                  "page": 1,
                  "prevPage": null,
                  "totalDocs": 25,
                  "totalPages": 3
                },
                "schema": {
                  "$ref": "#/components/schemas/FleetPaginationResponse"
                }
              }
            },
            "description": "Fleet list retrieved successfully",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "examples": {
                  "generalError": {
                    "value": {
                      "error": "CANT_SEND",
                      "message": "Cannot send fleet data"
                    }
                  },
                  "noCompany": {
                    "value": {
                      "error": "CIA_NOT_FOUND",
                      "message": "Company not found for user"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unauthorized or company not found\n- NO_TOKEN: JWT token not provided\n- TOKEN_NOT_VALID: JWT token is invalid or expired\n- CIA_NOT_FOUND: User does not have an associated company\n- CANT_SEND: General error sending data\n"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "List company vehicle fleet with pagination",
        "tags": [
          "Vehicles"
        ]
      },
      "post": {
        "deprecated": false,
        "description": "## Purpose\nRegisters a new vehicle in the company's fleet with associated metadata and optional file uploads.\n\n## Objective\nEnable companies to add vehicles to their fleet with detailed specifications including cargo types,\nshipping capabilities, and documentation (images and ITV certificates).\n\n## Use Cases\n- Register a newly acquired vehicle with all specifications\n- Upload vehicle documentation (image, ITV certificate)\n- Set a vehicle as the user's default for digital signature operations\n- Migrate fleet data from external systems\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request] --> B{Payment Limit OK?}\n  B -->|No| C[402 Payment Required]\n  B -->|Yes| D{User Has Company?}\n  D -->|No| E[401 CIA_NOT_FOUND]\n  D -->|Yes| F{Required Fields Present?}\n  F -->|No| G[400 Validation Error]\n  F -->|Yes| H{shipping_type = fresh?}\n  H -->|Yes| I{fresh_cargo_temp Valid?}\n  I -->|No| J[400 Temp Required]\n  I -->|Yes| K[Create Vehicle]\n  H -->|No| K\n  K --> L{default_vehicle = true?}\n  L -->|Yes| M[Update User Default]\n  L -->|No| N[Register Billing Usage]\n  M --> N\n  N --> O[Return Created Vehicle]\n```\n\n## Payment Limits\nThis endpoint is subject to payment plan limits via the `isPaymentUpdate` middleware.\nIf the company has reached its vehicle limit, the request will fail with error 402.\n\n## Conditional Validation\n- If `shipping_type = 'fresh'`, the field `fresh_cargo_temp` is REQUIRED (range: -20 to 40)\n- If `shipping_type = 'dry'`, the field `fresh_cargo_temp` MUST NOT be sent or must be null\n\n## cargo_type Format\nAccepts two formats:\n1. **JSON Array**: `[\"up\", \"lateral\", \"back\"]`\n2. **Comma-separated string**: `\"up, lateral, back\"`\n\nBoth formats are automatically converted to array internally.\n\n## File Uploads\n- `image`: Vehicle photo (JPEG, PNG)\n- `itv`: ITV inspection certificate (PDF, JPEG, PNG)\n\nFiles are uploaded to S3/MinIO storage and referenced by key in the response.\n\n## Default Vehicle\nIf `default_vehicle='true'`, this vehicle is set as the user's default for digital signature operations.\nThe user model is updated to reference this vehicle ID.\n\n## Billing Integration\nCreating a vehicle registers a usage event in the billing system for plan consumption tracking.\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "example": {
                  "cargo_type": [
                    "up",
                    "lateral",
                    "back"
                  ],
                  "default_vehicle": "true",
                  "fresh_cargo_temp": null,
                  "image": null,
                  "itv": null,
                  "plate": "8521EEE",
                  "shipping_type": "dry",
                  "vehicle_type": "tir"
                },
                "properties": {
                  "cargo_type": {
                    "description": "Supported cargo types. Accepts two formats:\n1. JSON Array: [\"up\", \"lateral\", \"back\"]\n2. Comma-separated string: \"up, lateral, back\"\n",
                    "example": [
                      "up",
                      "lateral",
                      "back"
                    ],
                    "oneOf": [
                      {
                        "items": {
                          "enum": [
                            "up",
                            "lateral",
                            "back"
                          ],
                          "type": "string"
                        },
                        "type": "array"
                      },
                      {
                        "pattern": "^(up|lateral|back)(,\\s*(up|lateral|back))*$",
                        "type": "string"
                      }
                    ]
                  },
                  "default_vehicle": {
                    "description": "If set to 'true', this vehicle becomes the user's default vehicle\nfor digital signature operations. Updates the user model.\n",
                    "enum": [
                      "true"
                    ],
                    "example": "true",
                    "type": "string"
                  },
                  "fresh_cargo_temp": {
                    "description": "Temperature for refrigerated cargo (REQUIRED if shipping_type='fresh', range -20 to 40)",
                    "example": -18,
                    "maximum": 40,
                    "minimum": -20,
                    "nullable": true,
                    "type": "number"
                  },
                  "image": {
                    "description": "Vehicle photo file (JPEG, PNG)",
                    "format": "binary",
                    "type": "string"
                  },
                  "itv": {
                    "description": "ITV inspection certificate file (PDF, JPEG, PNG)",
                    "format": "binary",
                    "type": "string"
                  },
                  "plate": {
                    "description": "Vehicle license plate (will be stored in lowercase)",
                    "example": "8521EEE",
                    "maxLength": 20,
                    "minLength": 3,
                    "type": "string"
                  },
                  "shipping_type": {
                    "description": "Type of shipping/transport",
                    "enum": [
                      "fresh",
                      "dry"
                    ],
                    "example": "dry",
                    "type": "string"
                  },
                  "vehicle_type": {
                    "description": "Type of vehicle",
                    "enum": [
                      "r3c",
                      "tir",
                      "rt",
                      "r2c",
                      "r2d",
                      "van",
                      "frc",
                      "f2c",
                      "adr",
                      "ft",
                      "pt",
                      "cc",
                      "hdcc",
                      "dump",
                      "live",
                      "cocar"
                    ],
                    "example": "tir",
                    "type": "string"
                  }
                },
                "required": [
                  "plate",
                  "vehicle_type",
                  "cargo_type",
                  "shipping_type"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "68fb5b7df42f3ccb7e37b62b",
                  "cargo": {
                    "kg_max": 30000,
                    "kg_min": 20000,
                    "max": 35,
                    "min": 20
                  },
                  "cargo_type": [
                    "up",
                    "lateral",
                    "back"
                  ],
                  "fresh_cargo_temp": null,
                  "image": "/images?file=vehicles/8521eee.jpg",
                  "itv": "/images?file=itv/8521eee.pdf",
                  "plate": "8521eee",
                  "shipping_type": "dry",
                  "updatedAt": "2025-07-22T10:15:30.000Z",
                  "vehicle_type": "tir"
                },
                "schema": {
                  "$ref": "#/components/schemas/Vehicle"
                }
              }
            },
            "description": "Vehicle created successfully",
            "headers": {}
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "error": "CANT_CREATE",
                  "message": "Cannot create vehicle"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Invalid request - validation failed\n- CANT_CREATE: General creation error\n- Missing required fields\n- Invalid cargo_type values\n- Invalid vehicle_type\n- Missing fresh_cargo_temp when shipping_type='fresh'\n"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "CIA_NOT_FOUND",
                  "message": "Company not found for user"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "User does not have an associated company"
          },
          "402": {
            "content": {
              "application/json": {
                "example": {
                  "error": "PAYMENT_REQUIRED",
                  "message": "Vehicle limit exceeded for current plan"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Payment Required - Vehicle limit exceeded for current plan.\nUpgrade your plan to create more vehicles.\n"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "USER_NOT_FOUND",
                  "message": "User not found"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "User not found (when using default_vehicle feature)"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Create a new vehicle in the fleet",
        "tags": [
          "Vehicles"
        ]
      }
    },
    "/company/vehicles/bulk/": {
      "post": {
        "deprecated": false,
        "description": "## Purpose\nEnables bulk creation of multiple vehicles from a CSV file upload.\n\n## Objective\nAllow companies to efficiently import their entire fleet or large batches of vehicles\nfrom external systems or spreadsheets.\n\n## Use Cases\n- Migrate existing fleet from another system\n- Import vehicles from fleet management spreadsheets\n- Bulk onboarding of new vehicles\n- Update multiple vehicles simultaneously\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive CSV File] --> B{Payment Limit OK?}\n  B -->|No| C[402 Payment Required]\n  B -->|Yes| D{File Present?}\n  D -->|No| E[404 FILE_NOT_FOUND]\n  D -->|Yes| F{Parse CSV}\n  F --> G{Headers Valid?}\n  G -->|No| H[Return ko: MIN_HEADERS]\n  G -->|Yes| I{Data Present?}\n  I -->|No| J[400 INVALID_DATA]\n  I -->|Yes| K[Process Each Line]\n  K --> L{Line Valid?}\n  L -->|Yes| M[Create Vehicle - Add to ok]\n  L -->|No| N[Add to ko with reason]\n  M --> O{More Lines?}\n  N --> O\n  O -->|Yes| K\n  O -->|No| P[Update Company]\n  P --> Q[Return ok and ko arrays]\n```\n\n## Payment Limits\nThis endpoint is subject to payment plan limits via the `isPaymentUpdate` middleware.\nIf the company has reached its vehicle limit, the request will fail with error 402\nbefore processing the CSV.\n\n## CSV Structure\n\n**Required headers (minimum):**\n- cargo_type\n- vehicle_type\n- plate\n- shipping_type\n\n**Optional headers:**\n- fresh_cargo_temp\n\n**Example CSV:**\n```csv\ncargo_type,fresh_cargo_temp,vehicle_type,plate,shipping_type\n\"up, lateral\",-20,f2c,8796HSN,fresh\nup,,rt,7628BCB,dry\nback,,r3c,4052SMR,dry\n```\n\n## Field Formats\n- **cargo_type**: Comma-separated string (e.g., \"up, lateral, back\")\n- **vehicle_type**: Single type code (e.g., \"tir\", \"van\", \"f2c\")\n- **plate**: License plate string\n- **shipping_type**: \"fresh\" or \"dry\"\n- **fresh_cargo_temp**: Number between -20 and 40 (required if shipping_type='fresh')\n\n## Response Structure\nReturns object with two arrays:\n- **ok**: Array of successfully created vehicles\n- **ko**: Array of failed vehicles with error details\n\nEach failed item includes:\n- line: CSV line number (1-based)\n- reason: Error code\n- data: Original CSV data for that line\n- details: Additional error information (if applicable)\n\n## Validation Per Line\n1. **MIN_HEADERS**: CSV headers don't contain all required fields\n2. **PLATE_INVALID**: License plate is empty or invalid\n3. **CARGO_TYPE_INVALID**: Cargo type contains invalid values\n4. **VEHICLE_TYPE_INVALID**: Vehicle type is not recognized\n5. **SHIPPING_TYPE_INVALID**: Shipping type is not 'fresh' or 'dry'\n6. **FRESH_CARGO_TEMP_INVALID**: Temperature missing, invalid, or out of range for 'fresh'\n7. **DRY_CARGO_TEMP_INVALID**: Temperature present when shipping_type is 'dry'\n8. **CANNOT_SAVE_VEHICLE**: Database error saving the vehicle\n\n## Partial Success\nThe endpoint processes all lines and returns both successful and failed vehicles.\nIf some vehicles fail validation, they appear in the 'ko' array while valid vehicles\nare still created and appear in the 'ok' array.\n",
        "parameters": [],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "properties": {
                  "data": {
                    "description": "CSV file with vehicle data (headers required)",
                    "format": "binary",
                    "type": "string"
                  }
                },
                "required": [
                  "data"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "ko": [
                    {
                      "data": {
                        "cargo_type": "invalid_type",
                        "plate": "ABC123",
                        "shipping_type": "dry",
                        "vehicle_type": "tir"
                      },
                      "details": "invalid_type",
                      "line": "3",
                      "reason": "CARGO_TYPE_INVALID"
                    },
                    {
                      "data": {
                        "cargo_type": "up",
                        "fresh_cargo_temp": "",
                        "plate": "XYZ789",
                        "shipping_type": "fresh",
                        "vehicle_type": "f2c"
                      },
                      "line": "5",
                      "reason": "FRESH_CARGO_TEMP_INVALID"
                    }
                  ],
                  "ok": [
                    {
                      "_id": "68fb5b7df42f3ccb7e37b62c",
                      "cargo": {
                        "kg_max": 4000,
                        "kg_min": 3000,
                        "max": 8,
                        "min": 6
                      },
                      "cargo_type": [
                        "up",
                        "lateral"
                      ],
                      "fresh_cargo_temp": -20,
                      "image": "",
                      "itv": "",
                      "plate": "8796hsn",
                      "shipping_type": "fresh",
                      "vehicle_type": "f2c"
                    },
                    {
                      "_id": "68fb5b7df42f3ccb7e37b62d",
                      "cargo": {
                        "kg_max": 15000,
                        "kg_min": 10000,
                        "max": 15,
                        "min": 10
                      },
                      "cargo_type": [
                        "up"
                      ],
                      "fresh_cargo_temp": null,
                      "image": "",
                      "itv": "",
                      "plate": "7628bcb",
                      "shipping_type": "dry",
                      "vehicle_type": "rt"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/BulkCreateResponse"
                }
              }
            },
            "description": "Bulk creation completed (may include partial failures).\nCheck 'ok' and 'ko' arrays for results.\n",
            "headers": {}
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "companySaveError": {
                    "value": {
                      "error": "CIA_SAVE_VEHICLES",
                      "message": "Cannot update company vehicles"
                    }
                  },
                  "invalidData": {
                    "value": {
                      "error": "INVALID_DATA",
                      "message": "CSV data is empty or invalid"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Invalid request\n- INVALID_DATA: CSV data is empty or malformed\n- CIA_SAVE_VEHICLES: Error updating company's vehicle list\n"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "CIA_NOT_FOUND",
                  "message": "Company not found for user"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "User does not have an associated company"
          },
          "402": {
            "content": {
              "application/json": {
                "example": {
                  "error": "PAYMENT_REQUIRED",
                  "message": "Vehicle limit exceeded for current plan"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Vehicle limit exceeded for current payment plan"
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "fileNotFound": {
                    "value": {
                      "error": "FILE_NOT_FOUND",
                      "message": "CSV file not provided"
                    }
                  },
                  "userNotFound": {
                    "value": {
                      "error": "USER_NOT_FOUND",
                      "message": "User not found"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "File or user not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Create multiple vehicles from CSV file",
        "tags": [
          "Vehicles"
        ]
      }
    },
    "/company/vehicles/bulk/notes/{lang}": {
      "get": {
        "deprecated": false,
        "description": "## Purpose\nReturns detailed instructions for using the bulk vehicle import feature in the requested language.\n\n## Objective\nProvide comprehensive guidance to users performing bulk imports for the first time,\nreducing errors and improving user experience.\n\n## Use Cases\n- First-time users learning how to bulk import vehicles\n- Reference documentation for CSV format and validation rules\n- Troubleshooting bulk import errors\n\n## Supported Languages\n- **es**: Spanish (Español)\n- **en**: English (default)\n\nIf an unsupported language code is provided, defaults to English.\n\n## Response\nReturns a plain text file with:\n- Content-Type: text/plain\n- Content-Disposition: attachment; filename=\"notes_{lang}.txt\"\n\nThe text file contains detailed instructions from the assets folder:\n- `src/assets/bulk_vehicles_notes_es.txt` (Spanish)\n- `src/assets/bulk_vehicles_notes_en.txt` (English)\n",
        "parameters": [
          {
            "description": "Language code for instructions (es=Spanish, en=English)",
            "in": "path",
            "name": "lang",
            "required": true,
            "schema": {
              "enum": [
                "es",
                "en"
              ],
              "example": "es",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "text/plain": {
                "schema": {
                  "example": "INSTRUCCIONES PARA IMPORTACIÓN MASIVA DE VEHÍCULOS\n\nEste archivo contiene las instrucciones detalladas...\n",
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "Instructions file generated successfully",
            "headers": {
              "Content-Disposition": {
                "schema": {
                  "example": "attachment; filename=\"notes_es.txt\"",
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "CIA_NOT_FOUND",
                  "message": "Company not found for user"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "User does not have an associated company"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "USER_NOT_FOUND",
                  "message": "User not found"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "User not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Download bulk import instructions in specified language",
        "tags": [
          "Vehicles"
        ]
      }
    },
    "/company/vehicles/bulk/template": {
      "get": {
        "deprecated": false,
        "description": "## Purpose\nGenerates and returns a CSV template file with headers and example data for bulk vehicle import.\n\n## Objective\nProvide companies with a properly formatted template to facilitate bulk vehicle creation,\nreducing errors and ensuring correct data structure.\n\n## Use Cases\n- Download template before creating bulk import file\n- Reference example data formats\n- Understand required and optional fields\n\n## Template Contents\nThe template includes:\n- All valid field headers (required and optional)\n- 3 example vehicle entries demonstrating different configurations:\n  1. Fresh cargo vehicle with temperature\n  2. Dry cargo vehicle (single cargo type)\n  3. Dry cargo vehicle (different cargo type)\n\n**CSV Format:**\n```csv\ncargo_type,fresh_cargo_temp,vehicle_type,plate,shipping_type\n\"up, lateral\",-20,f2c,8796HSN,fresh\nup,,rt,7628BCB,dry\nback,,r3c,4052SMR,dry\n```\n\n## Response\nReturns a downloadable CSV file with:\n- Content-Type: text/csv\n- Content-Disposition: attachment; filename=\"template.csv\"\n",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "text/csv": {
                "schema": {
                  "example": "cargo_type,fresh_cargo_temp,vehicle_type,plate,shipping_type\n\"up, lateral\",-20,f2c,8796HSN,fresh\nup,,rt,7628BCB,dry\nback,,r3c,4052SMR,dry\n",
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "CSV template file generated successfully",
            "headers": {
              "Content-Disposition": {
                "schema": {
                  "example": "attachment; filename=\"template.csv\"",
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "CIA_NOT_FOUND",
                  "message": "Company not found for user"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "User does not have an associated company"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "USER_NOT_FOUND",
                  "message": "User not found"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "User not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Download CSV template for bulk vehicle import",
        "tags": [
          "Vehicles"
        ]
      }
    },
    "/company/vehicles/types": {
      "get": {
        "deprecated": false,
        "description": "## Purpose\nRetrieves the complete list of vehicle types configured and visible in the system.\n\n## Objective\nEnable companies to view all available vehicle type options for creating or updating vehicles in their fleet.\n\n## Use Cases\n- Display vehicle type dropdown options when creating a new vehicle\n- Filter fleet by specific vehicle types in the interface\n- Understand vehicle type specifications and capabilities\n\n## Response Details\nReturns an array of vehicle type objects ordered alphabetically by name. Each type includes:\n- Code identifier used in vehicle creation\n- Localized names and descriptions in multiple languages\n- Visibility status\n\n**Note:** Only visible vehicle types are returned. Results are sorted by name in ascending order.\n",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "code": "tir",
                    "langs": [
                      {
                        "description": "Camión articulado con capacidad de 20-35 m³",
                        "lang": "es",
                        "name": "Tráiler"
                      },
                      {
                        "description": "Articulated truck with 20-35 m³ capacity",
                        "lang": "en",
                        "name": "Trailer"
                      }
                    ],
                    "name": "Tráiler",
                    "visible": true
                  },
                  {
                    "code": "van",
                    "langs": [
                      {
                        "description": "Furgoneta de reparto con 5-10 m³",
                        "lang": "es",
                        "name": "Furgoneta"
                      },
                      {
                        "description": "Delivery van with 5-10 m³",
                        "lang": "en",
                        "name": "Van"
                      }
                    ],
                    "name": "Furgoneta",
                    "visible": true
                  }
                ],
                "schema": {
                  "$ref": "#/components/schemas/VehicleTypesResponse"
                }
              }
            },
            "description": "List of vehicle types retrieved successfully",
            "headers": {}
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "error": "CANT_GET",
                  "message": "Cannot retrieve vehicle types"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Error retrieving vehicle types"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get available vehicle types",
        "tags": [
          "Vehicles"
        ]
      }
    },
    "/company/vehicles/{id}": {
      "delete": {
        "deprecated": false,
        "description": "## Purpose\nMarks a vehicle as deleted (soft delete) from the company's fleet.\n\n## Objective\nEnable companies to remove vehicles from active use while maintaining audit trail\nand historical data integrity.\n\n## Use Cases\n- Remove sold or decommissioned vehicles from active fleet\n- Archive old vehicles while preserving historical records\n- Clean up vehicle list without losing data\n\n## Soft Delete Behavior\nThis endpoint performs a **soft delete** using mongoose-delete plugin:\n- The vehicle is NOT permanently deleted from the database\n- The field `deleted` is set to `true`\n- The field `deletedAt` is set to current timestamp\n- The vehicle is removed from the company's vehicles array\n- Data remains in database for audit and recovery purposes\n\n**IMPORTANT:** This is NOT a permanent deletion. The vehicle can potentially be recovered\nby database administrators if needed.\n\n## Side Effects\n- Updates the company model to remove the vehicle reference from the vehicles array\n- The vehicle will no longer appear in fleet listings\n\n**Note:** Currently there is no ownership validation in this endpoint. Any authenticated\nuser can delete any vehicle if they know the ID. This may be a security concern.\n",
        "parameters": [
          {
            "description": "Unique identifier of the vehicle to delete",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "68fb5b7df42f3ccb7e37b62b",
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "68fb5b7df42f3ccb7e37b62b"
                },
                "schema": {
                  "properties": {
                    "_id": {
                      "description": "ID of the deleted vehicle",
                      "example": "68fb5b7df42f3ccb7e37b62b",
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Vehicle deleted successfully (soft delete)",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "CANT_DELETE",
                  "message": "Cannot delete vehicle"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Cannot delete vehicle or general error"
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "companyNotFound": {
                    "value": {
                      "error": "CIA_NOT_FOUND",
                      "message": "Company not found for user"
                    }
                  },
                  "vehicleNotFound": {
                    "value": {
                      "error": "VEHICLE_NOT_FOUND",
                      "message": "Vehicle not found"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Company or vehicle not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Soft delete a vehicle from the fleet",
        "tags": [
          "Vehicles"
        ]
      },
      "get": {
        "deprecated": false,
        "description": "## Purpose\nRetrieves complete details of a specific vehicle from the company's fleet.\n\n## Objective\nEnable companies to view full information about a single vehicle including all metadata,\nfile references, and calculated cargo capacity.\n\n## Use Cases\n- View complete vehicle details before editing\n- Display vehicle information in detail view\n- Retrieve image and ITV certificate URLs\n- Access calculated cargo capacity ranges\n\n## Security\nThis endpoint validates ownership - the vehicle must belong to the authenticated user's company.\nIf the vehicle exists but belongs to another company, returns 403 CIA_NOT_OWNER.\n\n## Image URLs\nURLs for images and ITV certificates follow the format `/images?file={s3_key}`.\nAccess these URLs with authentication to retrieve the actual files.\n\n## Calculated Fields\nThe `cargo` field is automatically calculated based on `vehicle_type` and contains\nvolume (m³) and weight (kg) capacity ranges.\n",
        "parameters": [
          {
            "description": "Unique identifier of the vehicle (MongoDB ObjectId)",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "68fb5b7df42f3ccb7e37b62b",
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "68fb5b7df42f3ccb7e37b62b",
                  "cargo": {
                    "kg_max": 30000,
                    "kg_min": 20000,
                    "max": 35,
                    "min": 20
                  },
                  "cargo_type": [
                    "up",
                    "lateral",
                    "back"
                  ],
                  "fresh_cargo_temp": null,
                  "image": "/images?file=vehicles/8521eee.jpg",
                  "itv": "/images?file=itv/8521eee.pdf",
                  "plate": "8521eee",
                  "shipping_type": "dry",
                  "vehicle_type": "tir"
                },
                "schema": {
                  "$ref": "#/components/schemas/Vehicle"
                }
              }
            },
            "description": "Vehicle details retrieved successfully",
            "headers": {}
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "error": "CANT_GET",
                  "message": "Cannot retrieve vehicle"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "General error retrieving vehicle"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "CIA_NOT_FOUND",
                  "message": "Company not found for user"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "User does not have an associated company"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": "CIA_NOT_OWNER",
                  "message": "Vehicle does not belong to user's company"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Forbidden. The vehicle exists but does not belong to the authenticated user's company.\nThis is a security error indicating the user is attempting to access a resource\nthat does not belong to them.\n"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "NOT_FOUND",
                  "message": "Vehicle not found"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Vehicle not found"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Get details of a specific vehicle",
        "tags": [
          "Vehicles"
        ]
      },
      "post": {
        "deprecated": false,
        "description": "## Purpose\nUpdates an existing vehicle's information including metadata and file uploads.\n\n## Objective\nEnable companies to modify vehicle specifications, update documentation, and manage\ndefault vehicle settings.\n\n## Use Cases\n- Update vehicle specifications (type, cargo capabilities)\n- Replace or remove vehicle images and ITV certificates\n- Change shipping type and temperature settings\n- Set or unset vehicle as user's default\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request] --> B{User Has Company?}\n  B -->|No| C[401 CIA_NOT_FOUND]\n  B -->|Yes| D{Vehicle Exists?}\n  D -->|No| E[404 VEHICLE_NOT_FOUND]\n  D -->|Yes| F{Company Owns Vehicle?}\n  F -->|No| G[403 CIA_NOT_OWNER]\n  F -->|Yes| H{shipping_type Changed?}\n  H -->|Yes to dry| I[Auto-remove fresh_cargo_temp]\n  H -->|Yes to fresh| J{fresh_cargo_temp Valid?}\n  J -->|No| K[400 Temp Required]\n  J -->|Yes| L[Update Vehicle]\n  H -->|No| L\n  I --> L\n  L --> M{default_vehicle = true?}\n  M -->|Yes| N[Update User Default]\n  M -->|No| O[Return Updated Vehicle]\n  N --> O\n```\n\n## File Deletion\nTo delete an existing file (image or itv), send the field with an empty string value.\nThe file will be removed from S3 storage and the field will be set to empty.\n\n**Example:**\n```json\n{\n  \"image\": \"\",\n  \"itv\": \"\"\n}\n```\n\n## Automatic Behavior\n- If `shipping_type` is changed to 'dry', `fresh_cargo_temp` is automatically removed\n- Only fields included in the request are updated; others remain unchanged\n- Files (image, itv) are excluded from automatic updates and require explicit handling\n\n## Partial Updates\nYou can update only specific fields. Fields not included in the request will retain\ntheir current values.\n\n## Security\nThis endpoint validates ownership - the vehicle must belong to the authenticated user's company.\n",
        "parameters": [
          {
            "description": "Unique identifier of the vehicle to update",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "example": "68fb5b7df42f3ccb7e37b62b",
              "pattern": "^[0-9a-f]{24}$",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "properties": {
                  "cargo_type": {
                    "description": "Supported cargo types. Accepts two formats:\n1. JSON Array: [\"up\", \"lateral\", \"back\"]\n2. Comma-separated string: \"up, lateral, back\"\n",
                    "example": [
                      "up",
                      "lateral"
                    ],
                    "oneOf": [
                      {
                        "items": {
                          "enum": [
                            "up",
                            "lateral",
                            "back"
                          ],
                          "type": "string"
                        },
                        "type": "array"
                      },
                      {
                        "pattern": "^(up|lateral|back)(,\\s*(up|lateral|back))*$",
                        "type": "string"
                      }
                    ]
                  },
                  "default_vehicle": {
                    "description": "If set to 'true', this vehicle becomes the user's default vehicle for digital signature operations",
                    "enum": [
                      "true"
                    ],
                    "example": "true",
                    "type": "string"
                  },
                  "fresh_cargo_temp": {
                    "description": "Temperature for refrigerated cargo (REQUIRED if shipping_type='fresh', range -20 to 40). Automatically removed if shipping_type changes to 'dry'.",
                    "example": -18,
                    "maximum": 40,
                    "minimum": -20,
                    "nullable": true,
                    "type": "number"
                  },
                  "image": {
                    "oneOf": [
                      {
                        "description": "New vehicle photo file (JPEG, PNG)",
                        "format": "binary",
                        "type": "string"
                      },
                      {
                        "description": "Send empty string to delete existing image",
                        "example": "",
                        "type": "string"
                      }
                    ]
                  },
                  "itv": {
                    "oneOf": [
                      {
                        "description": "New ITV inspection certificate file (PDF, JPEG, PNG)",
                        "format": "binary",
                        "type": "string"
                      },
                      {
                        "description": "Send empty string to delete existing ITV certificate",
                        "example": "",
                        "type": "string"
                      }
                    ]
                  },
                  "plate": {
                    "description": "Vehicle license plate (will be stored in lowercase)",
                    "example": "8521EEE",
                    "maxLength": 20,
                    "minLength": 3,
                    "type": "string"
                  },
                  "shipping_type": {
                    "description": "Type of shipping/transport",
                    "enum": [
                      "fresh",
                      "dry"
                    ],
                    "example": "dry",
                    "type": "string"
                  },
                  "vehicle_type": {
                    "description": "Type of vehicle",
                    "enum": [
                      "r3c",
                      "tir",
                      "rt",
                      "r2c",
                      "r2d",
                      "van",
                      "frc",
                      "f2c",
                      "adr",
                      "ft",
                      "pt",
                      "cc",
                      "hdcc",
                      "dump",
                      "live",
                      "cocar"
                    ],
                    "example": "tir",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "_id": "68fb5b7df42f3ccb7e37b62b",
                  "cargo": {
                    "kg_max": 30000,
                    "kg_min": 20000,
                    "max": 35,
                    "min": 20
                  },
                  "cargo_type": [
                    "up",
                    "lateral"
                  ],
                  "fresh_cargo_temp": null,
                  "image": "/images?file=vehicles/8521eee_updated.jpg",
                  "itv": "",
                  "plate": "8521eee",
                  "shipping_type": "dry",
                  "updatedAt": "2025-07-23T14:30:00.000Z",
                  "vehicle_type": "tir"
                },
                "schema": {
                  "$ref": "#/components/schemas/Vehicle"
                }
              }
            },
            "description": "Vehicle updated successfully",
            "headers": {}
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "CIA_NOT_FOUND",
                  "message": "Company not found for user"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "User does not have an associated company"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": "CIA_NOT_OWNER",
                  "message": "Vehicle does not belong to user's company"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Vehicle does not belong to the authenticated user's company"
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "userNotFound": {
                    "value": {
                      "error": "USER_NOT_FOUND",
                      "message": "User not found (when using default_vehicle)"
                    }
                  },
                  "vehicleNotFound": {
                    "value": {
                      "error": "VEHICLE_NOT_FOUND",
                      "message": "Vehicle not found"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Vehicle or user not found"
          },
          "503": {
            "content": {
              "application/json": {
                "example": {
                  "error": "SERVICE_UNAVAILABLE",
                  "message": "Error updating vehicle"
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Server error during update"
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "summary": "Update an existing vehicle",
        "tags": [
          "Vehicles"
        ]
      }
    },
    "/truckers/address/": {
      "get": {
        "summary": "List carrier addresses",
        "deprecated": false,
        "description": "Devuelve una lista paginada de todas las direcciones asociadas al transportista autenticado.\n\n**Características**:\n- Paginación configurable (page, limit)\n- Ordenación predeterminada por nombre (asc) y fecha creación (desc)\n- Se puede obtener lista completa con page=-1 y limit=-1\n\n**Parámetros**:\n- page: Número de página (1-based), usar -1 para desactivar paginación\n- limit: Máximo de resultados por página, usar -1 para desactivar paginación\n\n**Ejemplo de uso**:\n- Mostrar lista paginada en interfaz de usuario\n- Exportar todas las direcciones (usando page=-1)\n",
        "tags": [
          "Address"
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Número de página (1-based). \nValor especial -1 devuelve todos los resultados sin paginar.\n",
            "required": false,
            "example": 1,
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Límite de resultados por página.\nValor especial -1 devuelve todos los resultados sin paginar.\n",
            "required": false,
            "example": 20,
            "schema": {
              "type": "integer",
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta exitosa que contiene una lista paginada de direcciones.\nLa estructura sigue el formato de paginación de MongoDB.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddressList"
                },
                "example": {
                  "docs": [
                    {
                      "_id": "507f1f77bcf86cd799439011",
                      "name": "Oficina Central",
                      "street": "Calle Mayor 10",
                      "city": "Madrid",
                      "state": "Madrid",
                      "country": "España",
                      "postalCode": "28013"
                    },
                    {
                      "_id": "507f1f77bcf86cd799439012",
                      "name": "Almacén Norte",
                      "street": "Avenida de la Industria 25",
                      "city": "Barcelona",
                      "state": "Barcelona",
                      "country": "España",
                      "postalCode": "08001"
                    }
                  ],
                  "totalDocs": 2,
                  "limit": 20,
                  "page": 1,
                  "totalPages": 1
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      },
      "post": {
        "summary": "Create new address",
        "deprecated": false,
        "description": "Crea una nueva dirección asociada al transportista autenticado.\n\n**Validaciones**:\n- El nombre de la dirección debe ser único para el transportista\n- Todos los campos obligatorios deben estar presentes\n- El JWT debe corresponder a un transportista válido\n\n**Campos obligatorios**:\n- name: Nombre descriptivo de la dirección\n- street: Calle y número\n- city: Ciudad\n- state: Estado/provincia\n- country: País\n- postalCode: Código postal\n\n**Ejemplo de request**:\n```json\n{\n  \"name\": \"Oficina Central\",\n  \"street\": \"Calle Mayor 10\",\n  \"city\": \"Madrid\",\n  \"state\": \"Madrid\",\n  \"country\": \"España\",\n  \"postalCode\": \"28013\",\n  \"location\": {\n    \"lat\": 40.4168,\n    \"lng\": -3.7038\n  }\n}\n```\n",
        "tags": [
          "Address"
        ],
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Address"
              },
              "example": {
                "name": "Oficina Central",
                "company_name": "Mi Empresa S.L.",
                "street": "Calle Mayor 10",
                "city": "Madrid",
                "state": "Madrid",
                "country": "España",
                "postalCode": "28013",
                "location": {
                  "lat": 40.4168,
                  "lng": -3.7038
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Respuesta exitosa que contiene los datos completos de una dirección.\nIncluye todos los campos del schema Address.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Address"
                },
                "example": {
                  "_id": "507f1f77bcf86cd799439011",
                  "name": "Oficina Central",
                  "street": "Calle Mayor 10",
                  "city": "Madrid",
                  "state": "Madrid",
                  "country": "España",
                  "postalCode": "28013",
                  "location": {
                    "lat": 40.4168,
                    "lng": -3.7038
                  },
                  "createdAt": "2025-01-15T10:30:00.000Z",
                  "updatedAt": "2025-01-15T10:30:00.000Z"
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Error de autorización o validación.\nOcurre cuando el usuario no tiene permisos o los datos son inválidos.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Mensaje descriptivo del error"
                    }
                  }
                },
                "example": {
                  "error": "No tienes permisos para acceder a este recurso"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      },
      "put": {
        "summary": "Update existing address",
        "deprecated": false,
        "description": "Actualiza una dirección existente del transportista.\nRequiere que el campo _id esté presente en el request body.\n\n**Restricciones**:\n- Solo el propietario de la dirección puede modificarla\n- El nombre debe seguir siendo único si se modifica\n\n**Ejemplo de request**:\n```json\n{\n  \"_id\": \"507f1f77bcf86cd799439011\",\n  \"name\": \"Oficina Central Modificada\",\n  \"street\": \"Calle Mayor 10\",\n  \"city\": \"Madrid\",\n  \"postalCode\": \"28014\"\n}\n```\n",
        "tags": [
          "Address"
        ],
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Address"
              },
              "example": {
                "_id": "507f1f77bcf86cd799439011",
                "name": "Oficina Central Modificada",
                "street": "Calle Mayor 10",
                "city": "Madrid",
                "postalCode": "28014"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Respuesta exitosa que contiene los datos completos de una dirección.\nIncluye todos los campos del schema Address.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Address"
                },
                "example": {
                  "_id": "507f1f77bcf86cd799439011",
                  "name": "Oficina Central",
                  "street": "Calle Mayor 10",
                  "city": "Madrid",
                  "state": "Madrid",
                  "country": "España",
                  "postalCode": "28013",
                  "location": {
                    "lat": 40.4168,
                    "lng": -3.7038
                  },
                  "createdAt": "2025-01-15T10:30:00.000Z",
                  "updatedAt": "2025-01-15T10:30:00.000Z"
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Error de autorización o validación.\nOcurre cuando el usuario no tiene permisos o los datos son inválidos.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Mensaje descriptivo del error"
                    }
                  }
                },
                "example": {
                  "error": "No tienes permisos para acceder a este recurso"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/address/cities/{label}": {
      "get": {
        "summary": "Get Cities by State/Province",
        "deprecated": false,
        "description": "Devuelve una lista de ciudades asociadas a un estado o provincia específico.\n\n**Flujo recomendado**:\n1. Obtener países con /countries\n2. Obtener estados del país con /states/{label} \n3. Obtener ciudades del estado con este endpoint\n\n**Notas**:\n- El parámetro {label} debe coincidir exactamente con el nombre del estado\n- No distingue mayúsculas/minúsculas\n- Devuelve ciudades únicas sin duplicados\n",
        "tags": [
          "Address"
        ],
        "parameters": [
          {
            "name": "label",
            "in": "path",
            "description": "Nombre del estado/provincia para filtrar ciudades.\nDebe coincidir exactamente con los nombres devueltos por /states o /states/{label}\nEjemplos válidos:\n- \"Madrid\"\n- \"barcelona\" (case-insensitive)\n",
            "required": true,
            "example": "Madrid",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Lista de ciudades únicas del estado especificado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "examples": {
                  "1": {
                    "summary": "Éxito",
                    "value": "Madrid"
                  },
                  "2": {
                    "summary": "Éxito",
                    "value": "Alcalá de Henares"
                  },
                  "3": {
                    "summary": "Éxito",
                    "value": "Móstoles"
                  },
                  "4": {
                    "summary": "Éxito",
                    "value": "Fuenlabrada"
                  }
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/address/countries": {
      "get": {
        "summary": "Get available countries",
        "deprecated": false,
        "description": "Devuelve una lista de países donde el transportista tiene direcciones registradas.\nLos países se obtienen de las direcciones existentes en la base de datos y se normalizan\npara devolver nombres completos en lugar de códigos.\n\n**Ejemplo de uso**: \n- Para mostrar un selector de países en formularios de dirección\n- Para filtrar direcciones por país\n\n**Notas**:\n- Requiere autenticación JWT válida\n- Los nombres de países se devuelven en formato texto completo (ej: \"España\" en lugar de \"ES\")\n",
        "tags": [
          "Address"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Lista de países en formato texto",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "examples": {
                  "1": {
                    "summary": "Éxito",
                    "value": "España"
                  },
                  "2": {
                    "summary": "Éxito",
                    "value": "Francia"
                  },
                  "3": {
                    "summary": "Éxito",
                    "value": "Alemania"
                  },
                  "4": {
                    "summary": "Éxito",
                    "value": "Portugal"
                  }
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/address/states": {
      "get": {
        "summary": "Get all states/provinces",
        "deprecated": false,
        "description": "Devuelve una lista única de todos los estados/provincias registrados en las direcciones\ndel sistema, sin filtro por país.\n\n**Casos de uso**:\n- Búsqueda general de estados sin contexto de país\n- Validación de datos en formularios\n\n**Notas**:\n- Los estados se devuelven en su formato original como están registrados\n- Para obtener estados filtrados por país, usar /states/{label}\n",
        "tags": [
          "Address"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Lista de estados/provincias únicos",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "examples": {
                  "1": {
                    "summary": "Éxito",
                    "value": "Madrid"
                  },
                  "2": {
                    "summary": "Éxito",
                    "value": "Barcelona"
                  },
                  "3": {
                    "summary": "Éxito",
                    "value": "Valencia"
                  },
                  "4": {
                    "summary": "Éxito",
                    "value": "Lisboa"
                  }
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/address/states/{label}": {
      "get": {
        "summary": "Get states/provinces by country",
        "deprecated": false,
        "description": "Devuelve estados o provincias asociados a un país específico.\nEl parámetro {label} puede ser:\n- Nombre del país (ej: \"españa\")\n- Código de país (ej: \"ES\")\n\n**Ejemplo de flujo**:\n1. Obtener lista de países con /countries\n2. Seleccionar país y obtener sus estados con este endpoint\n\n**Notas**:\n- Los nombres de países son case-insensitive\n- Se aceptan nombres completos o códigos ISO\n",
        "tags": [
          "Address"
        ],
        "parameters": [
          {
            "name": "label",
            "in": "path",
            "description": "Nombre o código del país para filtrar estados.\nEjemplos válidos:\n- \"españa\"\n- \"ES\"\n- \"portugal\"\n- \"PT\"\n",
            "required": true,
            "example": "españa",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Lista de estados/provincias del país especificado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "examples": {
                  "1": {
                    "summary": "Éxito",
                    "value": "Madrid"
                  },
                  "2": {
                    "summary": "Éxito",
                    "value": "Barcelona"
                  },
                  "3": {
                    "summary": "Éxito",
                    "value": "Valencia"
                  },
                  "4": {
                    "summary": "Éxito",
                    "value": "Sevilla"
                  }
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/address/{id}": {
      "get": {
        "summary": "Get full details of an address",
        "deprecated": false,
        "description": "Devuelve todos los detalles de una dirección específica.\n\n**Validaciones**:\n- El ID debe corresponder a una dirección existente\n- Solo el propietario de la dirección puede ver sus detalles\n- Requiere autenticación JWT válida\n\n**Ejemplo de response**:\n```json\n{\n  \"_id\": \"507f1f77bcf86cd799439011\",\n  \"name\": \"Oficina Central\",\n  \"street\": \"Calle Mayor 10\",\n  \"city\": \"Madrid\",\n  \"state\": \"Madrid\",\n  \"country\": \"España\",\n  \"postalCode\": \"28013\",\n  \"location\": {\n    \"lat\": 40.4168,\n    \"lng\": -3.7038\n  },\n  \"createdAt\": \"2025-01-15T10:30:00.000Z\",\n  \"updatedAt\": \"2025-01-15T10:30:00.000Z\"\n}\n```\n",
        "tags": [
          "Address"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "ID único de la dirección a consultar.\nDebe ser un ObjectId válido de MongoDB.\n",
            "required": true,
            "example": "64917618ef73c37ccae60bfc",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta exitosa que contiene los datos completos de una dirección.\nIncluye todos los campos del schema Address.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Address"
                },
                "example": {
                  "_id": "507f1f77bcf86cd799439011",
                  "name": "Oficina Central",
                  "street": "Calle Mayor 10",
                  "city": "Madrid",
                  "state": "Madrid",
                  "country": "España",
                  "postalCode": "28013",
                  "location": {
                    "lat": 40.4168,
                    "lng": -3.7038
                  },
                  "createdAt": "2025-01-15T10:30:00.000Z",
                  "updatedAt": "2025-01-15T10:30:00.000Z"
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Error de autorización o validación.\nOcurre cuando el usuario no tiene permisos o los datos son inválidos.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Mensaje descriptivo del error"
                    }
                  }
                },
                "example": {
                  "error": "No tienes permisos para acceder a este recurso"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Dirección no encontrada",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      },
      "delete": {
        "summary": "Delete address",
        "deprecated": false,
        "description": "Elimina permanentemente una dirección específica del transportista.\n\n**Consideraciones**:\n- Solo se puede eliminar direcciones propias\n- La eliminación es permanente\n- Devuelve el _id de la dirección eliminada\n\n**Ejemplo de response exitoso**:\n```json\n{\n  \"_id\": \"507f1f77bcf86cd799439011\"\n}\n```\n",
        "tags": [
          "Address"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "ID de la dirección a eliminar",
            "required": true,
            "example": "64917511ef73c37ccae60bc4",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Dirección eliminada exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "_id": {
                      "type": "string",
                      "description": "ID de la dirección eliminada"
                    }
                  }
                },
                "example": {
                  "_id": "507f1f77bcf86cd799439011"
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Error de autorización o validación.\nOcurre cuando el usuario no tiene permisos o los datos son inválidos.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Mensaje descriptivo del error"
                    }
                  }
                },
                "example": {
                  "error": "No tienes permisos para acceder a este recurso"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/auctions/": {
      "get": {
        "summary": "List available auctions",
        "deprecated": false,
        "description": "Obtiene una lista paginada de subastas disponibles para el transportista autenticado.\n\nLas subastas devueltas pueden estar en diferentes estados:\n- published: Subastas activas y disponibles para pujar\n- awarded: Subastas asignadas pero pendientes de firma\n- approved: Subastas aprobadas y en curso\n- completed: Subastas finalizadas\n\nEl transportista solo verá subastas donde puede participar según:\n- Su ubicación geográfica\n- Tipo de vehículo\n- Capacidad de carga\n- Historial de servicio\n\nEjemplo de uso típico:\n1. Transportista consulta subastas disponibles\n2. Filtra por ubicación/carga\n3. Selecciona una para ver detalles y pujar\n",
        "tags": [
          "Auctions"
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Número de página a recuperar. \nPor defecto 1. Cada página contiene hasta {limit} elementos.\nEjemplo: ?page=2 para obtener la segunda página.\n",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Máximo número de subastas a devolver por página. \nValor por defecto 20, máximo permitido 100.\nEjemplo: ?limit=50 para obtener 50 subastas por página.\n",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Lista de subastas paginada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuctionList"
                },
                "example": {
                  "docs": [
                    {
                      "service_code": "ABC12345",
                      "status": "published",
                      "etl_date": "2025-08-15T08:00:00.000Z",
                      "etd_date": "2025-08-16T18:00:00.000Z",
                      "pallets_num": 12,
                      "pallets_type": "euro",
                      "cargo_weight": 1200,
                      "start_price": 850,
                      "bid_current": 920,
                      "my_bid": null,
                      "etl_address": {
                        "city": "Madrid",
                        "state": "Madrid",
                        "country": "ES"
                      },
                      "etd_address": {
                        "city": "Barcelona",
                        "state": "Barcelona",
                        "country": "ES"
                      }
                    }
                  ],
                  "totalDocs": 45,
                  "limit": 20,
                  "page": 1,
                  "totalPages": 3
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Error de autorización o validación.\nOcurre cuando el usuario no tiene permisos o los datos son inválidos.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Mensaje descriptivo del error"
                    }
                  }
                },
                "example": {
                  "error": "No tienes permisos para acceder a este recurso"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/auctions/bid": {
      "get": {
        "summary": "Get my active bids",
        "deprecated": false,
        "description": "Devuelve una lista paginada de todas las pujas activas del transportista autenticado.\n\nIncluye:\n- Pujas en subastas activas (status: published)\n- Pujas ganadoras pendientes de firma (status: awarded)\n- Pujas rechazadas o canceladas (status: rejected/cancelled)\n\nLas pujas se ordenan por fecha de actualización descendente.\n",
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Número de página a recuperar (por defecto 1)",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Máximo de pujas por página (por defecto 20, máximo 100)",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 20
            }
          },
          {
            "name": "filter",
            "in": "query",
            "description": "Filtro para las pujas:\n- all: Todas las pujas (valor por defecto)\n- open: Pujas en subastas abiertas\n- to_sign: Pujas ganadoras pendientes de firma\n- awarded: Pujas ganadoras\n- closed: Pujas en subastas cerradas\n- lost: Pujas perdidas\n- won: Pujas ganadas\n",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "open",
                "to_sign",
                "awarded",
                "closed",
                "lost",
                "won"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Lista de pujas del transportista",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuctionList"
                },
                "example": {
                  "docs": [
                    {
                      "service_code": "ABC12345",
                      "auction_status": "published",
                      "bid_status": "pending",
                      "amount": 920,
                      "created_at": "2025-08-11T10:30:00.000Z",
                      "updated_at": "2025-08-11T10:30:00.000Z",
                      "notes": "Disponible con camión refrigerado"
                    }
                  ],
                  "totalDocs": 5,
                  "limit": 20,
                  "page": 1
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Error de autorización o validación.\nOcurre cuando el usuario no tiene permisos o los datos son inválidos.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Mensaje descriptivo del error"
                    }
                  }
                },
                "example": {
                  "error": "No tienes permisos para acceder a este recurso"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      },
      "post": {
        "summary": "Create a new bid",
        "deprecated": false,
        "description": "Permite a un transportista realizar una puja en una subasta activa.\n\nRequisitos:\n- Usuario autenticado y activo\n- Cuenta de pago verificada y habilitada\n- Subasta en estado 'published'\n- Monto de puja válido (mayor que 0 y menor que la puja actual más baja)\n- Transportista cumple con los requisitos de la subasta (tipo de vehículo, capacidad, etc.)\n\nFlujo típico:\n1. Transportista consulta subastas disponibles\n2. Obtiene detalles de una subasta específica\n3. Realiza una puja con un monto competitivo\n4. Si la puja es la más baja, se convierte en la puja ganadora temporal\n\nEn caso de puja ganadora:\n- Se notifica al transportista y al cargador\n- Se bloquea la subasta para otras pujas si se alcanza el precio de adjudicación\n",
        "tags": [
          "Bids"
        ],
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "service_code",
                  "amount"
                ],
                "properties": {
                  "service_code": {
                    "type": "string",
                    "description": "Código único de la subasta (8 caracteres alfanuméricos).\nEjemplo: \"ABC12345\"\n"
                  },
                  "amount": {
                    "type": "number",
                    "minimum": 0.01,
                    "description": "Monto de la puja en EUR. \nDebe ser mayor que 0 y menor que la puja actual más baja.\nEjemplo: 850.50\n"
                  },
                  "notes": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "Notas opcionales para el cargador (máx 500 caracteres).\nEjemplo: \"Disponible con camión refrigerado\"\n"
                  }
                }
              },
              "example": {
                "service_code": "POLLIS7jq7n",
                "amount": 850.5,
                "notes": "Disponible con camión refrigerado"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Puja creada exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Bid"
                },
                "example": {
                  "service_code": "ABC12345",
                  "amount": 850.5,
                  "status": "pending",
                  "created_at": "2025-08-11T10:30:00.000Z",
                  "updated_at": "2025-08-11T10:30:00.000Z",
                  "notes": "Disponible con camión refrigerado"
                }
              }
            },
            "headers": {}
          },
          "400": {
            "description": "Error en la solicitud. Posibles causas:\n- Monto de puja inválido\n- Subasta no encontrada o no válida\n- Puja no cumple requisitos\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/400Error"
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Acceso denegado. Posibles causas:\n- Usuario no activo\n- Cuenta de pago no verificada\n- No tiene permiso para pujar en esta subasta\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/403Error"
                }
              }
            },
            "headers": {}
          },
          "409": {
            "description": "Conflicto. Posibles causas:\n- Subasta ya adjudicada\n- Puja más baja ya existe\n- Restricciones de tiempo/ubicación\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/409Error"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/auctions/bid/minimal/{service_code}": {
      "get": {
        "summary": "Calculate minimum costs to bid",
        "deprecated": false,
        "description": "Devuelve un cálculo detallado de los costos mínimos estimados para realizar una puja en la subasta.\n\nIncluye:\n- Costo base del servicio\n- Tiempo estimado de transporte\n- Distancia entre origen y destino\n- Tarifas y comisiones aplicables\n- Costo total mínimo recomendado\n- Porcentaje sobre el precio base\n\nEste cálculo ayuda al transportista a:\n1. Determinar un precio competitivo pero rentable\n2. Evaluar la viabilidad económica de la subasta\n3. Planificar sus costos operativos\n\nLos cálculos consideran:\n- Distancia de la ruta\n- Tipo de vehículo requerido\n- Características especiales de la carga (refrigeración, etc.)\n- Tarifas vigentes\n",
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "name": "service_code",
            "in": "path",
            "description": "Código único de la subasta (8 caracteres alfanuméricos).\nEjemplo: \"ABC12345\"\n",
            "required": true,
            "example": "ACOMADPT9Q4",
            "schema": {
              "type": "string",
              "pattern": "^[A-Z0-9]{8}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Desglose detallado de costos mínimos",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "time": {
                      "type": "number",
                      "description": "Tiempo estimado en horas",
                      "example": 5.5
                    },
                    "costs": {
                      "type": "object",
                      "description": "Desglose de costos individuales",
                      "properties": {
                        "fuel": {
                          "type": "number",
                          "description": "Costo estimado de combustible",
                          "example": 120.5
                        },
                        "tolls": {
                          "type": "number",
                          "description": "Peajes estimados",
                          "example": 45.75
                        },
                        "labor": {
                          "type": "number",
                          "description": "Costo de mano de obra",
                          "example": 85
                        }
                      }
                    },
                    "total": {
                      "type": "number",
                      "description": "Costo total mínimo recomendado",
                      "example": 785.25
                    },
                    "base": {
                      "type": "number",
                      "description": "Precio base de referencia",
                      "example": 650
                    },
                    "distance": {
                      "type": "integer",
                      "description": "Distancia en kilómetros",
                      "example": 350
                    },
                    "fee": {
                      "type": "number",
                      "description": "Comisión de la plataforma",
                      "example": 50.25
                    },
                    "percent_price": {
                      "type": "number",
                      "description": "Porcentaje sobre el precio base",
                      "example": 20.5
                    }
                  }
                },
                "example": {
                  "time": 5.5,
                  "costs": {
                    "fuel": 120.5,
                    "tolls": 45.75,
                    "labor": 85,
                    "maintenance": 35.25
                  },
                  "total": 785.25,
                  "base": 650,
                  "distance": 350,
                  "fee": 50.25,
                  "percent_price": 20.5
                }
              }
            },
            "headers": {}
          },
          "400": {
            "description": "Error en la solicitud. Posibles causas:\n- Código de subasta inválido\n- Subasta no disponible para cálculo\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/400Error"
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Acceso denegado. Posibles causas:\n- Usuario no autenticado\n- No tiene permiso para calcular costos de esta subasta\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/403Error"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "No encontrado. Posibles causas:\n- Subasta no existe\n- Datos insuficientes para cálculo\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/404Error"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/auctions/bid/{serviceCode}": {
      "delete": {
        "summary": "Delete an existing bid",
        "deprecated": false,
        "description": "Permite a un transportista eliminar una puja existente en una subasta.\n\nRequisitos:\n- Usuario autenticado y activo\n- La puja debe pertenecer al usuario o a su compañía\n- La subasta debe estar en un estado que permita eliminar pujas (published)\n- No puede eliminarse si ya es la puja ganadora (status: accepted)\n\nFlujo típico:\n1. Transportista consulta sus pujas activas\n2. Selecciona una puja para eliminar\n3. Si cumple las condiciones, la puja se marca como cancelled\n4. Si era la puja más baja, se actualiza la puja ganadora de la subasta\n",
        "tags": [
          "Bids"
        ],
        "parameters": [
          {
            "name": "serviceCode",
            "in": "path",
            "description": "Código único de la subasta (8 caracteres alfanuméricos).\nEjemplo: \"ABC12345\"\n",
            "required": true,
            "example": "POLLIS7jq7n",
            "schema": {
              "type": "string",
              "pattern": "^[A-Z0-9]{8}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Puja eliminada exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "service_code": {
                      "type": "string",
                      "description": "Código de la subasta afectada",
                      "example": "ABC12345"
                    },
                    "deleted": {
                      "type": "boolean",
                      "description": "Confirmación de eliminación",
                      "example": true
                    },
                    "deletedAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Fecha y hora de eliminación",
                      "example": "2025-08-11T11:45:00.000Z"
                    }
                  }
                },
                "example": {
                  "service_code": "ABC12345",
                  "deleted": true,
                  "deletedAt": "2025-08-11T11:45:00.000Z"
                }
              }
            },
            "headers": {}
          },
          "400": {
            "description": "Error en la solicitud. Posibles causas:\n- Subasta no encontrada o no válida\n- Puja no puede eliminarse en el estado actual\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/400Error"
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Acceso denegado. Posibles causas:\n- Usuario no activo\n- Puja no pertenece al usuario\n- No tiene permiso para eliminar esta puja\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/403Error"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "No encontrado. Posibles causas:\n- Subasta no existe\n- Puja no existe\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/404Error"
                }
              }
            },
            "headers": {}
          },
          "409": {
            "description": "Conflicto. Posibles causas:\n- Subasta ya adjudicada\n- Puja ya fue aceptada\n- Restricciones de tiempo\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/409Error"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/auctions/contract/{service_code}": {
      "get": {
        "summary": "Download auction PDF contract",
        "deprecated": false,
        "description": "Permite descargar el contrato PDF de una subasta adjudicada.\n\nRequisitos:\n- Usuario autenticado y activo\n- Debe ser el ganador de la subasta (amWinner: true)\n- La subasta debe estar en estado 'approved' o 'awarded'\n- El contrato debe estar disponible (signed_by_trucker: true)\n\nEl contrato incluye:\n- Detalles completos de la subasta\n- Información de las partes (cargador y transportista)\n- Términos y condiciones\n- Firma digital del transportista (si aplica)\n- Datos del vehículo asignado\n\nEl PDF se genera dinámicamente en el idioma del usuario (i18n).\n",
        "tags": [
          "Contracts"
        ],
        "parameters": [
          {
            "name": "service_code",
            "in": "path",
            "description": "Código único de la subasta (8 caracteres alfanuméricos).\nEjemplo: \"ABC12345\"\n",
            "required": true,
            "example": "ACOMADPT9Q4",
            "schema": {
              "type": "string",
              "pattern": "^[A-Z0-9]{8}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Archivo PDF del contrato generado dinámicamente",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            },
            "headers": {}
          },
          "400": {
            "description": "Error en la solicitud. Posibles causas:\n- Subasta no encontrada o no válida\n- Estado de subasta no permite descarga\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/400Error"
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Acceso denegado. Posibles causas:\n- Usuario no es el ganador de la subasta\n- Contrato no disponible para descarga\n- Faltan firmas requeridas\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/403Error"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "No encontrado. Posibles causas:\n- Subasta no existe\n- Contrato no generado\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/404Error"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/auctions/favorites": {
      "get": {
        "summary": "Get carrier's favorite auctions",
        "deprecated": false,
        "description": "Devuelve una lista paginada de subastas que el transportista ha marcado como favoritas.\n\nCaracterísticas:\n- Solo muestra subastas favoritas del usuario autenticado\n- Incluye subastas en todos los estados (activas, adjudicadas, completadas)\n- Mantiene el orden en que fueron marcadas como favoritas (más recientes primero)\n- Soporta paginación para manejar listas largas\n\nCampos importantes:\n- service_code: Identificador único de la subasta\n- status: Estado actual (published, awarded, approved, completed)\n- is_favorite: Siempre true (ya que son las favoritas del usuario)\n- favorite_date: Fecha cuando se marcó como favorita\n\nEjemplo de uso:\n1. Transportista marca subastas como favoritas desde la lista\n2. Consulta esta lista para ver solo sus favoritas\n3. Puede filtrar/ordenar según necesidades\n",
        "tags": [
          "Auctions"
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Número de página a recuperar (por defecto 1).\nEjemplo: ?page=2 para la segunda página.\n",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Máximo de subastas por página (por defecto 20, máximo 100).\nEjemplo: ?limit=50 para 50 resultados por página.\n",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Lista de subastas favoritas paginada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuctionList"
                },
                "example": {
                  "docs": [
                    {
                      "service_code": "ABC12345",
                      "status": "published",
                      "is_favorite": true,
                      "favorite_date": "2025-08-10T15:30:00.000Z",
                      "etl_date": "2025-08-15T08:00:00.000Z",
                      "etd_date": "2025-08-16T18:00:00.000Z",
                      "pallets_num": 12,
                      "start_price": 850,
                      "bid_current": 920
                    }
                  ],
                  "totalDocs": 8,
                  "limit": 20,
                  "page": 1,
                  "totalPages": 1
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Acceso denegado. Posibles causas:\n- Usuario no autenticado\n- Token JWT inválido o expirado\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/403Error"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/auctions/image/{code}": {
      "get": {
        "summary": "Get representative image of the auction",
        "deprecated": false,
        "description": "Devuelve una imagen PNG generada dinámicamente que representa visualmente la subasta.\n\nLa imagen incluye información clave como:\n- Código y estado de la subasta\n- Fechas y ubicaciones de carga/descarga\n- Tipo y cantidad de pallets\n- Peso y dimensiones de la carga\n- Precio actual y puja mínima\n- Indicador visual si es carga refrigerada\n\nCaracterísticas:\n- Imagen generada en tiempo real con los últimos datos\n- Formato PNG optimizado para web\n- Diseño responsive que se adapta a diferentes tamaños\n- Cacheada para mejorar el rendimiento\n\nUso típico:\n1. Mostrar vista previa visual en listados de subastas\n2. Compartir en redes sociales o mensajería\n3. Incrustar en aplicaciones móviles\n",
        "tags": [
          "Auctions"
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "description": "Código único de la subasta (8 caracteres alfanuméricos).\nEjemplo: \"ABC12345\"\n",
            "required": true,
            "example": "SANSEVMDGnd",
            "schema": {
              "type": "string",
              "pattern": "^[A-Z0-9]{8}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Imagen PNG generada dinámicamente",
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            },
            "headers": {}
          },
          "400": {
            "description": "Error en la solicitud. Posibles causas:\n- Código de subasta inválido\n- Formato no soportado\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/400Error"
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Acceso denegado. Posibles causas:\n- Usuario no autenticado\n- No tiene permiso para ver esta subasta\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/403Error"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "No encontrado. Posibles causas:\n- Subasta no existe\n- Imagen no disponible\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/404Error"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/auctions/search-location": {
      "get": {
        "summary": "Search loading/unloading locations",
        "deprecated": false,
        "description": "Busca ubicaciones disponibles para subastas según criterios geográficos.\n\nPermite buscar por:\n- Ciudad\n- Provincia/Estado\n- País\n- Código postal\n- Barrio\n\nEl parámetro 'from' determina si se buscan ubicaciones de:\n- etl: Lugares de carga (ETL - Estimated Time of Loading)\n- etd: Lugares de descarga (ETD - Estimated Time of Delivery)\n\nLa búsqueda es insensible a mayúsculas/minúsculas y maneja acentos automáticamente.\nEjemplo: Buscar \"Madrid\" encontrará también \"MADRID\" y \"Mádrid\".\n\nLa respuesta incluye resultados paginados con información geográfica normalizada.\n",
        "tags": [
          "Auctions"
        ],
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "description": "Término de búsqueda para ubicaciones. Puede ser:\n- Nombre de ciudad (ej: \"Barcelona\")\n- Código postal (ej: \"08001\")\n- Provincia (ej: \"Madrid\")\n- País (ej: \"España\")\n- Combinación separada por comas (ej: \"Madrid, Barcelona\")\n",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "description": "Tipo de ubicación a buscar:\n- etl: Lugares de carga (origen)\n- etd: Lugares de descarga (destino)\nSi no se especifica, busca en ambos tipos.\n",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "etl",
                "etd"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Lista paginada de ubicaciones encontradas",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "docs": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "city": {
                            "type": "string",
                            "description": "Nombre de la ciudad",
                            "example": "Barcelona"
                          },
                          "state": {
                            "type": "string",
                            "description": "Provincia/Estado",
                            "example": "Barcelona"
                          },
                          "country": {
                            "type": "string",
                            "description": "Código de país (ISO 2 letras)",
                            "example": "ES"
                          },
                          "zipcode": {
                            "type": "string",
                            "description": "Código postal",
                            "example": "08001"
                          },
                          "neighborhood": {
                            "type": "string",
                            "description": "Barrio (si está disponible)",
                            "example": "Gothic Quarter"
                          },
                          "province": {
                            "type": "string",
                            "description": "Provincia/Región",
                            "example": "Catalonia"
                          }
                        }
                      }
                    },
                    "totalDocs": {
                      "type": "integer",
                      "description": "Total de ubicaciones encontradas",
                      "example": 15
                    },
                    "limit": {
                      "type": "integer",
                      "description": "Límite de resultados por página",
                      "example": 10
                    },
                    "page": {
                      "type": "integer",
                      "description": "Página actual",
                      "example": 1
                    },
                    "totalPages": {
                      "type": "integer",
                      "description": "Total de páginas disponibles",
                      "example": 2
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Error de autorización o validación.\nOcurre cuando el usuario no tiene permisos o los datos son inválidos.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Mensaje descriptivo del error"
                    }
                  }
                },
                "example": {
                  "error": "No tienes permisos para acceder a este recurso"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/auctions/{code}": {
      "get": {
        "summary": "Get full auction details",
        "deprecated": false,
        "description": "Devuelve información detallada de una subasta específica identificada por su código único.\n\nIncluye:\n- Datos básicos de la subasta (fechas, ubicaciones, tipo de carga)\n- Estado actual (published, awarded, approved, completed)\n- Información de pujas (incluyendo la puja ganadora si existe)\n- Datos de entrega asociados (si aplica)\n- Direcciones completas de carga (ETL) y descarga (ETD)\n- Información sobre si el usuario actual puede:\n  * Realizar una puja (canBid)\n  * Firmar el contrato (canSign)\n  * Ver detalles de entrega (canSeeDelivery)\n\nCampos importantes:\n- service_code: Identificador único de la subasta\n- status: Estado actual (published, awarded, approved, completed)\n- bid_current: Valor actual de la puja más baja\n- my_bid: Puja del usuario actual (si existe)\n- amWinner: Indica si el usuario actual ganó la subasta\n",
        "tags": [
          "Auctions"
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "description": "Código único de la subasta (8 caracteres alfanuméricos).\nEjemplo: \"ABC12345\"\n",
            "required": true,
            "example": "ACOPOLF9d9F",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Detalles completos de la subasta",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Auction"
                },
                "example": {
                  "service_code": "ABC12345",
                  "status": "published",
                  "etl_date": "2025-08-15T08:00:00.000Z",
                  "etd_date": "2025-08-16T18:00:00.000Z",
                  "pallets_num": 12,
                  "pallets_type": "euro",
                  "cargo_weight": 1200,
                  "cargo_height": 2.4,
                  "is_fresh": false,
                  "start_price": 850,
                  "bid_current": 920,
                  "my_bid": null,
                  "amWinner": false,
                  "canBid": true,
                  "canSign": false,
                  "canSeeDelivery": false,
                  "etl_address": {
                    "city": "Madrid",
                    "state": "Madrid",
                    "country": "ES",
                    "zipcode": "28001"
                  },
                  "etd_address": {
                    "city": "Barcelona",
                    "state": "Barcelona",
                    "country": "ES",
                    "zipcode": "08001"
                  },
                  "bids": []
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Error de autorización o validación.\nOcurre cuando el usuario no tiene permisos o los datos son inválidos.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Mensaje descriptivo del error"
                    }
                  }
                },
                "example": {
                  "error": "No tienes permisos para acceder a este recurso"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/auth/activate/{token}": {
      "get": {
        "summary": "Activate account",
        "deprecated": false,
        "description": "Activa la cuenta del transportista usando el token de activación recibido por email.\n\nFlujo:\n1. Valida el token de activación\n2. Marca la cuenta como activa y verifica el email\n3. Elimina el token de activación\n4. Envía email de confirmación\n\nEl token es válido por 24 horas desde su generación.\n\nErrores comunes:\n- INVALID_TOKEN: Token no válido o expirado (400)\n- USER_NOT_FOUND: No existe usuario asociado al token (404)\n",
        "tags": [
          "Auth"
        ],
        "parameters": [
          {
            "name": "recovery token",
            "in": "path",
            "description": "Token de activación recibido por email",
            "required": true,
            "example": "a1b2c3d4e5f6g7h8i9j0",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "token",
            "in": "path",
            "description": "",
            "required": true,
            "example": "",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cuenta activada exitosamente",
            "content": {
              "text/html": {
                "schema": {
                  "type": "object",
                  "properties": {}
                },
                "example": "<html><body>Cuenta activada correctamente</body></html>"
              }
            },
            "headers": {}
          },
          "400": {
            "description": "Token inválido o expirado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {}
                },
                "example": {
                  "error": "INVALID_TOKEN"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Usuario no encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {}
                },
                "example": {
                  "error": "USER_NOT_FOUND"
                }
              }
            },
            "headers": {}
          }
        },
        "security": []
      }
    },
    "/truckers/auth/isComplete": {
      "get": {
        "summary": "Verify complete profile",
        "deprecated": false,
        "description": "Verifica si el perfil del transportista tiene todos los datos obligatorios completos.\n\nCampos verificados:\n- Nombre completo\n- Teléfono válido\n- NIF/CIF válido\n- Email verificado\n- Datos de empresa completos (si aplica)\n\nRequiere autenticación JWT válida.\n",
        "tags": [
          "Auth"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Estado del perfil",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProfileCompleteResponse"
                },
                "example": {
                  "complete": true
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "No autenticado o token inválido",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {}
                },
                "example": {
                  "error": "UNAUTHORIZED"
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "No tiene permisos para acceder",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {}
                },
                "example": {
                  "error": "FORBIDDEN"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/auth/login": {
      "post": {
        "summary": "Carrier Login",
        "deprecated": false,
        "description": "Autentica a un transportista en el sistema. Verifica las credenciales y devuelve un JWT si son válidas.\n\nFlujo:\n1. Valida email y contraseña\n2. Verifica que la cuenta esté activa\n3. Registra el acceso en el historial\n4. Genera token JWT con datos del usuario\n\nErrores comunes:\n- WRONG_CREDENTIALS: Credenciales incorrectas (400)\n- USER_NOT_ACTIVE: Cuenta suspendida o no activada (401)\n",
        "tags": [
          "Auth"
        ],
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              },
              "example": {
                "email": "truckertesting@test.com",
                "password": "Test1234"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Autenticación exitosa",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/200Token"
                },
                "example": {
                  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
                  "user": {
                    "id": "507f1f77bcf86cd799439011",
                    "name": "Juan Pérez",
                    "email": "transportista@ejemplo.com",
                    "status": true
                  }
                }
              }
            },
            "headers": {}
          },
          "400": {
            "description": "Credenciales inválidas o formato incorrecto",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {}
                },
                "example": {
                  "error": "WRONG_CREDENTIALS"
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Cuenta no activa o suspendida",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {}
                },
                "example": {
                  "error": "USER_NOT_ACTIVE"
                }
              }
            },
            "headers": {}
          }
        },
        "security": []
      }
    },
    "/truckers/auth/register": {
      "post": {
        "summary": "Carrier Registration",
        "deprecated": false,
        "description": "Crea una nueva cuenta de transportista y empresa asociada. \n\nProceso:\n1. Valida datos requeridos\n2. Verifica que el email no esté registrado\n3. Crea usuario y empresa en Stripe\n4. Envía email de activación\n\nCampos obligatorios:\n- name: Nombre completo\n- email: Email válido y único\n- password: Mínimo 8 caracteres con mayúsculas, minúsculas y números\n- phone: Teléfono de contacto\n- taxid: NIF/CIF válido\n\nErrores comunes:\n- USER_ALREADY_EXIST: Email ya registrado (406)\n- PASS_NOT_MATCH: Las contraseñas no coinciden (400)\n",
        "tags": [
          "Auth"
        ],
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterRequest"
              },
              "example": {
                "name": "Transportista Ejemplo",
                "email": "nuevo@transportistaaaaa.com",
                "password": "Password12345",
                "password_confirm": "Password12345",
                "phone": "+34611223344",
                "taxid": "X123456IU"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Registro exitoso",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Trucker"
                },
                "example": {
                  "id": "507f1f77bcf86cd799439011",
                  "name": "Transportista Ejemplo",
                  "email": "nuevo@transportista.com",
                  "status": false,
                  "createdAt": "2025-01-15T10:30:00.000Z"
                }
              }
            },
            "headers": {}
          },
          "400": {
            "description": "Datos inválidos o faltantes",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {}
                },
                "example": {
                  "error": "PASS_NOT_MATCH"
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Registro no permitido",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {}
                },
                "example": {
                  "error": "REGISTRATION_DISABLED"
                }
              }
            },
            "headers": {}
          }
        },
        "security": []
      }
    },
    "/truckers/contracts/": {
      "get": {
        "summary": "Get contract list",
        "deprecated": false,
        "description": "Obtiene la lista completa de contratos disponibles para el transportista autenticado.\n\n**Funcionalidad:**\n- Devuelve todos los contratos asociados al transportista\n- Incluye metadatos como nombre, versión y URL de descarga\n- Requiere autenticación JWT válida\n\n**Casos de uso:**\n- Mostrar lista de contratos en aplicación móvil\n- Integración con sistemas de gestión de flotas\n- Verificación de contratos vigentes\n\n**Ejemplo de respuesta:**\n```json\n[\n  {\n    \"service_code\": \"TRANS-2023-001\",\n    \"name\": \"Contrato de Transporte General\",\n    \"version\": \"1.2.0\",\n    \"pdf_url\": \"https://storage.cargoffer.com/contracts/TRANS-2023-001.pdf\",\n    \"createdAt\": \"2023-01-15T10:30:00Z\",\n    \"updatedAt\": \"2023-06-20T14:15:00Z\"\n  }\n]\n```\n",
        "tags": [
          "Contracts"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Lista de contratos",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContractList"
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "No autorizado. El token JWT es inválido o ha expirado.\nSe debe renovar la autenticación.\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      },
      "post": {
        "summary": "Send contracts by email",
        "deprecated": false,
        "description": "Envía uno o más contratos al email especificado del transportista.\n\n**Funcionalidad:**\n- Envía los PDFs de los contratos como adjuntos\n- Valida que los códigos de contrato existan\n- Verifica formato del email\n- Requiere autenticación JWT válida\n\n**Casos de uso:**\n- Envío automático al firmar contratos digitalmente\n- Reenvío de contratos a nuevo email\n- Compartir contratos con gestores de flota\n\n**Ejemplo de request:**\n```json\n{\n  \"contract_codes\": [\"TRANS-2023-001\", \"TRANS-2023-002\"],\n  \"email\": \"transportista@empresa.com\"\n}\n```\n\n**Notas:**\n- Límite de 10 contratos por envío\n- El email debe ser válido y estar asociado al transportista\n",
        "tags": [
          "Contracts"
        ],
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "contract_codes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Lista de códigos de contrato a enviar. Máximo 10 códigos por petición.\n",
                    "example": [
                      "TRANS-2023-001",
                      "TRANS-2023-002"
                    ]
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Email destino donde se enviarán los contratos. Debe ser un email válido asociado al transportista.\n",
                    "example": "transportista@empresa.com"
                  }
                }
              },
              "example": {
                "service_code": "ACOMADPT9Q4",
                "emails": [
                  "test@example.com",
                  "another@example.com"
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Emails enviados exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "400": {
            "description": "Datos inválidos. Posibles causas:\n- Formato de email incorrecto\n- Lista de contratos vacía\n- Más de 10 códigos de contrato\n",
            "headers": {}
          },
          "401": {
            "description": "No autorizado. El token JWT es inválido o ha expirado.\nSe debe renovar la autenticación.\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/contracts/{service_code}": {
      "get": {
        "summary": "Download Contract PDF",
        "deprecated": false,
        "description": "Descarga el documento PDF de un contrato específico identificado por su código único.\n\n**Funcionalidad:**\n- Devuelve el PDF del contrato como binario\n- Valida que el código de contrato exista\n- Requiere autenticación JWT válida\n- Incluye cabeceras Content-Type y Content-Disposition\n\n**Casos de uso:**\n- Visualización del contrato en navegador\n- Descarga local para archivo\n- Integración con sistemas de gestión documental\n\n**Ejemplo de URL:**\n`https://api.demo.cargoffer.com/truckers/contracts/TRANS-2023-001`\n",
        "tags": [
          "Contracts"
        ],
        "parameters": [
          {
            "name": "service_code",
            "in": "path",
            "description": "Código único del contrato a descargar.\nDebe coincidir exactamente con el código registrado.\n",
            "required": true,
            "example": "ACOMADPT9Q4",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "PDF del contrato",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "No autorizado. El token JWT es inválido o ha expirado.\nSe debe renovar la autenticación.\n",
            "headers": {}
          },
          "404": {
            "description": "Contrato no encontrado. Posibles causas:\n- El código de contrato no existe\n- El contrato no está asociado al transportista\n- El contrato ha sido eliminado\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/country/": {
      "get": {
        "summary": "Get list of enabled countries (public)",
        "deprecated": false,
        "description": "Obtiene una lista paginada de todos los países habilitados en el sistema.\nEste endpoint es público pero requiere autenticación básica.\n\n**Casos de uso:**\n- Mostrar lista de países disponibles para selección en formularios\n- Filtrar operaciones por país habilitado\n- Integración con otros sistemas que necesiten referencia de países\n\n**Notas:**\n- Solo devuelve países con enabled=true\n- Los campos sensibles como _id, deleted, createdAt son omitidos\n",
        "tags": [
          "Countries"
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Número de página para paginación (por defecto 1)",
            "required": false,
            "example": 1,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Límite de items por página. \nSi no se especifica usa el valor de ITEMS_PAGE del entorno.\nSi es <= 0 también usa ITEMS_PAGE.\n",
            "required": false,
            "example": 10,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Respuesta exitosa que devuelve una lista paginada de países\ncon metadatos de paginación.\n\n**Ejemplo:**\n```json\n{\n  \"docs\": [\n    {\n      \"code\": \"ES\",\n      \"name\": \"España\",\n      \"regex\": \"^[A-Z0-9]{9}$\"\n    },\n    {\n      \"code\": \"FR\",\n      \"name\": \"Francia\",\n      \"regex\": \"^[A-Z0-9]{10}$\"\n    }\n  ],\n  \"total\": 2,\n  \"limit\": 10,\n  \"page\": 1,\n  \"pages\": 1\n}\n```\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CountryList"
                }
              }
            },
            "headers": {}
          },
          "500": {
            "description": "Server error",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/country/regex/{code}": {
      "get": {
        "summary": "Get regex validation pattern for country",
        "deprecated": false,
        "description": "Devuelve el patrón de expresión regular para validar identificadores fiscales\ndel país especificado.\n\n**Casos de uso:**\n- Validar formatos de NIF/CIF/VAT en formularios\n- Integración con sistemas de facturación\n- Validación de documentos fiscales\n\n**Ejemplo de respuesta:**\n```json\n{\n  \"pattern\": \"^[A-Z0-9]{9}$\"\n}\n```\n\n**Notas:**\n- Los patrones siguen la sintaxis estándar de expresiones regulares\n- Devuelve 404 si el país no existe\n",
        "tags": [
          "Countries"
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "description": "Código ISO del país (ej. ES, FR, DE)",
            "required": true,
            "example": "ES",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Regex pattern",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "pattern": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Recurso no encontrado.\n\n**Posibles causas:**\n- País no existe\n- Recurso eliminado\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "NOT_FOUND"
                    },
                    "message": {
                      "type": "string",
                      "example": "País no encontrado"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "500": {
            "description": "Error interno del servidor.\n\n**Posibles causas:**\n- Fallo en base de datos\n- Excepción no controlada\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "SERVER_ERROR"
                    },
                    "message": {
                      "type": "string",
                      "example": "Error interno del servidor"
                    }
                  }
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/deliveries/": {
      "get": {
        "summary": "Get paginated list of deliveries",
        "deprecated": false,
        "description": "Este endpoint permite consultar las entregas asignadas al transportista con paginación y filtrado.\n\n**Funcionalidades:**\n- Listado paginado de entregas con información básica\n- Filtrado por estado (current, next, finished, all)\n- Ordenación por fechas de carga y descarga\n\n**Casos de uso:**\n- Visualizar entregas pendientes en el dashboard del transportista\n- Consultar historial de entregas completadas\n- Preparar agenda de próximas entregas\n\n**Notas:**\n- Requiere autenticación JWT válida\n- Los filtros aplican diferentes rangos de fechas:\n  - current: entregas en los próximos 7 días\n  - next: entregas después de 7 días\n  - finished: entregas completadas\n",
        "tags": [
          "Deliveries"
        ],
        "parameters": [
          {
            "name": "filter",
            "in": "query",
            "description": "Filtro de estado de entrega:\n- current: Entregas activas (próximos 7 días)\n- next: Entregas futuras (después de 7 días)  \n- finished: Entregas completadas\n- all: Todas las entregas sin filtrar\n",
            "required": false,
            "example": "current",
            "schema": {
              "type": "string",
              "enum": [
                "current",
                "next",
                "finished",
                "all"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Número de página para la paginación (por defecto 1)",
            "required": false,
            "example": 1,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Límite de resultados por página (por defecto 10, máximo 100)",
            "required": false,
            "example": 10,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Lista paginada de entregas",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryList"
                }
              }
            },
            "headers": {
              "X-Total-Count": {
                "description": "Número total de resultados",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Page": {
                "description": "Página actual",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Limit": {
                "description": "Límite de resultados por página",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Pages": {
                "description": "Número total de páginas",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Error de autorización o validación.\nOcurre cuando el usuario no tiene permisos o los datos son inválidos.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Mensaje descriptivo del error"
                    }
                  }
                },
                "example": {
                  "error": "No tienes permisos para acceder a este recurso"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/deliveries/delete/{id}": {
      "delete": {
        "summary": "Delete a delivery",
        "deprecated": false,
        "description": "Este endpoint permite eliminar una entrega específica identificada por su ID.\n\n**Restricciones:**\n- Solo puede eliminar entregas asignadas al transportista autenticado\n- No se pueden eliminar entregas en estado 'collected' o 'delivered'\n\n**Casos de uso:**\n- Cancelar una entrega que aún no ha comenzado\n- Eliminar una entrega creada por error\n\n**Notas:**\n- Requiere autenticación JWT válida\n- La eliminación es permanente y no se puede deshacer\n- Devuelve el ID de la entrega eliminada si tiene éxito\n",
        "tags": [
          "Deliveries"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "ID único de MongoDB de la entrega a eliminar.\nFormato: 24 caracteres hexadecimales.\n",
            "required": true,
            "example": "507f1f77bcf86cd799439011",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "_id": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/deliveries/eta": {
      "post": {
        "summary": "Update estimated time of arrival (ETA)",
        "deprecated": false,
        "description": "Este endpoint permite actualizar la fecha estimada de llegada (ETA) para una entrega en curso.\n\n**Funcionalidades:**\n- Actualiza la ETA considerando el tiempo de viaje restante\n- Registra nivel de combustible y carga extra si se proporcionan\n- Notifica automáticamente a la empresa contratante del cambio\n\n**Casos de uso:**\n- Ajustar ETA por retrasos en ruta (tráfico, averías)\n- Actualizar estimación por cambios en velocidad media\n- Reportar estado del vehículo (combustible, carga)\n\n**Validaciones:**\n- Solo funciona para entregas en estado 'collected'\n- La nueva ETA debe ser posterior a la actual\n- Nivel de combustible debe estar entre 0-100\n",
        "tags": [
          "Deliveries"
        ],
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ETAChangeRequest"
              },
              "example": {
                "service_code": "ABC12345",
                "date_eta": "2025-08-11T18:30:00.000Z",
                "extra_load": false,
                "tank_level": 65
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "ETA actualizada correctamente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string",
                      "example": "ETA actualizada correctamente"
                    },
                    "data": {
                      "$ref": "#/components/schemas/EstimatedETAResponse"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "message": "ETA actualizada correctamente",
                  "data": {
                    "etlDate": "2025-08-11T08:00:00.000Z",
                    "service_code": "ABC12345",
                    "timeCost": 365,
                    "fuelCost": 125.5,
                    "distance": 623.5,
                    "eta": "2025-08-11T18:30:00.000Z"
                  }
                }
              }
            },
            "headers": {}
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/deliveries/msg/{id}": {
      "get": {
        "summary": "Retrieve messages from a delivery",
        "deprecated": false,
        "description": "Este endpoint devuelve todos los mensajes asociados a una entrega específica.\n\n**Características:**\n- Devuelve mensajes ordenados cronológicamente (más recientes primero)\n- Incluye texto, fotos adjuntas y metadatos\n- Muestra información del autor (nombre y ID)\n\n**Casos de uso:**\n- Consultar el historial de comunicación sobre una entrega\n- Verificar actualizaciones o incidencias reportadas\n- Revisar fotos adjuntas de la carga/descarga\n\n**Notas:**\n- Requiere autenticación JWT válida\n- Solo devuelve mensajes de entregas asignadas al transportista\n- Los mensajes se almacenan en formato UTC\n",
        "tags": [
          "Deliveries"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "ID único de MongoDB de la entrega.\nFormato: 24 caracteres hexadecimales.\n",
            "required": true,
            "example": "6613a126d69d03d756048616",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Lista de mensajes de la entrega",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/200Messages"
                },
                "example": {
                  "messages": [
                    {
                      "authorId": "507f1f77bcf86cd799439011",
                      "authorName": "Juan Pérez",
                      "message": "Retraso por avería en el vehículo",
                      "files": [
                        "https://bucket.s3.amazonaws.com/photo1.jpg"
                      ],
                      "createdAt": "2025-08-11T12:30:00.000Z"
                    },
                    {
                      "authorId": "507f1f77bcf86cd799439012",
                      "authorName": "Sistema",
                      "message": "Entrega programada para 2025-08-12",
                      "files": [],
                      "createdAt": "2025-08-10T09:15:00.000Z"
                    }
                  ]
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      },
      "post": {
        "summary": "Send a message about a delivery",
        "deprecated": false,
        "description": "Este endpoint permite enviar mensajes relacionados con una entrega específica.\n\n**Características:**\n- Soporta texto y adjuntos (hasta 6 imágenes)\n- Registra automáticamente autor (nombre e ID) y timestamp\n- Almacena los mensajes asociados permanentemente a la entrega\n- Valida que la entrega permita mensajes (can_message=true)\n\n**Casos de uso:**\n- Reportar incidencias durante el transporte (averías, retrasos)\n- Enviar actualizaciones sobre el estado de la entrega\n- Compartir fotos como prueba de carga/descarga\n- Comunicar cambios en horarios o rutas\n\n**Notas:**\n- Requiere autenticación JWT válida\n- Solo funciona para entregas asignadas al transportista\n- El usuario autenticado se registra automáticamente como autor\n- Las imágenes se suben mediante multipart/form-data\n- El timestamp se genera automáticamente en el servidor (UTC)\n",
        "tags": [
          "Deliveries"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "ID único de MongoDB de la entrega.\nFormato: 24 caracteres hexadecimales.\n",
            "required": true,
            "example": "6613a126d69d03d756048616",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Message"
              },
              "example": {
                "message": "Mensaje de ejemplo sobre la entrega"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Mensaje enviado correctamente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string",
                      "example": "Mensaje enviado correctamente"
                    },
                    "data": {
                      "$ref": "#/components/schemas/Message"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/deliveries/notes/{id}": {
      "put": {
        "summary": "Save notes about a delivery",
        "deprecated": false,
        "description": "Este endpoint permite guardar notas privadas sobre una entrega específica.\n\n**Características:**\n- Almacena notas en formato texto plano\n- Registra automáticamente timestamp de creación/actualización\n- Las notas son visibles solo para el transportista\n- Soporta formato Markdown básico para organización\n\n**Casos de uso:**\n- Registrar observaciones importantes sobre la entrega\n- Guardar detalles de contacto o instrucciones especiales\n- Anotar información relevante para futuras referencias\n\n**Notas:**\n- Requiere autenticación JWT válida\n- Solo funciona para entregas asignadas al transportista\n- Las notas se almacenan en formato UTC\n- Cada actualización sobrescribe las notas anteriores\n",
        "tags": [
          "Deliveries"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "ID único de MongoDB de la entrega.\nFormato: 24 caracteres hexadecimales.\n",
            "required": true,
            "example": "6613a126d69d03d756048616",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Note"
              },
              "example": {
                "content": "# Notas importantes\n- Contacto en destino: María López - 612345678\n- Horario restringido de descarga: 8:00-14:00\n- Requiere documentación adicional en puerta\n- Número de pallets confirmados: 12\n"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Notas guardadas correctamente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Note"
                },
                "example": {
                  "content": "# Notas importantes\n- Contacto en destino: María López - 612345678\n- Horario restringido de descarga: 8:00-14:00\n- Requiere documentación adicional en puerta\n- Número de pallets confirmados: 12\n",
                  "timestamp": "2025-08-11T15:30:00.000Z"
                }
              }
            },
            "headers": {}
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/deliveries/route/{service_code}": {
      "get": {
        "summary": "Get optimized route for a delivery",
        "deprecated": false,
        "description": "Este endpoint devuelve la ruta optimizada entre los puntos de carga y descarga de una entrega.\n\n**Información incluida:**\n- Coordenadas de la ruta completa (array de puntos GPS)\n- Distancia total en kilómetros\n- Tiempo estimado de viaje en minutos\n- Instrucciones paso a paso para la navegación\n- Puntos de interés relevantes en la ruta\n\n**Casos de uso:**\n- Visualizar la ruta óptima en el mapa del transportista\n- Calcular tiempo y distancia estimados\n- Obtener instrucciones de navegación detalladas\n- Identificar puntos de descanso o repostaje\n\n**Notas:**\n- Requiere autenticación JWT válida\n- Solo funciona para entregas asignadas al transportista\n- La ruta se calcula usando el servicio de mapas interno\n- Los tiempos estimados consideran tráfico en tiempo real\n",
        "tags": [
          "Deliveries"
        ],
        "parameters": [
          {
            "name": "service_code",
            "in": "path",
            "description": "Código único de identificación de la entrega.\nFormato: 8 caracteres alfanuméricos en mayúsculas.\n",
            "required": true,
            "example": "ACOMADPT9Q4",
            "schema": {
              "type": "string",
              "pattern": "^[A-Z0-9]{8}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Información detallada de la ruta",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "distance": {
                      "type": "number",
                      "description": "Distancia total en kilómetros",
                      "example": 623.5
                    },
                    "duration": {
                      "type": "number",
                      "description": "Tiempo estimado en minutos",
                      "example": 365
                    },
                    "route": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "lat": {
                            "type": "number",
                            "description": "Latitud GPS",
                            "example": 40.416775
                          },
                          "lng": {
                            "type": "number",
                            "description": "Longitud GPS",
                            "example": -3.70379
                          },
                          "timestamp": {
                            "type": "string",
                            "format": "date-time",
                            "description": "Tiempo estimado de paso",
                            "example": "2025-08-11T15:30:00.000Z"
                          }
                        }
                      }
                    },
                    "steps": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "instruction": {
                            "type": "string",
                            "description": "Instrucción de navegación",
                            "example": "Tome la salida 12 hacia A-2/Barcelona"
                          },
                          "distance": {
                            "type": "number",
                            "description": "Distancia del tramo en km",
                            "example": 45.2
                          },
                          "duration": {
                            "type": "number",
                            "description": "Duración del tramo en minutos",
                            "example": 32
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "distance": 623.5,
                  "duration": 365,
                  "route": [
                    {
                      "lat": 40.416775,
                      "lng": -3.70379,
                      "timestamp": "2025-08-11T15:30:00.000Z"
                    },
                    {
                      "lat": 40.417775,
                      "lng": -3.70479,
                      "timestamp": "2025-08-11T15:35:00.000Z"
                    }
                  ],
                  "steps": [
                    {
                      "instruction": "Salga de Madrid por A-2",
                      "distance": 15.5,
                      "duration": 12
                    },
                    {
                      "instruction": "Tome la salida 12 hacia A-2/Barcelona",
                      "distance": 45.2,
                      "duration": 32
                    }
                  ]
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/deliveries/trackable": {
      "get": {
        "summary": "Get available deliveries for GPS tracking",
        "deprecated": false,
        "description": "Este endpoint devuelve una lista de entregas en curso que pueden ser seguidas mediante GPS en tiempo real.\n\n**Características principales:**\n- Solo incluye entregas en estado 'collected' (recogidas pero no entregadas)\n- Excluye entregas asociadas a subastas\n- Proporciona información esencial para seguimiento:\n  - Código de servicio único\n  - Estado actual de la entrega\n  - Direcciones completas de carga (ETL) y descarga (ETD)\n  - Coordenadas geográficas si están disponibles\n  - Última posición conocida del vehículo (si está disponible)\n\n**Casos de uso típicos:**\n- Visualizar todas las entregas activas en el mapa del transportista\n- Seleccionar una entrega específica para iniciar seguimiento GPS\n- Verificar qué entregas están actualmente en ruta\n- Planificar rutas múltiples cuando hay varias entregas activas\n- Monitorear progreso de entregas en curso\n\n**Validaciones y restricciones:**\n- Requiere autenticación JWT válida de transportista\n- Solo muestra entregas asignadas al transportista autenticado\n- Las entregas deben estar en estado 'collected' para aparecer\n- No incluye entregas completadas o canceladas\n- Máxima frecuencia de consulta: 1 vez por minuto\n\n**Ejemplo de flujo:**\n1. Transportista inicia sesión en la app móvil\n2. La app consulta este endpoint periódicamente (cada 2-5 minutos)\n3. Se muestran las entregas activas en el mapa con su última posición\n4. El transportista selecciona una para iniciar seguimiento detallado\n\n**Notas técnicas:**\n- Las coordenadas se devuelven en formato [longitud, latitud] (GeoJSON)\n- El estado 'collected' indica que la carga ha sido recogida pero no entregada\n- Las direcciones incluyen ciudad, código postal y provincia\n- La respuesta incluye cabecera X-Total-Count con el número total de entregas\n",
        "tags": [
          "Deliveries"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Lista de entregas disponibles para seguimiento GPS",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrackableDelivery"
                },
                "examples": {
                  "1": {
                    "summary": "Éxito",
                    "value": {
                      "service_code": "ABC12345",
                      "status": "collected",
                      "etl_address": {
                        "city": "Madrid",
                        "zipcode": "28001",
                        "province": "Madrid",
                        "location": {
                          "coordinates": [
                            -3.70379,
                            40.416775
                          ]
                        }
                      },
                      "etd_address": {
                        "city": "Barcelona",
                        "zipcode": "08001",
                        "province": "Barcelona",
                        "location": {
                          "coordinates": [
                            2.173404,
                            41.385064
                          ]
                        }
                      },
                      "last_position": {
                        "lat": 40.416775,
                        "lng": -3.70379,
                        "timestamp": "2025-08-11T15:09:19.000Z"
                      }
                    }
                  },
                  "2": {
                    "summary": "Éxito",
                    "value": {
                      "service_code": "DEF67890",
                      "status": "collected",
                      "etl_address": {
                        "city": "Valencia",
                        "zipcode": "46001",
                        "province": "Valencia",
                        "location": {
                          "coordinates": [
                            -0.376288,
                            39.469907
                          ]
                        }
                      },
                      "etd_address": {
                        "city": "Sevilla",
                        "zipcode": "41001",
                        "province": "Sevilla",
                        "location": {
                          "coordinates": [
                            -5.984459,
                            37.389092
                          ]
                        }
                      },
                      "last_position": {
                        "lat": 39.469907,
                        "lng": -0.376288,
                        "timestamp": "2025-08-11T15:05:42.000Z"
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Total-Count": {
                "example": 2,
                "description": "Número total de entregas trackeables",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "example": 58,
                "description": "Número de consultas restantes en el período actual",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Error de autorización o validación.\nOcurre cuando el usuario no tiene permisos o los datos son inválidos.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Mensaje descriptivo del error"
                    }
                  }
                },
                "example": {
                  "error": "No tienes permisos para acceder a este recurso"
                }
              }
            },
            "headers": {}
          },
          "429": {
            "description": "Demasiadas solicitudes. El límite de consultas es de 60 por hora.\nEspere antes de realizar nuevas consultas.\n",
            "headers": {
              "Retry-After": {
                "example": 60,
                "description": "Segundos hasta que se puede realizar otra consulta",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/deliveries/tracking/{service_code}": {
      "post": {
        "summary": "Update GPS tracking position",
        "deprecated": false,
        "description": "Este endpoint registra la posición GPS actual del vehículo para una entrega en curso y actualiza el estado de seguimiento.\n\n**Funcionalidades clave:**\n- Registra coordenadas GPS precisas (latitud, longitud)\n- Almacena timestamp exacto de la posición (UTC)\n- Actualiza la última posición conocida del transportista\n- Asocia automáticamente la posición a la entrega especificada\n- Mantiene historial de posiciones para trazabilidad\n\n**Casos de uso principales:**\n- Seguimiento en tiempo real durante el transporte\n- Generación de rutas optimizadas basadas en posición actual\n- Cálculo de tiempos estimados de llegada (ETA) dinámicos\n- Verificación geográfica para procesos de carga/descarga\n- Monitoreo de flota y gestión de activos\n\n**Validaciones y requisitos:**\n- Solo funciona para entregas en estado 'collected'\n- Coordenadas GPS deben ser válidas (lat: -90 a 90, lng: -180 a 180)\n- Timestamp debe ser actual (no más de 5 minutos de diferencia)\n- Máximo 1 actualización por minuto para evitar spam\n\n**Ejemplo de flujo:**\n1. App móvil obtiene posición GPS del dispositivo\n2. Envía posición al endpoint cada 2-5 minutos\n3. Sistema actualiza posición y recalcula ETA\n4. Empresa puede visualizar posición en tiempo real\n\n**Notas técnicas:**\n- Las coordenadas se almacenan en formato GeoJSON\n- El timestamp se convierte automáticamente a UTC\n- Las posiciones antiguas se archivan después de 30 días\n",
        "tags": [
          "Deliveries"
        ],
        "parameters": [
          {
            "name": "service_code",
            "in": "path",
            "description": "Código único de identificación de la entrega a trackear.\nFormato: 8 caracteres alfanuméricos en mayúsculas.\nDebe corresponder a una entrega en estado 'collected'.\n",
            "required": true,
            "example": "ACOMADPT9Q4",
            "schema": {
              "type": "string",
              "pattern": "^[A-Z0-9]{8}$"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TrackingPosition"
              },
              "example": {
                "service_code": "ACOMADPT9Q4",
                "location": {
                  "lat": 40.416775,
                  "lng": -3.70379
                },
                "timestamp": "2025-08-11T15:09:19.000Z"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Posición registrada correctamente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/200TrackingPosition"
                },
                "example": {
                  "service_code": "ABC12345",
                  "success": true,
                  "message": "Posición actualizada correctamente",
                  "updatedAt": "2025-08-11T15:09:20.000Z"
                }
              }
            },
            "headers": {
              "X-RateLimit-Remaining": {
                "example": 58,
                "description": "Número de actualizaciones restantes en el período actual",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Error de autorización o validación.\nOcurre cuando el usuario no tiene permisos o los datos son inválidos.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Mensaje descriptivo del error"
                    }
                  }
                },
                "example": {
                  "error": "No tienes permisos para acceder a este recurso"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "429": {
            "description": "Demasiadas solicitudes. El límite de actualizaciones es de 60 por hora.\nEspere antes de enviar nuevas posiciones.\n",
            "headers": {
              "Retry-After": {
                "example": 60,
                "description": "Segundos hasta que se puede realizar otra solicitud",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/deliveries/{service_code}": {
      "get": {
        "summary": "Get full delivery details",
        "deprecated": false,
        "description": "Este endpoint devuelve información detallada sobre una entrega específica identificada por su código de servicio.\n\n**Información incluida:**\n- Datos básicos de la entrega (código, estado, fechas)\n- Direcciones de carga y descarga (etl_address, etd_address)\n- Detalles de la carga (tipo, peso, pallets)\n- Información de la empresa contratante\n- Datos de contacto del responsable\n- Mensajes y notas asociadas\n\n**Casos de uso:**\n- Visualizar todos los detalles de una entrega específica\n- Consultar información para preparar la recogida/entrega\n- Verificar datos antes de iniciar una operación\n\n**Notas:**\n- Requiere autenticación JWT válida\n- Solo devuelve entregas asignadas al transportista autenticado\n",
        "tags": [
          "Deliveries"
        ],
        "parameters": [
          {
            "name": "service_code",
            "in": "path",
            "description": "Código único de identificación de la entrega.\nFormato: 8 caracteres alfanuméricos en mayúsculas.\n",
            "required": true,
            "example": "ACOMADPT9Q4",
            "schema": {
              "type": "string",
              "pattern": "^[A-Z0-9]{8}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Detalle completo de una entrega",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryDetail"
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/files/": {
      "get": {
        "summary": "Retrieve file from S3",
        "deprecated": false,
        "description": "Obtiene un archivo almacenado en Amazon S3. \n\nEste endpoint permite recuperar archivos genéricos (PDF, imágenes, binarios) previamente subidos al sistema.\nRequiere autenticación mediante JWT y el nombre exacto del archivo a recuperar.\n\n**Casos de uso:**\n- Descargar documentos PDF como facturas o contratos\n- Visualizar imágenes de perfil o documentos escaneados\n- Recuperar archivos binarios específicos\n\n**Ejemplo:**\n```\nGET /?file=factura_12345.pdf\n```\n",
        "tags": [
          "Files"
        ],
        "parameters": [
          {
            "name": "file",
            "in": "query",
            "description": "Nombre exacto del archivo almacenado en S3.\nDebe incluir la extensión del archivo (ej: .pdf, .jpg).\n\n**Ejemplos válidos:**\n- contrato_transporte_2023.pdf\n- perfil_usuario_123.jpg\n- licencia_conducir_456.png\n",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Archivo obtenido exitosamente desde S3.\n\nEl contenido devuelto varía según el tipo de archivo solicitado:\n- PDF: application/pdf\n- Imágenes: image/jpeg o image/png  \n- Otros tipos: application/octet-stream\n\n**Ejemplo de respuesta exitosa:**\n```http\nHTTP/1.1 200 OK\nContent-Type: application/pdf\nContent-Disposition: attachment; filename=\"factura_12345.pdf\"\n\n[binary data]\n```\n",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "El archivo solicitado no existe en S3.\n\n**Posibles causas:**\n- Nombre de archivo incorrecto\n- El archivo fue eliminado\n- El archivo nunca fue subido\n\n**Ejemplo de respuesta:**\n```json\n{\n  \"error\": \"File not found\",\n  \"message\": \"El archivo solicitado no existe\"\n}\n```\n",
            "headers": {}
          },
          "500": {
            "description": "Error interno del servidor al intentar obtener el archivo.\n\n**Posibles causas:**\n- Problemas de conexión con S3\n- Permisos insuficientes\n- Error inesperado en el servidor\n\n**Ejemplo de respuesta:**\n```json\n{\n  \"error\": \"Internal Server Error\",\n  \"message\": \"No se pudo recuperar el archivo\"\n}\n```\n",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/hscode/find": {
      "get": {
        "summary": "Search HS codes by term",
        "deprecated": false,
        "description": "Realiza una búsqueda textual en los códigos HS (Sistema Armonizado) y sus descripciones.\nLa búsqueda es case-insensitive y busca coincidencias parciales en títulos y etiquetas.\n\n**Ejemplo de uso**:\n- Búsqueda rápida de códigos HS por producto\n- Autocompletado en interfaces de usuario\n- Identificación de categorías HS relevantes\n\n**Notas**:\n- Devuelve un máximo de 20 resultados por tipo (headings y subheadings)\n- Los resultados se ordenan alfabéticamente por título\n- La búsqueda ignora comillas simples en el término\n",
        "tags": [
          "Hscode"
        ],
        "parameters": [
          {
            "name": "term",
            "in": "query",
            "description": "Término de búsqueda (ej. \"animales\", \"electronicos\").\nPuede ser una palabra parcial o completa del título o descripción.\n",
            "required": true,
            "schema": {
              "type": "string",
              "example": "caballos"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Resultados de búsqueda HS",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HSFindResult"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "No se encontraron resultados para el término de búsqueda",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/payment/register_stripe": {
      "post": {
        "summary": "Acceder al portal de cliente de Stripe para gestionar suscripción",
        "deprecated": false,
        "description": "## Purpose\nGenera una sesión del portal de cliente de Stripe donde la compañía puede\ngestionar su suscripción, cambiar de plan, actualizar tarjeta o ver facturas.\n\n## Objective\nProporcionar acceso al portal self-service de Stripe con validación de\nque la cuenta esté activa y no expirada.\n\n## Use Cases\n- Cambiar de plan Basic a Professional o viceversa\n- Cancelar suscripción activa\n- Actualizar método de pago de la suscripción\n- Ver historial de facturas de Stripe\n- Descargar facturas en PDF\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request - returnUrl] --> B{returnUrl provided?}\n  B -->|No| C[400 MISSING_RETURN_URL]\n  B -->|Yes| D{User authenticated?}\n  D -->|No| E[404 USER_NOT_FOUND]\n  D -->|Yes| F{Company found?}\n  F -->|No| G[401 CIA_NOT_FOUND]\n  F -->|Yes| H{UserActivity exists?}\n  H -->|No| I[404 USER_NOT_FOUND]\n  H -->|Yes| J{Account not expired?}\n  J -->|Expired| K[404 USER_EXPIRED]\n  J -->|Active| L[Create Customer Portal session]\n  L --> M[200 Portal URL]\n```\n\n## Notes\n- Requiere autenticación JWT (bearerAuth)\n- A diferencia del módulo truckers, **valida que la cuenta no esté expirada**\n- Verifica `userActivity.dateEnd > now` antes de crear la sesión\n- La URL de retorno (`returnUrl`) es obligatoria en el body\n- La sesión del portal expira en ~5 minutos (comportamiento de Stripe)\n- Requiere `stripe_customer` configurado en la compañía\n",
        "tags": [
          "payment-processing"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "returnUrl"
                ],
                "properties": {
                  "returnUrl": {
                    "type": "string",
                    "format": "uri",
                    "description": "URL donde Stripe redirige al salir del portal",
                    "minLength": 10,
                    "maxLength": 2048,
                    "example": "https://app.cargoffer.com/admin/config"
                  }
                }
              },
              "example": {
                "returnUrl": "https://app.cargoffer.com/admin/config"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "URL del portal de cliente de Stripe",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "boolean",
                      "example": true
                    },
                    "data": {
                      "type": "string",
                      "description": "URL de la sesión del portal de Stripe",
                      "example": "https://billing.stripe.com/p/session/test_..."
                    }
                  }
                },
                "example": {
                  "status": true,
                  "data": "https://billing.stripe.com/p/session/test_AbCd..."
                }
              }
            },
            "headers": {}
          },
          "400": {
            "description": "URL de retorno no proporcionada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "MISSING_RETURN_URL"
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Compañía no encontrada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "CIA_NOT_FOUND"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Usuario no encontrado o cuenta expirada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "user_not_found": {
                    "value": {
                      "message": "USER_NOT_FOUND"
                    }
                  },
                  "user_expired": {
                    "summary": "Cuenta de usuario expirada",
                    "value": {
                      "message": "USER_EXPIRED"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "500": {
            "description": "Error al crear sesión del portal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/truckers/payment/setup_payment_method": {
      "post": {
        "summary": "Configurar nuevo método de pago",
        "deprecated": false,
        "description": "## Purpose\nGenera una URL del portal de Stripe para que la compañía pueda agregar\nun nuevo método de pago (tarjeta de crédito/débito) de forma segura.\n\n## Objective\nRedirigir al usuario al portal de Stripe donde puede registrar su\ninformación de tarjeta sin exponer datos sensibles al backend.\n\n## Use Cases\n- Agregar la primera tarjeta de crédito/débito\n- Añadir métodos de pago adicionales (máx. 10)\n- Reemplazar tarjetas caducadas con nuevas\n\n## Validation Flow\n```mermaid\nflowchart TD\n  A[Receive Request - returnUrl] --> B{returnUrl provided?}\n  B -->|No| C[400 MISSING_RETURN_URL]\n  B -->|Yes| D{User authenticated?}\n  D -->|No| E[404 USER_NOT_FOUND]\n  D -->|Yes| F{Company found?}\n  F -->|No| G[401 CIA_NOT_FOUND]\n  F -->|Yes| H{stripe_customer exists?}\n  H -->|No| I[404 STRIPE_CUSTOMER_NOT_FOUND]\n  H -->|Yes| J{Count < 10?}\n  J -->|No| K[200 - Empty string]\n  J -->|Yes| L[Create payment methods portal]\n  L --> M[200 - Portal URL]\n```\n\n## Notes\n- Requiere autenticación JWT (bearerAuth)\n- Límite de 10 métodos de pago por compañía; si se alcanza retorna `\"\"` (string vacío)\n- Requiere que la compañía tenga `stripe_customer` (usar `/register_stripe` primero)\n- La `returnUrl` es donde Stripe redirige al usuario tras añadir el método\n",
        "tags": [
          "payment-methods"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "returnUrl"
                ],
                "properties": {
                  "returnUrl": {
                    "type": "string",
                    "format": "uri",
                    "description": "URL a la que redirige Stripe al completar la configuración",
                    "example": "https://app.cargoffer.com/admin/payment-methods"
                  }
                }
              },
              "example": {
                "returnUrl": "https://app.cargoffer.com/admin/payment-methods"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "URL del portal o string vacío si se alcanzó el límite de 10",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "boolean",
                      "example": true
                    },
                    "data": {
                      "type": "string",
                      "description": "URL del portal de Stripe o string vacío",
                      "example": "https://billing.stripe.com/p/session/..."
                    }
                  }
                },
                "examples": {
                  "con_url": {
                    "summary": "URL del portal generada",
                    "value": {
                      "status": true,
                      "data": "https://billing.stripe.com/p/session/test_AbCd..."
                    }
                  },
                  "limite_alcanzado": {
                    "summary": "Límite de 10 métodos alcanzado",
                    "value": {
                      "status": true,
                      "data": ""
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "400": {
            "description": "URL de retorno no proporcionada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "MISSING_RETURN_URL"
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Compañía no encontrada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "CIA_NOT_FOUND"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Usuario o cliente Stripe no encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "user_not_found": {
                    "value": {
                      "message": "USER_NOT_FOUND"
                    }
                  },
                  "stripe_not_found": {
                    "value": {
                      "message": "STRIPE_CUSTOMER_NOT_FOUND"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "500": {
            "description": "Error al crear sesión del portal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/truckers/settings/legal": {
      "get": {
        "summary": "Obtain legal documents",
        "deprecated": false,
        "description": "Devuelve todos los documentos legales disponibles en el idioma del usuario.\nSi no se especifica idioma, usa español por defecto.\n\nRequiere autenticación JWT válida.\nNo requiere permisos de administrador.\n",
        "operationId": "getLegalDocuments",
        "tags": [
          "Settings"
        ],
        "parameters": [
          {
            "name": "lang",
            "in": "query",
            "description": "Código de idioma preferido (ej. 'es', 'en')",
            "required": false,
            "example": "es",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Documentos obtenidos exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "code": {
                        "type": "string",
                        "description": "Código único del documento",
                        "example": "privacy"
                      },
                      "lang": {
                        "type": "string",
                        "description": "Código de idioma del documento",
                        "example": "es"
                      },
                      "name": {
                        "type": "string",
                        "description": "Nombre del documento",
                        "example": "Política de Privacidad"
                      },
                      "text": {
                        "type": "string",
                        "description": "Contenido HTML del documento",
                        "example": "<h1>Política de Privacidad</h1><p>Texto completo...</p>"
                      },
                      "visible": {
                        "type": "boolean",
                        "description": "Si el documento está visible",
                        "example": true
                      }
                    }
                  }
                },
                "examples": {
                  "1": {
                    "summary": "Éxito",
                    "value": [
                      {
                        "code": "privacy",
                        "lang": "es",
                        "name": "Política de Privacidad",
                        "text": "<h1>Política de Privacidad</h1><p>Texto completo...</p>",
                        "visible": true
                      },
                      {
                        "code": "terms",
                        "lang": "es",
                        "name": "Términos y Condiciones",
                        "text": "<h1>Términos y Condiciones</h1><p>Texto completo...</p>",
                        "visible": true
                      }
                    ]
                  }
                }
              }
            },
            "headers": {}
          },
          "500": {
            "description": "Error interno del servidor",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/settings/legal/{slug}": {
      "get": {
        "summary": "Get legal document by code",
        "deprecated": false,
        "description": "Devuelve un documento legal específico identificado por su código (slug).\nBusca tanto por código directo como por combinación código+idioma.\n\nRequiere autenticación JWT válida.\nNo requiere permisos de administrador.\n",
        "operationId": "getLegalDocumentBySlug",
        "tags": [
          "Settings"
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "description": "Código identificador del documento (ej. \"privacy\")",
            "required": true,
            "example": "",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "description": "Código de idioma preferido (ej. 'es', 'en')",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Documento legal obtenido exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegalDocument"
                },
                "example": {
                  "code": "privacy",
                  "lang": "es",
                  "name": "Política de Privacidad",
                  "text": "<h1>Política de Privacidad</h1><p>Texto completo...</p>",
                  "visible": true
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Documento no encontrado. Razones posibles:\n- El código no existe\n- El documento no está visible\n- No existe versión en el idioma solicitado\n",
            "headers": {}
          },
          "500": {
            "description": "Error interno del servidor",
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/vehicles/": {
      "get": {
        "summary": "Get my vehicle fleet",
        "deprecated": false,
        "description": "Devuelve la lista completa de vehículos asociados a la compañía del transportista autenticado.\n\n**Casos de uso:**\n- Visualizar todos los vehículos de la flota\n- Filtrar vehículos por características (implementado en el cliente)\n- Obtener información para mantenimiento o gestión de flota\n\n**Ejemplo de respuesta:**\n```json\n[\n  {\n    \"_id\": \"60a1b2c3d4e5f67890123456\",\n    \"licensePlate\": \"1234ABC\",\n    \"type\": \"RIGIDO\",\n    \"brand\": \"Volvo\",\n    \"model\": \"FH16\",\n    \"year\": 2022,\n    \"images\": [\"https://storage.example.com/vehicles/1234ABC.jpg\"],\n    \"createdAt\": \"2022-05-15T10:00:00Z\",\n    \"updatedAt\": \"2022-05-15T10:00:00Z\"\n  }\n]\n```\n",
        "tags": [
          "Vehicles"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Lista de vehículos obtenida exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VehicleList"
                },
                "example": {
                  "_id": "60a1b2c3d4e5f67890123456",
                  "licensePlate": "1234ABC",
                  "type": "RIGIDO",
                  "brand": "Volvo",
                  "model": "FH16",
                  "year": 2022,
                  "images": [
                    "https://storage.example.com/vehicles/1234ABC.jpg"
                  ],
                  "createdAt": "2022-05-15T10:00:00.000Z",
                  "updatedAt": "2022-05-15T10:00:00.000Z"
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Error de autorización o validación.\nOcurre cuando el usuario no tiene permisos o los datos son inválidos.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Mensaje descriptivo del error"
                    }
                  }
                },
                "example": {
                  "error": "No tienes permisos para acceder a este recurso"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      },
      "post": {
        "summary": "Create New Vehicle",
        "deprecated": false,
        "description": "Registra un nuevo vehículo en la flota del transportista autenticado.\nPermite subir imágenes del vehículo que se almacenan en Amazon S3.\n\n**Campos requeridos:**\n- licensePlate: Matrícula del vehículo\n- type: Tipo de vehículo (debe existir en el sistema)\n\n**Casos de uso:**\n- Añadir nuevos vehículos a la flota\n- Registrar vehículos recién adquiridos\n- Actualizar flota con nuevas unidades\n\n**Ejemplo de petición:**\n```json\n{\n  \"licensePlate\": \"1234ABC\",\n  \"type\": \"RIGIDO\",\n  \"brand\": \"Volvo\",\n  \"model\": \"FH16\",\n  \"year\": 2022\n}\n```\n\n**Notas:**\n- Las imágenes se deben enviar como archivos multipart\n- El tamaño máximo por imagen es de 5MB\n- Formatos soportados: JPG, PNG\n",
        "tags": [
          "Vehicles"
        ],
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {}
              },
              "example": {
                "plate": "ABC-123",
                "vehicle_type": "NONE",
                "cargo_type": "lateral",
                "shipping_type": "fresh",
                "fresh_cargo_temp": 4,
                "default_vehicle": "true"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Vehículo creado exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Vehicle"
                },
                "example": {
                  "_id": "60a1b2c3d4e5f67890123456",
                  "licensePlate": "1234ABC",
                  "type": "RIGIDO",
                  "brand": "Volvo",
                  "model": "FH16",
                  "year": 2022,
                  "images": [
                    "https://storage.example.com/vehicles/1234ABC.jpg"
                  ],
                  "createdAt": "2022-05-15T10:00:00.000Z",
                  "updatedAt": "2022-05-15T10:00:00.000Z"
                }
              }
            },
            "headers": {}
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Error de autorización o validación.\nOcurre cuando el usuario no tiene permisos o los datos son inválidos.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Mensaje descriptivo del error"
                    }
                  }
                },
                "example": {
                  "error": "No tienes permisos para acceder a este recurso"
                }
              }
            },
            "headers": {}
          },
          "409": {
            "description": "Conflicto - La matrícula ya existe en el sistema",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "La matrícula 1234ABC ya está registrada"
                    }
                  }
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/vehicles/types": {
      "get": {
        "summary": "Get available vehicle types",
        "deprecated": false,
        "description": "Este endpoint devuelve la lista completa de tipos de vehículos registrados en el sistema.\nCada tipo incluye su identificador único, nombre y código de referencia.\n\n**Casos de uso:**\n- Mostrar opciones al crear/editar un vehículo\n- Filtrar vehículos por tipo\n- Validar tipos de vehículo permitidos\n\n**Ejemplo de respuesta:**\n```json\n[\n  {\n    \"_id\": \"60a1b2c3d4e5f67890123456\",\n    \"name\": \"Camión Rígido\",\n    \"code\": \"RIGIDO\"\n  },\n  {\n    \"_id\": \"60a1b2c3d4e5f67890123457\",\n    \"name\": \"Tráiler\",\n    \"code\": \"TRAILER\"\n  }\n]\n```\n",
        "tags": [
          "Vehicles"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Lista de tipos de vehículo obtenida exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VehicleTypeList"
                },
                "examples": {
                  "1": {
                    "summary": "Éxito",
                    "value": {
                      "_id": "60a1b2c3d4e5f67890123456",
                      "name": "Camión Rígido",
                      "code": "RIGIDO"
                    }
                  },
                  "2": {
                    "summary": "Éxito",
                    "value": {
                      "_id": "60a1b2c3d4e5f67890123457",
                      "name": "Tráiler",
                      "code": "TRAILER"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Error de autorización o validación.\nOcurre cuando el usuario no tiene permisos o los datos son inválidos.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Mensaje descriptivo del error"
                    }
                  }
                },
                "example": {
                  "error": "No tienes permisos para acceder a este recurso"
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/truckers/vehicles/{id}": {
      "get": {
        "summary": "Get vehicle details",
        "deprecated": false,
        "description": "Devuelve la información completa de un vehículo específico de la flota,\nidentificado por su ID único.\n\n**Casos de uso:**\n- Ver información detallada de un vehículo\n- Mostrar datos para mantenimiento o inspección\n- Obtener información para edición\n\n**Parámetros:**\n- id (requerido): ID único del vehículo (MongoDB ObjectId)\n\n**Ejemplo de respuesta:**\n```json\n{\n  \"_id\": \"60a1b2c3d4e5f67890123456\",\n  \"licensePlate\": \"1234ABC\",\n  \"type\": \"RIGIDO\",\n  \"brand\": \"Volvo\",\n  \"model\": \"FH16\",\n  \"year\": 2022,\n  \"images\": [\"https://storage.example.com/vehicles/1234ABC.jpg\"],\n  \"createdAt\": \"2022-05-15T10:00:00Z\",\n  \"updatedAt\": \"2022-05-15T10:00:00Z\"\n}\n```\n",
        "tags": [
          "Vehicles"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "ID único del vehículo (MongoDB ObjectId)",
            "required": true,
            "example": "68fb879c38a3e0bf15db5014",
            "schema": {
              "type": "string",
              "example": "60a1b2c3d4e5f67890123456"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Detalles del vehículo obtenidos exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Vehicle"
                },
                "example": {
                  "_id": "60a1b2c3d4e5f67890123456",
                  "licensePlate": "1234ABC",
                  "type": "RIGIDO",
                  "brand": "Volvo",
                  "model": "FH16",
                  "year": 2022,
                  "images": [
                    "https://storage.example.com/vehicles/1234ABC.jpg"
                  ],
                  "createdAt": "2022-05-15T10:00:00.000Z",
                  "updatedAt": "2022-05-15T10:00:00.000Z"
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Error de autorización o validación.\nOcurre cuando el usuario no tiene permisos o los datos son inválidos.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Mensaje descriptivo del error"
                    }
                  }
                },
                "example": {
                  "error": "No tienes permisos para acceder a este recurso"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Vehículo no encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "Vehículo no encontrado"
                    }
                  }
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      },
      "put": {
        "summary": "Update vehicle by ID",
        "deprecated": false,
        "description": "Actualiza la información de un vehículo específico identificado por su ID.\nPermite modificar todos los campos excepto la matrícula (licensePlate).\nLas imágenes enviadas reemplazarán completamente las existentes.\n\n**Parámetros:**\n- id (requerido): ID único del vehículo a actualizar\n\n**Campos modificables:**\n- type: Tipo de vehículo\n- brand: Marca\n- model: Modelo\n- year: Año\n- images: Imágenes (se reemplazan completamente)\n\n**Casos de uso:**\n- Actualizar información técnica de un vehículo específico\n- Reemplazar imágenes de un vehículo\n- Corregir datos erróneos de un vehículo en particular\n\n**Ejemplo de petición:**\n```json\n{\n  \"type\": \"RIGIDO\",\n  \"brand\": \"Volvo\",\n  \"model\": \"FH16\",\n  \"year\": 2022\n}\n```\n",
        "tags": [
          "Vehicles"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "ID único del vehículo a actualizar (MongoDB ObjectId)",
            "required": true,
            "example": "68fb879c38a3e0bf15db5014",
            "schema": {
              "type": "string",
              "example": "60a1b2c3d4e5f67890123456"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {}
              },
              "example": {
                "plate": "ABC-123",
                "vehicle_type": "NONE",
                "cargo_type": "[\"lateral\"]",
                "shipping_type": "dry",
                "fresh_cargo_temp": 4,
                "default_vehicle": "true"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Vehículo actualizado exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Vehicle"
                },
                "example": {
                  "_id": "60a1b2c3d4e5f67890123456",
                  "licensePlate": "1234ABC",
                  "type": "RIGIDO",
                  "brand": "Volvo",
                  "model": "FH16",
                  "year": 2022,
                  "images": [
                    "https://storage.example.com/vehicles/1234ABC_v2.jpg"
                  ],
                  "createdAt": "2022-05-15T10:00:00.000Z",
                  "updatedAt": "2022-05-15T11:30:00.000Z"
                }
              }
            },
            "headers": {}
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Error de autorización o validación.\nOcurre cuando el usuario no tiene permisos o los datos son inválidos.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Mensaje descriptivo del error"
                    }
                  }
                },
                "example": {
                  "error": "No tienes permisos para acceder a este recurso"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Vehículo no encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "Vehículo no encontrado"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "409": {
            "description": "Conflicto - La matrícula no puede ser modificada",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "No se permite modificar la matrícula del vehículo"
                    }
                  }
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      },
      "delete": {
        "summary": "Delete vehicle by ID",
        "deprecated": false,
        "description": "Elimina permanentemente un vehículo específico de la flota,\nidentificado por su ID único.\n\n**Parámetros:**\n- id (requerido): ID único del vehículo a eliminar (MongoDB ObjectId)\n\n**Casos de uso:**\n- Dar de baja un vehículo específico que ya no está en servicio\n- Eliminar registros duplicados o erróneos\n- Limpiar la flota de vehículos obsoletos\n\n**Notas:**\n- La eliminación es permanente y no se puede deshacer\n- Se eliminarán también las imágenes asociadas de S3\n- Requiere autenticación JWT válida\n",
        "tags": [
          "Vehicles"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "ID único del vehículo a eliminar (MongoDB ObjectId)",
            "required": true,
            "example": "68fb8d1291f60f196404d8a6",
            "schema": {
              "type": "string",
              "example": "60a1b2c3d4e5f67890123456"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Vehículo eliminado exitosamente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Vehículo eliminado correctamente"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {}
          },
          "403": {
            "description": "Error de autorización o validación.\nOcurre cuando el usuario no tiene permisos o los datos son inválidos.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "description": "Mensaje descriptivo del error"
                    }
                  }
                },
                "example": {
                  "error": "No tienes permisos para acceder a este recurso"
                }
              }
            },
            "headers": {}
          },
          "404": {
            "description": "Vehículo no encontrado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "Vehículo no encontrado"
                    }
                  }
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/vehicles_types/": {
      "get": {
        "tags": [
          "vehicle-types"
        ],
        "summary": "Get visible vehicle types",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyAuth": []
          }
        ],
        "description": "Este endpoint devuelve una lista paginada de los tipos de vehículos visibles en el sistema.\n\n**Funcionalidad:**\n- Filtra solo los tipos de vehículos marcados como visibles\n- Soporta paginación con parámetros estándar (limit, page)\n- Permite ordenar por placa del vehículo\n- Devuelve información básica como tipo de vehículo, placa, imagen e ITV\n\n**Casos de uso:**\n1. Listar vehículos disponibles para asignar a transportistas\n2. Mostrar opciones de vehículos en formularios de creación\n3. Integración con apps móviles para mostrar flota disponible\n\n**Ejemplo de respuesta:**\n```json\n{\n  \"docs\": [\n    {\n      \"_id\": \"60a1b2c3d4e5f67890123456\",\n      \"code\": \"TRUCK\",\n      \"name\": \"Camión\",\n      \"description\": \"Vehículo pesado para transporte de mercancías\",\n      \"visible\": true,\n      \"createdAt\": \"2023-05-15T10:30:00Z\",\n      \"updatedAt\": \"2023-05-15T10:30:00Z\"\n    }\n  ],\n  \"total\": 1,\n  \"limit\": 10,\n  \"page\": 1,\n  \"pages\": 1\n}\n```\n",
        "responses": {
          "200": {
            "$ref": "#/components/responses/200VehicleTypes"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "TokenResponse": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "Token JWT para autenticación"
          },
          "expiresIn": {
            "type": "integer",
            "format": "int64",
            "description": "Timestamp de expiración del token (milisegundos desde epoch)"
          }
        }
      },
      "Address": {
        "type": "object",
        "description": "Representa una dirección física asociada a un transportista.\nContiene información de ubicación geográfica y datos postales.\n",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Identificador único de la dirección (ObjectId de MongoDB)",
            "example": "507f1f77bcf86cd799439011"
          },
          "name": {
            "type": "string",
            "description": "Nombre descriptivo de la dirección (ej. \"Oficina Central\", \"Almacén\")",
            "example": "Oficina Central"
          },
          "street": {
            "type": "string",
            "description": "Calle y número de la dirección",
            "example": "Calle Mayor 10"
          },
          "city": {
            "type": "string",
            "description": "Ciudad de la dirección",
            "example": "Madrid"
          },
          "state": {
            "type": "string",
            "description": "Estado o provincia de la dirección",
            "example": "Madrid"
          },
          "country": {
            "type": "string",
            "description": "País de la dirección (en formato texto completo)",
            "example": "España"
          },
          "postalCode": {
            "type": "string",
            "description": "Código postal de la dirección",
            "example": "28013"
          },
          "location": {
            "type": "object",
            "description": "Coordenadas geográficas de la dirección",
            "properties": {
              "lat": {
                "type": "number",
                "description": "Latitud en formato decimal",
                "example": 40.4168
              },
              "lng": {
                "type": "number",
                "description": "Longitud en formato decimal",
                "example": -3.7038
              }
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha de creación en formato ISO 8601",
            "example": "2025-01-15T10:30:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha de última actualización en formato ISO 8601",
            "example": "2025-01-15T10:30:00.000Z"
          }
        },
        "example": {
          "_id": "507f1f77bcf86cd799439011",
          "name": "Oficina Central",
          "street": "Calle Mayor 10",
          "city": "Madrid",
          "state": "Madrid",
          "country": "España",
          "postalCode": "28013",
          "location": {
            "lat": 40.4168,
            "lng": -3.7038
          },
          "createdAt": "2025-01-15T10:30:00.000Z",
          "updatedAt": "2025-01-15T10:30:00.000Z"
        }
      },
      "AddressList": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Address"
        }
      },
      "Auction": {
        "type": "object",
        "properties": {
          "service_code": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "published",
              "awarded",
              "approved",
              "completed"
            ]
          },
          "etl_date": {
            "type": "string",
            "format": "date-time"
          },
          "etd_date": {
            "type": "string",
            "format": "date-time"
          },
          "pallets_num": {
            "type": "integer"
          },
          "pallets_type": {
            "type": "string"
          },
          "cargo_weight": {
            "type": "number"
          },
          "cargo_height": {
            "type": "number"
          },
          "is_fresh": {
            "type": "boolean"
          },
          "fresh_cargo_temp": {
            "type": "number"
          },
          "start_price": {
            "type": "number"
          },
          "bid_current": {
            "type": "number"
          },
          "my_bid": {
            "type": "number"
          },
          "etl_address": {
            "$ref": "#/components/schemas/Address"
          },
          "etd_address": {
            "$ref": "#/components/schemas/Address"
          }
        }
      },
      "AuctionList": {
        "type": "object",
        "properties": {
          "docs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Auction"
            }
          },
          "totalDocs": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "page": {
            "type": "integer"
          }
        }
      },
      "Delivery": {
        "type": "object",
        "properties": {
          "service_code": {
            "type": "string",
            "description": "Código único de la entrega"
          },
          "status": {
            "type": "string",
            "description": "Estado actual de la entrega"
          },
          "etl_date": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha estimada de carga"
          },
          "etd_date": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha estimada de descarga"
          },
          "etl_address": {
            "type": "object",
            "properties": {
              "city": {
                "type": "string"
              },
              "zipcode": {
                "type": "string"
              },
              "province": {
                "type": "string"
              }
            }
          },
          "etd_address": {
            "type": "object",
            "properties": {
              "city": {
                "type": "string"
              },
              "zipcode": {
                "type": "string"
              },
              "province": {
                "type": "string"
              }
            }
          },
          "pallets_num": {
            "type": "integer",
            "description": "Número de pallets"
          },
          "cargo_weight": {
            "type": "number",
            "description": "Peso de la carga"
          },
          "can_message": {
            "type": "boolean",
            "description": "Si se pueden enviar mensajes"
          }
        }
      },
      "DeliveryList": {
        "type": "object",
        "properties": {
          "docs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Delivery"
            }
          },
          "total": {
            "type": "integer",
            "example": 100
          },
          "limit": {
            "type": "integer",
            "example": 10
          },
          "page": {
            "type": "integer",
            "example": 1
          },
          "pages": {
            "type": "integer",
            "example": 10
          }
        },
        "required": [
          "docs",
          "total",
          "limit",
          "page",
          "pages"
        ]
      },
      "Message": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string"
          },
          "photos": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "OilStation": {
        "type": "object",
        "description": "Representa una estación de servicio/gasolinera en el sistema.\nContiene toda la información necesaria para identificar y localizar la estación.\n",
        "properties": {
          "id": {
            "type": "string",
            "description": "Identificador único generado automáticamente por el sistema",
            "readOnly": true,
            "example": "5f8d0d55b54764421b7156da"
          },
          "name": {
            "type": "string",
            "description": "Nombre comercial de la estación de servicio",
            "example": "Estación Central"
          },
          "address": {
            "type": "string",
            "description": "Dirección postal completa de la estación",
            "example": "Calle Mayor 123"
          },
          "city": {
            "type": "string",
            "description": "Ciudad donde se encuentra la estación",
            "example": "Madrid"
          },
          "country": {
            "type": "string",
            "description": "Código de país ISO 3166-1 alpha-2 donde se encuentra la estación",
            "example": "ES"
          },
          "latitude": {
            "type": "number",
            "format": "double",
            "description": "Coordenada geográfica de latitud para ubicación precisa",
            "example": 40.4168
          },
          "longitude": {
            "type": "number",
            "format": "double",
            "description": "Coordenada geográfica de longitud para ubicación precisa",
            "example": -3.7038
          },
          "fuels": {
            "type": "array",
            "description": "Tipos de combustibles disponibles en la estación",
            "items": {
              "type": "string",
              "enum": [
                "diesel",
                "gasolina95",
                "gasolina98",
                "GLP",
                "electric"
              ],
              "example": "gasolina95"
            },
            "example": [
              "diesel",
              "gasolina95"
            ]
          },
          "isActive": {
            "type": "boolean",
            "description": "Indica si la estación está activa y disponible en el sistema",
            "example": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "readOnly": true,
            "description": "Fecha de creación (controlado por servidor)",
            "example": "2019-09-20T16:03:18.575000+00:00"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "readOnly": true,
            "description": "Fecha de última actualización (controlado por servidor)",
            "example": "2019-09-20T16:03:18.575000+00:00"
          },
          "deletedAt": {
            "type": "string",
            "format": "date-time",
            "readOnly": true,
            "description": "Fecha de eliminación (controlado por servidor, opcional)",
            "example": "2019-09-20T16:03:18.575000+00:00"
          }
        },
        "required": [
          "name",
          "address",
          "city",
          "country"
        ],
        "example": {
          "id": "5f8d0d55b54764421b7156da",
          "name": "Estación Central",
          "address": "Calle Mayor 123",
          "city": "Madrid",
          "country": "ES",
          "latitude": 40.4168,
          "longitude": -3.7038,
          "fuels": [
            "diesel",
            "gasolina95"
          ],
          "isActive": true
        }
      },
      "Trucker": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "ID único del transportista en formato MongoDB ObjectId",
            "example": "507f1f77bcf86cd799439011"
          },
          "name": {
            "type": "string",
            "description": "Nombre completo del transportista",
            "example": "Juan Pérez López"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email del transportista, debe ser único en el sistema",
            "example": "juan.perez@example.com"
          },
          "phone": {
            "type": "string",
            "description": "Número de teléfono del transportista",
            "example": "+34611223344"
          },
          "status": {
            "type": "boolean",
            "description": "Indica si la cuenta del transportista está activa (true) o suspendida (false)",
            "example": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha y hora de creación del registro en formato ISO 8601",
            "example": "2025-01-15T10:30:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha y hora de última actualización en formato ISO 8601",
            "example": "2025-01-20T15:45:00.000Z"
          },
          "taxid": {
            "type": "string",
            "description": "NIF/CIF del transportista, usado para verificación y facturación",
            "example": "X1234567Z"
          }
        }
      },
      "TruckerList": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Trucker"
        }
      },
      "Vehicle": {
        "type": "object",
        "description": "Representa un vehículo en el sistema con todos sus atributos.\nIncluye información básica, imágenes y metadatos de creación.\n",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Identificador único del vehículo (MongoDB ObjectId)",
            "example": "60a1b2c3d4e5f67890123456"
          },
          "licensePlate": {
            "type": "string",
            "description": "Matrícula del vehículo (única en el sistema)",
            "example": "1234ABC"
          },
          "type": {
            "type": "string",
            "description": "Tipo de vehículo (debe coincidir con los tipos registrados)",
            "example": "RIGIDO"
          },
          "brand": {
            "type": "string",
            "description": "Marca del vehículo",
            "example": "Volvo"
          },
          "model": {
            "type": "string",
            "description": "Modelo del vehículo",
            "example": "FH16"
          },
          "year": {
            "type": "integer",
            "description": "Año de fabricación del vehículo",
            "example": 2022
          },
          "images": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            },
            "description": "URLs de las imágenes del vehículo almacenadas en S3",
            "example": [
              "https://storage.example.com/vehicles/1234ABC.jpg"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha de creación del registro",
            "example": "2022-05-15T10:00:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha de última actualización",
            "example": "2022-05-15T10:00:00.000Z"
          }
        },
        "example": {
          "_id": "60a1b2c3d4e5f67890123456",
          "licensePlate": "1234ABC",
          "type": "RIGIDO",
          "brand": "Volvo",
          "model": "FH16",
          "year": 2022,
          "images": [
            "https://storage.example.com/vehicles/1234ABC.jpg"
          ],
          "createdAt": "2022-05-15T10:00:00.000Z",
          "updatedAt": "2022-05-15T10:00:00.000Z"
        }
      },
      "VehicleList": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Vehicle"
        }
      },
      "Bid": {
        "type": "object",
        "properties": {
          "service_code": {
            "type": "string"
          },
          "amount": {
            "type": "number"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "accepted",
              "rejected",
              "cancelled"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "notes": {
            "type": "string"
          }
        }
      },
      "Contract": {
        "type": "object",
        "properties": {
          "service_code": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "signed",
              "cancelled"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "pdf_url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "LoginRequest": {
        "type": "object",
        "required": [
          "email",
          "password"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email registrado del transportista. Debe ser una dirección válida.",
            "example": "transportista@ejemplo.com"
          },
          "password": {
            "type": "string",
            "format": "password",
            "description": "Contraseña del transportista. Mínimo 8 caracteres, debe contener al menos 1 mayúscula, 1 minúscula y 1 número.",
            "example": "Password123"
          }
        },
        "example": {
          "email": "transportista@ejemplo.com",
          "password": "Password123"
        }
      },
      "RegisterRequest": {
        "type": "object",
        "required": [
          "name",
          "email",
          "password",
          "phone",
          "taxid"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Nombre completo del transportista. Mínimo 3 caracteres.",
            "example": "Juan Pérez López"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email válido y único en el sistema. Se usará para comunicación y login.",
            "example": "transportista@ejemplo.com"
          },
          "password": {
            "type": "string",
            "format": "password",
            "description": "Contraseña segura con los siguientes requisitos:\n- Mínimo 8 caracteres\n- Al menos 1 mayúscula\n- Al menos 1 minúscula  \n- Al menos 1 número\n",
            "example": "Password123"
          },
          "password_confirm": {
            "type": "string",
            "format": "password",
            "description": "Confirmación de contraseña, debe coincidir exactamente con password",
            "example": "Password123"
          },
          "phone": {
            "type": "string",
            "description": "Número de teléfono válido con código de país",
            "example": "+34611223344"
          },
          "taxid": {
            "type": "string",
            "description": "NIF/CIF válido según el país del transportista",
            "example": "X1234567Z"
          }
        },
        "example": {
          "name": "Transportista Ejemplo",
          "email": "nuevo@transportista.com",
          "password": "Password123",
          "password_confirm": "Password123",
          "phone": "+34611223344",
          "taxid": "X1234567Z"
        }
      },
      "ProfileCompleteResponse": {
        "type": "object",
        "properties": {
          "complete": {
            "type": "boolean",
            "description": "Indica si el perfil del transportista está completo con todos los datos obligatorios.\n- true: Perfil completo, puede operar normalmente\n- false: Faltan datos obligatorios, necesita completar perfil\n",
            "example": true
          }
        },
        "example": {
          "complete": false
        }
      },
      "CategoryItem": {
        "type": "object",
        "description": "Estructura base para todos los tipos de categorías (secciones, capítulos, partidas, subpartidas). Contiene el código identificativo y la descripción normalizada.\n**Procesamiento aplicado**: - Títulos: Elimina prefijos como \"section\", \"chapter\", \"heading\", \"subheading\" - Labels: Combina múltiples etiquetas y limpia caracteres especiales",
        "properties": {
          "title": {
            "type": "string",
            "description": "Código identificativo normalizado de la categoría.\n\n**Transformaciones aplicadas**:\n- Elimina prefijos textuales (ej: \"section 01\" → \"01\")\n- Mantiene solo la parte numérica del código\n- Para partidas: 4 dígitos (ej: \"0101\")\n- Para subpartidas: 6 dígitos (ej: \"010121\")\n\n**Ejemplos**:\n- \"section 01\" → \"01\"\n- \"heading 0101\" → \"0101\"\n- \"subheading 010121\" → \"010121\"",
            "example": "010121"
          },
          "label": {
            "type": "string",
            "description": "Descripción legible para humanos, normalizada y limpia.\n\n**Transformaciones aplicadas**:\n- Combina múltiples etiquetas en un solo string\n- Elimina caracteres especiales como \"-\", \":\", \"--\"\n- Normaliza espacios (elimina múltiples espacios)\n- Convierte a minúsculas (excepto la primera letra)\n\n**Ejemplo de transformación**:\nOriginal: [\"Reproductores\", \"-\", \"de\", \"raza\", \"pura\"]\nResultado: \"Reproductores de raza pura\"\n\n**Casos de uso**:\n- Mostrar en interfaces de usuario\n- Búsqueda por texto\n- Generación de documentos",
            "example": "Reproductores de raza pura"
          }
        }
      },
      "CategorySection": {
        "type": "object",
        "description": "Estructura jerárquica completa de categorías de mercancías.\nRepresenta la relación padre-hijo entre secciones, capítulos, partidas y subpartidas.\n\n**Niveles jerárquicos**:\n1. Sección (nivel más alto)\n2. Capítulo (dentro de sección)\n3. Partida (4 dígitos, dentro de capítulo)\n4. Subpartida (6 dígitos, dentro de partida)\n\n**Notas**:\n- Todos los niveles excepto sección son opcionales\n- La estructura puede terminar en cualquier nivel\n- Cada nivel hereda las propiedades de CategoryItem\n",
        "properties": {}
      },
      "CategorySearchResult": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/CategoryItem"
        },
        "description": "Resultados de búsqueda que coinciden con el término proporcionado.\nOrdenados alfabéticamente por título y limitados a 20 resultados por tipo\n(partidas + subpartidas). Cada item contiene título y label normalizados.",
        "example": [
          {
            "title": "0101",
            "label": "Caballos asnos mulas y burdéganos vivos"
          },
          {
            "title": "010121",
            "label": "Reproductores de raza pura"
          }
        ]
      },
      "AddressGoogleMaps": {
        "type": "object",
        "description": "Objeto de dirección de Google Maps para geocodificación.\nSe utiliza para normalizar y geolocalizar direcciones físicas.\n",
        "properties": {
          "formatted_address": {
            "type": "string",
            "description": "Dirección completa formateada según Google Maps",
            "example": "Calle Ejemplo 123, 28001 Madrid, España"
          },
          "place_id": {
            "type": "string",
            "description": "Identificador único de lugar en Google Maps",
            "example": "ChIJ1234567890"
          },
          "geometry": {
            "type": "object",
            "description": "Información geográfica de la dirección",
            "properties": {
              "location": {
                "type": "object",
                "description": "Coordenadas geográficas del lugar",
                "properties": {
                  "lat": {
                    "type": "number",
                    "description": "Latitud en formato decimal",
                    "example": 40.4168
                  },
                  "lng": {
                    "type": "number",
                    "description": "Longitud en formato decimal",
                    "example": -3.7038
                  }
                }
              }
            }
          }
        },
        "example": {
          "formatted_address": "Calle Ejemplo 123, 28001 Madrid, España",
          "place_id": "ChIJ1234567890",
          "geometry": {
            "location": {
              "lat": 40.4168,
              "lng": -3.7038
            }
          }
        }
      },
      "TruckerCiaDetail": {
        "type": "object",
        "description": "Modelo completo de datos de una compañía de transportistas.\nIncluye información básica, dirección y datos bancarios.\n",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Identificador único de la compañía en la base de datos",
            "example": "507f1f77bcf86cd799439011"
          },
          "name": {
            "type": "string",
            "description": "Nombre legal de la compañía",
            "example": "Transportes Ejemplo S.L."
          },
          "taxId": {
            "type": "string",
            "description": "Identificador fiscal de la compañía (CIF/NIF)",
            "example": "B12345678"
          },
          "email": {
            "type": "string",
            "description": "Email de contacto principal",
            "example": "contacto@transportes.com"
          },
          "phone": {
            "type": "string",
            "description": "Teléfono de contacto con código de país",
            "example": "+34911234567"
          },
          "address": {
            "type": "object",
            "description": "Dirección física completa de la compañía",
            "properties": {
              "street": {
                "type": "string",
                "description": "Calle y número",
                "example": "Calle Ejemplo 123"
              },
              "city": {
                "type": "string",
                "description": "Ciudad",
                "example": "Madrid"
              },
              "state": {
                "type": "string",
                "description": "Provincia/Estado",
                "example": "Madrid"
              },
              "postalCode": {
                "type": "string",
                "description": "Código postal",
                "example": "28001"
              },
              "country": {
                "type": "string",
                "description": "País",
                "example": "España"
              },
              "coordinates": {
                "type": "array",
                "description": "Coordenadas geográficas [longitud, latitud]",
                "items": {
                  "type": "number"
                },
                "example": [
                  40.4168,
                  -3.7038
                ]
              }
            }
          },
          "bank": {
            "type": "string",
            "description": "Últimos 4 dígitos de la cuenta bancaria (formato enmascarado)",
            "example": "****1234"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha de creación del registro",
            "example": "2023-01-01T00:00:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha de última actualización",
            "example": "2023-01-15T12:30:45.000Z"
          }
        },
        "example": {
          "_id": "507f1f77bcf86cd799439011",
          "name": "Transportes Ejemplo S.L.",
          "taxId": "B12345678",
          "email": "contacto@transportes.com",
          "phone": "+34911234567",
          "address": {
            "street": "Calle Ejemplo 123",
            "city": "Madrid",
            "state": "Madrid",
            "postalCode": "28001",
            "country": "España",
            "coordinates": [
              40.4168,
              -3.7038
            ]
          },
          "bank": "****1234",
          "createdAt": "2023-01-01T00:00:00.000Z",
          "updatedAt": "2023-01-15T12:30:45.000Z"
        }
      },
      "TruckerCiaEditRequest": {
        "type": "object",
        "description": "Modelo de datos para actualizar una compañía de transportistas.\nTodos los campos son opcionales, solo se actualizarán los campos proporcionados.\n",
        "properties": {
          "name": {
            "type": "string",
            "description": "Nuevo nombre legal de la compañía",
            "example": "Transportes Actualizados S.L."
          },
          "taxId": {
            "type": "string",
            "description": "Nuevo identificador fiscal (CIF/NIF)",
            "example": "B87654321"
          },
          "email": {
            "type": "string",
            "description": "Nuevo email de contacto principal",
            "example": "nuevo@transportes.com"
          },
          "phone": {
            "type": "string",
            "description": "Nuevo teléfono con código de país",
            "example": "+34987654321"
          },
          "addressGoogleMaps": {
            "$ref": "#/components/schemas/AddressGoogleMaps",
            "description": "Objeto de dirección de Google Maps para geocodificación.\nSi se proporciona, se usará para actualizar la dirección física.\n"
          },
          "bank": {
            "type": "string",
            "description": "Número completo de cuenta bancaria.\n- Se almacenan solo los últimos 4 dígitos (****1234)\n- Si el valor contiene asteriscos (****1234), se ignorará\n- Requiere formato válido según el país\n",
            "example": "ES9121000418450200051332"
          }
        },
        "example": {
          "name": "Transportes Actualizados S.L.",
          "taxId": "B87654321",
          "email": "nuevo@transportes.com",
          "phone": "+34987654321",
          "addressGoogleMaps": {
            "formatted_address": "Calle Nueva 456, Madrid, España",
            "place_id": "ChIJ1234567890",
            "geometry": {
              "location": {
                "lat": 40.4168,
                "lng": -3.7038
              }
            }
          },
          "bank": "ES9121000418450200051332"
        }
      },
      "ContractList": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Contract"
        }
      },
      "Country": {
        "type": "object",
        "description": "Representa un país en el sistema con sus propiedades básicas\ny reglas de validación para identificadores fiscales.\n",
        "properties": {
          "code": {
            "type": "string",
            "description": "Código ISO 3166-1 alpha-2 del país (2 letras mayúsculas).\nEste campo es único y actúa como identificador principal.\n",
            "minLength": 2,
            "maxLength": 2,
            "pattern": "^[A-Z]{2}$",
            "example": "ES"
          },
          "name": {
            "type": "string",
            "description": "Nombre completo del país en español.\nDebe ser único en el sistema.\n",
            "minLength": 3,
            "maxLength": 100,
            "example": "España"
          },
          "regex": {
            "type": "string",
            "description": "Patrón de expresión regular para validar identificadores fiscales\n(NIF, CIF, VAT) del país. Sigue la sintaxis estándar de regex.\n",
            "example": "^[A-Z0-9]{9}$"
          },
          "enabled": {
            "type": "boolean",
            "description": "Indica si el país está habilitado para operaciones.\nLos países deshabilitados no aparecen en listados públicos.\n",
            "default": true,
            "example": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha y hora de creación del registro en formato ISO 8601.\nGenerado automáticamente por el sistema.\n",
            "example": "2025-01-15T10:30:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha y hora de última actualización en formato ISO 8601.\nActualizado automáticamente por el sistema.\n",
            "example": "2025-01-20T14:45:00.000Z"
          }
        },
        "required": [
          "code",
          "name",
          "regex"
        ]
      },
      "CountryList": {
        "type": "object",
        "description": "Respuesta paginada de países que incluye metadatos de paginación\ny la lista de países solicitada.\n\n**Estructura típica de respuesta:**\n```json\n{\n  \"docs\": [...],\n  \"total\": 50,\n  \"limit\": 10,\n  \"page\": 1,\n  \"pages\": 5\n}\n```\n",
        "properties": {
          "docs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Country"
            },
            "description": "Array con los países solicitados.\nPuede estar vacío si no hay resultados.\n"
          },
          "total": {
            "type": "integer",
            "description": "Número total de países que coinciden con la consulta,\nindependientemente de la paginación.\n",
            "example": 50
          },
          "limit": {
            "type": "integer",
            "description": "Número máximo de países devueltos por página.\nCoincide con el parámetro `limit` de la consulta o usa el valor por defecto.\n",
            "example": 10
          },
          "page": {
            "type": "integer",
            "description": "Número de página actual (1-based).\nCoincide con el parámetro `page` de la consulta.\n",
            "example": 1
          },
          "pages": {
            "type": "integer",
            "description": "Número total de páginas disponibles según el total de resultados\ny el límite por página.\n",
            "example": 5
          }
        },
        "required": [
          "docs",
          "total",
          "limit",
          "page",
          "pages"
        ]
      },
      "Notification": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Identificador único de la notificación",
            "example": "5f8d3b7a9d6c4a2b1e0f9d8c"
          },
          "type": {
            "type": "string",
            "description": "Tipo de notificación (alert, message, update)",
            "example": "alert"
          },
          "message": {
            "type": "string",
            "description": "Contenido textual de la notificación",
            "example": "Nueva carga disponible en Barcelona"
          },
          "read": {
            "type": "boolean",
            "description": "Indica si la notificación ha sido marcada como leída",
            "example": false
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha y hora de creación de la notificación (ISO 8601)",
            "example": "2025-08-10T14:30:22.000Z"
          }
        }
      },
      "DeliveryDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Delivery"
          },
          {
            "type": "object",
            "properties": {
              "info_extra": {
                "type": "string"
              },
              "pallets_type": {
                "type": "string"
              },
              "cargo_type": {
                "type": "string"
              },
              "cargo_height": {
                "type": "number"
              },
              "is_fresh": {
                "type": "boolean"
              },
              "fresh_cargo_temp": {
                "type": "number"
              },
              "description": {
                "type": "string"
              },
              "hscode": {
                "type": "string"
              },
              "company": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "contact_person": {
                    "type": "object",
                    "properties": {
                      "phone": {
                        "type": "string"
                      },
                      "email": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "TrackingPosition": {
        "type": "object",
        "properties": {
          "service_code": {
            "type": "string"
          },
          "location": {
            "type": "object",
            "properties": {
              "lat": {
                "type": "number"
              },
              "lng": {
                "type": "number"
              }
            }
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TrackableDelivery": {
        "type": "object",
        "properties": {
          "service_code": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "etl_address": {
            "type": "object",
            "properties": {}
          },
          "etd_address": {
            "type": "object",
            "properties": {}
          }
        }
      },
      "Note": {
        "type": "object",
        "properties": {
          "content": {
            "type": "string"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ETAChangeRequest": {
        "type": "object",
        "properties": {
          "date_eta": {
            "type": "string",
            "format": "date-time",
            "description": "Nueva fecha estimada de llegada"
          },
          "extra_load": {
            "type": "boolean",
            "description": "Si hay carga extra"
          },
          "tank_level": {
            "type": "number",
            "description": "Nivel de combustible (0-100)"
          },
          "service_code": {
            "type": "string",
            "description": "Código de servicio de la entrega"
          }
        }
      },
      "EstimatedETAResponse": {
        "type": "object",
        "properties": {
          "etlDate": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha estimada de carga"
          },
          "service_code": {
            "type": "string"
          },
          "timeCost": {
            "type": "number",
            "description": "Tiempo estimado en minutos"
          },
          "fuelCost": {
            "type": "number",
            "description": "Coste estimado de combustible"
          },
          "distance": {
            "type": "number",
            "description": "Distancia estimada en km"
          },
          "eta": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha estimada de llegada"
          }
        }
      },
      "DGTRecord": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "description": "ID único del registro"
          },
          "dgt_code": {
            "type": "string",
            "description": "Código DGT"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "resolved"
            ],
            "description": "Estado del registro"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha de creación"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha de última actualización"
          }
        }
      },
      "DGTList": {
        "type": "object",
        "properties": {
          "docs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DGTRecord"
            }
          },
          "total": {
            "type": "integer",
            "description": "Total de registros"
          },
          "limit": {
            "type": "integer",
            "description": "Límite por página"
          },
          "page": {
            "type": "integer",
            "description": "Página actual"
          },
          "pages": {
            "type": "integer",
            "description": "Total de páginas"
          }
        }
      },
      "DGTCreateRequest": {
        "type": "object",
        "description": "Request para crear un nuevo registro DGT",
        "properties": {
          "dgt_code": {
            "type": "string",
            "description": "Código DGT proporcionado por la DGT.\n\nEste código se usará para:\n- Crear el registro DGT\n- Asociar todas las entregas en estado 'collected' del usuario\n\n**Nota:** Si no hay entregas en estado 'collected',\nse crea el registro con deliveries vacío.\n",
            "example": "DGT2023XYZ123"
          }
        },
        "required": [
          "dgt_code"
        ]
      },
      "DGTEditRequest": {
        "type": "object",
        "description": "Request para actualizar un registro DGT existente.\n\n**Nota importante sobre el parámetro `code`:**\n- El controlador lee el parámetro `code` del request\n- Este valor se asigna al campo `dgt_code` del modelo\n- El valor por defecto de `status` es \"resolved\" si no se proporciona\n\n**Campos opcionales:**\n- Ambos campos son opcionales\n- Al menos uno debe proporcionarse para realizar la actualización\n",
        "properties": {
          "code": {
            "type": "string",
            "description": "Nuevo código DGT.\n\n**Importante:** Este parámetro se llama `code` en el request\npero se asigna al campo `dgt_code` del modelo.\n\nEl controlador hace:\n`const updateData = { status: status, dgt_code: code };`\n",
            "example": "DGT2023XYZ456"
          },
          "status": {
            "type": "string",
            "description": "Nuevo estado del registro DGT",
            "enum": [
              "pending",
              "resolved"
            ],
            "default": "resolved",
            "example": "resolved"
          }
        }
      },
      "DGTEmailRequest": {
        "type": "object",
        "description": "Request para enviar documentos DGT por email",
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "Dirección de correo electrónico de destino.\n\nDebe ser una dirección de email válida.\nEl idioma del email se determina por la preferencia i18n del usuario.\n",
            "example": "destinatario@empresa.com"
          },
          "dgt_code": {
            "type": "string",
            "description": "Código DGT existente del usuario autenticado.\n\nEl registro DGT debe existir y pertenecer al usuario autenticado.\nSe buscan las entregas asociadas para generar los documentos.\n",
            "example": "DGT2023XYZ123"
          }
        },
        "required": [
          "email",
          "dgt_code"
        ]
      },
      "Driver": {
        "type": "object",
        "description": "Representa un conductor asociado a una compañía de transportistas.\nContiene toda la información del perfil del conductor y su estado en el sistema.\n",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Identificador único del conductor en el sistema.\nGenerado automáticamente al crear el conductor.\n",
            "example": "507f1f77bcf86cd799439011"
          },
          "name": {
            "type": "string",
            "description": "Nombre completo del conductor",
            "minLength": 3,
            "maxLength": 100,
            "example": "Juan Pérez López"
          },
          "email": {
            "type": "string",
            "description": "Correo electrónico único del conductor.\nSe utiliza para notificaciones y acceso al sistema.\n",
            "format": "email",
            "example": "juan.perez@transporte.com"
          },
          "phone": {
            "type": "string",
            "description": "Teléfono de contacto del conductor.\nFormato internacional recomendado.\n",
            "example": "+34666555444"
          },
          "image": {
            "type": "string",
            "description": "URL de la foto de perfil del conductor.\nAlmacenada en el servicio de almacenamiento en la nube.\n",
            "format": "uri",
            "example": "https://bucket.s3.amazonaws.com/drivers/507f1f77bcf86cd799439011.jpg"
          },
          "status": {
            "type": "string",
            "description": "Estado actual del conductor en el sistema.\nDetermina qué operaciones puede realizar.\n",
            "enum": [
              "active",
              "inactive",
              "pending"
            ],
            "example": "active"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha y hora de creación del registro.\nFormato ISO 8601.\n",
            "example": "2025-01-15T10:30:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha y hora de la última actualización.\nActualizado automáticamente en cada modificación.\n",
            "example": "2025-03-20T14:15:22.000Z"
          }
        }
      },
      "DriverList": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Driver"
        }
      },
      "DriverStatusRequest": {
        "type": "object",
        "description": "Esquema para cambiar el estado operacional de un conductor.\n\nEste objeto controla el acceso y permisos del conductor en el sistema.\nEl campo `reason` determina automáticamente el valor booleano del campo `status`.\n\n**Lógica de negocio:**\n- `reason = \"NONE\"` → `status = true` (conductor habilitado)\n- Cualquier otro `reason` → `status = false` (conductor deshabilitado)\n\n**Auditoría:**\nLos campos opcionales (`reasonDate`, `reasonMessage`) permiten documentar\nel motivo y contexto del cambio de estado para fines de auditoría.\n",
        "required": [
          "id",
          "reason"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "ID único del conductor (MongoDB ObjectId).\nTambién se acepta como `_id` en el body o query parameters.\n",
            "pattern": "^[0-9a-fA-F]{24}$",
            "example": "507f1f77bcf86cd799439011"
          },
          "reason": {
            "type": "string",
            "description": "Estado operacional del conductor que determina sus permisos de acceso.\n\n**Valores válidos:**\n- `NONE`: Conductor activo sin restricciones (status=true)\n- `PENDING`: En proceso de validación/aprobación (status=false)\n- `ACTIVE`: Aprobado pero temporalmente inactivo (status=false)\n- `BLOCKED`: Bloqueado por incumplimiento de políticas (status=false)\n- `BAD_USER`: Usuario problemático, acceso permanentemente denegado (status=false)\n\n**Solo `NONE` habilita completamente al conductor.**\n",
            "enum": [
              "NONE",
              "PENDING",
              "ACTIVE",
              "BLOCKED",
              "BAD_USER"
            ],
            "example": "BLOCKED"
          },
          "reasonDate": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha asociada al cambio de estado (opcional).\n\n**Casos de uso:**\n- Fecha de vencimiento de documentación\n- Fecha de inicio de suspensión\n- Fecha límite para resolver el problema\n\n**Formato:** ISO 8601 (ej. 2025-10-29T10:30:00Z)\n\n**Nota:** Se limpia automáticamente si `reason = \"NONE\"`\n",
            "example": "2025-10-29T10:30:00Z"
          },
          "reasonMessage": {
            "type": "string",
            "description": "Mensaje descriptivo del motivo del cambio (opcional).\n\n**Buenas prácticas:**\n- Ser específico sobre el problema\n- Incluir acciones requeridas para resolverlo\n- Indicar documentación o requisitos faltantes\n- Mantener tono profesional y constructivo\n\n**Ejemplos:**\n- \"Licencia de conducir vencida. Renovar antes del 2025-12-31\"\n- \"Pendiente de verificación de antecedentes penales\"\n- \"Múltiples quejas de clientes. Suspensión temporal por 30 días\"\n\n**Nota:** Se limpia automáticamente si `reason = \"NONE\"`\n",
            "maxLength": 500,
            "example": "Licencia de conducir vencida. Renovar antes del 2025-12-31"
          }
        }
      },
      "FeeResponse": {
        "type": "object",
        "properties": {
          "fee": {
            "type": "number",
            "format": "float",
            "description": "Porcentaje de tarifa aplicable a las transacciones del transportista.\nEste valor se obtiene de la configuración más reciente del sistema (Settings)\no usa el valor por defecto de 6% si no hay configuración.\n",
            "example": 6
          }
        },
        "x-examples": {
          "default": {
            "value": {
              "fee": 6
            }
          },
          "premium": {
            "value": {
              "fee": 5.5
            }
          },
          "basic": {
            "value": {
              "fee": 7
            }
          }
        },
        "example": {
          "fee": 6.5
        }
      },
      "HSFindResult": {
        "type": "array",
        "description": "Representa los resultados de una búsqueda en el sistema HS.\nPuede incluir headings y subheadings que coincidan con el término buscado.\n",
        "items": {
          "type": "object",
          "description": "Elemento individual de resultado de búsqueda",
          "properties": {
            "title": {
              "type": "string",
              "description": "Código HS del resultado (ej. \"0101\" para heading, \"0101.21\" para subheading).\nPermite identificar el nivel de clasificación.\n",
              "example": "0101.21"
            },
            "label": {
              "type": "string",
              "description": "Descripción completa del resultado.\nProporciona contexto sobre los productos incluidos.\n",
              "example": "Caballos para reproducción"
            },
            "url": {
              "type": "string",
              "description": "URL completa para acceder al detalle de este código HS.\nPuede usarse para navegación directa en la jerarquía.\n",
              "example": "/hs/I/01/0101/0101.21"
            }
          }
        }
      },
      "Invoice": {
        "type": "object",
        "required": [
          "number",
          "date",
          "amount",
          "status",
          "truckerId"
        ],
        "properties": {
          "_id": {
            "type": "string",
            "description": "ID único de MongoDB generado automáticamente.\nFormato: ObjectId hexadecimal de 24 caracteres.\n",
            "example": "507f1f77bcf86cd799439011"
          },
          "number": {
            "type": "string",
            "description": "Número único de factura.\nFormato libre pero debe ser único en el sistema.\n",
            "minLength": 3,
            "maxLength": 50,
            "example": "FAC-2023-001"
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "Fecha de emisión de la factura.\nFormato: YYYY-MM-DD (ISO 8601)\n",
            "example": "2023-05-15T00:00:00.000Z"
          },
          "amount": {
            "type": "number",
            "format": "float",
            "description": "Importe total de la factura.\nMínimo: 0.01\nMáximo: 9999999.99\n",
            "minimum": 0.01,
            "maximum": 9999999.99,
            "example": 1250.5
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "issued",
              "paid",
              "cancelled"
            ],
            "description": "Estado actual de la factura.\n- draft: Borrador (editable)\n- issued: Emitida (no editable)\n- paid: Pagada\n- cancelled: Cancelada\n",
            "example": "issued"
          },
          "truckerId": {
            "type": "string",
            "description": "ID del transportista dueño de la factura.\nDebe coincidir con el JWT de autenticación.\n",
            "example": "507f1f77bcf86cd799439011"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha de creación automática en el sistema.\nFormato: ISO 8601 (YYYY-MM-DDTHH:MM:SSZ)\n",
            "example": "2023-05-15T10:30:00.000Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha de última actualización.\nActualizada automáticamente al modificar la factura.\n",
            "example": "2023-05-16T08:45:00.000Z"
          }
        },
        "example": {
          "_id": "507f1f77bcf86cd799439011",
          "number": "FAC-2023-001",
          "date": "2023-05-15T00:00:00.000Z",
          "amount": 1250.5,
          "status": "issued",
          "truckerId": "507f1f77bcf86cd799439011",
          "createdAt": "2023-05-15T10:30:00.000Z",
          "updatedAt": "2023-05-16T08:45:00.000Z"
        }
      },
      "InvoiceResponse": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Invoice"
        }
      },
      "Station": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "location": {
            "type": "object",
            "properties": {
              "lat": {
                "type": "number",
                "format": "float"
              },
              "lng": {
                "type": "number",
                "format": "float"
              }
            }
          },
          "services": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "rating": {
            "type": "number",
            "format": "float",
            "minimum": 0,
            "maximum": 5
          }
        }
      },
      "Parking": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "location": {
            "type": "object",
            "properties": {
              "lat": {
                "type": "number",
                "format": "float"
              },
              "lng": {
                "type": "number",
                "format": "float"
              }
            }
          },
          "capacity": {
            "type": "integer"
          },
          "price": {
            "type": "number",
            "format": "float"
          },
          "rating": {
            "type": "number",
            "format": "float",
            "minimum": 0,
            "maximum": 5
          }
        }
      },
      "MinimalAuctionResponse": {
        "type": "object",
        "description": "Respuesta con datos mínimos de una subasta de transporte",
        "properties": {
          "distance": {
            "type": "number",
            "format": "float",
            "description": "Distancia estimada en kilómetros (ruta óptima)",
            "example": 450.5
          },
          "duration": {
            "type": "number",
            "format": "float",
            "description": "Duración estimada en minutos (incluye tiempos de espera)",
            "example": 320.25
          },
          "costs": {
            "$ref": "#/components/schemas/CostBreakdown"
          }
        },
        "example": {
          "distance": 450.5,
          "duration": 320.25,
          "costs": {
            "fuel": 285.75,
            "tolls": 120.5,
            "total": 406.25
          }
        }
      },
      "CostBreakdown": {
        "type": "object",
        "description": "Desglose de costos estimados",
        "properties": {
          "fuel": {
            "type": "number",
            "format": "float",
            "description": "Costo estimado de combustible en euros",
            "example": 285.75
          },
          "tolls": {
            "type": "number",
            "format": "float",
            "description": "Costo estimado de peajes en euros",
            "example": 120.5
          },
          "total": {
            "type": "number",
            "format": "float",
            "description": "Costo total estimado (combustible + peajes)",
            "example": 406.25
          }
        }
      },
      "CostRequest": {
        "type": "object",
        "properties": {
          "origin": {
            "type": "object",
            "properties": {
              "zipCode": {
                "type": "string",
                "description": "Código postal de origen"
              },
              "countryCode": {
                "type": "string",
                "description": "Código de país (ISO 2 letras)"
              }
            }
          },
          "destiny": {
            "type": "object",
            "properties": {
              "zipCode": {
                "type": "string",
                "description": "Código postal de destino"
              },
              "countryCode": {
                "type": "string",
                "description": "Código de país (ISO 2 letras)"
              }
            }
          },
          "vehicle": {
            "type": "string",
            "description": "Tipo de vehículo (opcional)"
          },
          "hscode": {
            "type": "string",
            "description": "Código HS (opcional)"
          }
        }
      },
      "CostResponse": {
        "type": "object",
        "description": "Respuesta con cálculo de costos de transporte",
        "properties": {
          "distance": {
            "type": "number",
            "format": "float",
            "description": "Distancia calculada en kilómetros",
            "example": 325.75
          },
          "duration": {
            "type": "number",
            "format": "float",
            "description": "Duración estimada en minutos",
            "example": 245.5
          },
          "costs": {
            "$ref": "#/components/schemas/CostBreakdown"
          }
        },
        "example": {
          "distance": 325.75,
          "duration": 245.5,
          "costs": {
            "fuel": 210.25,
            "tolls": 85.3,
            "total": 295.55
          }
        }
      },
      "PaymentUrl": {
        "type": "object",
        "properties": {
          "id_plan": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "price": {
            "type": "number"
          },
          "url": {
            "type": "string",
            "description": "URL de checkout de Stripe"
          }
        }
      },
      "LegalDocument": {
        "type": "object",
        "description": "Documento legal requerido por la plataforma (términos, privacidad, etc.).\nCada documento puede tener múltiples versiones por idioma.\n",
        "properties": {
          "code": {
            "type": "string",
            "description": "Código único identificador del documento (ej. \"terms\", \"privacy\")",
            "example": "privacy"
          },
          "lang": {
            "type": "string",
            "description": "Código de idioma del documento (ej. \"es\", \"en\")",
            "example": "es"
          },
          "name": {
            "type": "string",
            "description": "Nombre legible del documento",
            "example": "Política de Privacidad"
          },
          "text": {
            "type": "string",
            "description": "Contenido completo del documento en formato HTML/texto",
            "example": "<h1>Política de Privacidad</h1><p>Texto completo...</p>"
          },
          "visible": {
            "type": "boolean",
            "description": "Indica si el documento está visible para los usuarios",
            "example": true
          }
        },
        "example": {
          "code": "privacy",
          "lang": "es",
          "name": "Política de Privacidad",
          "text": "<h1>Política de Privacidad</h1><p>Protección de datos personales...</p>",
          "visible": true
        }
      },
      "relatedTo": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "example": "delivery"
          },
          "value": {
            "type": "string",
            "example": "delivery"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "description": "Respuesta de error estándar",
        "properties": {
          "status": {
            "type": "boolean",
            "description": "Indica si la petición fue exitosa (false para errores)",
            "example": false
          },
          "message": {
            "type": "string",
            "description": "Código o mensaje de error",
            "example": "NOT_FOUND"
          }
        },
        "required": [
          "status",
          "message"
        ]
      },
      "Coordinates": {
        "type": "object",
        "description": "Coordenadas geográficas",
        "properties": {
          "lat": {
            "type": "number",
            "format": "float",
            "description": "Latitud en formato WGS84",
            "example": 40.416775
          },
          "lng": {
            "type": "number",
            "format": "float",
            "description": "Longitud en formato WGS84",
            "example": -3.70379
          }
        },
        "required": [
          "lat",
          "lng"
        ],
        "example": {
          "lat": 40.416775,
          "lng": -3.70379
        }
      },
      "FuelPrices": {
        "type": "object",
        "description": "Precios de combustible por tipo",
        "properties": {
          "diesel": {
            "type": "number",
            "format": "float",
            "description": "Precio del diésel por litro en EUR",
            "example": 1.45
          },
          "gasoline95": {
            "type": "number",
            "format": "float",
            "description": "Precio de gasolina 95 octanos por litro en EUR",
            "example": 1.55
          },
          "gasoline98": {
            "type": "number",
            "format": "float",
            "description": "Precio de gasolina 98 octanos por litro en EUR",
            "example": 1.65
          },
          "adblue": {
            "type": "number",
            "format": "float",
            "description": "Precio del AdBlue por litro en EUR",
            "example": 0.85
          },
          "lpg": {
            "type": "number",
            "format": "float",
            "description": "Precio del GLP por litro en EUR",
            "example": 0.95
          }
        },
        "example": {
          "diesel": 1.45,
          "gasoline95": 1.55,
          "gasoline98": 1.65
        }
      },
      "FuelStation": {
        "type": "object",
        "description": "Información de una estación de servicio",
        "properties": {
          "id": {
            "type": "string",
            "description": "Identificador único de la estación",
            "example": "station_001"
          },
          "name": {
            "type": "string",
            "description": "Nombre de la estación de servicio",
            "example": "Estación de Servicio Madrid Norte"
          },
          "location": {
            "$ref": "#/components/schemas/Coordinates"
          },
          "distance": {
            "type": "number",
            "format": "float",
            "description": "Distancia desde el punto de referencia en kilómetros",
            "example": 2.5
          },
          "fuelPrices": {
            "$ref": "#/components/schemas/FuelPrices"
          },
          "services": {
            "type": "array",
            "description": "Servicios disponibles en la estación",
            "items": {
              "type": "string"
            },
            "example": [
              "24h",
              "restaurante",
              "parking"
            ]
          },
          "brand": {
            "type": "string",
            "description": "Marca o cadena de la estación",
            "example": "Repsol"
          },
          "openingHours": {
            "type": "string",
            "description": "Horario de atención",
            "example": "24/7"
          }
        },
        "required": [
          "id",
          "name",
          "location",
          "distance",
          "fuelPrices",
          "services"
        ],
        "example": {
          "id": "station_001",
          "name": "Estación de Servicio Madrid Norte",
          "location": {
            "lat": 40.416775,
            "lng": -3.70379
          },
          "distance": 2.5,
          "fuelPrices": {
            "diesel": 1.45,
            "gasoline95": 1.55,
            "gasoline98": 1.65
          },
          "services": [
            "24h",
            "restaurante",
            "parking"
          ],
          "brand": "Repsol",
          "openingHours": "24/7"
        }
      },
      "FuelType": {
        "type": "object",
        "description": "Tipo de combustible disponible",
        "properties": {
          "code": {
            "type": "string",
            "description": "Código identificador del combustible",
            "example": "diesel"
          },
          "name": {
            "type": "string",
            "description": "Nombre del combustible",
            "example": "Gasóleo A"
          },
          "description": {
            "type": "string",
            "description": "Descripción detallada del combustible",
            "example": "Diésel para vehículos pesados"
          }
        },
        "required": [
          "code",
          "name",
          "description"
        ],
        "example": {
          "code": "diesel",
          "name": "Gasóleo A",
          "description": "Diésel para vehículos pesados"
        }
      },
      "UserMinimal": {
        "type": "object",
        "description": "Usuario transportista minimal",
        "properties": {
          "_id": {
            "type": "string",
            "description": "ID único del usuario",
            "example": "507f1f77bcf86cd799439011"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email del usuario",
            "example": "usuario@ejemplo.com"
          },
          "name": {
            "type": "string",
            "description": "Nombre del usuario",
            "example": "Juan"
          }
        }
      },
      "TruckerUserEdit": {
        "type": "object",
        "description": "Datos para editar el perfil del usuario actual",
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "Nuevo email del usuario",
            "example": "nuevo@email.com"
          },
          "name": {
            "type": "string",
            "description": "Nuevo nombre del usuario",
            "minLength": 1,
            "maxLength": 100,
            "example": "Juan"
          },
          "lastname": {
            "type": "string",
            "description": "Nuevo apellido del usuario",
            "minLength": 1,
            "maxLength": 100,
            "example": "Pérez"
          },
          "phone": {
            "type": "string",
            "description": "Nuevo teléfono del usuario",
            "minLength": 9,
            "maxLength": 15,
            "example": "+34666555444"
          },
          "taxid": {
            "type": "string",
            "description": "Nueva identificación fiscal",
            "minLength": 6,
            "maxLength": 9,
            "example": "12345678A"
          },
          "i18n": {
            "type": "string",
            "description": "Nuevo código de idioma",
            "enum": [
              "es",
              "en"
            ],
            "example": "es"
          },
          "image": {
            "type": "string",
            "format": "binary",
            "description": "Nueva imagen de perfil (multipart/form-data)"
          }
        },
        "example": {
          "email": "nuevo@email.com",
          "name": "Juan",
          "lastname": "Pérez",
          "phone": "+34666555444"
        }
      },
      "TicketReason": {
        "type": "object",
        "description": "Motivo disponible para crear tickets de soporte",
        "properties": {
          "code": {
            "type": "string",
            "description": "Código identificador del motivo",
            "example": "auction"
          },
          "label": {
            "type": "string",
            "description": "Etiqueta legible del motivo",
            "example": "Subasta"
          },
          "description": {
            "type": "string",
            "description": "Descripción detallada del motivo",
            "example": "Problemas con subastas de carga"
          }
        },
        "required": [
          "code",
          "label"
        ]
      },
      "ServiceCode": {
        "type": "string",
        "description": "Código único de servicio para operaciones (entregas o subastas)",
        "example": "DEL-2023-001234",
        "pattern": "^[A-Z]{3}-[0-9]{4}-[0-9]{6}$"
      },
      "TicketMessage": {
        "type": "object",
        "description": "Mensaje de ticket para añadir a ticket existente",
        "required": [
          "_id",
          "message"
        ],
        "properties": {
          "_id": {
            "type": "string",
            "description": "ID del ticket al que se añade el mensaje",
            "example": "68fb76e8ac704f79dd2e4fcb"
          },
          "message": {
            "type": "string",
            "description": "Contenido del mensaje",
            "example": "Adjunto fotos de los daños en la mercancía"
          }
        }
      },
      "RelatedTo": {
        "type": "string",
        "description": "Código de operación relacionada (entrega o subasta).\nValores permitidos:\n- config: Problemas con configuración de cuenta o perfil\n- contract: Consultas o problemas con contratos\n- address: Errores en direcciones de entrega\n- document: Problemas con documentos subidos\n- delivery: Incidencias en entregas/procesos logísticos\n- auction: Problemas con subastas de carga\n- messages: Consultas sobre mensajes/conversaciones\n- user: Problemas con otros usuarios\n- bid: Consultas sobre ofertas/pujas\n- others: Otros motivos no categorizados\n",
        "enum": [
          "config",
          "contract",
          "address",
          "document",
          "delivery",
          "auction",
          "messages",
          "user",
          "bid",
          "others"
        ],
        "example": "auction"
      },
      "Ticket": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "resolved",
              "closed",
              "cancelByClient"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "nameAuthor": {
            "type": "string"
          },
          "relatedTo": {
            "type": "string"
          }
        }
      },
      "TicketDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Ticket"
          },
          {
            "type": "object",
            "properties": {
              "messages": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "authorId": {
                      "type": "string"
                    },
                    "authorName": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "files": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "TicketList": {
        "type": "object",
        "properties": {
          "docs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Ticket"
            }
          },
          "total": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "page": {
            "type": "integer"
          },
          "pages": {
            "type": "integer"
          }
        }
      },
      "TruckerUser": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "description": "ID único del usuario"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email del usuario"
          },
          "name": {
            "type": "string",
            "description": "Nombre del usuario"
          },
          "lastname": {
            "type": "string",
            "description": "Apellidos del usuario"
          },
          "phone": {
            "type": "string",
            "description": "Teléfono de contacto"
          },
          "image": {
            "type": "string",
            "description": "URL de la imagen de perfil"
          },
          "i18n": {
            "type": "string",
            "description": "Código de idioma preferido"
          },
          "status": {
            "type": "boolean",
            "description": "Estado activo/inactivo"
          },
          "emailVerified": {
            "type": "boolean",
            "description": "Si el email está verificado"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha de creación"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha de última actualización"
          }
        }
      },
      "TruckerUserCreate": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "name": {
            "type": "string"
          },
          "lastname": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          }
        }
      },
      "Lang": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "Código de idioma (ej. 'es', 'en')"
          },
          "name": {
            "type": "string",
            "description": "Nombre del idioma"
          }
        }
      },
      "PaginatedUsers": {
        "type": "object",
        "description": "Respuesta paginada de usuarios",
        "required": [
          "docs",
          "totalDocs",
          "limit",
          "page",
          "totalPages"
        ],
        "properties": {
          "docs": {
            "type": "array",
            "description": "Lista de usuarios en la página actual",
            "items": {
              "$ref": "#/components/schemas/UserMinimal"
            }
          },
          "totalDocs": {
            "type": "integer",
            "description": "Total de usuarios encontrados",
            "example": 25
          },
          "limit": {
            "type": "integer",
            "description": "Cantidad de usuarios por página",
            "example": 10
          },
          "page": {
            "type": "integer",
            "description": "Página actual",
            "example": 1
          },
          "totalPages": {
            "type": "integer",
            "description": "Total de páginas disponibles",
            "example": 3
          },
          "pagingCounter": {
            "type": "integer",
            "description": "Índice del primer resultado",
            "example": 1
          },
          "hasPrevPage": {
            "type": "boolean",
            "description": "Indica si hay página anterior",
            "example": false
          },
          "hasNextPage": {
            "type": "boolean",
            "description": "Indica si hay página siguiente",
            "example": true
          },
          "prevPage": {
            "type": "integer",
            "description": "Número de página anterior",
            "nullable": true,
            "example": null
          },
          "nextPage": {
            "type": "integer",
            "description": "Número de página siguiente",
            "nullable": true,
            "example": 2
          }
        }
      },
      "VehicleType": {
        "type": "object",
        "description": "Representa un tipo de vehículo en el sistema. Contiene información sobre las características\ny capacidades del vehículo para transporte de mercancías.\n\n**Tipos válidos:** Los definidos en Vehicle.validTypes del modelo\n**Tipos de envío válidos:** Los definidos en Vehicle.validShippingTypes del modelo\n",
        "properties": {
          "_id": {
            "type": "string",
            "description": "ID único generado por MongoDB",
            "example": "60a1b2c3d4e5f67890123456"
          },
          "code": {
            "type": "string",
            "description": "Código identificador único del tipo de vehículo.\nSe utiliza internamente para referencias y validaciones.\n",
            "example": "TRUCK"
          },
          "name": {
            "type": "string",
            "description": "Nombre legible del tipo de vehículo.\nSe muestra en interfaces de usuario y formularios.\n",
            "example": "Camión de carga"
          },
          "description": {
            "type": "string",
            "description": "Descripción detallada de las características y capacidades del vehículo.\nIncluye información sobre dimensiones, peso máximo, tipo de carga soportada, etc.\n",
            "example": "Vehículo pesado con capacidad de hasta 24 toneladas y 13.6m de largo"
          },
          "visible": {
            "type": "boolean",
            "description": "Indica si este tipo de vehículo está visible y disponible para selección.\nLos tipos ocultos no aparecen en listados ni formularios.\n",
            "example": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha y hora de creación del registro en el sistema",
            "example": "2023-05-15T10:30:00+00:00"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Fecha y hora de la última actualización del registro",
            "example": "2023-05-20T14:45:00+00:00"
          }
        }
      },
      "VehicleTypeList": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/VehicleType"
        }
      },
      "403Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "Mensaje descriptivo del error"
          }
        }
      },
      "400Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        }
      },
      "404Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        }
      },
      "409Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        }
      },
      "200Token": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string"
          },
          "user": {
            "$ref": "#/components/schemas/Trucker"
          }
        }
      },
      "200TrackingPosition": {
        "type": "object",
        "properties": {
          "service_code": {
            "type": "string"
          }
        }
      },
      "200Messages": {
        "type": "object",
        "properties": {
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Message"
            }
          }
        }
      },
      "200TicketDetail": {
        "$ref": "#/components/schemas/TicketDetail"
      },
      "VehicleTypesResponse": {
        "type": "object",
        "description": "Respuesta paginada con lista de tipos de vehículos.\nSigue el formato estándar de paginación usado en la API.\n\n**Estructura:**\n- docs: Array con los documentos solicitados\n- total: Número total de documentos disponibles\n- limit: Límite de documentos por página\n- page: Página actual\n- pages: Número total de páginas\n",
        "properties": {
          "docs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VehicleType"
            },
            "description": "Lista de tipos de vehículos en la página actual"
          },
          "total": {
            "type": "integer",
            "description": "Número total de tipos de vehículos disponibles",
            "example": 25
          },
          "limit": {
            "type": "integer",
            "description": "Número máximo de resultados por página",
            "example": 10
          },
          "page": {
            "type": "integer",
            "description": "Número de página actual (1-based)",
            "example": 1
          },
          "pages": {
            "type": "integer",
            "description": "Número total de páginas disponibles",
            "example": 3
          }
        }
      }
    },
    "securitySchemes": {
      "apiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-KEY",
        "description": "API Key for authentication"
      },
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "JWT Bearer authentication"
      }
    },
    "parameters": {
      "pageParam": {
        "name": "page",
        "in": "query",
        "description": "Número de página para paginación",
        "required": false,
        "schema": {
          "type": "integer",
          "default": 1,
          "minimum": 1
        }
      },
      "limitParam": {
        "name": "limit",
        "in": "query",
        "description": "Número máximo de resultados por página",
        "required": false,
        "schema": {
          "type": "integer",
          "default": 20,
          "minimum": 1,
          "maximum": 100
        }
      },
      "minDateParam": {
        "name": "minDate",
        "in": "query",
        "description": "Fecha mínima para filtrar (formato YYYY-MM-DD)",
        "required": false,
        "schema": {
          "type": "string",
          "format": "date",
          "example": "2019-09-20T00:00:00+00:00"
        }
      },
      "maxDateParam": {
        "name": "maxDate",
        "in": "query",
        "description": "Fecha máxima para filtrar (formato YYYY-MM-DD)",
        "required": false,
        "schema": {
          "type": "string",
          "format": "date",
          "example": "2019-09-20T00:00:00+00:00"
        }
      },
      "searchParam": {
        "name": "search",
        "in": "query",
        "description": "Texto para buscar en código de servicio, método de carga, tipo de palets o descripción",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "statusParam": {
        "name": "status",
        "in": "query",
        "description": "Estado de la entrega para filtrar",
        "required": false,
        "schema": {
          "type": "string",
          "enum": [
            "pending",
            "in_progress",
            "completed",
            "cancelled"
          ],
          "example": "pending"
        }
      }
    },
    "responses": {
      "200Address": {
        "description": "Dirección individual",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Address"
            }
          }
        }
      },
      "200AddressList": {
        "description": "Lista de direcciones",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AddressList"
            }
          }
        }
      },
      "400Error": {
        "description": "Error en la solicitud",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "401Error": {
        "description": "No autorizado",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "200Auction": {
        "description": "Subasta individual",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Auction"
            }
          }
        }
      },
      "200AuctionList": {
        "description": "Lista de subastas",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AuctionList"
            }
          }
        }
      },
      "200Delivery": {
        "description": "Entrega individual",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Delivery"
            }
          }
        }
      },
      "200DeliveryList": {
        "description": "Lista de entregas",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/DeliveryList"
            }
          }
        }
      },
      "200Messages": {
        "description": "Lista de mensajes",
        "content": {
          "application/json": {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/Message"
              }
            }
          }
        }
      },
      "200Binary": {
        "description": "Archivo binario (PDF o ZIP)",
        "content": {
          "application/pdf": {
            "schema": {
              "type": "string",
              "format": "binary"
            }
          },
          "application/zip": {
            "schema": {
              "type": "string",
              "format": "binary"
            }
          }
        }
      },
      "200Truckers": {
        "description": "Lista de transportistas cercanos",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/TruckerList"
            },
            "examples": {
              "successResponse": {
                "summary": "Example of a successful response",
                "value": [
                  {
                    "id": "550e8400-e29b-41d4-a716-446655440000",
                    "name": "Transportes López S.L.",
                    "distance": 5.3,
                    "location": {
                      "lat": 40.4168,
                      "lng": -3.7038
                    }
                  },
                  {
                    "id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
                    "name": "Cargas Veloz S.A.",
                    "distance": 8.1,
                    "location": {
                      "lat": 40.4175,
                      "lng": -3.7042
                    }
                  }
                ]
              }
            }
          }
        }
      },
      "403Error": {
        "description": "No autorizado o error en la solicitud",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string",
                  "description": "Mensaje descriptivo del error"
                }
              }
            },
            "examples": {
              "invalidApiKey": {
                "summary": "Invalid API Key",
                "value": {
                  "error": "API Key no válida o expirada"
                }
              },
              "missingParams": {
                "summary": "Missing parameters",
                "value": {
                  "error": "Se requieren los parámetros lat y lng"
                }
              },
              "invalidLocation": {
                "summary": "Invalid location",
                "value": {
                  "error": "Las coordenadas proporcionadas no son válidas"
                }
              }
            }
          }
        }
      },
      "200Vehicle": {
        "description": "Vehículo individual",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Vehicle"
            }
          }
        }
      },
      "200VehicleList": {
        "description": "Lista de vehículos",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/VehicleList"
            }
          }
        }
      },
      "200VehicleTypes": {
        "description": "Respuesta exitosa que contiene una lista paginada de tipos de vehículos.\n\n**Formato de respuesta:**\n- Siempre devuelve un objeto con la estructura de paginación\n- Incluso para resultados vacíos, devuelve el objeto con docs=[]\n\n**Códigos de estado alternativos:**\n- 400: Error en parámetros de consulta\n- 401: No autorizado (token JWT inválido o expirado)\n- 500: Error interno del servidor\n\n**Ejemplo de error:**\n```json\n{\n  \"error\": \"Invalid token\",\n  \"message\": \"The authentication token is invalid or expired\",\n  \"statusCode\": 401\n}\n```\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/VehicleTypesResponse"
            },
            "example": {
              "docs": [
                {
                  "_id": "60a1b2c3d4e5f67890123456",
                  "code": "TRUCK",
                  "name": "Camión",
                  "description": "Vehículo pesado para transporte de mercancías",
                  "visible": true,
                  "createdAt": "2023-05-15T10:30:00+00:00",
                  "updatedAt": "2023-05-15T10:30:00+00:00"
                },
                {
                  "_id": "60a1b2c3d4e5f67890123457",
                  "code": "VAN",
                  "name": "Furgoneta",
                  "description": "Vehículo ligero para paquetería",
                  "visible": true,
                  "createdAt": "2023-05-16T09:15:00+00:00",
                  "updatedAt": "2023-05-16T09:15:00+00:00"
                }
              ],
              "total": 2,
              "limit": 10,
              "page": 1,
              "pages": 1
            }
          }
        }
      },
      "200VehicleType": {
        "description": "Tipo de vehículo individual",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/VehicleType"
            }
          }
        }
      },
      "200DeleteResponse": {
        "description": "Confirmación de eliminación",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "_id": {
                  "type": "string",
                  "description": "ID del tipo eliminado"
                }
              }
            }
          }
        }
      }
    }
  }
}