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. \n\
        Las 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.

            Ejemplos:

            - Formato de fecha inválido

            - Valores de paginación incorrectos

            '
  /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.


        Requisitos:

        - El usuario debe pertenecer a la compañía propietaria de la subasta

        - La subasta debe existir y no estar eliminada


        Campos incluidos en la respuesta:

        - Todos los campos básicos (serviceCode, status, dates)

        - Información completa de carga/descarga (etl/etd)

        - Lista de pujas realizadas (si existen)

        - Información del ganador (si la subasta está cerrada)

        - Documentos asociados (contratos, firmas)


        Ejemplo de uso:

        ```

        GET /api/auction/ABC123

        ```


        La respuesta incluye todos los campos del modelo Auction más relaciones pobladas.

        '
      parameters:
      - name: serviceCode
        in: path
        required: true
        schema:
          type: string
        description: 'Código único de identificación de la subasta.

          Formato: 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos
          aleatorios (ej: VIGMURnT4FN).

          Se genera automáticamente al crear la subasta.

          '
      responses:
        '200':
          $ref: '#/components/responses/200Auction'
          description: 'Detalles completos de la subasta incluyendo:

            - Información básica (serviceCode, status, dates)

            - Direcciones de carga/descarga (etl/etd_address)

            - Lista de pujas (bids) con historial

            - Ganador actual (bidWinner) si aplica

            - Documentos asociados (contracts, signatures)

            '
        '400':
          $ref: '#/components/responses/400Error'
          description: 'Error al procesar la solicitud. Posibles causas:

            - serviceCode no válido o no encontrado

            - Formato de parámetros incorrecto

            '
        '401':
          $ref: '#/components/responses/401Error'
          description: 'No autorizado. El usuario no tiene permisos para ver esta
            subasta.

            Requiere autenticación válida y pertenecer a la compañía propietaria.

            '
    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.

          Formato: 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos
          aleatorios (ej: VIGMURnT4FN).

          Se genera automáticamente al crear la subasta.

          '
      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.


        Requisitos:

        - La subasta debe estar en estado ''draft'' o ''planned''

        - El usuario debe ser el creador o administrador de la compañía

        - No debe tener pujas asociadas ni estar publicada


        Validaciones realizadas:

        1. Verifica que la subasta exista y sea eliminable

        2. Comprueba que no tenga pujas asociadas

        3. Confirma que el usuario tiene permisos

        4. Elimina el registro de la base de datos


        Ejemplo de uso:

        ```

        DELETE /api/auction/ABC123

        ```


        La respuesta confirma la eliminación exitosa con el serviceCode.

        '
      parameters:
      - name: serviceCode
        in: path
        required: true
        schema:
          type: string
        description: 'Código único de identificación de la subasta a eliminar.

          Formato: 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos
          aleatorios (ej: VIGMURnT4FN).

          '
      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:

            - La subasta no está en estado borrador

            - Ya tiene pujas asociadas

            - No se encontró la subasta

            '
        '401':
          $ref: '#/components/responses/401Error'
          description: 'No autorizado. Requiere:

            - Autenticación válida

            - Ser el creador o administrador de la compañía

            '
  /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:

            - status: Cambiado a ''published''

            - date_start: Actualizado si era nulo

            - updatedAt: Fecha de última modificación

            '
        '400':
          $ref: '#/components/responses/400Error'
          description: 'Error en la publicación. Posibles causas:

            - La subasta no está en estado draft

            - Fechas inválidas o incoherentes

            - Dimensiones de carga no válidas

            - Falta información requerida

            '
        '401':
          $ref: '#/components/responses/401Error'
          description: 'No autorizado. El usuario no tiene permisos para publicar
            esta subasta.

            '
  /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.


        Requisitos:

        - La subasta debe estar en estado ''published''

        - Debe tener al menos una puja válida

        - El usuario debe ser propietario o administrador de la compañía

        - La subasta no debe estar ya cerrada o cancelada


        Acciones realizadas:

        1. Cierra la subasta (status = ''closed'')

        2. Asigna el ganador (bidWinner)

        3. Genera un contrato preliminar

        4. Crea un registro de entrega (delivery)


        Ejemplo de uso:

        ```

        POST /api/auction/acceptCurrent/ABC123

        ```


        La respuesta incluye la subasta actualizada con estado ''closed'' y el ganador
        asignado.

        '
      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:

            - status: Cambiado a ''closed''

            - bidWinner: Información del transportista ganador

            - award_price: Precio acordado

            - delivery: ID del registro de entrega creado

            '
        '400':
          $ref: '#/components/responses/400Error'
          description: 'Error al aceptar la puja. Posibles causas:

            - La subasta no tiene pujas válidas

            - La subasta ya está cerrada/cancelada

            - El usuario no tiene permisos

            '
        '401':
          $ref: '#/components/responses/401Error'
          description: 'No autorizado. El usuario no tiene permisos para cerrar esta
            subasta.

            Requiere ser propietario o administrador de la compañía.

            '
  /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:

            - status: ''assigned'' (asignado directamente)

            - trucker_user: ID del transportista asignado

            - trucker_vehicle: ID del vehículo asignado

            - auction: null (no asociado a subasta pública)

            '
        '400':
          $ref: '#/components/responses/400Error'
          description: 'Error al crear la subasta privada. Posibles causas:

            - La compañía no es multitennant

            - Vehicle o trucker no existen o no pertenecen a la compañía

            - Fechas o dimensiones inválidas

            '
        '401':
          $ref: '#/components/responses/401Error'
          description: 'No autorizado. Requiere:

            - Autenticación válida

            - Compañía multitennant

            - Permisos de administrador

            '
  /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.


        Características:

        - Listado paginado

        - Ordenado por fecha de actualización (más recientes primero)

        - Filtrado por estado, fechas y búsqueda textual

        - Incluye relaciones básicas (direcciones, pujas, ganador)


        Parámetros opcionales de query:

        - page: Número de página (default 1)

        - limit: Items por página (default valor de ITEMS_PAGE en .env)

        - status: Filtro por estado (draft, published, closed, cancelled)

        - search: Texto para buscar en service_code o descripción

        - minDate/maxDate: Rango de fechas para creación o modificación


        Ejemplo de uso:

        ```

        GET /api/auction?page=1&limit=20&status=published&search=urgente

        ```


        La respuesta incluye metadatos de paginación y los campos básicos de cada
        subasta.

        '
      responses:
        '200':
          $ref: '#/components/responses/200AuctionList'
          description: 'Lista paginada de subastas. Cada item incluye:

            - service_code: Identificador único (ej: VIGMURnT4FN)

            - status: Estado actual

            - etl_date/etd_date: Fechas de carga/descarga

            - cargo_weight/cargo_height: Dimensiones

            - bid_current: Puja actual (si aplica)

            - bidWinner: Ganador (si aplica)

            - createdAt/updatedAt: Fechas creación/actualización


            Metadatos de paginación:

            - total: Total de items

            - pages: Total de páginas

            - page: Página actual

            - limit: Items por página

            '
        '400':
          $ref: '#/components/responses/400Error'
          description: 'Error en los parámetros de búsqueda. Posibles causas:

            - Valores de paginación inválidos

            - Formato de fechas incorrecto

            - Estado no válido

            '
        '401':
          $ref: '#/components/responses/401Error'
          description: 'No autorizado. Requiere autenticación válida y pertenecer
            a la compañía.

            '
    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:

            - service_code: Identificador único generado (ej: VIGMURnT4FN)

            - status: ''draft'' (requiere publicación explícita)

            - etl_address/etd_address: Direcciones pobladas

            - createdAt: Fecha de creación

            '
        '400':
          $ref: '#/components/responses/400Error'
          description: 'Error al crear la subasta. Posibles causas:

            - Fechas inválidas o incoherentes

            - Dimensiones de carga no válidas

            - Direcciones no encontradas

            - Falta información requerida

            '
        '401':
          $ref: '#/components/responses/401Error'
          description: 'No autorizado. Requiere:

            - Autenticación válida

            - Compañía activa

            - Permisos para crear subastas

            '
  /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.


        Criterios de inclusión:

        - Subastas en estado ''closed''

        - Con ganador asignado (bidWinner)

        - Donde la compañía o el transportista no han firmado aún

        - Ordenadas por fecha de cierre (más recientes primero)


        Parámetros opcionales de query:

        - page: Número de página (default 1)

        - limit: Items por página (default valor de ITEMS_PAGE en .env)

        - signedBy: Filtro por firmante pendiente (''company'' o ''trucker'')


        Ejemplo de uso:

        ```

        GET /api/auction/sign?page=1&limit=10&signedBy=company

        ```


        La respuesta incluye paginación y los campos básicos de cada subasta.

        '
      responses:
        '200':
          $ref: '#/components/responses/200AuctionList'
          description: 'Lista paginada de subastas pendientes de firma. Cada item
            incluye:

            - service_code: Identificador único (ej: VIGMURnT4FN)

            - status: ''closed''

            - bidWinner: Información del transportista ganador

            - signed_by_company/signed_by_trucker: Estados de firma

            - createdAt/closedAt: Fechas relevantes

            '
        '400':
          $ref: '#/components/responses/400Error'
          description: 'Error en los parámetros de búsqueda. Posibles causas:

            - Valores de paginación inválidos

            - Filtro signedBy no válido

            '
        '401':
          $ref: '#/components/responses/401Error'
          description: 'No autorizado. Requiere autenticación válida y pertenecer
            a la compañía.

            '
  /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.


        Requisitos:

        - La subasta debe estar en estado ''closed''

        - Debe tener un ganador asignado (bidWinner)

        - El usuario debe pertenecer a la compañía propietaria o ser el transportista
        ganador


        Campos devueltos:

        - signed: Estado general de firma (ambas partes)

        - signedAt: Fecha de última firma

        - signed_by_company: Firma de la compañía (true/false)

        - signed_by_trucker: Firma del transportista (true/false)

        - company_signature: Firma digital de la compañía (si existe)

        - trucker_signature: Firma digital del transportista (si existe)


        Ejemplo de uso:

        ```

        GET /api/auction/sign/ABC123

        ```


        La respuesta incluye el estado detallado de las firmas.

        '
      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:

            - Subasta no encontrada o no cerrada

            - Sin ganador asignado

            - Parámetros inválidos

            '
        '401':
          $ref: '#/components/responses/401Error'
          description: 'No autorizado. El usuario no tiene permisos para ver este
            estado.

            Requiere ser:

            - Miembro de la compañía propietaria

            - Transportista ganador de la subasta

            '
    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\n\
        3. 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:

            - La subasta ya está en favoritos

            - Subasta no encontrada

            - Falta información requerida

            '
        '401':
          $ref: '#/components/responses/401Error'
          description: 'No autorizado. Requiere:

            - Autenticación válida

            - Pertenecer a la compañía propietaria

            '
    delete:
      tags:
      - auction
      summary: Revoke signature
      security:
      - apiKeyAuth: []
      description: 'Revoca la firma digital de una subasta por parte de la compañía.


        Requisitos:

        - La subasta debe estar en estado ''closed''

        - Debe tener una firma previa de la compañía

        - El usuario debe ser administrador o propietario

        - No debe haber delivery asociado creado


        Acciones realizadas:

        1. Elimina la firma digital de la compañía

        2. Cambia el estado signed_by_company a false

        3. Elimina cualquier contrato generado

        4. Elimina el delivery si no ha sido aceptado


        Ejemplo de uso:

        ```

        DELETE /api/auction/sign/ABC123

        ```


        La respuesta confirma la revocación exitosa.

        '
      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:

            - La subasta no tiene firma previa

            - Ya existe un delivery aceptado

            - El usuario no tiene permisos

            - La subasta no está en estado válido

            '
        '401':
          $ref: '#/components/responses/401Error'
          description: 'No autorizado. Requiere permisos de administrador/owner.

            '
  /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:\n\
        1. Valida que la subasta cumpla los requisitos\n2. Genera documento PDF con\
        \ plantilla 'file_contract'\n3. Almacena el PDF en S3/almacenamiento local\n\
        4. 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:

            - contract_url: URL del PDF generado

            - contract_version: 1 (versión inicial)

            - contract_createdAt: Fecha de generación

            - award_price: Precio acordado

            - special_conditions: Términos especiales

            '
        '400':
          $ref: '#/components/responses/400Error'
          description: 'Error al generar contrato. Posibles causas:

            - Subasta no encontrada o no cerrada

            - Sin ganador asignado

            - Precio menor al de la puja ganadora

            - Ya existe contrato generado

            - Campos obligatorios faltantes

            '
        '401':
          $ref: '#/components/responses/401Error'
          description: 'No autorizado. Requiere:

            - API Key válida

            - Permisos de administrador

            - Pertenecer a la compañía propietaria

            '
        '409':
          description: 'Conflicto. Posibles causas:

            - Subasta ya tiene contrato generado

            - Operación concurrente en curso

            '
  /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.

          Formato: 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos
          aleatorios (ej: VIGMURnT4FN).

          '
      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.

                Formato: "attachment; filename=CONTRACT_{serviceCode}.pdf"

                Ejemplo: "attachment; filename=CONTRACT_ABC123.pdf"

                '
            Content-Type:
              schema:
                type: string
              description: application/pdf
        '400':
          $ref: '#/components/responses/400Error'
          description: 'Error al generar contrato. Posibles causas:

            - Subasta no encontrada o no cerrada (status != ''closed'')

            - Faltan firmas (signed_by_company o signed_by_trucker = false)

            - No hay ganador asignado (bidWinner = null)

            - Error al renderizar plantilla PDF

            '
        '401':
          $ref: '#/components/responses/401Error'
          description: 'No autorizado. Requiere:

            - API Key válida

            - Ser miembro de compañía propietaria O transportista ganador

            - Permisos para ver contratos

            '
        '404':
          description: 'Contrato no encontrado. Posibles causas:

            - Subasta no existe

            - Contrato no generado previamente

            - Permisos insuficientes

            '
    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\n\
        3. 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.

          Formato: 3 letras del origen, 3 letras del destino y 5 caracteres alfanuméricos
          aleatorios (ej: VIGMURnT4FN).

          '
      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:

            - award_price: Nuevo precio acordado

            - special_conditions: Términos actualizados

            - contract_updatedAt: Fecha de última modificación

            - contract_version: Incrementa en 1

            '
        '400':
          $ref: '#/components/responses/400Error'
          description: 'Error al actualizar contrato. Posibles causas:

            - Subasta no encontrada o no cerrada

            - Delivery en estado no editable

            - Campos inválidos o faltantes

            - Precio menor al mínimo aceptable

            '
        '401':
          $ref: '#/components/responses/401Error'
          description: 'No autorizado. Requiere:

            - API Key válida

            - Permisos de administrador

            - Pertenecer a la compañía propietaria

            '
        '409':
          description: 'Conflicto. Posibles causas:

            - Contrato ya firmado por ambas partes

            - Delivery en progreso o completado

            - Versión desactualizada (concurrent update)

            '
    delete:
      tags:
      - auction
      summary: Delete contract
      security:
      - apiKeyAuth: []
      description: 'Elimina permanentemente el contrato asociado a una subasta.


        Requisitos:

        - La subasta debe estar en estado ''closed''

        - No debe haber delivery asociado en estado ''in_progress'' o ''completed''

        - El usuario debe ser administrador de la compañía

        - No debe haber firmas digitales registradas


        Acciones realizadas:

        1. Elimina el documento PDF del contrato del almacenamiento

        2. Limpia las referencias al contrato en la subasta

        3. Registra la acción en el historial de auditoría

        4. Notifica al transportista si ya había sido informado


        Validaciones realizadas:

        1. Verifica que la subasta exista y esté cerrada

        2. Comprueba que no haya entregas en progreso

        3. Valida permisos del usuario

        4. Confirma que no hay firmas digitales


        Ejemplo de uso:

        ```

        DELETE /api/auction/contract/ABC123

        ```


        La respuesta confirma la eliminación exitosa.

        '
      parameters:
      - name: serviceCode
        in: path
        required: true
        schema:
          type: string
        description: 'Código único de la subasta asociada al contrato.

          Formato: 3 letras del origen, 3 letras del destino y 5 caracteres alfanumericos
          aleatorios (ej: VIGMURnT4FN).

          '
      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.

                      Valores posibles: ''none'', ''pending'', ''cancelled''

                      '
        '400':
          $ref: '#/components/responses/400Error'
          description: 'Error al eliminar contrato. Posibles causas:

            - Subasta no encontrada o no cerrada

            - Delivery en estado no permitido

            - Firmas digitales existentes

            - Permisos insuficientes

            '
        '401':
          $ref: '#/components/responses/401Error'
          description: 'No autorizado. Requiere:

            - API Key válida

            - Permisos de administrador

            - Pertenecer a la compañía propietaria

            '
        '409':
          description: 'Conflicto. Posibles causas:

            - Contrato ya firmado por alguna parte

            - Delivery en progreso o completado

            - Operación concurrente en curso

            '
  /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.


        Características:

        - Listado paginado

        - Ordenado por fecha de creación (más recientes primero)

        - Incluye las subastas asociadas con sus datos completos

        - Filtrado opcional por texto y fechas


        Parámetros opcionales de query:

        - page: Número de página (default 1)

        - limit: Items por página (default valor de ITEMS_PAGE en .env)

        - search: Texto para buscar en fav_name o description

        - minDate/maxDate: Rango de fechas de creación del favorito


        Ejemplo de uso:

        ```

        GET /api/auction/favorites?page=1&limit=10&search=urgente

        ```


        La respuesta incluye metadatos de paginación y los campos básicos de cada
        subasta favorita.

        '
      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:

            - Valores de paginación inválidos

            - Formato de fechas incorrecto

            '
        '401':
          $ref: '#/components/responses/401Error'
          description: 'No autorizado. Requiere:

            - Autenticación válida

            - Pertenecer a la compañía propietaria

            '
    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:

            - La subasta ya está en favoritos

            - Subasta no encontrada

            - Falta información requerida

            '
        '401':
          $ref: '#/components/responses/401Error'
          description: 'No autorizado. Requiere:

            - Autenticación válida

            - Pertenecer a la compañía propietaria

            - Permisos de edició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:

        - Información completa de la subasta original

        - Metadatos del favorito (nombre personalizado, fechas)

        - Relaciones pobladas (direcciones, pujas, ganador si aplica)


        Requisitos:

        - El ID del favorito debe ser válido y existir

        - El usuario debe pertenecer a la compañía propietaria

        - La subasta asociada no debe haber sido eliminada


        Validaciones realizadas:

        1. Verifica que el favorito exista y pertenezca a la compañía

        2. Comprueba que la subasta asociada siga existiendo

        3. Pobla todas las relaciones relevantes


        Ejemplo de uso:

        ```

        GET /api/auction/favorites/60a1b2c3d4e5f6a1b2c3d4e5

        ```


        La respuesta incluye tanto los datos de la subasta como los metadatos específicos
        del favorito.

        '
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
        description: 'ID único del registro favorito en la base de datos.

          Formato: ObjectId de MongoDB (24 caracteres hexadecimales)

          Ejemplo: 60a1b2c3d4e5f6a1b2c3d4e5

          '
      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:

            - ID con formato inválido

            - Favorito no encontrado

            - Subasta asociada eliminada

            - Parámetros inválidos

            '
        '401':
          $ref: '#/components/responses/401Error'
          description: 'No autorizado. Requiere:

            - Autenticación válida con API Key

            - Pertenecer a la compañía propietaria del favorito

            - Permisos de lectura

            '
    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.

          Formato: 24 caracteres hexadecimales (ObjectId)

          Ejemplo: 60a1b2c3d4e5f6a1b2c3d4e5

          '
      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:

            - ID con formato inválido

            - Campos no permitidos en la actualización

            - Faltan campos obligatorios (fav_name)

            - Subasta asociada eliminada

            '
        '401':
          $ref: '#/components/responses/401Error'
          description: 'No autorizado. Requiere:

            - Autenticación válida con API Key

            - Pertenecer a la compañía propietaria

            - Permisos de edició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.


        Requisitos:

        - El ID del favorito debe existir en la base de datos

        - El usuario debe pertenecer a la compañía propietaria

        - No debe haber operaciones pendientes asociadas al favorito


        Validaciones realizadas:

        1. Verifica que el favorito exista y pertenezca a la compañía

        2. Comprueba que no haya operaciones pendientes asociadas

        3. Elimina el registro de forma permanente


        Diferencias con PUT:

        - Eliminación permanente vs actualización

        - No requiere body request

        - No se puede deshacer


        Ejemplo de uso:

        ```

        DELETE /api/auction/favorites/60a1b2c3d4e5f6a1b2c3d4e5

        ```


        La respuesta incluye confirmación y metadatos de la eliminación.

        '
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
        description: 'ID único del registro favorito en MongoDB.

          Formato: 24 caracteres hexadecimales (ObjectId)

          Ejemplo: 60a1b2c3d4e5f6a1b2c3d4e5

          '
      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:

            - ID con formato inválido

            - Favorito no encontrado

            - Operaciones pendientes asociadas

            - El usuario no es propietario

            '
        '401':
          $ref: '#/components/responses/401Error'
          description: 'No autorizado. Requiere:

            - Autenticación válida con API Key

            - Pertenecer a la compañía propietaria

            - Permisos de administrador

            '
  /api/delivery/active:
    get:
      tags:
      - delivery
      summary: Get active deliveries
      description: 'Obtiene la lista de entregas actualmente activas (con fecha de
        entrega futura).


        Las entregas activas son aquellas que:

        - Tienen estado ''pending'' o ''in_progress''

        - Su fecha de entrega (etd_date) es posterior a la fecha actual

        - Pertenecen a la compañía del usuario autenticado


        Parámetros opcionales de filtrado:

        - page: Número de página para paginación (por defecto 1)

        - limit: Número máximo de resultados por página (por defecto ITEMS_PAGE)

        - minDate: Fecha mínima para filtrar por fecha de inicio o carga

        - maxDate: Fecha máxima para filtrar por fecha de fin o descarga

        - search: Texto para buscar en código de servicio, método de carga, tipo de
        palets o descripción

        - status: Estado de la entrega (pending, in_progress, completed, cancelled)


        Ejemplo de uso:

        1. Monitorizar entregas en curso

        2. Planificar recursos para próximas entregas

        3. Filtrar entregas por fechas o estados

        '
      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.


        La respuesta incluye:

        - Información básica de la entrega (código, estado, fechas)

        - Direcciones de carga (ETL) y descarga (ETD)

        - Detalles del vehículo asignado

        - Información de subastas relacionadas

        - Mensajes asociados (si los hay)

        - Indicador si permite enviar mensajes (can_message)


        Requisitos:

        - El código de servicio debe existir

        - La entrega debe pertenecer a la compañía del usuario autenticado


        Ejemplo de uso:

        1. Verificar estado actual de una entrega

        2. Obtener información para seguimiento

        3. Consultar detalles para resolver incidencias

        '
      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.


        El documento incluye:

        - Datos del remitente y destinatario

        - Lugar y fecha de carga/descarga

        - Naturaleza de la mercancía

        - Número de bultos

        - Peso bruto

        - Instrucciones especiales


        Requisitos:

        - El código de servicio debe existir

        - La entrega debe pertenecer a la compañía del usuario

        - La entrega debe tener información completa para generar el CMR


        Ejemplo de uso:

        1. Generar CMR para entregas internacionales

        2. Proporcionar documentación a transportistas

        3. Cumplir con requisitos legales de transporte

        '
      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.


        El archivo ZIP puede contener:

        - Documento CMR (si aplica)

        - Facturas o albaranes

        - Fotos de la mercancía

        - Documentación aduanera

        - Otros documentos adjuntos


        Requisitos:

        - El código de servicio debe existir

        - La entrega debe pertenecer a la compañía del usuario

        - Debe haber documentos disponibles para descargar


        Ejemplo de uso:

        1. Obtener toda la documentación de una entrega

        2. Compartir documentos con clientes o proveedores

        3. Archivar documentación de entregas completadas

        '
      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.


        Parámetros de filtrado disponibles:

        - page: Número de página (por defecto 1)

        - limit: Resultados por página (por defecto ITEMS_PAGE)

        - minDate/maxDate: Rango de fechas de creación

        - minETL/maxETL: Rango de fechas de carga (ETL)

        - minETD/maxETD: Rango de fechas de descarga (ETD)

        - status: Estado de la entrega

        - search: Búsqueda en código, descripción o tipo de palets

        - cargo_type: Tipo de carga (pallets u otros)


        La respuesta incluye metadatos de paginación y lista de entregas con:

        - Código de servicio

        - Estado actual

        - Fechas relevantes (creación, carga, descarga)

        - Direcciones de carga/descarga

        - Información básica de la mercancía

        '
      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.


        Los mensajes incluyen:

        - Contenido del mensaje

        - Autor (nombre completo del usuario)

        - Fecha y hora de creación

        - Estado de lectura (si aplica)


        Requisitos:

        - El código de servicio debe existir

        - La entrega debe pertenecer a la compañía del usuario

        - La entrega debe estar activa (can_message: true)


        Ejemplo de uso:

        1. Consultar historial de comunicación sobre una entrega

        2. Verificar actualizaciones o instrucciones recientes

        3. Revisar conversaciones anteriores

        '
      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.


        El mensaje será visible para:

        - Usuarios de la misma compañía

        - Transportistas asignados (si aplica)

        - Personal administrativo con permisos


        Requisitos:

        - El código de servicio debe existir

        - La entrega debe pertenecer a la compañía del usuario

        - La entrega debe estar activa (can_message: true)

        - El cuerpo del mensaje no debe estar vacío


        Ejemplo de uso:

        1. Notificar incidencias en la entrega

        2. Proporcionar instrucciones adicionales

        3. Confirmar recepción o cambios

        '
      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:

            - Faltan campos obligatorios

            - Formato incorrecto en coordenadas

            - Nombre de estación ya existe en la ciudad

            '
  /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:

            - Faltan campos obligatorios

            - Formato incorrecto en coordenadas

            - Nombre de estación ya existe en la ciudad

            '
        '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.

            Código de estado 204 indica éxito sin contenido de respuesta.

            '
        '404':
          description: 'Estación no encontrada con el ID proporcionado.

            Verifique que el ID sea correcto y que la estación exista.

            '
  /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.


        **Casos de uso:**

        - Encontrar transportistas disponibles para asignar a una carga

        - Mostrar transportistas cercanos en un mapa

        - Filtrar transportistas por proximidad


        **Notas:**

        - Requiere autenticación mediante API Key

        - La distancia se calcula en kilómetros

        - El radio máximo permitido es de 100 km

        '
      parameters:
      - name: lat
        in: query
        required: true
        schema:
          type: number
          format: float
          minimum: -90
          maximum: 90
        description: 'Latitud geográfica en formato decimal (WGS84).

          Ejemplo: 40.4168 (para Madrid)

          Rango válido: -90 a 90

          Precisión recomendada: 6 decimales

          '
      - name: lng
        in: query
        required: true
        schema:
          type: number
          format: float
          minimum: -180
          maximum: 180
        description: 'Longitud geográfica en formato decimal (WGS84).

          Ejemplo: -3.7038 (para Madrid)

          Rango válido: -180 a 180

          Precisión recomendada: 6 decimales

          '
      - 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\n\
          Ejemplos:\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:

            - VEHICLE_NOT_FOUND

            - INVALID_VEHICLE_TYPE

            - INVALID_SHIPPING_TYPE

            - INVALID_CARGO_TYPES

            - Error en validación de matrícula

            '
          $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:

            - VEHICLE_NOT_FOUND

            - VEHICLE_IN_USE (si está asignado a transportes)

            '
          $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:**\n\
        1. 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.


        **Características:**

        - Procesamiento masivo de direcciones

        - Soporta formato Total Logistik y estándar

        - Validación individual de cada fila

        - Respuesta detallada con éxitos y errores


        **Formato CSV esperado:**

        - Campos válidos: name, company_name, phone, lat, lng

        - Delimitador: coma

        - Primera fila: cabeceras


        **Requiere suscripción activa** (validado por middleware isPaymentUpdate)

        '
      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.


        **Incluye información sobre:**

        - Formato del CSV requerido

        - Validaciones aplicadas

        - Campos obligatorios y opcionales

        - Errores comunes y cómo evitarlos

        '
      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.


        **La plantilla incluye:**

        - Cabeceras correctas

        - Una fila de ejemplo

        - Formato compatible con el endpoint de importación

        '
      parameters: []
      responses:
        '200':
          content:
            text/csv:
              schema:
                example: 'name,company_name,phone,lat,lng

                  Cargoffer,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.


        **Nota:** Este endpoint existe para clientes HTTP que no soportan el método
        PUT.

        Funcionalmente es idéntico a PUT /company/address.


        **Prioridad de ID:** body._id (usado) > query.id


        **Timestamps son convertidos a UTC** por middleware checkUTC.

        '
      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.


        **Nota:** Este endpoint existe para clientes HTTP que no soportan el método
        PUT.

        Funcionalmente es idéntico a PUT /company/address/{id}.


        **Prioridad de ID:** params.id (usado) > body._id > query.id


        **Timestamps son convertidos a UTC** por middleware checkUTC.

        '
      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.


        **Características:**

        - Formato estándar CSV con encabezados

        - Incluye todos los campos de dirección

        - Coordenadas separadas en longitud/latitud


        **Ejemplo de respuesta:**

        ```csv

        "ID","Name","Company","Phone","Street","StreetNumber","City","State","Zipcode","Country","gps_long","gps_lat","Neighborhood","Province","NameAddress"

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

        ```


        **Notas:**

        - Requiere token JWT válido

        - El archivo se descarga directamente

        - Formato compatible con Excel y otras herramientas

        '
      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"

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

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

                '
              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\n\
        Authorization: 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:**\n\
        1. 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

        Recupera todas las API Keys creadas por el usuario autenticado, permitiendo
        auditar

        y gestionar las credenciales de acceso programático propias.


        ## Objetivo

        Proporcionar al usuario visibilidad sobre sus API Keys activas, incluyendo
        sus tipos

        y códigos temporales necesarios para eliminación.


        ## Casos de Uso

        - Auditar qué API Keys están activas para el usuario

        - Obtener el temp_code necesario para eliminar una key específica

        - Verificar el tipo de permisos de cada key

        - Identificar keys antiguas que deberían rotarse


        ## Flujo de operación

        1. Valida que el usuario exista y pertenezca a una compañía

        2. Busca todas las claves asociadas al ID del usuario

        3. Oculta parcialmente las claves por seguridad (muestra primeros 8 y últimos
        4 caracteres)

        4. Devuelve la lista de claves con sus metadatos


        ## Mecanismo de Ocultamiento

        Las API Keys se muestran parcialmente:

        - Formato: `sk_live_*****5678` (primeros 8 + asteriscos + últimos 4 caracteres)

        - Las claves completas solo se muestran al momento de creación

        - No es posible recuperar la clave completa posteriormente


        ## Consideraciones importantes

        - Solo devuelve claves propias del usuario (no de otros usuarios de la compañía)

        - Las claves eliminadas (soft delete) no aparecen en la lista

        - El campo `temp_code` es necesario para operaciones de eliminació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:

                    - admin: Acceso completo a todas las funcionalidades administrativas

                    - auction: Acceso específico para operaciones relacionadas con
                    subastas

                    - delivery_invoice: Acceso específico para operaciones de facturas
                    de entrega


                    Los tipos no son acumulativos; cada clave tiene acceso solo a
                    su ámbito definido.

                    '
                  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.

          String alfanumérico generado aleatoriamente de aproximadamente 25 caracteres.


          Este código se obtiene de la respuesta de GET /company/apikey/ o POST /company/apikey/.

          '
        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.


        ## Propósito

        Proporcionar acceso completo al historial de subastas con capacidades de búsqueda
        y filtrado.


        ## Objetivo

        Permitir a las empresas gestionar, buscar y filtrar sus subastas por múltiples
        criterios como

        fechas, estado, tipo de carga, y búsqueda de texto libre.


        ## Casos de Uso

        - Visualizar historial completo de envíos de la empresa

        - Buscar subastas por código de servicio o descripción

        - Filtrar por rango de fechas de creación, carga o descarga

        - Filtrar por estado (draft, published, awarded, etc.)

        - Excluir borradores o subastas vacías de los resultados

        - Integración con sistemas externos de gestión logística

        '
      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:

          - service_code (código de servicio)

          - etl_cargo_method (método de carga)

          - pallets_type (tipo de pallets)

          - description (descripción del envío)

          '
        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\n\
        necesaria 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\n\
        Cuando 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\n\
        2. 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.


        ## Propósito

        Proporcionar acceso rápido a subastas que están actualmente recibiendo pujas.


        ## Objetivo

        Facilitar el monitoreo en tiempo real de subastas activas para toma de decisiones
        rápidas.


        ## Casos de Uso

        - Dashboard de subastas en curso

        - Monitoreo de pujas entrantes en tiempo real

        - Alertas de subastas próximas a finalizar

        - Integraciones con sistemas de notificaciones

        '
      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.


        ## Propósito

        Facilitar carga masiva para alto volumen.


        ## Objetivo

        Automatizar creación de múltiples subastas.


        ## Casos de Uso

        - Importar planificación mensual

        - Cargar desde ERP

        - Automatizar logística enterprise


        ## Límites

        - Máximo: 500 filas

        - Tamaño: 5MB

        '
      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.


        ## Propósito

        Proporcionar guía completa.


        ## Objetivo

        Minimizar errores con documentación clara.

        '
      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.


        ## Propósito

        Proporcionar guía para carga masiva.


        ## Objetivo

        Reducir errores con formato correcto.

        '
      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.


        ## Propósito

        Documentar visualmente la entrega.


        ## Objetivo

        Proveer evidencia fotográfica del estado de mercancía.


        ## Casos de Uso

        - Documentar entrega con fotos

        - Registrar incidencias visuales

        - Completar proceso documentado


        **Nota:** Este endpoint aplica únicamente a auctions en estado `approved`
        (es decir, que tienen un envío asociado).

        '
      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.


        ## Propósito

        Permitir cancelación cuando sea necesario.


        ## Objetivo

        Facilitar gestión de imprevistos.


        ## Casos de Uso

        - Cancelar por cambios en logística

        - Anular por problemas

        '
      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.


        ## Propósito

        Proporcionar documento contractual oficial.


        ## Objetivo

        Facilitar descarga para archivo y gestión documental.


        ## Casos de Uso

        - Descargar para archivo físico

        - Adjuntar a sistemas ERP

        - Auditoría de documentació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.


        ## Propósito

        Proporcionar capacidad de recuperación de subastas vacías para completar su
        información.


        ## Objetivo

        Evitar pérdida de subastas iniciadas permitiendo su edición desde estado vacío.


        ## Casos de Uso

        - Recuperar subasta creada sin datos completos

        - Retomar edición de subasta guardada parcialmente

        - Aprovechar service_code ya generado

        '
      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.


        ## Propósito

        Proporcionar acceso a todas las plantillas de rutas guardadas.


        ## Objetivo

        Facilitar la selección y reutilización de configuraciones frecuentes.


        ## Casos de Uso

        - Autocompletar formularios de creación de subastas

        - Buscar plantillas de rutas específicas

        - Gestionar catálogo de rutas frecuentes

        - Integración con sistemas de planificació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.


        ## Propósito

        Facilitar la creación rápida de subastas recurrentes mediante plantillas guardadas.


        ## Objetivo

        Mejorar la eficiencia operativa permitiendo reutilizar configuraciones de
        rutas frecuentes.


        ## Casos de Uso

        - Guardar rutas recurrentes semanales o mensuales

        - Crear plantillas para clientes habituales

        - Reutilizar configuraciones complejas de carga

        - Agilizar proceso de creación de subastas similares


        ## Datos Guardados

        - Direcciones de carga y descarga (etl_address, etd_address)

        - Configuración de carga (tipo, peso, volumen, pallets)

        - Métodos de carga/descarga

        - Configuración de temperatura (si es refrigerado)

        - Horarios preferenciales de carga/descarga

        - Descripción y notas

        '
      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.

        El ID puede proporcionarse en el body como "_id".


        ## Propósito

        Permitir ajustes en plantillas de rutas guardadas.


        ## Objetivo

        Mantener actualizadas las configuraciones de rutas frecuentes.


        ## Casos de Uso

        - Actualizar direcciones después de cambio de almacén

        - Ajustar pesos o volúmenes según nuevos productos

        - Modificar horarios preferenciales

        - Corregir errores en configuración guardada

        '
      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.


        ## Propósito

        Permitir edición múltiple de favoritos en una sola petición.


        ## Objetivo

        Mejorar la eficiencia al actualizar varias rutas favoritas simultáneamente.


        ## Casos de Uso

        - Actualizar múltiples rutas después de cambio de almacén

        - Modificar configuraciones de varios favoritos a la vez

        - Sincronizar favoritos con sistema externo

        '
      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.

        Alternativa a PUT /favorites/{id} con semántica POST.


        ## Propósito

        Proporcionar endpoint POST para edición de favorito individual.


        ## Objetivo

        Facilitar integración con clientes que prefieren POST sobre PUT.

        '
      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.


        ## Propósito

        Permitir limpieza de plantillas obsoletas o no utilizadas.


        ## Objetivo

        Mantener organizado el catálogo de rutas favoritas.


        ## Casos de Uso

        - Eliminar rutas que ya no se utilizan

        - Limpiar favoritos duplicados

        - Remover plantillas de clientes inactivos

        '
      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.

        Alternativa a PUT /favorites con ID en body.


        ## Propósito

        Proporcionar endpoint RESTful estándar para actualización.


        ## Objetivo

        Facilitar integración con clientes HTTP estándar.

        '
      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.


        ## Propósito

        Finalizar el período de recepción de ofertas y preparar para adjudicación.


        ## Objetivo

        Cerrar el proceso de pujas para proceder con la evaluación final y selección.


        ## Casos de Uso

        - Finalizar período de pujas antes de tiempo límite

        - Preparar subasta para adjudicación inmediata

        - Evitar nuevas ofertas durante evaluación


        ## Estados Válidos

        Solo se pueden bloquear subastas en: planned, empty, published

        '
      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.


        ## Propósito

        Facilitar envíos directos sin proceso de subasta pública.


        ## Objetivo

        Permitir asignación directa a transportistas de confianza o con acuerdos previos.


        ## Casos de Uso

        - Transporte especializado con transportista certificado

        - Envíos urgentes con transportista disponible inmediatamente

        - Relaciones comerciales establecidas con tarifas negociadas

        - Mercancías peligrosas con licencias específicas

        '
      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.


        ## Propósito

        Restringir la visibilidad de una subasta a proveedores preseleccionados.


        ## Objetivo

        Facilitar procesos de cotización cerrados con proveedores homologados.


        ## Casos de Uso

        - Invitación a transportistas homologados

        - Cotización cerrada con proveedores preferentes

        - Cumplimiento de políticas de compras con lista aprobada

        '
      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.


        ## Propósito

        Permitir rechazo de ofertas no satisfactorias y continuar con el proceso de
        selección.


        ## Objetivo

        Proporcionar flexibilidad en la selección rechazando ofertas inadecuadas.


        ## Casos de Uso

        - Rechazar oferta de transportista con mal historial

        - Declinar precio fuera de presupuesto

        - Eliminar transportista no confiable del proceso


        ## Proceso de Rechazo

        1. Encuentra TODAS las pujas del usuario rechazado

        2. Elimina todas las pujas de ese usuario

        3. Recalcula la mejor puja entre las restantes

        4. Actualiza bidWinner con la nueva mejor oferta

        '
      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.


        ## Propósito

        Facilitar el seguimiento de contratos pendientes de formalización.


        ## Objetivo

        Proporcionar visibilidad de subastas que requieren firma digital.


        ## Casos de Uso

        - Dashboard de pendientes de firma

        - Recordatorios de firmas pendientes

        - Gestión de contratos en formalizació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.


        ## Propósito

        Formalizar digitalmente el acuerdo de transporte.


        ## Objetivo

        Completar el proceso legal de aceptación del contrato con firma electrónica.


        ## Casos de Uso

        - Firmar contrato después de revisión

        - Iniciar generación de documentación

        - Activar inicio de servicio

        '
      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.


        ## Propósito

        Proporcionar acceso completo a todos los datos de una subasta incluyendo relaciones
        populadas.


        ## Objetivo

        Permitir visualización detallada de subastas con toda la información necesaria
        para

        gestión, seguimiento y toma de decisiones.


        ## Casos de Uso

        - Ver detalles completos antes de aceptar o rechazar pujas

        - Consultar historial de pujas recibidas

        - Verificar información de direcciones de carga y descarga

        - Revisar estado de firmas digitales

        - Acceder a delivery asociado si existe

        - Verificar ruta calculada con distancia


        ## Datos Populados


        La respuesta incluye los siguientes campos con información completa:


        - **etl_address**: Dirección de carga con location, name_address, name

        - **etd_address**: Dirección de descarga con location, name_address, name

        - **bids**: Array completo de todas las pujas recibidas

        - **bidWinner**: Puja ganadora actual (si existe)

        - **bidAsigned**: Puja asignada final (si existe)

        - **rejectedBy**: Array de IDs de usuarios rechazados

        - **delivery**: ID del delivery asociado (si existe)

        - **routeMinimal**: Objeto con distancia calculada en kilómetros

        '
      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.


        ## Propósito

        Permitir que los gestores/administradores reenvíen el email de activación

        a usuarios registrados que no lo han recibido o su token ha expirado.


        ## Casos de Uso

        - Usuario no recibió el email de activación tras el registro

        - El email de activación fue al spam o carpeta de promociones

        - El token anterior expiró y se necesita uno nuevo

        - Administrador reenvía activación a un usuario específico


        ## Flujo del Proceso

        1. Cliente solicita reenvío de email mediante ID de usuario o email

        2. Middleware m.isGestor verifica que el usuario tiene rol de gestor/admin/dev
        (401 si no)

        3. Servidor busca usuario por ID o email proporcionado

        4. Si el usuario existe y no está activo, genera nuevo token

        5. Si el usuario ya está activo, puede enviar notificación pero no reactiva

        6. Se envía email de activación con el nuevo token

        7. Se retorna confirmación de envío al cliente

        8. Por seguridad, no se revela si el email existe cuando no se encuentra


        ## Seguridad

        - Se genera nuevo token para seguridad

        - Requiere permisos de gestión (rol gestor, admin o dev)

        - No revela si el usuario existe (por seguridad)

        - Requiere parámetro ID o email (al menos uno)

        - Token anterior queda invalidado con el nuevo envío

        '
      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

        enviado por correo electrónico, verificando el email y habilitando el acceso.


        ## Propósito

        Activar una cuenta de empresa recién registrada mediante el token de activación

        enviado por correo electrónico, verificando el email y habilitando el acceso.


        ## Casos de Uso

        - Usuario hace clic en el enlace de activación del email de registro

        - Usuario accede directamente a la URL con el token de activación

        - Sistema activa la cuenta tras verificar el token


        ## Flujo del Proceso

        1. Usuario recibe email de bienvenida con enlace de activación

        2. Usuario hace clic en el enlace que apunta a este endpoint GET

        3. Servidor busca usuario por token de recuperación en base de datos

        4. Si el usuario existe y el token es válido, marca cuenta como activa

        5. Se establece status: true, emailVerified: true y emailVerifiedDate

        6. Se elimina el token de activación (marca como usado)

        7. Se envía email de confirmación de activación al usuario

        8. Se renderiza plantilla HTML de éxito

        9. Si el token no es válido, se renderiza error


        ## Seguridad

        - Token es de un solo uso

        - Solo cuentas inactivas pueden ser activadas

        - Se verifica email en el momento de activación

        - Token tiene validez limitada

        - No requiere autenticación (usuario aún no puede acceder)

        '
      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

        los datos obligatorios completos.


        ## Propósito

        Verificar que el perfil del usuario de empresa esté completo antes de permitir

        ciertas acciones que requieren información completa (crear subastas, publicar
        ofertas).


        ## Casos de Uso

        - Sistema verifica completitud del perfil antes de permitir crear subastas

        - UI muestra indicador de perfil incompleto al usuario

        - Se muestra mensaje de acción requerida para completar perfil

        - Verificación periódica en dashboard del usuario


        ## Flujo del Proceso

        1. Usuario autenticado solicita verificación de perfil

        2. Middleware m.isLoged verifica que el usuario está autenticado (401 si no)

        3. Cliente envía GET con token JWT en headers

        4. Servidor valida token JWT y extrae ID de usuario

        5. Servidor busca usuario en base de datos por ID

        6. Sistema verifica campos obligatorios del perfil

        7. Si todos los campos requeridos están completos, retorna true

        8. Si faltan campos requeridos, retorna false

        9. El indicador permite al frontend mostrar mensajes apropiados


        ## Campos Verificados

        - Nombre completo

        - Email válido y verificado

        - Datos de empresa completos

        - Dirección fiscal configurada

        - Teléfono de contacto

        - Información de pagos configurada


        ## Seguridad

        - Requiere usuario autenticado (JWT válido)

        - No expone información específica sobre qué campos faltan

        '
      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\n\
        4. 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)\n\
        6. 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\n\
        9. 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:\n\
        Email 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.


        ## Propósito

        Renderizar una plantilla HTML que permite al usuario establecer una nueva
        contraseña

        mediante el token de recuperación recibido por email.


        ## Casos de Uso

        - Usuario hace clic en el enlace de recuperación del email

        - Usuario accede directamente a la URL con el token de recuperación

        - El navegador redirige al formulario de cambio de contraseña


        ## Flujo del Proceso

        1. Usuario recibe email de recuperación con enlace al token

        2. Usuario hace clic en el enlace que apunta a este endpoint GET

        3. Servidor valida el token de recuperación en base de datos

        4. Si el token es válido y corresponde a un usuario, renderiza formulario
        HTML

        5. Si el token es inválido o ha expirado, muestra error

        6. El formulario permite ingresar nueva contraseña y confirmación

        7. Al enviar el formulario, se llama al endpoint POST /company/auth/recovery_password


        ## Seguridad

        - El token es de un solo uso

        - El token tiene validez limitada

        - No requiere autenticación (usuario aún no puede acceder)

        - Se valida que el token exista y sea válido

        '
      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.


        ## Propósito

        Permitir que un usuario establezca una nueva contraseña después de olvidar
        la anterior,

        validando el token de recuperación recibido por correo electrónico.


        ## Casos de Uso

        - Usuario completa el formulario de recuperación con nueva contraseña

        - Usuario envía los datos del formulario al backend

        - Sistema actualiza la contraseña y notifica el éxito


        ## Flujo del Proceso

        1. Usuario completa el formulario de recuperación con nueva contraseña

        2. Cliente envía POST con token, password y password_confirm

        3. Servidor valida que las contraseñas estén presentes y no estén vacías

        4. Servidor valida que password y password_confirm coincidan

        5. Servidor busca usuario por token de recuperación en base de datos

        6. Si el token es válido, actualiza la contraseña con hash bcrypt

        7. Se elimina el token de recuperación (marca como usado)

        8. Se renderiza plantilla HTML de éxito

        9. Si el token no existe, renderiza error


        ## Seguridad

        - Contraseña se hashea con bcrypt antes de guardar

        - Token se elimina tras uso (no reutilizable)

        - Contraseña nueva debe tener mínimo 8 caracteres

        - Se valida que password y password_confirm coincidan exactamente

        '
      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\n\
        3. Crea dirección fiscal\n4. Crea empresa en base de datos\n5. Crea usuario\
        \ administrador\n6. Configura cuenta Stripe (customer + connected account)\n\
        7. Envía email de bienvenida\n8. Crea tarifa inicial gratuita por 1 mes\n\
        9. Vincula con proveedor existente si el taxid coincide\n\n### Flujo detallado:\n\
        1. 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)\n\
        5. 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\n\
        9. 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).

        Genera una nueva contraseña aleatoria y la envía por email.


        ## Propósito

        Permitir que los administradores reseteen la contraseña de cualquier usuario

        generando una contraseña temporal segura y enviándola por email.


        ## Casos de Uso

        - Administrador necesita resetear contraseña de un usuario bloqueado

        - Usuario no puede acceder y requiere intervención administrativa

        - Soporte técnico resetea credenciales de usuario


        ## Flujo del Proceso

        1. Administrador solicita reset de contraseña de usuario

        2. Middleware m.isGestor verifica que el usuario tiene rol de gestor/admin/dev
        (401 si no)

        3. Cliente envía POST con ID del usuario en la URL

        4. Servidor busca usuario por ID en base de datos

        5. Si el usuario no existe, retorna error 404

        6. Si el usuario existe, genera contraseña aleatoria segura

        7. Hashea la nueva contraseña con bcrypt

        8. Guarda nueva contraseña en base de datos

        9. Envía email con la nueva contraseña generada

        10. Retorna datos del usuario actualizados (sin incluir la contraseña)

        11. Registra acción en historial de seguridad


        ## Seguridad

        - Requiere autenticación y permisos de gestión (rol gestor, admin o dev)

        - Contraseña generada aleatoriamente es segura (mínimo 8 caracteres)

        - Se envía por email para asegurar que el usuario real la reciba

        - Requiere autorización adecuada para realizar esta operació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

        Endpoint para buscar subastas de transporte disponibles con filtros avanzados.

        Permite filtrar por ubicaciones de carga/descarga, tipo de carga y paginación.


        ## Objetivo

        Facilitar a los transportistas la búsqueda de subastas publicadas que coincidan
        con

        sus capacidades y rutas de interés, optimizando el proceso de encuentros entre

        oferta y demanda de transporte.


        ## Casos de Uso

        - Transportista buscando cargas disponibles en una ruta específica (ej: Madrid
        a Barcelona)

        - Transportista con vehículos refrigerados buscando carga de temperatura controlada

        - Empresas verificando subastas activas en el mercado

        - Integración con sistemas de gestión de flotas para automatizar la búsqueda
        de cargas


        **Ejemplo de uso**:

        ```bash

        GET /company/bid-auctions?addr_load=Madrid&addr_delivery=Barcelona&is_fresh=true&page=2

        ```

        '
      parameters:
      - description: 'Número de página para paginación.

          Valor por defecto 1. Mínimo 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.

          Valor por defecto 20. Rango permitido 1-100.

          '
        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.

          Búsqueda por coincidencia parcial (case insensitive).

          Ejemplo: "Madrid" o "28001"

          '
        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.

          Búsqueda por coincidencia parcial (case insensitive).

          Ejemplo: "Barcelona" o "08001"

          '
        in: query
        name: addr_delivery
        required: false
        schema:
          example: Barcelona
          maxLength: 100
          minLength: 2
          type: string
      - description: 'Filtro para carga refrigerada.

          - true: solo subastas que requieren refrigeración

          - false: solo subastas que no requieren refrigeración

          - omitir: incluir todos los tipos

          '
        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.

            Devuelve array de subastas con metadatos de paginación.

            '
          headers: {}
        '400':
          description: 'Parámetros inválidos. Posibles causas:

            - Formato de página o límite incorrecto

            - Valor booleano inválido para is_fresh

            '
          headers: {}
        '401':
          description: 'No autorizado. Token JWT inválido, expirado o no proporcionado.

            '
          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.

                    Debe tener máximo 2 decimales.

                    Debe ser menor que la puja actual (si existe).

                    '
                  example: 850.5
                  format: float
                  minimum: 0.01
                  type: number
                service_code:
                  description: 'Código único identificador del servicio/subasta.

                    Formato: TRANS-XXXXX donde XXXXX son 5 dígitos.

                    '
                  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.

            Devuelve los detalles actualizados de la subasta.

            '
          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:

            - BID_NOT_VALID: Monto inválido (negativo o formato incorrecto)

            - SERVICE_CODE_NOT_PROVIDED: Código de servicio no proporcionado

            - AUCTION_NOT_ELIGIBLE: Servicio no encontrado o no elegible para pujas

            - BID_TOO_HIGH: Puja mayor que la puja actual más baja

            '
          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:

            - Token JWT inválido o expirado

            - Usuario sin permisos de transportista

            '
          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\n\
        participaciones 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.

          Valor por defecto 1. Mínimo 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.

          Valor por defecto 20. Rango permitido 1-50.

          '
        in: query
        name: limit
        required: false
        schema:
          default: 20
          example: 20
          maximum: 50
          minimum: 1
          type: integer
      - description: 'Filtra subastas por estado del usuario:

          - **all**: Todas las subastas con pujas del usuario (default)

          - **open**: Subastas publicadas activas (date_end >= hoy, status: published)

          - **to_sign**: Subastas adjudicadas pendientes de firma (status: awarded,
          signed_by_trucker: false)

          - **awarded**: Todas las subastas adjudicadas (status: awarded)

          - **closed**: Subastas cerradas (status: locked, canceled, rejected)

          - **lost**: Subastas aprobadas pero no ganadas (status: approved, usuario
          no es ganador)

          - **won**: Subastas aprobadas y ganadas (status: approved, usuario es ganador)

          '
        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.

          Búsqueda por coincidencia parcial (case insensitive).

          '
        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.

          Búsqueda por coincidencia parcial (case insensitive).

          '
        in: query
        name: addr_delivery
        required: false
        schema:
          example: Barcelona
          maxLength: 100
          minLength: 2
          type: string
      - description: 'Filtrar por descripción de la carga.

          Búsqueda por coincidencia parcial (case insensitive).

          '
        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.

          Filtra subastas con carga que no exceda esta altura.

          '
        in: query
        name: cargo_height
        required: false
        schema:
          example: 150
          maximum: 500
          minimum: 0
          type: integer
      - description: 'Peso máximo de carga en kilogramos.

          Filtra subastas con carga que no exceda este peso.

          '
        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.

            Incluye metadatos de paginación y array de subastas con las pujas del
            usuario.

            '
          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:

            - Token JWT inválido o expirado

            - Usuario no autenticado

            '
          headers: {}
      security:
      - bearerAuth: []
      - apiKeyAuth: []
      summary: Get my active auctions
      tags:
      - Bid Auctions
  /company/bid-auctions/minimal/{service_code}:
    get:
      deprecated: false
      description: '## Propósito

        Devuelve los costos estimados para realizar una puja en una subasta.

        Incluye cálculo de distancia, peajes, combustible y tarifas de plataforma.


        ## Objetivo

        Permitir a los transportistas calcular rápidamente el costo estimado de una
        puja

        sin tener que cargar todos los detalles de la subasta.


        ## Casos de Uso

        - Verificación rápida de viabilidad económica de una subasta

        - Integración con sistemas que requieren respuestas ligeras

        - Widgets de dashboard con información resumida

        - Cálculo de margen de beneficio antes de pujar


        **Ejemplo de uso**:

        ```bash

        GET /company/bid-auctions/minimal/TRANS-12345

        ```


        **Nota**: Este endpoint devuelve información de costos completa, no solo

        datos básicos de la puja. El nombre "minimal" se refiere a que es una

        respuesta ligera comparada con obtener la subasta completa.

        '
      parameters:
      - description: 'Código único del servicio/subasta.

          Formato: TRANS-XXXXX donde XXXXX son 5 dígitos.

          '
        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.

                  Devuelve información completa de costos para que el transportista

                  pueda calcular su viabilidad económica.

                  '
                properties:
                  distance:
                    description: 'Distancia de la ruta calculada en kilómetros.

                      Basada en la ruta óptima entre origen y destino.

                      '
                    example: 450.5
                    format: float
                    minimum: 0
                    type: number
                  duration:
                    description: 'Duración estimada del viaje en minutos.

                      Considera velocidad promedio y tráfico esperado.

                      '
                    example: 320
                    minimum: 0
                    type: integer
                  fee:
                    description: 'Tarifa de plataforma aplicada (porcentaje).

                      Ejemplo: 4.2 significa 4.2% sobre el costo base.

                      '
                    example: 4.2
                    format: float
                    minimum: 0
                    type: number
                  fuel_cost:
                    description: 'Costo estimado de combustible para el viaje.

                      Calculado según consumo promedio y distancia.

                      '
                    example: 180.0
                    format: float
                    minimum: 0
                    type: number
                  percent_price:
                    description: 'Ajuste por volumen/peso (opcional).

                      Solo presente si la carga excede la capacidad normal del vehículo.

                      Se añade al costo total cuando aplica.

                      '
                    example: 50.0
                    format: float
                    minimum: 0
                    nullable: true
                    type: number
                  toll_cost:
                    description: 'Costo estimado de peajes en la ruta.

                      Calculado según peajes activos en el trayecto.

                      '
                    example: 25.5
                    format: float
                    minimum: 0
                    type: number
                  total:
                    description: 'Costo de transporte base sin tarifa de plataforma.

                      Calculado según distancia, duración y costos operativos.

                      '
                    example: 1200.0
                    format: float
                    minimum: 0
                    type: number
                  total_cost:
                    description: 'Costo total con tarifa de plataforma aplicada.

                      Es el monto final que el transportista pagaría por el servicio.

                      '
                    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.

            Devuelve objeto completo con desglose de costos estimados.

            '
          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:

            - NO_TOKEN: Token JWT inválido o expirado

            - Usuario no autenticado

            '
          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:

            - El código no existe

            - El usuario no tiene permisos para ver esta puja

            '
          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

        Busca y devuelve ubicaciones (ciudad, estado, país, código postal, provincia)

        presentes en subastas publicadas. Permite búsqueda por texto libre que coincide

        con múltiples campos de dirección.


        ## Objetivo

        Facilitar la autocompletación de campos de ubicación al buscar subastas,

        ofreciendo sugerencias de ubicaciones válidas basadas en subastas activas.


        ## Casos de Uso

        - Autocompletar campo de origen al buscar subastas

        - Descubrir ubicaciones disponibles en la plataforma

        - Filtrar subastas por ubicación con sugerencias inteligentes


        **Ejemplo de uso**:

        ```bash

        GET /company/bid-auctions/search-location?search=Madrid&page=1&limit=10

        ```


        **Nota**: Este endpoint utiliza caché para mejorar el rendimiento.

        Busca en TODAS las subastas publicadas, no solo las del usuario.

        '
      parameters:
      - description: 'Número de página para paginación.

          Valor por defecto 1. Mínimo 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.

          Valor por defecto según ITEMS_PAGE del entorno. Rango permitido 1-100.

          '
        in: query
        name: limit
        required: false
        schema:
          example: 20
          maximum: 100
          minimum: 1
          type: integer
      - description: 'Término de búsqueda para ubicaciones.

          Busca coincidencias parciales (case insensitive) en:

          - city (ciudad)

          - state (estado/región)

          - country (país)

          - zipcode (código postal)

          - neighborhood (barrio)

          - province (provincia)


          Se pueden usar múltiples términos separados por coma.

          Ejemplo: "Madrid,España" buscará coincidencias en ambos términos.

          '
        in: query
        name: search
        required: false
        schema:
          example: Madrid
          maxLength: 200
          type: string
      - description: 'Indica qué tipo de dirección buscar:

          - **etl**: Direcciones de carga (origen)

          - **etd**: Direcciones de descarga (destino)

          - omitir: busca en direcciones de carga por defecto

          '
        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.

            Devuelve array de ubicaciones con metadatos de paginación.

            '
          headers: {}
        '400':
          description: 'Parámetros inválidos. Posibles causas:

            - Formato de página o límite incorrecto

            '
          headers: {}
        '401':
          description: 'No autorizado. Token JWT inválido, expirado o no proporcionado.

            '
          headers: {}
        '500':
          description: 'Error interno del servidor.

            '
          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

        Busca y devuelve ubicaciones presentes únicamente en las subastas donde

        el usuario autenticado ha realizado pujas. Permite búsqueda por texto

        libre que coincide con múltiples campos de dirección.


        ## Objetivo

        Ofrecer al usuario sugerencias de ubicaciones basadas en su historial

        de participación en subastas, facilitando la búsqueda de subastas

        relacionadas con sus rutas de interés previas.


        ## Casos de Uso

        - Autocompletar campo de ubicación filtrado por historial del usuario

        - Descubrir rutas frecuentes basadas en pujas anteriores

        - Buscar subastas similares a las que el usuario ya participó


        **Ejemplo de uso**:

        ```bash

        GET /company/bid-auctions/search-my-location?search=Barcelona&page=1&limit=10

        ```


        **Nota**: A diferencia de `/search-location`, este endpoint solo busca

        en subastas donde el usuario tiene pujas registradas.

        '
      parameters:
      - description: 'Número de página para paginación.

          Valor por defecto 1. Mínimo 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.

          Valor por defecto según ITEMS_PAGE del entorno. Rango permitido 1-100.

          '
        in: query
        name: limit
        required: false
        schema:
          example: 20
          maximum: 100
          minimum: 1
          type: integer
      - description: 'Término de búsqueda para ubicaciones.

          Busca coincidencias parciales (case insensitive) en:

          - city (ciudad)

          - state (estado/región)

          - country (país)

          - zipcode (código postal)

          - neighborhood (barrio)

          - province (provincia)


          Se pueden usar múltiples términos separados por coma.

          Ejemplo: "Barcelona,España" buscará coincidencias en ambos términos.

          También acepta formato objeto: `search[city]=Barcelona`

          '
        in: query
        name: search
        required: false
        schema:
          example: Barcelona
          maxLength: 200
          type: string
      - description: 'Indica qué tipo de dirección buscar:

          - **etl**: Direcciones de carga (origen)

          - **etd**: Direcciones de descarga (destino)

          - omitir: busca en direcciones de carga por defecto

          '
        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.

            Devuelve array de ubicaciones con metadatos de paginación.

            '
          headers: {}
        '400':
          description: 'Parámetros inválidos. Posibles causas:

            - Formato de página o límite incorrecto

            '
          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:

            - USER_NOT_LOGGED_IN: Token JWT inválido, expirado o no proporcionado

            - Usuario no autenticado

            '
          headers: {}
        '500':
          description: 'Error interno del servidor.

            '
          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

        Permite a un usuario eliminar una puja que haya realizado previamente,

        siempre que la subasta aún no haya sido adjudicada.


        ## Objetivo

        Permitir a los transportistas retractarse de una puja antes de que la subasta

        sea adjudicada, manteniendo la flexibilidad en el proceso de participación.


        ## Casos de Uso

        - Transportista se da cuenta que cometió un error en el monto de la puja

        - Transportista ya no está disponible para la fecha de transporte

        - Transportista encontró una mejor oportunidad en otra subasta

        - Error en los datos de la puja que requiere corregirse


        ## Restricciones

        - Solo se pueden eliminar pujas propias

        - La subasta debe estar en estado ''published''

        - Requiere autenticación mediante JWT

        - No se puede eliminar si la subasta ya fue adjudicada


        **Ejemplo de uso**:

        ```bash

        DELETE /company/bid-auctions/TRANS-12345

        ```

        '
      parameters:
      - description: 'Código único del servicio/subasta donde se eliminó la puja.

          Formato: TRANS-XXXXX donde XXXXX son 5 dígitos.

          '
        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.

            Devuelve mensaje de confirmació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:

            - NO_TOKEN: Token JWT inválido o expirado

            - TOKEN_NOT_VALID: Token no válido para este usuario

            - Usuario no es el propietario de la puja

            '
          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:

            - SERVICE_CODE_NOT_PROVIDED: El código de servicio no existe

            - BID_NOT_FOUND: El usuario no tiene pujas en esta subasta

            '
          headers: {}
      security:
      - bearerAuth: []
      - apiKeyAuth: []
      summary: Delete a bid
      tags:
      - Bid Auctions
    get:
      deprecated: false
      description: '## Propósito

        Devuelve todos los datos detallados de una subasta específica, incluyendo:

        - Información de carga/descarga

        - Especificaciones técnicas de la carga

        - Historial de pujas

        - Estado actual de la subasta


        ## Objetivo

        Proporcionar información completa para que los transportistas puedan tomar

        decisiones informadas antes de realizar una puja.


        ## Casos de Uso

        - Ver detalles completos antes de pujar

        - Revisar estado de una subasta en la que se participó

        - Integración con sistemas de gestión de flotas

        - Análisis de competencia y precios de mercado


        **Ejemplo de uso**:

        ```bash

        GET /company/bid-auctions/TRANS-12345

        ```


        **Campos computados incluidos**:

        Este endpoint extiende el schema base `Auction` con campos computados:

        - `canBid`: true si el usuario puede realizar pujas (estado: published)

        - `amWinner`: true si el usuario es el ganador (estado: awarded o approved)

        - `canSign`: true si el usuario puede firmar (ganador y no firmado por company)

        - `canSeeDelivery`: true si el usuario puede ver el delivery (ganador con
        delivery asociado)


        **Ubicación de código**: `src/features/company/bid-auction/company.details.controller.js`

        '
      parameters:
      - description: 'Código único identificador de la subasta.

          Formato: TRANS-XXXXX donde XXXXX son 5 dígitos.

          '
        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.

            Incluye toda la información técnica y comercial relevante,

            más campos computados según el usuario actual.

            '
          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:

            - NO_TOKEN: Token JWT inválido o expirado

            - Usuario no autenticado

            '
          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:

            - El código no existe

            - La subasta fue eliminada

            - El usuario no tiene permisos para verla

            '
          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.

          Formato: TRANS-XXXXX donde XXXXX son 5 dígitos.

          '
        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.

                    Formatos aceptados: JPEG, PNG (máx. 5MB)

                    '
                  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.

            La subasta pasa a estado ''completed'' y se crea el delivery.

            '
          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:

            - INVALID_FILE: Archivo no es una imagen válida

            - FILE_TOO_LARGE: Tamaño excede el límite de 5MB

            - INVALID_FORMAT: Formato no soportado (solo JPEG/PNG)

            - AUCTION_NOT_SIGNED: La subasta no cumple requisitos para firma

            '
          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:

            - NO_TOKEN: Token JWT inválido o expirado

            - Usuario no es el ganador de la subasta

            - Subasta no está en estado ''awarded''

            '
          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\n\
        flowchart 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).


          Formato: `YYYY-MM-DD`.

          Si no se especifica, usa el inicio del mes actual.

          '
        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).


          Formato: `YYYY-MM-DD`.

          Si no se especifica, usa la fecha actual.

          '
        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.


          Formato: `YYYY-MM-DD`.

          Se procesa con granularidad mensual (se extraerá el mes).

          '
        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.


          Formato: `YYYY-MM-DD`.

          Se procesa con granularidad mensual (se extraerá el mes).

          '
        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.

                          Campos obtenidos de `delivery.parseInvoice()`.

                          '
                        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.

                          Un elemento por cada mes en el rango con datos.

                          '
                        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.

                                **Campo condicional**: solo presente si hay deliveries
                                relacionados en el periodo.

                                '
                              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.

                                **Campo condicional**: solo presente si hay deliveries
                                relacionados.

                                '
                              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.

                    - `true`: Permitir recursos adicionales con cargo extra

                    - `false`: Desactivar recursos que superen los límites del plan

                    '
                  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.

                    Default: 50

                    '
                  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.

                    Si no se proporciona, se requiere contexto de empresa en la request.

                    '
                  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\n\
        flowchart 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).

          Si no se proporciona, se usa la empresa del contexto de la request.

          '
        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.

          Se busca en ambos campos (title y label) con regex case-insensitive.

          '
        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\n\
        Permitir 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\n\
        Si 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`\n\
        2. 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.

          El `service_code` identifica el delivery y `emails` contiene la lista de
          destinatarios.

          '
        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.

            La respuesta confirma que el email fue enviado a la cola de ecmr_back,

            pero NO confirma la entrega efectiva a los destinatarios.

            '
        '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.

            **Códigos de error:**

            - `SERVICE_CODE_REQUIRED`: Falta el parámetro `service_code`

            - `EMAILS_REQUIRED`: Falta el parámetro `emails` o está vacío (array con
            longitud 0)

            '
        '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.

            **Códigos de error:**

            - `USER_NOT_FOUND`: El usuario autenticado no existe en la base de datos

            - `AUTHORIZATION_TOKEN_REQUIRED`: Falta el token en los headers

            - Payment validation failed (middleware `isPaymentUpdate`): La compañía
            no tiene estado de pago válido

            '
        '403':
          content:
            application/json:
              example:
                message: PAYMENT_REQUIRED
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: 'Prohibido - La compañía no tiene permisos de pago actualizados.

            El middleware `isPaymentUpdate` rechazó la solicitud por falta de pago
            activo.

            '
        '404':
          content:
            application/json:
              example:
                message: NOT_FOUND
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: 'No encontrado - El delivery no existe para esta compañía.

            **Códigos de error:**

            - `NOT_FOUND`: No existe un delivery con ese `service_code` para esta
            compañía

            '
        '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.

            **Posibles causas:**

            - El servicio ecmr_back no está disponible

            - Timeout esperando el envío de emails (10 segundos máximo)

            - Error interno en el servicio ecmr_back

            '
      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.

          Este código es generado automáticamente al crear el delivery y permite identificar
          de forma unívoca la operación de transporte asociada.

          **Formato típico:** PREFIJO-AÑO-NÚMERO (ejemplo: TRANS-2023-0042)

          '
        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.

            El archivo es transmitido como stream binario directo desde el servicio
            ecmr_back.

            '
          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.

                Formato: attachment; filename="{service_code}.pdf"

                '
              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.

            **Posibles códigos de error:**

            - `USER_NOT_FOUND`: El usuario autenticado no existe en la base de datos

            - `AUTHORIZATION_TOKEN_REQUIRED`: Falta el token en los headers o es inválido

            '
        '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.

            **Posibles causas:**

            - El código de servicio es incorrecto o no pertenece a esta compañía

            - El delivery existe pero aún no se ha generado el CMR

            - El archivo CMR fue eliminado o no está disponible en ecmr_back

            **Códigos de error:**

            - `NOT_FOUND`: El delivery no existe para esta compañía

            - `FILE_NOT_AVAILABLE`: El CMR no está disponible en ecmr_back

            '
        '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.

            **Posibles causas:**

            - El servicio ecmr_back no está disponible

            - Timeout esperando la generación del PDF (30 segundos máximo)

            - Error interno en el servicio ecmr_back

            '
      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:

            - El token JWT es inválido o ha expirado

            - El usuario no tiene permisos para acceder a estos datos

            '
          headers: {}
        '404':
          description: 'Compañía no encontrada. Ocurre cuando:

            - El usuario no está asociado a ninguna compañía

            - La compañía fue eliminada

            '
          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:

            - Faltan campos obligatorios

            - El formato del email es inválido

            - La dirección no puede ser validada

            '
          headers: {}
        '401':
          description: 'No autorizado. El token JWT es inválido o ha expirado.

            Se debe renovar el token mediante el endpoint de autenticación.

            '
          headers: {}
        '404':
          description: 'Compañía no encontrada. Ocurre cuando:

            - El usuario no está asociado a ninguna compañía

            - La compañía fue eliminada

            '
          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.

        La firma se almacena como una imagen en formato base64 y se asocia al perfil
        de la compañía.


        **Requisitos:**

        - Formato de imagen: PNG, JPG o JPEG

        - Tamaño máximo: 2MB

        - Relación de aspecto recomendada: 3:1 (ancho:alto)


        **Casos de uso:**

        - Firmar documentos digitales

        - Mostrar firma en facturas y contratos


        **Ejemplo de petición:**

        ```

        PUT /editSign

        Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

        Authorization: Bearer [token]


        ----WebKitFormBoundary7MA4YWxkTrZu0gW

        Content-Disposition: form-data; name="image"; filename="firma.png"

        Content-Type: image/png


        (binary data)

        ----WebKitFormBoundary7MA4YWxkTrZu0gW--

        ```

        '
      parameters: []
      requestBody:
        content:
          multipart/form-data:
            schema:
              properties:
                image:
                  description: 'Archivo de imagen con la firma digital de la compañía.


                    **Requisitos:**

                    - Formatos aceptados: PNG, JPG, JPEG

                    - Tamaño máximo: 2MB

                    - Se recomienda relación de aspecto 3:1 (ancho:alto)


                    **Proceso:**

                    1. El archivo se convierte automáticamente a base64

                    2. Se almacena con el prefijo data URI (ej: "data:image/png;base64,...")

                    3. Se valida que sea una imagen válida


                    **Ejemplo de uso:**

                    ```

                    Content-Disposition: form-data; name="image"; filename="firma.png"

                    Content-Type: image/png


                    (binary data)

                    ```

                    '
                  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:

            - El archivo no es una imagen válida

            - Supera el tamaño máximo permitido

            - Tiene un formato no soportado

            '
          headers: {}
        '401':
          description: 'No autorizado. El token JWT es inválido o ha expirado.

            Se debe renovar el token mediante el endpoint de autenticación.

            '
          headers: {}
        '404':
          description: 'Compañía no encontrada. Ocurre cuando:

            - El usuario no está asociado a ninguna compañía

            - La compañía fue eliminada

            '
          headers: {}
        '500':
          description: 'Error procesando el archivo. Ocurre cuando:

            - Hay un error al convertir la imagen a base64

            - Fallo al guardar en la base de datos

            '
          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.

            Se debe renovar el token mediante el endpoint de autenticación.

            '
          headers: {}
        '404':
          description: 'Compañía no encontrada. Ocurre cuando:

            - El usuario no está asociado a ninguna compañía

            - La compañía fue eliminada

            '
          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:

            - CAN_NOT_CREATE: Error general al crear el ticket

            - Tipo de autor no válido (author debe ser: anonymous, company, o trucker)

            - Categoría no reconocida (relatedTo inválido)

            - Timestamp UTC inválido (middleware checkUTC)

            '
          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.

        Los contratos incluyen información completa sobre:

        - Datos de la empresa y transportista

        - Detalles del envío (origen, destino, tipo de carga)

        - Condiciones económicas

        - Estados de firma


        Ejemplo de uso típico:

        1. Listar contratos pendientes de firma

        2. Consultar historial de envíos

        3. Verificar condiciones de contratos activos

        '
      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.

        El sistema genera automáticamente el PDF con los datos actuales del contrato
        antes de enviarlo.


        Casos de uso típicos:

        - Envío a transportista para revisión previa a firma

        - Compartición con departamento financiero

        - Archivo interno en la empresa


        Requisitos:

        - El contrato debe existir y estar asociado a la empresa autenticada

        - Se debe proporcionar al menos una dirección de email válida

        '
      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:

            - Código de servicio no proporcionado

            - Lista de emails vacía o no proporcionada

            '
          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.

        El PDF incluye todos los datos del contrato con formato profesional:

        - Encabezado con logos y datos de las empresas

        - Detalles completos del envío

        - Condiciones económicas y de pago

        - Espacios para firmas digitales o físicas


        Características del PDF generado:

        - Formato A4 estándar

        - Marcas de agua con código único

        - Diseño responsivo para impresió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í)

                '
              schema:
                format: binary
                type: string
          description: 'PDF generado correctamente. Contiene:

            - Todas las cláusulas contractuales

            - Datos actualizados de las partes

            - Sellos de autenticidad

            '
          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:

            - Token JWT inválido o expirado

            - Error al generar el PDF del contrato

            '
          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:

            - El código de servicio es correcto

            - El contrato no ha sido eliminado

            - Tiene permisos para acceder a este contrato

            '
          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).


          - Valor mínimo: 1

          - Por defecto: 1

          - Los valores inválidos (< 1, NaN) pueden ser gestionados por mongoose-paginate

          '
        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.


          - Mínimo: 1

          - Por defecto: 25 (desde process.env.ITEMS_PAGE)

          - Nota: Actualmente el backend no aplica un límite máximo

          '
        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.


            La respuesta incluye los metadatos completos de paginación de mongoose-paginate-v2.

            '
          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\n\
        Authorization: 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}\n\
        Content-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).

                    Debe ser único. Se convertirá automáticamente a minúsculas.

                    '
                  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}\n\
        Content-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).

                    Debe coincidir con un país existente.

                    '
                  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\n\
        La 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');\n\
        const { 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.


          Valores válidos:

          - `taxID`: Números de identificación fiscal (NIF, CIF, VAT, etc.)

          - `phoneNumber`: Formatos de número de teléfono internacional

          - `plate`: Formatos de matrícula de vehículo

          - `email`: Formato de dirección de correo electrónico (genérico)


          Los valores inválidos devolverán un objeto de datos vacío sin error.

          '
        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.

                      Las claves son códigos de país ISO de 2 letras o "ALL" para
                      patrones genéricos.

                      Los valores son cadenas regex listas para usar con `new RegExp()`.

                      '
                    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.


            La estructura de respuesta siempre es `{status: 200, data: {...}}` donde
            data contiene el mapa de regex.


            **Cobertura por tipo**:

            - `taxID`: 15 países europeos

            - `phoneNumber`: 46+ países incluyendo el patrón genérico "ALL"

            - `plate`: 3 países (ES, FR, PT)

            - `email`: Patrón genérico con clave "ALL"

            '
          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\n\
        Eliminar 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: '

        Devuelve estadísticas de subastas y entregas para el panel de control empresarial
        de Cargoffer.


        ## Propósito

        Proporcionar métricas agregadas de actividad operativa y financiera de subastas
        y entregas

        para el panel de control (dashboard) de una empresa, permitiendo análisis
        de rendimiento

        y seguimiento de actividad en un rango de fechas específico.


        ## Objetivo

        Permitir a las empresas visualizar el desempeño de sus operaciones logísticas
        mediante

        indicadores clave de rendimiento (KPIs) basados en subastas y entregas realizadas.


        ## Casos de uso

        - Dashboard principal: visualización de KPIs del último año (valores por defecto)

        - Informes trimestrales: GET /?minDate=2024-01-01&maxDate=2024-03-31

        - Análisis mensual: GET /?minDate=2024-05-01&maxDate=2024-05-31

        - Análisis anual: GET /?minDate=2023-01-01&maxDate=2023-12-31


        ## Métricas de subastas


        Para cada grupo de subastas se devuelven los siguientes datos agregados:


        - **total_sale**: Monto total de ventas calculado según la lógica de pujas

        - **total_auctions**: Cantidad de subastas en el grupo

        - **total_savings**: Ahorro total vs precio inicial (total_start_price - total_sale)

        - **sumBidWinner**: Suma de montos de pujas ganadoras

        - **total_start_price**: Suma de precios iniciales de todas las subastas


        **Cálculo de total_sale por subasta**:

        1. Si existe puja ganadora (bidWinner): usa el monto de esa puja

        2. Si no, pero existe puja asignada (bidAsigned): usa el monto de esa puja

        3. Si no tiene ninguna puja: usa el precio inicial (start_price)


        **Grupos de subastas**:

        - **auctions**: Todas las subastas (estados: planned, published, awarded,
        approved)

        - **publishedAuctions**: Subastas publicadas (status: "published")

        - **awardedAuctions**: Subastas adjudicadas (status: "awarded")

        - **approvedAuctions**: Subastas aprobadas (status: "approved")

        - **plannedAuctions**: Subastas planificadas (status: "planned")


        ## Métricas de entregas


        Para cada grupo de entregas se devuelven los siguientes datos agregados:


        - **total_sale**: Monto total de entregas (basado en pujas ganadoras o asignadas)

        - **total_deliveries**: Cantidad de entregas en el grupo

        - **sumBidWinner**: Suma de montos de pujas ganadoras


        **Grupos de entregas**:

        - **plannedDeliveries**: Entregas planificadas (status: "planned")

        - **collectedDeliveries**: Entregas recogidas (status: "collected")

        - **deliveredDeliveries**: Entregas completadas (status: "delivered")


        ## Filtrado por rango de fechas


        Los parámetros `minDate` y `maxDate` permiten filtrar las estadísticas por
        período.

        El campo de fecha usado para el filtro depende del tipo de estadística y estado:


        **Para subastas**:

        - Se filtra por fecha de fin de la subasta (`date_end`)

        - Estados incluidos: planned, published, awarded, approved


        **Para entregas, el campo de fecha depende del estado**:

        - **collected** (recogidas): fecha estimada de carga (`etl_date`)

        - **delivered** (completadas): fecha estimada de entrega (`etd_date`)

        - **planned/replanned/accepted**: fecha de creación (`createdAt`)


        Esta lógica permite análisis más precisos según la fase del proceso logístico.


        **Valores por defecto**:

        - `minDate`: 1 año antes de la fecha actual

        - `maxDate`: Fecha actual


        ## Ejemplo de uso


        **Dashboard principal** (último año):

        ```http

        GET /company/dashboard/

        Authorization: Bearer {jwt_token}

        ```


        **Informe trimestral**:

        ```http

        GET /company/dashboard/?minDate=2024-01-01&maxDate=2024-03-31

        Authorization: Bearer {jwt_token}

        ```


        ## Notas importantes


        - Requiere autenticación con token JWT

        - Solo devuelve estadísticas de la empresa del usuario autenticado

        - Si el usuario no tiene empresa asignada, retorna error 404

        - Los estados de subastas y entregas están predefinidos en los modelos

        - Los cálculos se realizan mediante agregaciones MongoDB para optimizar rendimiento'
      parameters:
      - description: 'Fecha de inicio para filtrar estadísticas (formato YYYY-MM-DD).

          Por defecto es 1 año antes de la fecha actual.


          Ejemplos:

          - 2024-01-15 (15 de enero de 2024)

          - 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).

          Por defecto es la fecha actual.


          Ejemplos:

          - 2024-05-20 (20 de mayo de 2024)

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


            **Grupos de subastas** (AuctionStats):

            - auctions: Todas las subastas (planned, published, awarded, approved)

            - publishedAuctions: Subastas publicadas

            - awardedAuctions: Subastas adjudicadas

            - approvedAuctions: Subastas aprobadas

            - plannedAuctions: Subastas planificadas


            **Grupos de entregas** (DeliveryStats):

            - plannedDeliveries: Entregas planificadas

            - collectedDeliveries: Entregas recogidas

            - deliveredDeliveries: Entregas completadas


            Cada grupo de subastas incluye: total_sale, total_auctions, total_savings,
            sumBidWinner, total_start_price

            Cada 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.


            Soluciones:

            1. Verificar que el token sea válido y no haya expirado

            2. Asegurarse que el usuario tenga rol de empresa

            3. 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:

            - El ID de empresa asociado al usuario no existe

            - El usuario no tiene empresa asignada

            - La empresa fue desactivada


            Soluciones:

            1. Verificar que el usuario tenga una empresa válida asignada

            2. 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

          O etl_date >= minDate (condición OR).

          '
        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

          O etd_date <= maxDate (condición OR).

          '
        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:

          - accepted: Aceptado por transportista

          - planned: Planificado

          - replanned: Replanificado (cambios pendientes de aprobación)

          - collected: Carga recogida

          - issue: Incidente en curso

          - delivered: Entregado

          - paid: Pagado

          - claimed: Reclamado

          - canceled: Cancelado

          '
        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:

          - pallets: Palets

          - full: Carga completa

          - package: Paquete

          - trailer: Trailer completo

          '
        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:

          - service_code: Código único del envío (búsqueda parcial, case-insensitive)

          - custom_code: Código de referencia personalizado

          - etl_cargo_method: Método de carga

          - pallets_type: Tipo de palets

          - description: Descripción de la carga

          '
        example: DEL-12345
        in: query
        name: search
        required: false
        schema:
          maxLength: 200
          minLength: 1
          type: string
      - description: 'Incluir envíos cancelados en los resultados.

          - false (default): Excluye envíos con status=canceled

          - true: Incluye envíos cancelados

          '
        example: false
        in: query
        name: show_canceled
        required: false
        schema:
          default: false
          type: boolean
      - description: 'Incluir envíos reclamados en los resultados.

          - false (default): Excluye envíos con status=claimed

          - true: Incluye envíos reclamados

          '
        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.

            Se debe autenticar nuevamente.

            '
        '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

            o no tiene permisos para acceder a estos datos.

            '
      security:
      - bearerAuth: []
      - apiKeyAuth: []
      summary: List all shipments
      tags:
      - Delivery
  /company/delivery/active:
    get:
      deprecated: false
      description: '## Purpose

        Obtiene una lista paginada de los envíos activos de la empresa (aquellos con

        etd_date >= fecha actual).


        ## Objective

        Permitir a las empresas monitorear rápidamente sus envíos pendientes de ejecución

        o en curso para gestión operativa del día a día.


        ## Use Cases

        - Monitorear envíos en curso o pendientes

        - Visualizar envíos que requieren atención inmediata

        - Gestionar la logística de operaciones activas del día

        - Preparar recursos para próximos recogidas/entregas


        ## Filtering

        Este endpoint acepta todos los mismos parámetros de filtrado que GET /company/delivery/

        (minCreate, maxCreate, minAuction, maxAuction, minStart, maxStart, minEnd,
        maxEnd,

        minETL, maxETL, minETD, maxETD, minDate, maxDate, minETA, maxETA, status,

        cargo_type, search, show_canceled, show_claimed).


        Adicionalmente, aplica automáticamente un filtro:

        - etd_date >= new Date() (solo envíos con fecha de descarga futura)

        '
      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/

          '
        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.

            Se debe autenticar nuevamente.

            '
        '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

            o no tiene permisos para acceder a estos datos.

            '
      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.

            '
        '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.

            '
        '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.

            '
        '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

            o no pertenece a la empresa del usuario autenticado.

            '
      security:
      - bearerAuth: []
      - apiKeyAuth: []
      summary: Approve delivery changes
      tags:
      - Delivery
  /company/delivery/customcode/{id}:
    put:
      deprecated: false
      description: '## Purpose

        Actualiza el código de referencia personalizado de un envío.


        ## Objective

        Permitir a las empresas asignar sus propios códigos de referencia a los envíos

        para integración con sistemas internos y mejor trazabilidad.


        ## Use Cases

        - Asignar un código de pedido interno al envío

        - Vincular el envío con un sistema externo ERP/WMS

        - Agregar una referencia fácilmente reconocible para el equipo

        - Sincronizar códigos con sistemas de gestión empresarial


        ## Notes

        - El parámetro {id} debe ser MongoDB _id (no service_code)

        - custom_code es opcional, acepta string vacío

        - Longitud máxima: 50 caracteres

        - Puede actualizarse múltiples veces sin restricciones de estado

        '
      parameters:
      - description: 'MongoDB _id del envío a actualizar.

          NOTA: Requiere MongoDB _id, no service_code.

          '
        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).

                    Puede ser string vacío para eliminar el código actual.

                    '
                  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

            o no pertenece a la empresa del usuario autenticado.

            '
      security:
      - bearerAuth: []
      - apiKeyAuth: []
      summary: Save custom code
      tags:
      - Delivery
  /company/delivery/documents/{service_code}:
    get:
      deprecated: false
      description: '## Purpose

        Lista todos los documentos asociados a un envío.


        ## Objective

        Permitir a las empresas consultar la documentación adjunta a un envío

        para su gestión y descarga individual.


        ## Use Cases

        - Verificar qué documentos están adjuntos al envío

        - Obtener lista de documentos para descarga selectiva

        - Revisar documentación subida por la empresa o el transportista


        ## Notes

        - El nombre del documento se extrae del path (split por ''--'')

        - Solo se retorna el _id y el nombre legible del documento

        - No incluye el contenido del archivo (usar GET /documents/{service_code}/{document_id})

        '
      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

        Sube nuevos documentos a un envío, guardándolos en S3.


        ## Objective

        Permitir a las empresas adjuntar documentación adicional a sus envíos

        para complementar la información del transporte.


        ## Use Cases

        - Adjuntar facturas o albaranes al envío

        - Subir fotos de la carga antes del transporte

        - Agregar documentos aduaneros o certificados

        - Compartir documentación relevante con el transportista


        ## Notes

        - Acepta hasta 4 archivos por petición (multipart/form-data)

        - Los archivos se guardan en S3 bajo la clave del archivo

        - Cada documento se registra con owner_company (ID del usuario)

        - Retorna el delivery completo con el array documents actualizado

        - Aplicable tanto para empresa como para transportista

        '
      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

        Elimina un documento específico de un envío, borrándolo también de S3.


        ## Objective

        Permitir a las empresas gestionar la documentación de sus envíos,

        eliminando archivos que ya no sean necesarios o fueron subidos por error.


        ## Use Cases

        - Eliminar documentos subidos por error

        - Remover documentación obsoleta

        - Gestionar el espacio de almacenamiento

        - Corregir errores en la documentación adjunta


        ## Notes

        - Elimina el documento del array documents

        - Elimina el archivo físico de S3

        - Retorna el delivery completo con el array documents actualizado

        - Operación irreversible para el archivo en S3

        '
      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

        Descarga un documento específico de un envío desde S3.


        ## Objective

        Permitir a las empresas descargar individualmente los documentos adjuntos

        a un envío según sus necesidades.


        ## Use Cases

        - Descargar un documento específico sin descargar todo el ZIP

        - Visualizar un documento en el navegador

        - Compartir un documento concreto con terceros


        ## Notes

        - El archivo se sirve directamente desde S3

        - Content-Type depende del tipo de archivo

        - Verifica que el usuario tenga permisos (company o trucker_cia)

        '
      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

        Descarga un archivo ZIP con toda la documentación generada del envío.


        ## Objective

        Permitir a las empresas descargar un paquete completo con todos los documentos

        relacionados con un envío para archivo o compartir con terceros.


        ## Use Cases

        - Descargar documentación completa para archivo legal

        - Compartir todos los documentos con el transportista

        - Guardar copia de seguridad de la documentación del envío

        - Generar expediente completo para auditorías


        ## Generated Contents

        El ZIP incluye los siguientes archivos PDF cuando la información está disponible:

        - **CMR PDF**: Carta de porte electrónico

        - **Contract PDF**: Contrato de transporte (si tiene auction)

        - **Chat PDF**: Historial de mensajes intercambiados (si hay mensajes)

        - **Issues PDF**: Reporte de incidencias (si las hay)

        - **Details PDF**: Detalles completos del envío


        ## Notes

        - El archivo se genera dinámicamente

        - Solo se incluyen PDFs que tengan información disponible

        - El archivo se elimina del servidor temporal después de enviarse

        - Content-Type: application/zip

        '
      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

        Obtiene el historial completo de mensajes de un envío.


        ## Objective

        Permitir a las empresas consultar toda la conversación mantenida con

        el transportista sobre un envío específico.


        ## Use Cases

        - Revisar el historial de comunicaciones

        - Verificar instrucciones dadas al transportista

        - Consultar acuerdos verbales por escrito

        - Revisar archivos compartidos previamente


        ## Notes

        - El parámetro {id} puede ser service_code o MongoDB _id

        - Los mensajes se ordenan por createdAt DESC (más recientes primero)

        - Las URLs de archivos incluyen el prefijo /images?file=

        '
      parameters:
      - description: 'Identificador del envío. Puede ser service_code o MongoDB _id.

          '
        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.

          '
        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.

            '
        '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

        Obtiene la ruta y datos de geolocalización de un envío específico.


        ## Objective

        Proporcionar información detallada de tracking y ruta para seguimiento

        visual en mapas y herramientas de monitoreo.


        ## Use Cases

        - Visualizar la ruta completa del envío en un mapa

        - Obtener puntos de geolocalización de recogida y entrega

        - Recuperar la polyline de la ruta para renderizado

        - Consultar firmas y fechas de entrega/recogida


        ## Notes

        - Retorna la polyline codificada de la ruta

        - Incluye coordenadas de pickup y delivery con fechas

        - Útil para integración con mapas interactivos

        '
      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

        Obtiene la lista de transportistas de la empresa que pueden ser rastreados.


        ## Objective

        Proporcionar a las empresas un listado de sus transportistas asociados

        con información de posición para seguimiento en tiempo real.


        ## Use Cases

        - Ver qué transportistas están disponibles para seguimiento GPS

        - Consultar información de contacto de transportistas

        - Obtener lista de opciones para asignación de envíos

        - Monitorear flota de transportistas asociados


        ## Notes

        - Retorna truckers asociados a la empresa (cia.truckers)

        - Incluye información de posición actual si está disponible

        - No requiere parámetros adicionales

        '
      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\n\
        flowchart 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.

            '
        '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.

            '
        '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

            o no pertenece a la empresa del usuario autenticado.

            '
        '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.

            '
      security:
      - bearerAuth: []
      - apiKeyAuth: []
      summary: Cancel delivery service
      tags:
      - Delivery
    get:
      deprecated: false
      description: '## Purpose

        Obtiene los detalles completos de un envío específico, incluyendo toda la
        información

        relevante para su gestión y seguimiento.


        ## Objective

        Proporcionar a las empresas una vista completa y detallada de un envío individual,

        incluyendo información de direcciones, vehículo, mensajes, estado y capacidades
        de mensajería.


        ## Use Cases

        - Consultar detalles completos de un envío antes de realizar cambios

        - Verificar información completa de carga y direcciones

        - Revisar mensajes intercambiados con el transportista

        - Comprobar si el envío permite mensajería activa (can_message)


        ## Notes

        - El parámetro {id} puede ser un service_code (ej: DEL-12345) o un MongoDB
        _id

        - Incluye información de direcciones, vehículo, auction y mensajes

        - El campo can_message indica si se pueden enviar mensajes (isActive)

        - Los mensajes se ordenan por createdAt DESC (más recientes primero)

        - Las URLs de archivos en mensajes incluyen el prefijo /images?file=

        '
      parameters:
      - description: 'Identificador del envío. Puede ser:

          - service_code (ej: DEL-12345)

          - MongoDB _id (ej: 507f1f77bcf86cd799439011)

          '
        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

            o no pertenece a la empresa del usuario autenticado.

            '
      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\n\
        Permitir 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:

            - Los datos proporcionados son inválidos

            '
        '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,

            o no se pueden editar los campos solicitados para el estado actual.

            '
        '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

            o no pertenece a la empresa del usuario autenticado.

            '
      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\n\
        Authorization: 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.

          Útil para dividir la visualización cuando hay muchos documentos.

          '
        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.

          Permite balancear rendimiento (valores bajos) vs comodidad (valores altos).

          '
        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.

          Los tipos disponibles se obtienen del endpoint /documents/types.

          Útil para mostrar solo facturas, contratos u otras categorías.

          '
        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).

                    Formatos soportados: PDF, DOCX, XLSX, JPG, PNG.

                    Tamaño máximo: 10MB por archivo.

                    **Nota**: filePaths se genera automáticamente, no enviar manualmente.

                    '
                  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:\n\
        1. Autenticación mediante JWT válido (cualquier rol de usuario autenticado)\n\
        2. 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\n\
        5. Configuración de cabeceras `Content-Type`, `Content-Length` y `Content-Disposition`\n\
        6. 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\n\
        Authorization: Bearer {token}\n```\n\n### Ejemplo de respuesta exitosa:\n\
        ```\nHTTP/1.1 200 OK\nContent-Type: application/pdf\nContent-Length: 204800\n\
        Content-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.

          Se obtiene del campo `filePaths` de un documento existente.

          Ejemplos:

          - `documents/company1/1701234567890_factura.pdf`

          - `https://bucket.s3.eu-west-2.amazonaws.com/documents/company1/factura.pdf`

          '
        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).\n\
        Los archivos en S3/MinIO NO se eliminan permanentemente.\n\n### Flujo de operación:\n\
        1. Autenticación mediante JWT válido con rol administrador\n2. Validación\
        \ de permisos de administración\n3. Verificación de existencia del documento\n\
        4. 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.

          Se obtiene al crear el documento o al listar documentos.

          Debe corresponder a un documento existente y accesible para el usuario.

          '
        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.

          Se obtiene al crear el documento o al listar documentos.

          Debe corresponder a un documento existente y accesible para el usuario.

          '
        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).

                    Estos archivos se acumularán con los de versiones anteriores.

                    Formatos: PDF, DOCX, XLSX, JPG, PNG (máx 10MB c/u).

                    '
                  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

        Recupera 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.


        ## Objective

        Proporcionar 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.


        ## Use Cases

        - Visualizar el historial completo de facturación de la empresa

        - Integración con sistemas contables externos

        - Auditoría de pagos y estados de facturación

        - Exportación de datos fiscales a terceros


        ## Notes

        - Las facturas se filtran automáticamente por los usuarios de la compañía
        autenticada

        - El parámetro `sortBy` permite ordenar por cualquier campo del modelo (formato:
        campo_sentido, ej: createdAt_-1)

        - Usa mongoose-paginate-v2 para la paginació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

          Sentido: 1 (ascendente), -1 (descendente)

          Ejemplos: createdAt_-1, serial_number_1, paid_1

          '
        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).

            El token JWT no corresponde a ninguna compañía registrada.

            '
        '404':
          content:
            application/json:
              example:
                message: USER_NOT_FOUND
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: 'Usuario no encontrado (USER_NOT_FOUND).

            El token JWT no corresponde a ningún usuario válido.

            '
      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\n\
        Permitir 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\n\
        flowchart 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).

            El token JWT no corresponde a ningún usuario válido.

            '
      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\n\
        Permitir 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).

                      - Si paid=true: fecha actual del servidor

                      - Si paid=false: null

                      '
                    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.


            Posibles casos:

            - `NOT_FOUND`: El ID proporcionado no existe en la base de datos.

            - `USER_NOT_FOUND`: El token JWT no corresponde a ningún usuario válido.

            '
      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).

                    Debe ser una fecha válida en formato ISO 8601.

                    '
                  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).

                    No hay validación de valores específicos - cualquier string es
                    aceptado.

                    Ejemplos comunes: transferencia, tarjeta, efectivo, cheque, PayPal

                    '
                  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.

                      - Si paid=true: fecha del request body

                      - Si paid=false: null

                      '
                    format: date-time
                    nullable: true
                    type: string
                  payment_method:
                    description: 'Método de pago.

                      - Si paid=true: valor del request body

                      - Si paid=false: cadena vacía

                      '
                    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.


            Posibles casos:

            - `NOT_FOUND`: El ID proporcionado no existe en la base de datos.

            - `USER_NOT_FOUND`: El token JWT no corresponde a ningún usuario válido.

            '
      security:
      - bearerAuth: []
      - apiKeyAuth: []
      summary: Update invoice payment details
      tags:
      - invoices
  /company/invoices/{id}:
    get:
      deprecated: false
      description: '## Purpose

        Recupera 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.


        ## Objective

        Permitir la consulta detallada de facturas para visualización, verificación
        de estados de pago, y auditoría de transacciones específicas.


        ## Use Cases

        - Visualizar detalles completos de una factura antes del pago

        - Verificar el estado actual de pago de una factura

        - Auditoría de transacciones específicas

        - Obtener datos para generar documentación adicional


        ## Notes

        - El ID puede venir en el path parameter, query parameter o body (prioridad:
        body > query > params)

        - Solo se pueden consultar facturas propias

        '
      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).

            El token JWT no corresponde a ninguna compañía registrada.

            '
        '404':
          content:
            application/json:
              example:
                message: NOT_FOUND:{"_id":"507f191e810c19729de860ea"}
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: 'Factura no encontrada (NOT_FOUND).

            Posibles causas:

            - El ID no existe en la base de datos

            - La factura pertenece a otra compañía

            '
      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.

        Permite filtrar por término de búsqueda y controlar la paginación.


        **Casos de uso:**

        - Visualizar el historial de incidencias

        - Buscar incidencias específicas por texto

        - Implementar paginación en el frontend


        **Lógica relacionada:** `ctrl.get()` en routes.js

        '
      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.

          Ejemplo: `10` para mostrar 10 incidencias por página.

          Por defecto es 20, máximo permitido es 100.

          '
        in: query
        name: limit
        required: false
        schema:
          default: 20
          minimum: 1
          type: integer
      - description: 'Término de búsqueda para filtrar incidencias.

          Busca en los campos: código de servicio, código interno y mensajes.

          Ejemplo: `"problema entrega"` buscará incidencias que contengan ese texto.

          '
        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.

            Incluye metadatos de paginación y array de incidencias.

            '
          headers: {}
        '401':
          description: 'No autorizado. El token JWT es inválido o ha expirado.

            Incluir token válido en el header Authorization.

            '
          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.

        Permite hasta 6 archivos adjuntos (ver implementación en routes.js).


        **Casos de uso:**

        - Reportar problemas con entregas, subastas u otros servicios

        - Solicitar soporte técnico

        - Notificar incidencias en operaciones logísticas


        **Lógica relacionada:** `ctrl.create()` en routes.js con middleware multerS3

        '
      parameters: []
      requestBody:
        content:
          multipart/form-data:
            schema:
              properties:
                files:
                  description: 'Archivos adjuntos (opcional).

                    Máximo 6 archivos (configuración en routes.js).

                    Formatos soportados: PDF, JPG, PNG, DOCX.

                    Tamaño máximo por archivo: 5MB.

                    '
                  items:
                    format: binary
                    type: string
                  type: array
                internal_code:
                  description: 'Código interno de referencia (opcional).

                    Ejemplo: "FACT-456" para referenciar una factura.

                    '
                  example: ''
                  type: string
                message:
                  description: 'Mensaje descriptivo del problema (obligatorio).

                    Mínimo 20 caracteres, máximo 1000.

                    Ejemplo: "El transportista no se presentó en el punto de recogida"

                    '
                  example: Esto es un problema
                  type: string
                relatedTo:
                  description: 'Entidad relacionada (opcional).

                    Ejemplos: "delivery", "auction", "invoice".

                    '
                  example: ''
                  type: string
                service_code:
                  description: 'Código del servicio relacionado (obligatorio).

                    Ejemplo: "DEL-123" para una entrega.

                    Se pueden obtener códigos válidos con GET /service_codes

                    '
                  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.

            Devuelve los detalles de la incidencia recién creada.

            '
          headers: {}
        '400':
          description: 'Datos inválidos. Posibles causas:

            - Faltan campos obligatorios (service_code, message)

            - Mensaje demasiado corto/largo

            - Demasiados archivos adjuntos (>6)

            - Tipo/tamaño de archivo no permitido

            '
          headers: {}
        '401':
          description: 'No autorizado. El usuario autenticado no tiene una empresa
            asociada.


            **CIA_NOT_FOUND**:

            - El usuario autenticado no tiene una empresa asignada

            - La empresa fue desactivada o eliminada

            '
          headers: {}
        '404':
          description: 'Usuario no encontrado. El token JWT no corresponde a un usuario
            válido en el sistema.


            **USER_NOT_FOUND**:

            - El token contiene un ID de usuario que no existe

            - El usuario fue eliminado después de generar el token

            '
          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.

        Estos motivos ayudan a categorizar y priorizar los tickets de soporte.


        **Casos de uso:**

        - Mostrar dropdown de selección al crear/modificar incidencias

        - Filtrar incidencias por motivo

        - Generar reportes estadísticos por tipo de problema


        **Lógica relacionada:** `cInfo.getReasons()` en routes.js

        '
      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.

            Array de strings donde cada elemento es un motivo válido.

            '
          headers: {}
        '401':
          description: 'No autorizado. El token JWT es inválido o ha expirado.

            Incluir token válido en el header Authorization.

            '
          headers: {}
        '404':
          description: 'Usuario no encontrado. El token JWT no corresponde a un usuario
            válido en el sistema.


            **USER_NOT_FOUND**:

            - El token contiene un ID de usuario que no existe

            - El usuario fue eliminado después de generar el token

            '
          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.

        Estos códigos identifican los diferentes tipos de servicios logísticos.


        **Casos de uso:**

        - Mostrar dropdown de selección al crear incidencias

        - Validar códigos antes de enviar formularios


        **Lógica relacionada:** `cInfo.getCodes()` en routes.js

        '
      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.

            Array de strings donde cada elemento es un código válido.

            '
          headers: {}
        '401':
          description: 'No autorizado. El token JWT es inválido o ha expirado.

            Incluir token válido en el header Authorization.

            '
          headers: {}
        '404':
          description: 'Usuario no encontrado. El token JWT no corresponde a un usuario
            válido en el sistema.


            **USER_NOT_FOUND**:

            - El token contiene un ID de usuario que no existe

            - El usuario fue eliminado después de generar el token

            '
          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.

        Esta acción es irreversible y debe usarse con precaución.


        **Casos de uso:**

        - Eliminar tickets creados por error

        - Limpieza de incidencias resueltas (según políticas de retención)


        **Lógica relacionada:** `ctrl.delete()` en routes.js

        '
      parameters:
      - description: 'ID único de la incidencia a eliminar.

          Ejemplo: "60d5ec9a3a5f8e001f3e4a1c"

          '
        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.

            Devuelve el ID de la incidencia eliminada.

            '
          headers: {}
        '401':
          description: 'No autorizado. El token JWT es inválido o ha expirado.

            Incluir token válido en el header Authorization.

            '
          headers: {}
        '404':
          description: 'Incidencia no encontrada.

            Verificar que el ID proporcionado es correcto y pertenece a la empresa.

            '
          headers: {}
      security:
      - bearerAuth: []
      - apiKeyAuth: []
      summary: Delete an incident
      tags:
      - Issues
    get:
      deprecated: false
      description: 'Devuelve todos los detalles de una incidencia específica, incluyendo:

        - Información básica (códigos, estado)

        - Historial completo de mensajes

        - Archivos adjuntos


        **Casos de uso:**

        - Visualizar el detalle completo de un ticket

        - Mostrar el historial de conversaciones

        - Acceder a archivos adjuntos


        **Lógica relacionada:** `ctrl.getIssue()` en routes.js

        '
      parameters:
      - description: 'ID único de la incidencia.

          Ejemplo: "60d5ec9a3a5f8e001f3e4a1c"

          '
        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.

            Incluye información básica, mensajes y archivos.

            '
          headers: {}
        '401':
          description: 'No autorizado. El token JWT es inválido o ha expirado.

            Incluir token válido en el header Authorization.

            '
          headers: {}
        '404':
          description: 'Incidencia no encontrada.

            Verificar que el ID proporcionado es correcto y pertenece a la empresa.

            '
          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.

        Permite hasta 2 archivos adjuntos (ver implementación en routes.js).


        **Casos de uso:**

        - Responder a un ticket de soporte

        - Proporcionar información adicional sobre una incidencia

        - Adjuntar documentos relevantes (facturas, fotos, etc.)


        **Lógica relacionada:** `ctrl.saveMessage()` en routes.js con middleware multerS3

        '
      parameters:
      - description: 'ID único de la incidencia a actualizar.

          Ejemplo: "60d5ec9a3a5f8e001f3e4a1c"

          '
        example: 68f7718862055e63347a58ee
        in: path
        name: id
        required: true
        schema:
          type: string
      requestBody:
        content:
          multipart/form-data:
            schema:
              properties:
                files:
                  description: 'Archivos adjuntos (opcional).

                    Máximo 2 archivos (configuración en routes.js).

                    Formatos soportados: PDF, JPG, PNG, DOCX.

                    Tamaño máximo por archivo: 5MB.

                    '
                  items:
                    format: binary
                    type: string
                  type: array
                message:
                  description: 'Contenido del mensaje (obligatorio).

                    Mínimo 10 caracteres, máximo 1000.

                    Ejemplo: "Adjunto fotos del daño reportado"

                    '
                  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.

            Devuelve los detalles completos de la incidencia con el nuevo mensaje.

            '
          headers: {}
        '400':
          description: 'Datos inválidos. Posibles causas:

            - Mensaje faltante o demasiado corto/largo

            - Demasiados archivos adjuntos (>2)

            - Tipo/tamaño de archivo no permitido

            '
          headers: {}
        '401':
          description: 'No autorizado. El token JWT es inválido o ha expirado.

            Incluir token válido en el header Authorization.

            '
          headers: {}
        '404':
          description: 'Incidencia no encontrada.

            Verificar que el ID proporcionado es correcto y pertenece a la empresa.

            '
          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".

        Esta acción registra la fecha/hora UTC actual como momento de resolución.


        **Casos de uso:**

        - Cerrar tickets cuando se ha solucionado el problema

        - Actualizar el estado de incidencias en flujos de trabajo

        - Generar métricas de tiempo de resolución


        **Lógica relacionada:** `ctrl.setResolved()` en routes.js con middleware checkUTC

        '
      parameters:
      - description: 'ID único de la incidencia a marcar como resuelta.

          Ejemplo: "60d5ec9a3a5f8e001f3e4a1c"

          '
        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.

            Devuelve los detalles actualizados de la incidencia.

            '
          headers: {}
        '401':
          description: 'No autorizado. El token JWT es inválido o ha expirado.

            Incluir token válido en el header Authorization.

            '
          headers: {}
        '404':
          description: 'Incidencia no encontrada.

            Verificar que el ID proporcionado es correcto y pertenece a la empresa.

            '
          headers: {}
      security:
      - bearerAuth: []
      - apiKeyAuth: []
      summary: Mark incident as resolved
      tags:
      - Issues
  /company/minimal:
    get:
      description: '## Propósito

        Calcula el precio mínimo viable para el transporte de mercancías entre dos
        puntos,

        incorporando todos los costes operativos, comisiones de plataforma y márgenes
        de beneficio.

        Es el endpoint de pricing principal utilizado en la plataforma.


        ## Objetivo

        Proporcionar un precio mínimo preciso que cubra todos los costes operativos
        manteniendo

        tarifas competitivas para los servicios de transporte de mercancías.


        ## Casos de Uso

        - Establecer importes mínimos de puja en la creación de subastas

        - Validar si el precio de una puja es económicamente viable

        - Calcular precios suelo para contratos de transporte

        - Soporte para algoritmos de precios dinámicos

        - Generar estimaciones de precio para clientes

        - Evitar precios por debajo del coste en pujas competitivas


        ## Algoritmo de Precio

        El cálculo del precio mínimo sigue esta fórmula:

        1. **Coste Base**: Coste bruto de la API de enrutamiento externo (combustible,
        mantenimiento, tiempo)

        2. **Factor Aleatorio**: `settings.pricing.today_random` % de variación sobre
        el coste base

        3. **Margen de Ganancia**: `settings.pricing.general_gain` % añadido sobre
        el coste

        4. **Comisión de Plataforma**: `settings.pricing.fee` % (normalmente 3%)

        5. **Margen de Seguridad**: 5% adicional sobre el total

        6. **Precio por Volumen**: `(volumen / 30) * costeTotal` — proporcional al
        volumen de carga

        7. **Precio por Peso**: `(peso / 44000) * costeTotal` — solo se aplica cuando
        peso >= 22.000 kg (50%)


        Precio final = `max(precioPorVolumen, precioPorPeso, costeTotal/2)` con un
        mínimo de 100€

        Consumo medio de combustible utilizado: 32L/100km


        ## Parámetros de Ubicación Requeridos

        **Es obligatorio proporcionar al menos uno de estos pares de ubicación:**


        | Par | Parámetros | Prioridad |

        |-----|-----------|-----------|

        | IDs de dirección | `idEtl` + `idEtd` | 1ª (máxima) |

        | Códigos postales | `zipcodeStart` + `zipcodeEnd` | 2ª |

        | Coordenadas GPS | `coordsStart` + `coordsEnd` | 3ª |


        Si no se proporciona ningún par, la respuesta será `400 INVALID_PARAMETERS`.


        ## Reglas de Validación

        - Volumen máximo: 30 m³ (se limita si se supera)

        - Peso máximo: 44.000 kg (se limita si se supera)

        - Precio mínimo: 100€ (evita precios cero o negativos)

        - Mismo origen y destino devuelve valores mínimos (1 km, 0€)

        - Si `volume` no se proporciona, se aplica el factor de precio al 100%


        ## Respuestas de Error

        - 400: No se proporcionó ningún identificador de ubicación (`INVALID_PARAMETERS`)

        - 404: Direcciones no encontradas

        - 500: Error del servicio externo o configuración de precios no encontrada

        '
      parameters:
      - description: 'ID de la dirección de origen en la base de datos (MongoDB ObjectId,
          24 hex).

          Prioridad máxima. **Debe usarse junto con `idEtd`.**

          Si se proporciona, `zipcodeStart` y `coordsStart` son ignorados.

          '
        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).

          **Debe usarse junto con `idEtl`.**

          '
        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`.**

          Se usa cuando no se proporciona `idEtl`.

          '
        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`.**

          Se usa cuando no se proporciona `idEtd`.

          '
        in: query
        name: zipcodeEnd
        required: false
        schema:
          example: 08001
          maxLength: 10
          minLength: 3
          type: string
      - description: 'Coordenadas GPS de origen en formato `"latitud,longitud"`.

          **Debe usarse junto con `coordsEnd`.**

          Busca la dirección más cercana en un radio de 100 km.

          Se usa cuando no se proporcionan `idEtl` ni `zipcodeStart`.

          '
        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"`.

          **Debe usarse junto con `coordsStart`.**

          Busca la dirección más cercana en un radio de 100 km.

          '
        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.

          - Máximo: 30 m³ (valores superiores se limitan a 30).

          - Si no se proporciona, se aplica el factor de precio al 100% (precio máximo).

          - El precio se calcula proporcionalmente: `(volume / 30) * totalCost`.

          '
        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.

          - Máximo: 44.000 kg (valores superiores se limitan a 44.000).

          - El precio por peso **solo se aplica cuando el peso >= 22.000 kg** (50%
          del máximo).

          - Si no se proporciona o es 0, no se aplica precio por peso.

          '
        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.

            Ocurre cuando la petición no incluye ninguno de: `idEtl`, `idEtd`,

            `zipcodeStart`, `zipcodeEnd`, `coordsStart`, `coordsEnd`.

            '
        '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

        Obtiene la información de peajes de una subasta existente usando su MongoDB
        ObjectId (`_id`).

        Proporciona datos detallados de peajes para la ruta específica asociada a
        la subasta.


        ## Objetivo

        Permitir el acceso rápido a la información de peajes de mercancías subastadas,
        facilitando

        el desglose transparente de costes y la validación de pujas.


        ## Casos de Uso

        - Mostrar los costes de peajes en el detalle de subastas para transportistas

        - Validar importes de pujas frente a los costes de peajes

        - Proporcionar desglose de costes en resúmenes de subastas

        - Soporte para cálculos de coste total en decisiones de puja

        - Generar informes de costes para subastas finalizadas


        ## Flujo del Proceso

        1. Consultar la subasta por MongoDB `_id` (ObjectId)

        2. Poblar las direcciones de recogida (etl_address) y entrega (etd_address)

        3. Extraer las coordenadas GPS del campo de ubicación de la dirección

        4. Consultar la API externa de peajes para la ruta

        5. Devolver los datos de peajes con coordenadas de inicio/fin y costes


        ## Información de la Respuesta

        - **start**: Coordenadas GPS de origen (lat, lng)

        - **end**: Coordenadas GPS de destino (lat, lng)

        - **tolls**: Array con la información de peajes de la ruta

        - **tollCost**: Coste total de todos los peajes en EUR


        ## Respuestas de Error

        - 400: ID de subasta faltante o inválido

        - 404: Subasta con el ID indicado no encontrada

        - 500: Error del servicio de peajes externo o coordenadas de dirección faltantes

        '
      parameters:
      - description: 'MongoDB ObjectId de la subasta (`_id`).

          Se usa para buscar la subasta y obtener sus direcciones de recogida y entrega.

          Debe ser un ObjectId hexadecimal válido de 24 caracteres.

          '
        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

        Calcula las emisiones de dióxido de carbono (CO2) para el transporte de mercancías

        entre un punto de origen y un punto de destino. Permite evaluar el impacto
        medioambiental

        de las operaciones logísticas.


        ## Objetivo

        Proporcionar cálculos precisos de emisiones de CO2 para apoyar iniciativas
        de sostenibilidad,

        informes medioambientales y optimización de rutas ecológicas en el transporte
        de mercancías.


        ## Casos de Uso

        - Generar informes de impacto medioambiental para operaciones de transporte

        - Comparar emisiones de CO2 entre distintas opciones de ruta

        - Seguimiento de la huella de carbono en dashboards de sostenibilidad corporativa

        - Optimizar rutas para minimizar el impacto ambiental

        - Cumplir con normativas medioambientales y requisitos de reporte


        ## Método de Cálculo

        El cálculo de CO2 tiene en cuenta:

        - Distancia total de la ruta (en kilómetros)

        - Peso y volumen de la carga

        - Tipo de vehículo y consumo medio de combustible (32L/100km — fijo para el
        cálculo de CO2)

        - Tipo de combustible y factores de emisión

        - Nota: los peajes NO están incluidos en el cálculo de CO2


        ## Prioridad de Parámetros

        El endpoint acepta identificadores de ubicación en múltiples formatos. Orden
        de prioridad:

        1. **id** (auction_id): Usa las direcciones de recogida/entrega de la subasta

        2. **idEtl/idEtd**: Usa direcciones por ID de base de datos

        3. **zipcodeStart/zipcodeEnd**: Usa códigos postales para encontrar la dirección
        más cercana

        4. **coordsStart/coordsEnd**: Usa coordenadas GPS (formato lat,lng)

        5. **countryOrigin/countryDestiny**: Códigos de país para el enrutamiento


        Se debe proporcionar al menos un par de identificadores de ubicación.


        ## Respuestas de Error

        - 400: Parámetros inválidos, campos requeridos faltantes o combinaciones de
        identificadores incompatibles

        - 404: Subasta o direcciones no encontradas

        - 500: Error del servicio de enrutamiento externo

        '
      parameters:
      - description: 'ID de la subasta (ObjectId). Cuando se proporciona, usa las
          direcciones de recogida

          y entrega asociadas a la subasta. Tiene mayor prioridad sobre el resto de
          parámetros

          de ubicació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

          y el país del punto de recogida. Debe usarse junto con idEtd.

          '
        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

          y el país del punto de entrega. Debe usarse junto con idEtl.

          '
        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.

          Alternativa a idEtl/coordsStart.

          '
        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.

          Alternativa a idEtd/coordsEnd.

          '
        in: query
        name: zipcodeEnd
        required: false
        schema:
          example: 08001
          maxLength: 10
          minLength: 3
          type: string
      - description: 'Coordenadas GPS de origen en formato "latitud,longitud".

          Busca la dirección más cercana en un radio de 100 km.

          Alternativa a idEtl/zipcodeStart.

          '
        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".

          Busca la dirección más cercana en un radio de 100 km.

          Alternativa a idEtd/zipcodeEnd.

          '
        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'').

          Se usa cuando la dirección no tiene información de país.

          '
        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'').

          Se usa cuando la dirección no tiene información de país.

          '
        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.

          Valor máximo: 30 m³. Los valores superiores se limitan a 30.

          '
        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.

          Valor máximo: 44.000 kg. Los valores superiores se limitan a 44.000.

          '
        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

          para su visualización en mapa.

          '
        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

        Calcula los costes brutos del transporte de mercancías entre origen y destino

        utilizando la API externa de enrutamiento CargoRutas. Devuelve un desglose
        detallado

        de costes sin aplicar márgenes de plataforma ni ajustes de precio.


        ## Objetivo

        Proporcionar datos de coste bruto precisos para el transporte de mercancías,
        útiles

        para análisis de costes internos, cálculo de márgenes y comparación de rutas.


        ## Casos de Uso

        - Analizar componentes de coste bruto sin comisiones de plataforma

        - Comparar costes entre distintas opciones de ruta

        - Análisis de costes internos y cálculo de margen

        - Soporte para decisiones de precios basadas en costes


        ## Componentes de Coste Devueltos

        - **Combustible**: Basado en distancia y consumo medio (35L/100km por defecto)

        - **Peajes**: Cuando `with_tolls=true`, incluye los costes de peajes de autopista

        - **Mantenimiento**: Aceite, ruedas/neumáticos, embrague, filtro de aire,
        correa

        - **Tiempo**: Duración estimada del trayecto en minutos

        - **Monetario**: Coste total bruto en EUR


        ## Nota

        Este endpoint devuelve costes brutos de la API de enrutamiento sin aplicar
        los márgenes

        de la plataforma (ganancia, comisión, factor aleatorio). Usa `GET /company/minimal`

        para obtener el precio mínimo de subasta con todos los márgenes aplicados.


        ## Prioridad de Parámetros

        Prioridad de identificadores de ubicación:

        1. **id** (auction_id): Usa las direcciones de recogida/entrega de la subasta

        2. **idEtl/idEtd**: IDs de direcciones en base de datos

        3. **zipcodeStart/zipcodeEnd**: Búsqueda por código postal

        4. **coordsStart/coordsEnd**: Coordenadas GPS

        5. **countryOrigin/countryDestiny**: Códigos de país (fallback)


        ## Respuestas de Error

        - 400: Parámetros inválidos o identificadores de ubicación faltantes

        - 404: Subasta o direcciones no encontradas

        - 500: Error del servicio de enrutamiento externo

        '
      parameters:
      - description: 'ID de la subasta (ObjectId). Usa las direcciones de recogida/entrega
          de la subasta.

          Opcional — omitir para usar otros parámetros de ubicació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.

          '
        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.

          '
        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.

          '
        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.

          '
        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.

          '
        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.

          '
        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'').

          '
        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'').

          '
        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³.

          Afecta al precio de forma proporcional.

          '
        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.

          El precio por peso se aplica cuando el peso es >= 50% del máximo.

          '
        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

          de peajes de la ruta e incluye `tollCost` en la respuesta.

          '
        in: query
        name: with_tolls
        required: false
        schema:
          example: true
          type: boolean
      - description: 'Incluir las coordenadas de los puntos intermedios en la respuesta
          para

          su visualización en mapa.

          '
        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

        Calcula los costes de transporte para un par origen-destino utilizando nombres
        de ciudad

        como identificadores de ubicación en lugar de coordenadas o IDs de base de
        datos.

        Es la variante POST del cálculo de costes, que acepta los datos en el cuerpo
        de la petición.


        ## Objetivo

        Proporcionar un endpoint de cálculo de costes fácil de usar mediante nombres
        de ciudad

        legibles por humanos, útil para integraciones donde las coordenadas no están
        disponibles.


        ## Casos de Uso

        - Calcular costes usando nombres de ciudad en lugar de coordenadas

        - Integración desde sistemas externos que envían datos basados en ciudad

        - Estimación rápida de costes para herramientas de planificación

        - Comparación de tarifas de transportistas por nombre de ruta


        ## Formato de la Petición

        Espera un objeto JSON con:

        - **origin** (requerido): Nombre de la ciudad de origen (ej: "Madrid", "Barcelona")

        - **destination** (requerido): Nombre de la ciudad de destino (ej: "Vigo",
        "Bilbao")

        - **with_tolls** (opcional): Incluir costes de peajes en el cálculo (por defecto:
        false)


        ## Método de Cálculo

        Usa el mismo motor de cálculo que `GET /company/minimal/costs`:

        - La API de enrutamiento externo proporciona los costes base

        - Coste de combustible basado en distancia y consumo medio

        - Costes de peajes cuando `with_tolls=true`

        - Costes de mantenimiento (aceite, ruedas, embrague, etc.)

        - Costes de tiempo/duración


        ## Integración Externa

        Este endpoint se integra con el servicio de enrutamiento Transcend para proporcionar

        datos precisos de rutas y peajes en rutas europeas.


        ## Respuestas de Error

        - 400: Cuerpo de la petición inválido o campos requeridos faltantes

        - 404: Ubicación de origen o destino no encontrada

        - 500: Error del servicio de enrutamiento externo

        '
      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.

                    El sistema busca las ubicaciones que coincidan.

                    '
                  example: Vigo
                  type: string
                origin:
                  description: 'Nombre de la ciudad de origen o identificador de ubicación.

                    El sistema busca las ubicaciones que coincidan.

                    '
                  example: Madrid
                  type: string
                with_tolls:
                  description: 'Incluir los costes de peajes en el cálculo.

                    '
                  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

        Obtiene información geográfica y temporal detallada de una ruta de transporte
        de mercancías,

        incluyendo los puntos intermedios para su visualización en mapa y planificación
        del trayecto.


        ## Objetivo

        Proporcionar datos completos de la ruta para navegación GPS, renderizado en
        mapas

        y sistemas de planificación logística.


        ## Casos de Uso

        - Mostrar rutas en interfaces de mapa para conductores y gestores

        - Calcular el ETA (tiempo estimado de llegada) con precisión

        - Planificar paradas de descanso y puntos de repostaje en la ruta

        - Visualizar alternativas de ruta en herramientas de planificación logística

        - Generar instrucciones de navegación giro a giro

        - Seguimiento del progreso del conductor respecto a la ruta planificada


        ## Información de la Respuesta

        - **distance**: Distancia total de la ruta en metros

        - **duration**: Tiempo estimado de viaje en segundos

        - **points**: Array de coordenadas GPS que definen el trazado de la ruta

        - **tolls**: Información de peajes cuando `with_tolls=true`

        - **departure/arrival**: Timestamps de salida y llegada estimados


        ## Prioridad de Parámetros

        Prioridad de identificadores de ubicación (igual que en /co2):

        1. **id** (auction_id): Usa las direcciones de la subasta

        2. **idEtl/idEtd**: IDs de direcciones en base de datos

        3. **zipcodeStart/zipcodeEnd**: Búsqueda por código postal

        4. **coordsStart/coordsEnd**: Coordenadas GPS

        5. **countryOrigin/countryDestiny**: Códigos de país


        ## Respuestas de Error

        - 400: Parámetros inválidos o identificadores de ubicación faltantes

        - 404: Direcciones o subasta no encontradas

        - 500: Error del servicio de enrutamiento externo

        '
      parameters:
      - description: 'ID de la subasta (ObjectId). Usa las direcciones de recogida/entrega
          de la subasta.

          Opcional — omitir para usar otros parámetros de ubicació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.

          '
        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.

          '
        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.

          '
        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.

          '
        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.

          '
        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.

          '
        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'').

          '
        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'').

          '
        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.

          **Por defecto: `true`** — los datos de peajes se incluyen salvo que se indique
          `false` explícitamente.

          '
        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

          para su visualización en mapa.

          '
        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.

        Diferente de /all porque incluye paginación y filtros avanzados.

        Requiere autenticación mediante JWT.


        Casos de uso:

        - Consultar transportistas con paginación para grandes volúmenes

        - Filtrar transportistas por tax ID (búsqueda parcial)

        - Filtrar transportistas por estado habilitado

        - Implementar interfaces con listado paginado


        Notas:

        - La búsqueda por taxId es case insensitive

        - Los resultados se ordenan por nombre alfabéticamente

        - Incluye metadatos de paginación (total, página, límite, totalPages)

        - El límite máximo documentado (100) NO está validado en el backend actualmente

        '
      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.

        Requiere autenticación mediante JWT.


        Casos de uso:

        - Registrar nuevos transportistas para gestionar envíos

        - Mantener un directorio de transportistas disponibles

        - Asociar transportistas a operaciones logísticas


        Ejemplo de flujo:

        1. Obtener token de autenticación

        2. Registrar transportista con datos básicos

        3. Habilitar transportista cuando esté listo para operar

        '
      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.


            **NOTA IMPORTANTE**: Actualmente el endpoint devuelve el objeto Company
            completo en lugar del Carrier creado.

            Esto es un comportamiento conocido del sistema. El ID del carrier creado
            estará en el array `my_carriers` de la respuesta.

            '
          headers: {}
        '400':
          description: 'Datos de entrada inválidos. Posibles causas:

            - Nombre vacío o muy corto

            - NIF/CIF con formato incorrecto

            - Email inválido

            '
          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.

        Requiere autenticación mediante JWT.


        Casos de uso:

        - Consultar el directorio completo de transportistas

        - Filtrar transportistas activos/inactivos en el cliente

        - Obtener datos básicos para seleccionar transportistas


        Notas:

        - No incluye paginación, devuelve todos los resultados

        - Permite filtrar por estado habilitado mediante query parameter

        - Los resultados NO están ordenados por defecto (se devuelven en orden de
        inserción en MongoDB)

        '
      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.

        Requiere autenticación mediante JWT.


        Casos de uso:

        - Consultar el equipo de conductores de un transportista

        - Filtrar conductores activos/inactivos

        - Obtener datos básicos para asignar conductores a operaciones


        Notas:

        - El transportista debe existir previamente

        - La respuesta incluye metadatos como el conteo total

        '
      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.

            Retorna un array directo de conductores (NO envuelto en objeto con propiedad
            "drivers").


            **Estructura de IDs en cada driver**:

            - `_id`: ID del subdocumento dentro del array `drivers` del carrier

            - `associated`: ID del usuario `trucker_user` en la colección `truckers_users`
            (referencia al usuario real)

            '
          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.\n\
        Requiere 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).

                    Si no se proporciona, se usará "123456" por defecto.

                    '
                  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.


            **NOTA IMPORTANTE**: El endpoint retorna el objeto Carrier completo con
            el array de drivers actualizado,

            NO solo el driver creado. El nuevo driver estará en el último elemento
            del array `drivers`.


            **Estructura de IDs en drivers**:

            - `_id`: ID del subdocumento dentro del array `drivers` del carrier

            - `associated`: ID del usuario `trucker_user` en la colección `truckers_users`
            (referencia al usuario real)

            '
          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:

            - Nombre vacío o muy corto

            - DNI/NIE con formato incorrecto

            - Email inválido

            - Teléfono mal formado

            '
          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.

        Requiere autenticación mediante JWT.


        Casos de uso:

        - Eliminar conductores que ya no trabajan con el transportista

        - Limpiar registros duplicados o incorrectos


        Notas:

        - Esta acción es irreversible

        - El transportista y conductor deben existir previamente

        - Solo se pueden eliminar conductores asociados al transportista especificado

        - El endpoint valida que el conductor pertenezca al transportista (usando
        el campo `associated`)

        '
      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:

            - Transportista no existe

            - Conductor no existe o no pertenece al transportista especificado

            '
          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

                    con el usuario trucker_user asociado (campo ''associated'').

                    Se recomienda NO modificar este campo una vez creado.

                    '
                  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:

            - Nombre vacío o muy corto

            - Email inválido

            - Teléfono mal formado

            '
          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:

            - Transportista no existe

            - Conductor no existe en la base de datos

            - Conductor no pertenece al transportista especificado

            '
          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.

        Requiere autenticación mediante JWT.


        Casos de uso:

        - Consultar conductores con paginación para grandes volúmenes

        - Filtrar conductores por tax ID (búsqueda parcial)

        - Implementar interfaces con listado paginado


        Notas:

        - La búsqueda por taxid es case insensitive

        - Incluye metadatos de paginación (total, página, límite, totalPages)

        - El límite se toma de la variable de entorno ITEMS_PAGE si no se especifica

        '
      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.

        Requiere autenticación mediante JWT.


        Casos de uso:

        - Obtener los transportistas asignados a una subasta específica

        - Verificar qué carriers participarán en una operación

        - Preparar información para contacto con transportistas


        Notas:

        - Si la subasta no es para proveedores (for_providers=false), devuelve array
        vacío

        - Si la subasta no tiene proveedores asignados, devuelve array vacío

        - Los carriers se identifican por su taxid en la lista de providers de la
        subasta

        '
      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.

        Requiere autenticación mediante JWT.


        Casos de uso:

        - Eliminar transportistas que ya no trabajan con la compañía

        - Limpiar registros duplicados o incorrectos


        Notas:

        - Esta acción es irreversible

        - Se eliminarán también todos los conductores asociados

        - El ID del transportista debe ser válido y existente

        - El transportista debe estar asociado a la compañía autenticada

        '
      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.

        Requiere autenticación mediante JWT.


        Casos de uso:

        - Verificar datos completos de un transportista

        - Mostrar información detallada en interfaces de usuario

        - Validar estado y configuración de un transportista


        Notas:

        - El ID del transportista debe ser válido y existente

        - Solo se puede acceder a transportistas asociados a la compañía

        - Incluye información de conductores asociados si existen

        '
      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.

        Requiere autenticación mediante JWT.


        Casos de uso:

        - Actualizar información de contacto (email, phone)

        - Modificar razón social o NIF/CIF

        - Activar/desactivar transportistas temporalmente

        - Gestionar conductores asociados al transportista


        Notas:

        - Se pueden actualizar todos los campos: name, email, phone, taxid, enabled,
        drivers

        - Los campos no incluidos en el request mantienen su valor actual

        - El ID del transportista debe ser válido y existente

        - El transportista debe estar asociado a la compañía autenticada

        '
      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:

            - Email con formato inválido

            - Cuerpo de la solicitud mal formado

            - Tax ID duplicado

            '
          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\n\
        Permitir 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.


            El objeto de respuesta contiene hasta 6 elementos distribuidos entre:

            - Subastas pendientes de firma (prioridad máxima)

            - 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.


            **CIA_NOT_FOUND**:

            - El usuario autenticado no tiene una empresa asignada

            - La empresa fue desactivada o eliminada

            - 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.


            **USER_NOT_FOUND**:

            - El token contiene un ID de usuario que no existe

            - 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\n\
        Permitir 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}`\n\
        3. **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.


          **Alternativas**: El ID también puede enviarse vía query parameter (?id=)
          o en el body ({"id": "..."}).


          Formato: 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.


            **CIA_NOT_FOUND**:

            - El usuario autenticado no tiene una empresa asignada

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


            **USER_NOT_FOUND**:

            - El token contiene un ID de usuario que no existe


            **NOTIFICATION_NOT_FOUND**:

            - El ID proporcionado no corresponde a ninguna notificació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\n\
        flowchart 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)\n\
        3. **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.


          **Alternativas**: El ID también puede enviarse vía query parameter (?id=)
          o en el body ({"id": "..."}).


          Formato: 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.


            La respuesta contiene la notificación tal como existed antes de ser eliminada.

            Esta información puede ser útil para:

            - Confirmar qué se eliminó

            - Actualizar la UI localmente

            - 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.


            **CIA_NOT_FOUND**:

            - El usuario autenticado no tiene una empresa asignada

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


            **USER_NOT_FOUND**:

            - El token contiene un ID de usuario que no existe


            **NOTIFICATION_NOT_FOUND**:

            - El ID proporcionado no corresponde a ninguna notificació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

        Genera un enlace de Stripe Account Link para que la compañía complete

        o retome el proceso de vinculación de su cuenta bancaria.


        ## Objective

        Iniciar o continuar el flujo de onboarding de Stripe Connect para

        configurar la cuenta bancaria donde se recibirán las transferencias.


        ## Use Cases

        - Iniciar la vinculación de cuenta bancaria por primera vez

        - Retomar un proceso de onboarding interrumpido o expirado

        - Actualizar datos bancarios existentes

        - Completar requisitos pendientes de Stripe


        ## Notes

        - Requiere autenticación JWT (bearerAuth)

        - Genera un `account_link` de tipo `account_onboarding`

        - El enlace expira en ~5 minutos (por defecto de Stripe)

        - Requiere que `stripe_account` esté configurado en `payment_settings`

        - Stripe redirige a `/onboarding/return` al completar y a `/onboarding/refresh`
        si expira

        '
      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

        Funcionalidad idéntica al `GET /bank_account_link`. Disponible como POST

        para clientes que requieran enviar datos adicionales en el body.


        ## Objective

        Generar un nuevo Account Link de Stripe para el proceso de onboarding

        de cuenta bancaria mediante método POST.


        ## Use Cases

        - Alternativa POST para clientes que prefieren este método

        - Reiniciar el proceso de vinculación bancaria enviando parámetros

        - Integración con flujos que usan exclusivamente POST


        ## Notes

        - Requiere autenticación JWT (bearerAuth)

        - Misma lógica que `GET /bank_account_link`

        - Requiere `stripe_account` configurado en `payment_settings`

        '
      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\n\
        Actualizar `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_`.

          Añadido automáticamente por Stripe en la URL de refresh.

          '
        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_`.

          Añadido automáticamente por Stripe en la URL de retorno.

          '
        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\n\
        flowchart 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

        Recupera todos los métodos de pago (tarjetas) registrados para la

        compañía del usuario autenticado.


        ## Objective

        Mostrar las tarjetas disponibles indicando cuál es la predeterminada

        y cuáles pueden eliminarse de forma segura.


        ## Use Cases

        - Listar tarjetas en la sección de configuración de pagos

        - Identificar la tarjeta predeterminada para futuros pagos

        - Determinar qué tarjetas pueden eliminarse (no están en deliveries activos)


        ## Notes

        - Requiere autenticación JWT (bearerAuth)

        - `is_default`: coincide con `payment_settings.default_payment_method`

        - `can_delete: false` si el método está en uso en deliveries activos o es
        el predeterminado

        - Solo devuelve datos parciales de tarjeta (PCI compliant: sin número completo)

        '
      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\n\
        Inicializar 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

        Recupera la información completa de la cuenta Stripe Connect asociada

        a la compañía, necesaria para recibir transferencias de pago.


        ## Objective

        Mostrar el estado de la cuenta Stripe Connect incluyendo requisitos

        pendientes de verificación y cuentas bancarias vinculadas.


        ## Use Cases

        - Verificar el estado del onboarding de Stripe Connect

        - Detectar requisitos pendientes (`requirements.currently_due`)

        - Mostrar información de la cuenta en el dashboard de pagos

        - Comprobar si la compañía puede recibir pagos (`payouts_enabled`)


        ## Notes

        - Requiere autenticación JWT (bearerAuth)

        - Busca `stripe_account` en `cia.payment_settings` o `currentUser.payment_settings`

        - Retorna el objeto completo de la cuenta Stripe (todos los campos de Stripe)

        - Requiere que `stripe_account` esté configurado previamente

        '
      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

        Actualiza la información de la cuenta Stripe Connect de la compañía,

        incluyendo datos del representante legal y perfil de negocio.


        ## Objective

        Completar o actualizar el onboarding de Stripe Connect para cumplir

        con los requisitos KYC/AML de Stripe y poder recibir pagos.


        ## Use Cases

        - Completar el perfil de negocio durante el onboarding inicial

        - Actualizar información del representante legal (director)

        - Resolver campos en `requirements.currently_due`

        - Proporcionar documentación adicional requerida por Stripe


        ## Notes

        - Requiere autenticación JWT (bearerAuth)

        - Crea o actualiza la `person` (representante legal) en Stripe Connect

        - Guarda el ID de la persona en `cia.stripe_director`

        - La actualización de datos de cuenta es asíncrona en Stripe

        - Requiere que `stripe_account` esté configurado en `payment_settings`

        '
      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

        Consulta si la compañía autenticada ha aceptado los términos y condiciones

        de pago de Stripe.


        ## Objective

        Determinar si se debe mostrar el modal de aceptación de términos antes

        de permitir operaciones de pago, comprobando el campo

        `payment_settings.tos_acceptance.accepted` de la compañía.


        ## Use Cases

        - Verificar al cargar la sección de configuración de pagos

        - Determinar si mostrar u ocultar el banner de aceptación de términos

        - Prerequisito antes de habilitar Stripe o agregar métodos de pago


        ## Notes

        - Requiere autenticación JWT (bearerAuth)

        - Retorna `success: true` si ya fueron aceptados, `success: false` si no

        - No lanza error cuando los términos no están aceptados (responde 200)

        '
      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

        Consulta si los pagos con Stripe están habilitados para la compañía

        del usuario autenticado.


        ## Objective

        Permitir al frontend conocer el estado actual de la integración con Stripe

        para mostrar u ocultar funcionalidades de pago.


        ## Use Cases

        - Verificar si mostrar el toggle de pagos en el dashboard

        - Comprobar configuración antes de intentar procesar una entrega

        - Estado del switch de activación en la UI de configuración


        ## Notes

        - Requiere autenticación JWT (bearerAuth)

        - Retorna solo el campo `withStripe` del objeto `payment_settings`

        - `withStripe: true` indica que los pagos están activos y configurados

        '
      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\n\
        flowchart 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`\n\
        El campo `data` debe enviarse como **string JSON** en el formulario multipart.\n\
        Contiene 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.

                    Campos requeridos: `name`, `surname`, `taxid`, `email`, `image`.

                    El campo `image` es la firma digital en base64 con prefijo data
                    URI.

                    '
                  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.

                    Estructura: `{ "coords": { "latitude": 40.4168, "longitude": -3.7038
                    } }`

                    '
                  example: '{"coords":{"latitude":40.4168,"longitude":-3.7038}}'
                  format: json
                  type: string
                images:
                  description: 'Fotos de prueba de entrega (opcional).

                    Máximo 6 imágenes. Formatos aceptados: JPG, PNG.

                    Tamaño máximo por archivo: 5 MB. Se almacenan en AWS S3.

                    '
                  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}`.

                    Se invalida tras la confirmació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:

            - Faltan parámetros requeridos (`service_code`, `token`, `data`)

            - Formato incorrecto del JSON en el campo `data`

            '
        '401':
          content:
            application/json:
              example:
                message: NOT_ALLOWED
                status: false
          description: 'Operación no permitida. Ocurre cuando:

            - La entrega ya está en estado final (`delivered`, `canceled`)

            - La firma (`image`) no está presente en el campo `data`

            '
        '404':
          content:
            application/json:
              example:
                message: DELIVERY_NOT_FOUND
                status: false
          description: 'Entrega no encontrada. Posibles causas:

            - El `token` o `service_code` son incorrectos o no coinciden

            - La entrega fue eliminada

            '
        '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.


        Las incidencias pueden ser:

        - Retrasos o problemas durante el transporte

        - Daños en la mercancía

        - Problemas con el vehículo

        - Cualquier otra incidencia relevante


        **Este endpoint es PÚBLICO — no requiere autenticación.**


        ## Casos de uso

        - El conductor reporta una avería o accidente

        - El receptor reporta mercancía dañada

        - Se registra cualquier incidente durante la entrega


        ## Notas técnicas

        - Código fuente: `src/features/company/qr_delivery/controller.js` → `createIsuue`

        - Las incidencias se almacenan en el modelo `Issue`

        - El campo `relatedTo: "delivery"` requiere `deliveryId`

        '
      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.


        El token QR se genera al crear la entrega. Al escanear el QR con la app móvil,

        este endpoint devuelve los detalles de la entrega y en el proceso **rota**
        el token:

        - Elimina el `qr_token` de la entrega

        - Genera un nuevo `confirm_token` (usado en `PUT /confirm`)


        **Este endpoint es PÚBLICO — no requiere autenticación.**


        ## Flujo típico de uso

        1. El transportista llega al destino y escanea el QR

        2. La app llama a este endpoint con el token del QR

        3. Se muestran los detalles de la entrega al firmante

        4. El firmante confirma mediante `PUT /company/qr/confirm`


        ## Campos devueltos

        - Datos del envío (`service_code`, `status`, `cargo_type`, etc.)

        - Dirección de carga y descarga (`etl_address`, `etd_address`)

        - Vehículo y conductor asignado

        - Fechas estimadas y reales

        - `confirm_token` para usar en la confirmación


        ## Notas técnicas

        - Código fuente: `src/features/company/qr_delivery/controller.js` → `getDeliveryFromToken`

        - El `qr_token` original queda invalidado tras la primera consulta

        - El `confirm_token` generado es necesario para completar la entrega

        '
      operationId: getDeliveryFromQr
      parameters:
      - description: 'Token QR único asociado a la entrega.

          Se obtiene del código QR generado por el sistema.

          '
        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:

            - El token QR no existe, ya ha sido usado o ha expirado

            - La entrega fue eliminada del sistema

            - El formato del token es incorrecto

            '
        '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\n\
        3. 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).


        **Propósito:**

        - Permitir a administradores actualizar documentos legales

        - Mantener actualizada la información legal de la plataforma

        - Soportar múltiples idiomas


        **Requisitos:**

        - Requiere autenticación JWT con rol ''admin'' o ''dev''

        - El middleware checkUTC valida timestamps UTC

        - Los códigos numéricos son ignorados automáticamente


        **Estructura del payload:**

        - Objeto anidado por código de documento (terms, privacy, cookies)

        - Cada código contiene objetos por idioma (es, en)

        - Cada idioma tiene: name, text (HTML), visible


        **Comportamiento:**

        - Sobrescribe completamente los documentos existentes

        - No es un merge parcial

        - Los documentos con visible=false no se retornan en endpoints públicos

        '
      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.


        **Funcionalidades de búsqueda:**

        - Filtrado por texto (nombre, apellido, email, taxid)

        - Autocompletado para selectores

        - Paginación configurable


        **Modos de respuesta:**

        - **Modo básico**: Solo _id, name, lastname (para transportistas)

        - **Modo completo**: Todos los campos relevantes (para empresas)

        - **Modo con vehículos**: Incluye populate del vehículo por defecto


        **Parámetros de búsqueda:**

        - `page`: Número de página (default: 1)

        - `limit`: Elementos por página (default: 10)

        - `search`: Búsqueda de texto libre

        - `autocomplete`: Filtrado para autocompletar

        - `extra=vehicles`: Incluye información de vehículos


        **Campos devueltos (modo empresa):**

        - _id, name, lastname, email, phone, taxid

        - address, allowSearch, accountType

        - default_vehicle (si extra=vehicles)

        - emailVerified


        **Respuestas:**

        - 200 OK: Listado paginado de transportistas

        - 401 Unauthorized: Token inválido o compañía no encontrada

        - 404 Not Found: Usuario no encontrado


        **Ejemplo de uso:**

        ```bash

        GET /company/truckers/?page=1&limit=20&search=juan&extra=vehicles

        ```

        '
      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.


        **Proceso de creación:**

        1. Validación de datos obligatorios (email, taxid)

        2. Verificación de unicidad (email y taxid únicos)

        3. Generación automática de contraseña (si no se proporciona)

        4. Hash seguro de contraseña

        5. Subida de imagen de perfil a S3 (opcional)

        6. Envío de email con credenciales

        7. Asociación automática a la compañía


        **Validaciones automáticas:**

        - Email único en el sistema

        - Taxid (DNI/NIE/CIF) único y formato válido

        - Teléfono con formato válido

        - Rol válido según modelo


        **Gestión de contraseñas:**

        - Si se envía: Se almacena hash seguro

        - Si no se envía: Se genera automáticamente y se envía por email


        **Gestión de imágenes:**

        - Soporte para subida multipart/form-data

        - Almacenamiento en AWS S3

        - Campo `image` contiene la key de S3


        **Email de bienvenida:**

        - Se envía automáticamente al transportista

        - Incluye credenciales de acceso

        - Personalizado con nombre de la compañía

        - Multiidioma según i18n del usuario


        **Límites:**

        - Verificación de plan de suscripción (middleware canCreateUser)

        - Requiere permisos multitenant


        **Respuestas:**

        - 200 OK: Transportista creado correctamente

        - 400 Bad Request: Datos inválidos o falta información

        - 401 Unauthorized: Compañía no encontrada

        - 406 Not Acceptable: Email o taxid ya existen

        - 503 Service Unavailable: Error al guardar en BD

        '
      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.


        **Idiomas soportados:**

        - `es`: Español (bulk_trucker_notes_es.txt)

        - `en`: Inglés (bulk_trucker_notes_en.txt) - por defecto


        **Contenido de las notas:**

        - Instrucciones paso a paso

        - Formato requerido de datos

        - Ejemplos de valores válidos

        - Códigos de error comunes

        - Tips de importación

        - Validaciones aplicadas


        **Ubicación de archivos:**

        - src/assets/bulk_trucker_notes_es.txt

        - src/assets/bulk_trucker_notes_en.txt


        **Formato de salida:**

        - Content-Type: text/plain

        - Nombre archivo: notes_{lang}.txt

        - Codificación: UTF-8


        **Casos de uso:**

        - Ayuda contextual en interfaz de importación

        - Documentación para usuarios finales

        - Guía de referencia rápida


        **Respuestas:**

        - 200 OK: Archivo de texto con notas

        - 401 Unauthorized: Compañía no encontrada

        - 404 Not Found: Usuario no encontrado

        '
      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


                  ## Formato de Archivo

                  - Tipo: CSV (valores separados por comas)

                  - Codificación: UTF-8

                  ...

                  '
                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.


        **Contenido de la plantilla:**

        - Headers con todos los campos obligatorios

        - 3 filas de ejemplo con datos ficticios

        - Formato correcto para importación


        **Campos incluidos:**

        - name, lastname, email, phone, taxid, default_vehicle


        **Uso recomendado:**

        1. Descargar plantilla

        2. Rellenar con datos reales

        3. Subir mediante POST /bulk/


        **Formato de salida:**

        - Content-Type: text/csv

        - Nombre archivo: template.csv

        - Codificación: UTF-8


        **Ejemplo de contenido:**

        ```csv

        name,lastname,email,phone,taxid,default_vehicle

        Pepe,Valbuena,pvalbuena@example.com,+34818809171,60262969W,11425LKJ

        Valentina,Pedreiro,vpedreiro@example.com,936247383,Z3244875V,66541HGF

        Pedro,Jiménez,pjimenez@example.com,34668936908,Q6495302I,778987FFD

        ```


        **Respuestas:**

        - 200 OK: Descarga de archivo CSV

        - 401 Unauthorized: Compañía no encontrada

        - 404 Not Found: Usuario no encontrado

        '
      parameters: []
      responses:
        '200':
          content:
            text/csv:
              schema:
                example: 'name,lastname,email,phone,taxid,default_vehicle

                  Pepe,Valbuena,pvalbuena@example.com,+34818809171,60262969W,11425LKJ

                  Valentina,Pedreiro,vpedreiro@example.com,936247383,Z3244875V,66541HGF

                  '
                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\n\
        Proporcionar 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.


          **Formato:** ObjectId de MongoDB (24 caracteres hexadecimales)


          **Ejemplo:** 000000000000000000000001 (ID de prueba en base de datos testing)

          '
        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.


            **Nota:** Esta respuesta NO está disponible actualmente debido al error
            crítico del middleware.

            '
        '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.


            Este es el error que actualmente recibe cualquier solicitud a este endpoint.


            También puede ocurrir por:

            - CANT_GET_CONTACT: Error al procesar la solicitud en el controller

            '
        '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.

            '
        '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'').


            **Nota:** Este error ocurriría si el middleware funcionara correctamente
            y el token no tuviera accountType=''multitennant''.

            '
        '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.


            **Nota:** Actualmente no se puede llegar a esta respuesta debido al error
            previo del middleware.

            '
      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).


        **Caso de uso:**

        - Búsqueda rápida por documento de identidad

        - Verificación de existencia antes de crear

        - Consulta desde sistemas externos por taxid


        **Formato de taxid:**

        - España: DNI (12345678A), NIE (X1234567A), CIF (B12345678)

        - Longitud: 6-9 caracteres

        - Se almacena en mayúsculas


        **Respuesta:**

        - Devuelve perfil completo del transportista

        - Incluye todos los detalles (parseFleetDetail)


        **Respuestas:**

        - 200 OK: Transportista encontrado

        - 404 Not Found: No existe transportista con ese taxid


        **Nota de seguridad:**

        Este endpoint requiere middleware multitenant pero no verifica

        pertenencia a compañía. Considerar añadir validación adicional.

        '
      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.


        **Proceso de eliminación:**

        1. Verificación de pertenencia a la compañía

        2. Eliminación de imagen de perfil en S3 (si existe)

        3. Soft delete del registro (mongoose-delete)

        4. El registro se marca como deleted pero se mantiene en BD


        **Consideraciones importantes:**

        - Es una eliminación lógica (soft delete), no física

        - La imagen se elimina permanentemente de S3

        - El transportista puede ser restaurado si es necesario

        - Las referencias en deliveries/auctions se mantienen


        **Seguridad:**

        - Solo puede eliminar transportistas de su propia compañía

        - Verificación automática de pertenencia


        **Respuestas:**

        - 200 OK: Transportista eliminado (devuelve {_id})

        - 403 Forbidden: No pertenece a la compañía

        - 404 Not Found: Transportista no encontrado


        **Advertencia:**

        El código actual marca como peligroso esta operación.

        Revisar impacto en deliveries y auctions antes de eliminar.

        '
      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.


        **Validaciones de seguridad:**

        - El transportista debe pertenecer a la compañía del usuario autenticado

        - Verificación automática de pertenencia

        - Control de acceso por origen (trucker/company)


        **Información devuelta:**

        - Perfil completo del transportista

        - Datos de contacto

        - Información fiscal

        - Vehículo asignado

        - Estado de verificación de email

        - Configuración de búsqueda pública


        **Respuestas:**

        - 200 OK: Detalles del transportista

        - 403 Forbidden: Transportista no pertenece a la compañía

        - 404 Not Found: Transportista no encontrado

        '
      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.


        **Proceso de actualización:**

        1. Verificación de pertenencia a la compañía

        2. Validación de cambios de email (unicidad)

        3. Validación de datos según modelo

        4. Gestión de imagen (subida/eliminación en S3)

        5. Guardado de cambios


        **Validaciones especiales:**

        - Si se cambia el email, se verifica que no exista otro usuario con ese email

        - No se permite cambiar a email ya existente

        - La imagen anterior se elimina de S3 si se sube una nueva o se elimina


        **Gestión de imágenes:**

        - Nueva imagen: Se sube a S3 y se elimina la anterior

        - Sin imagen en body: Se elimina de S3 y se pone a null

        - Imagen existente sin cambios: Se mantiene


        **Campos editables:**

        - name, lastname, email, phone, taxid

        - default_vehicle, allowSearch

        - image (vía multipart)

        - address (objeto completo)


        **Respuestas:**

        - 200 OK: Transportista actualizado

        - 400 Bad Request: Datos inválidos

        - 403 Forbidden: No pertenece a la compañía

        - 404 Not Found: Transportista no encontrado

        - 406 Not Acceptable: Email ya existe

        '
      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

        Devuelve una lista paginada de todos los usuarios pertenecientes a la compañía

        del usuario autenticado, excluyendo usuarios con rol ''dev''.


        ## Objective

        Permitir a administradores y gestores ver el listado completo de usuarios

        de su compañía con opciones de búsqueda y paginación.


        ## Use Cases

        - Listar usuarios para gestión administrativa

        - Buscar usuarios por nombre, email o apellidos

        - Paginar listados grandes de usuarios


        ## Authentication

        - Requiere JWT válido (middleware m.isLoged)

        - Usuario debe estar autenticado

        - Solo devuelve usuarios de la compañía del usuario autenticado


        ## Filtering & Pagination

        - **search**: Busca por nombre, email o apellidos (búsqueda insensible a mayúsculas)

        - **page**: Número de página (default: 1)

        - **limit**: Resultados por página (default: ITEMS_PAGE de entorno)

        - Excluye usuarios con rol ''dev'' automáticamente


        ## Notes

        - Retorna paginación con mongoose-paginate-v2

        - Los resultados usan model.parse() para transformació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).

          Busca en campos: name, email, lastname.

          '
        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,

                    se genera una automáticamente. Requisitos: mínimo 8 caracteres,

                    al menos 1 mayúscula, 1 minúscula, 1 número.

                    '
                  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.

                    - dev: Superadmin con acceso total

                    - admin: Gestión completa de la compañía

                    - gestor: Operaciones diarias

                    '
                  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:

            - Email no proporcionado

            - Error al guardar (rollback automático)

            '
        '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:

            - Usuario no es administrador

            - Plan no permite crear más usuarios

            '
        '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

        Devuelve el historial de accesos (login) de un usuario específico con información

        detallada de IP, navegador, sistema operativo y geolocalización.


        ## Objective

        Facilitar la auditoría de seguridad y detección de accesos sospechosos

        mediante el análisis de patrones de acceso.


        ## Use Cases

        - Auditoría de seguridad

        - Detección de accesos desde ubicaciones inusuales

        - Análisis de dispositivos utilizados

        - Investigación de accesos no autorizados


        ## Authentication

        - Requiere JWT válido (middleware m.isLoged)


        ## Response Data

        Cada registro de acceso incluye:

        - IP address

        - Browser (name, version)

        - OS (name, version)

        - Device (name, version)

        - Geolocalización (country, region, city, timezone)


        ## Geolocation

        - Usa geoip-lite para determinar ubicación desde IP

        - Puede no estar disponible para todas las IPs (privadas, VPN, etc.)


        ## Pagination

        - Retorna resultados paginados

        - Parámetros: page, limit

        '
      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:

            - Token JWT inválido o expirado

            '
        '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

        Devuelve el registro de acciones realizadas por un usuario específico.


        ## Objective

        Facilitar la auditoría y análisis de actividad de usuarios para

        seguridad y cumplimiento.


        ## Use Cases

        - Auditoría de seguridad

        - Análisis de actividad del usuario

        - Detección de comportamientos inusuales

        - Investigación de incidencias


        ## Authentication

        - Requiere JWT válido (middleware m.isLoged)


        ## Action Types

        - Cambios en perfil

        - Operaciones de autenticación

        - Cambios de contraseña

        - Modificaciones de configuración

        - Creación/edición de subastas

        - Gestión de entregas


        ## Pagination

        - Retorna resultados paginados

        - Parámetros: page, limit

        '
      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:

            - Token JWT inválido o expirado

            '
        '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

        Permite a un administrador resetear la contraseña de cualquier usuario

        de la compañía, generando una nueva contraseña aleatoria y enviándola por
        email.


        ## Objective

        Facilitar la recuperación de acceso de usuarios por parte de administradores

        cuando olvidan su contraseña o necesitan ayuda.


        ## Use Cases

        - Usuario olvidó su contraseña y pide ayuda al admin

        - Admin necesita restablecer acceso de un usuario

        - Reset de seguridad por solicitud del usuario


        ## Authentication & Authorization

        - Requiere JWT válido (middleware m.isLoged)

        - Requiere rol admin o dev (middleware m.isAdmin)


        ## Behavior

        - Si no se proporciona password, genera una automáticamente con tools.generatePass()

        - La contraseña generada tiene 8 caracteres con mayúsculas, minúsculas y números

        - Hashea la contraseña con model.getPassword()

        - Envía email con la nueva contraseña usando mail.sendNewPass()

        - Verifica que el usuario pertenezca a la compañía del admin


        ## Validations

        - Usuario debe existir

        - Usuario debe pertenecer a la compañía del admin

        - Si se proporciona password, se usa esa en lugar de generar


        ## Password Handling

        - Si se proporciona password en el body, se usa esa

        - Si no se proporciona, se genera automáticamente: 8 caracteres, mayúsculas,
        minúsculas, números

        - La contraseña se hashea con model.getPassword()

        - Se envía por email al usuario en su idioma configurado (user.i18n)


        ## Notes

        - Diferente de POST /changePass donde el usuario cambia su propia contraseña

        - Este endpoint es para que el admin resetee la contraseña de otro usuario

        '
      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,

                    se genera una automáticamente de 8 caracteres con mayúsculas,

                    minúsculas y números.

                    '
                  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\n\
        1. 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\n\
        7. 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.

                    '
                  example: NuevaContraseña456
                  maxLength: 50
                  minLength: 8
                  type: string
                current:
                  description: 'Contraseña actual del usuario (debe ser válida).

                    Se valida con model.isValidPassword().

                    '
                  example: MiContraseñaActual123
                  minLength: 8
                  type: string
                new_pass:
                  description: 'Nueva contraseña deseada. Debe cumplir con requisitos
                    de seguridad:

                    - Mínimo 8 caracteres

                    - Al menos 1 mayúscula

                    - Al menos 1 minúscula

                    - Al menos 1 número

                    - No puede ser igual a las últimas 5 contraseñas

                    '
                  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:

            - Campos faltantes en el request

            - Error al guardar

            '
        '401':
          content:
            application/json:
              example:
                message: NO_TOKEN
              schema:
                $ref: ../company-docs/openapi.yaml#/components/schemas/ErrorResponse
          description: 'No autorizado. Posibles causas:

            - Token JWT inválido o expirado

            - Usuario no tiene permisos

            '
        '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

        Deshabilita un usuario estableciendo status=false. El usuario permanece

        en la base de datos pero no puede iniciar sesión.


        ## Objective

        Permitir a administradores deshabilitar usuarios temporalmente sin

        eliminarlos permanentemente de la base de datos.


        ## Use Cases

        - Deshabilitar temporalmente un usuario

        - Suspender acceso mientras se investiga un incidente

        - Bloquear usuario temporalmente por seguridad


        ## Authentication & Authorization

        - Requiere JWT válido (middleware m.isLoged)

        - Requiere rol admin o dev (middleware m.isAdmin)


        ## Behavior

        - Busca usuario por ID

        - Establece user.status = false

        - Retorna usuario actualizado

        - No elimina el registro de la base de datos


        ## Notes

        - Este endpoint solo deshabilita (status=false)

        - NO elimina físicamente el registro

        - Diferente de DELETE /:id que sí hace soft delete

        - El usuario puede ser reactivado cambiando status a true


        ## Comparison

        - **DELETE /:id**: Soft delete completo (deleted=true, deletedAt set)

        - **DELETE /delete/:id**: Solo deshabilita (status=false)

        - **PUT /disabled/disable/:id**: Soft delete con razón adicional

        '
      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

        Verifica si un usuario está deshabilitado (soft deleted) buscando por email.

        Útil para verificar si un email puede ser reutilizado.


        ## Objective

        Facilitar la verificación del estado de usuarios deshabilitados para

        decidir si un email está disponible para reuso o si un usuario puede ser reactivado.


        ## Use Cases

        - Verificar si un email ya está en uso por un usuario deshabilitado

        - Decidir si se puede reactivar un usuario existente

        - Prevenir duplicados al crear nuevos usuarios


        ## Authentication & Authorization

        - Requiere JWT válido (middleware m.isLoged)

        - Requiere rol admin o dev (middleware m.isAdmin)


        ## Behavior

        - Usa model.findOneDeleted() para incluir usuarios eliminados

        - Busca por email (query parameter requerido)

        - Retorna información sobre el estado del usuario


        ## Notes

        - Este endpoint busca en usuarios deshabilitados (deleted=true)

        - Útil para decidir si se debe reactivar o crear nuevo usuario

        '
      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:

            - Email 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)
      security:
      - bearerAuth: []
      summary: Check if user is disabled
      tags:
      - users
  /company/users/disabled/disable/{id}:
    put:
      description: '## Purpose

        Deshabilita un usuario con soft delete (método preferido sobre DELETE simple)

        permitiendo guardar una razón y mensaje explicativo.


        ## Objective

        Permitir a administradores deshabilitar usuarios de forma controlada con

        registro de la razón, facilitando auditoría y posible reactivación.


        ## Use Cases

        - Deshabilitar usuario por falta de pago

        - Suspender cuenta por incumplimiento

        - Bloquear usuario temporalmente con motivo documentado

        - Deshabilitar empleado que deja la empresa


        ## Authentication & Authorization

        - Requiere JWT válido (middleware m.isLoged)

        - Requiere rol admin o dev (middleware m.isAdmin)


        ## Behavior

        - Ejecuta user.delete() (soft delete de mongoose-delete)

        - Establece status = false

        - Actualiza campos de razón: reason, reasonMessage

        - Guarda fecha de deshabilitación en reasonDate

        - Establece deleted = true y deletedAt automáticamente


        ## Request Body

        - **reason**: Razón de deshabilitación (default: "USER_DISABLED")

        - **message**: Mensaje explicativo opcional


        ## Comparison with other disable methods

        - **DELETE /:id**: Soft delete simple (deleted=true)

        - **DELETE /delete/:id**: Solo deshabilita (status=false)

        - **PUT /disabled/disable/:id**: Soft delete + razón + mensaje (RECOMENDADO)


        ## Process

        1. Busca usuario por ID

        2. Si no existe, retorna 404

        3. Ejecuta user.delete() para soft delete

        4. Actualiza reason y reasonMessage del body

        5. Establece reasonDate = now

        6. Establece status = false

        7. Guarda cambios

        8. Retorna confirmación con deletedAt

        '
      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).

                    Visible para administradores y puede incluirse en notificaciones.

                    '
                  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").

                    Puede ser cualquiera de las razones válidas: NONE, BAD_USER,

                    PENDING, ACTIVE, BLOCKED, o una personalizada.

                    '
                  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

        Reactiva un usuario deshabilitado (restaura de soft delete) permitiéndole

        acceder nuevamente a la plataforma.


        ## Objective

        Permitir a administradores reactivar usuarios que fueron deshabilitados

        mediante soft delete, restaurando su acceso completo.


        ## Use Cases

        - Reactivar un empleado que regresa a la empresa

        - Restaurar acceso deshabilitado por error

        - Reactivar usuario tras completar verificación


        ## Authentication & Authorization

        - Requiere JWT válido (middleware m.isLoged)

        - Requiere rol admin o dev (middleware m.isAdmin)


        ## Behavior

        - Busca usuario con model.findOneDeleted()

        - Restaura usando user.restore() de mongoose-delete

        - Resetea estado: status = true, reason = "NONE"

        - Limpia reasonDate y reasonMessage

        - Establece mensaje: "Usuario reactivado del estado deshabilitado"


        ## Process Flow

        1. Busca usuario deshabilitado por ID

        2. Si no existe, retorna 404

        3. Ejecuta user.restore() para revertir soft delete

        4. Establece status = true

        5. Resetea reason = "NONE"

        6. Limpia reasonDate y reasonMessage

        7. Guarda cambios

        8. Retorna usuario reactivado


        ## Notes

        - Este endpoint restaura usuarios eliminados con soft delete

        - Diferente de POST /status/:id que solo cambia status

        - Recupera el usuario completamente (elimina flag deleted)

        '
      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

        Solicita documentación adicional a un usuario. Esta funcionalidad está

        prevista para futura implementación en el frontend.


        ## Objective

        Permitir a administradores solicitar documentos adicionales a los usuarios

        para verificación de identidad, compliance, etc.


        ## Use Cases

        - Solicitar documentación fiscal adicional

        - Pedir documentos de identificación

        - Requerir contratos o permisos especiales


        ## Authentication & Authorization

        - Requiere JWT válido (middleware m.isLoged)

        - Requiere rol admin o dev (middleware m.isAdmin)


        ## Status

        - Actualmente el endpoint solo guarda el usuario sin lógica adicional

        - Pendiente de implementación completa en el frontend

        - Puede estar en desarrollo para futuras funcionalidades


        ## Notes

        - Este endpoint parece incompleto según el código actual

        - Se recomienda verificar si está en uso antes de depender de él

        '
      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

        Devuelve el idioma preferido configurado por el usuario autenticado.


        ## Objective

        Permitir a la aplicación conocer el idioma del usuario para personalizar

        la interfaz y contenido.


        ## Use Cases

        - Mostrar interfaz en el idioma correcto

        - Cargar contenido traducido

        - Enviar emails en el idioma preferido


        ## Authentication

        - Requiere JWT válido (middleware m.isLoged)


        ## Supported Languages

        - es (Español)

        - en (Inglés)

        - fr (Francés)

        - de (Alemá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:

            - Token JWT inválido o expirado

            - Usuario no tiene permisos

            '
      security:
      - bearerAuth: []
      summary: Get user language preference
      tags:
      - users
    post:
      description: '## Purpose

        Permite al usuario configurar su idioma preferido.


        ## Objective

        Facilitar la personalización del idioma de la interfaz y contenidos

        según las preferencias del usuario.


        ## Use Cases

        - Usuario cambia el idioma de la interfaz

        - Usuario selecciona su idioma preferido en registro

        - Usuario actualiza preferencias de idioma


        ## Authentication

        - Requiere JWT válido (middleware m.isLoged)


        ## Persistence

        - El cambio se guarda en la base de datos

        - Afecta a todas las respuestas futuras de la API

        - Se usa para enviar emails en el idioma correcto


        ## Supported Languages

        - es (Español)

        - en (Inglés)

        - fr (Francés)

        - de (Alemá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:

            - Idioma no soportado

            - Campo language faltante

            '
        '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

        Devuelve el perfil completo del usuario actualmente autenticado mediante JWT.

        Incluye todos los datos personales, preferencias y configuración del usuario.


        ## Objective

        Permitir al usuario obtener su propia información de perfil para mostrarla
        en la interfaz

        y verificar su estado de verificación.


        ## Use Cases

        - Mostrar información del perfil en el frontend

        - Verificar estado de verificación de email

        - Obtener preferencias de idioma y zona horaria

        - Cargar datos del usuario en formularios de edición


        ## Authentication

        - Requiere JWT válido (middleware m.isLoged)

        - Usuario debe estar autenticado


        ## Notes

        - Retorna datos usando model.parseMe() con información extendida

        - Error 404 si userData no existe en el token

        '
      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:

            - Token JWT inválido o expirado

            - Usuario no tiene permisos

            '
        '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

        Permite al usuario autenticado actualizar su propia información de perfil.

        Solo actualiza los campos proporcionados en el request (PATCH semantics).


        ## Objective

        Facilitar la actualización de datos personales y preferencias del usuario
        por sí mismo.


        ## Use Cases

        - Actualizar datos personales desde formulario de perfil

        - Cambiar preferencias de idioma

        - Actualizar número de teléfono

        - Modificar zona horaria

        - Cambiar email (con validación de duplicados)


        ## Authentication

        - Requiere JWT válido (middleware m.isLoged)

        - Usuario debe estar autenticado

        - Requiere validación UTC (mTools.checkUTC)


        ## Validations

        - Fechas deben estar en formato ISO8601 (YYYY-MM-DD)

        - Teléfono debe ser válido según el país

        - Email validado para evitar duplicados

        - Datos validados con model.validateData()

        '
      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:

            - Datos de formulario no válidos (model.validateData)

            - Email ya existe (USER_ALREADY_EXIST)

            - Formato de fecha incorrecto

            '
        '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

        Envía un email de verificación al usuario actual para activar su cuenta.

        Útil cuando el email anterior expiró o no llegó.


        ## Objective

        Permitir a los usuarios solicitar un nuevo email de verificación para

        completar el proceso de activación de su cuenta.


        ## Use Cases

        - Usuario no recibió el email de verificación original

        - El enlace de verificación expiró

        - Usuario quiere cambiar su email y necesita verificar el nuevo


        ## Authentication

        - Requiere JWT válido (middleware m.isLoged)


        ## Process

        1. Genera token de verificación único con tools.generateToken()

        2. Guarda token en user.recovery_token

        3. Envía email usando mail.sendActive() en el idioma del usuario

        4. Retorna confirmación de envío


        ## Email Content

        - Subject: Depende del idioma (user.i18n)

        - Body: Incluye enlace con token de verificación

        - Language: Usa el idioma configurado del usuario (user.i18n)


        ## Notes

        - No valida si el email ya está verificado

        - Sobrescribe cualquier token anterior

        - El token se usa en el endpoint de verificación de auth


        ## Error Messages

        - NOT_VALID (401): Email no válido o vacío

        '
      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:

            - Token JWT inválido o expirado

            - Email del usuario no es válido

            '
        '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.

                    Cualquier otro valor desactiva al usuario.

                    '
                  enum:
                  - NONE
                  - BAD_USER
                  - PENDING
                  - ACTIVE
                  - BLOCKED
                  example: NONE
                  type: string
                reasonDate:
                  description: 'Fecha/hora del cambio de estado (opcional). Si no
                    se proporciona,

                    se usa la fecha actual. Formato ISO8601.

                    '
                  example: '2025-02-12T14:30:00.000Z'
                  format: date-time
                  nullable: true
                  type: string
                reasonMessage:
                  description: 'Mensaje explicativo del cambio de estado (opcional).

                    Visible para el usuario y administradores.

                    '
                  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:

            - Razón no válida

            - Error al guardar

            '
        '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\n\
        flowchart 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

        Devuelve la información completa de un usuario específico de la compañía.


        ## Objective

        Permitir visualizar el perfil de cualquier usuario de la compañía,

        útil para administradores y gestores.


        ## Use Cases

        - Visualizar perfil de otro usuario (solo admin/gestor)

        - Verificar información de contacto

        - Consultar datos de un usuario específico


        ## Authentication

        - Requiere JWT válido (middleware m.isLoged)

        - Usuario debe estar autenticado

        - Solo devuelve datos si el usuario pertenece a la misma compañía


        ## Notes

        - Los usuarios no administradores pueden ver su propio perfil

        - El controlador verifica que el usuario pertenezca a la compañía

        '
      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:

            - Token JWT inválido o expirado

            - Usuario no tiene permisos para ver este perfil

            '
        '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

        Permite a un administrador editar el perfil de cualquier usuario de la compañía.


        ## Objective

        Facilitar la gestión de usuarios por parte de administradores, permitiendo

        modificar datos de cualquier usuario de la compañía.


        ## Use Cases

        - Actualizar datos de un usuario como administrador

        - Cambiar rol de usuario

        - Modificar email de usuario

        - Actualizar información de contacto


        ## Authentication & Authorization

        - Requiere JWT válido (middleware m.isLoged)

        - Requiere rol admin o dev (middleware m.isAdmin)

        - Requiere validación UTC (mTools.checkUTC)


        ## Validations

        - Email validado para evitar duplicados

        - Rol validado con model.getValidRole()

        - Datos validados con model.validateData()

        - Verifica que el usuario pertenezca a la compañía


        ## Notes

        - Similar a PUT /me pero para cualquier usuario de la compañía

        - Solo administradores pueden acceder

        '
      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:

            - Datos de formulario no válidos

            - Email ya existe (USER_ALREADY_EXIST)

            '
        '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\n\
        Enable 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

            - NO_TOKEN: JWT token not provided

            - TOKEN_NOT_VALID: JWT token is invalid or expired

            - CIA_NOT_FOUND: User does not have an associated company

            - CANT_SEND: General error sending data

            '
      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\n\
        Accepts two formats:\n1. **JSON Array**: `[\"up\", \"lateral\", \"back\"]`\n\
        2. **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\n\
        Creating 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:

                    1. JSON Array: ["up", "lateral", "back"]

                    2. Comma-separated string: "up, lateral, back"

                    '
                  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

                    for digital signature operations. Updates the user model.

                    '
                  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

            - CANT_CREATE: General creation error

            - Missing required fields

            - Invalid cargo_type values

            - Invalid vehicle_type

            - Missing fresh_cargo_temp when shipping_type=''fresh''

            '
        '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.

            Upgrade your plan to create more vehicles.

            '
        '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\n\
        3. **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).

            Check ''ok'' and ''ko'' arrays for results.

            '
          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

            - INVALID_DATA: CSV data is empty or malformed

            - CIA_SAVE_VEHICLES: Error updating company''s vehicle list

            '
        '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

        Returns detailed instructions for using the bulk vehicle import feature in
        the requested language.


        ## Objective

        Provide comprehensive guidance to users performing bulk imports for the first
        time,

        reducing errors and improving user experience.


        ## Use Cases

        - First-time users learning how to bulk import vehicles

        - Reference documentation for CSV format and validation rules

        - Troubleshooting bulk import errors


        ## Supported Languages

        - **es**: Spanish (Español)

        - **en**: English (default)


        If an unsupported language code is provided, defaults to English.


        ## Response

        Returns a plain text file with:

        - Content-Type: text/plain

        - Content-Disposition: attachment; filename="notes_{lang}.txt"


        The text file contains detailed instructions from the assets folder:

        - `src/assets/bulk_vehicles_notes_es.txt` (Spanish)

        - `src/assets/bulk_vehicles_notes_en.txt` (English)

        '
      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


                  Este archivo contiene las instrucciones detalladas...

                  '
                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,\n\
        reducing 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\n\
        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```\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

                  "up, lateral",-20,f2c,8796HSN,fresh

                  up,,rt,7628BCB,dry

                  back,,r3c,4052SMR,dry

                  '
                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

        Retrieves the complete list of vehicle types configured and visible in the
        system.


        ## Objective

        Enable companies to view all available vehicle type options for creating or
        updating vehicles in their fleet.


        ## Use Cases

        - Display vehicle type dropdown options when creating a new vehicle

        - Filter fleet by specific vehicle types in the interface

        - Understand vehicle type specifications and capabilities


        ## Response Details

        Returns an array of vehicle type objects ordered alphabetically by name. Each
        type includes:

        - Code identifier used in vehicle creation

        - Localized names and descriptions in multiple languages

        - Visibility status


        **Note:** Only visible vehicle types are returned. Results are sorted by name
        in ascending order.

        '
      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

        Marks a vehicle as deleted (soft delete) from the company''s fleet.


        ## Objective

        Enable companies to remove vehicles from active use while maintaining audit
        trail

        and historical data integrity.


        ## Use Cases

        - Remove sold or decommissioned vehicles from active fleet

        - Archive old vehicles while preserving historical records

        - Clean up vehicle list without losing data


        ## Soft Delete Behavior

        This endpoint performs a **soft delete** using mongoose-delete plugin:

        - The vehicle is NOT permanently deleted from the database

        - The field `deleted` is set to `true`

        - The field `deletedAt` is set to current timestamp

        - The vehicle is removed from the company''s vehicles array

        - Data remains in database for audit and recovery purposes


        **IMPORTANT:** This is NOT a permanent deletion. The vehicle can potentially
        be recovered

        by database administrators if needed.


        ## Side Effects

        - Updates the company model to remove the vehicle reference from the vehicles
        array

        - The vehicle will no longer appear in fleet listings


        **Note:** Currently there is no ownership validation in this endpoint. Any
        authenticated

        user can delete any vehicle if they know the ID. This may be a security concern.

        '
      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

        Retrieves complete details of a specific vehicle from the company''s fleet.


        ## Objective

        Enable companies to view full information about a single vehicle including
        all metadata,

        file references, and calculated cargo capacity.


        ## Use Cases

        - View complete vehicle details before editing

        - Display vehicle information in detail view

        - Retrieve image and ITV certificate URLs

        - Access calculated cargo capacity ranges


        ## Security

        This endpoint validates ownership - the vehicle must belong to the authenticated
        user''s company.

        If the vehicle exists but belongs to another company, returns 403 CIA_NOT_OWNER.


        ## Image URLs

        URLs for images and ITV certificates follow the format `/images?file={s3_key}`.

        Access these URLs with authentication to retrieve the actual files.


        ## Calculated Fields

        The `cargo` field is automatically calculated based on `vehicle_type` and
        contains

        volume (m³) and weight (kg) capacity ranges.

        '
      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.

            This is a security error indicating the user is attempting to access a
            resource

            that does not belong to them.

            '
        '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:

                    1. JSON Array: ["up", "lateral", "back"]

                    2. Comma-separated string: "up, lateral, back"

                    '
                  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.


        **Características**:

        - Paginación configurable (page, limit)

        - Ordenación predeterminada por nombre (asc) y fecha creación (desc)

        - Se puede obtener lista completa con page=-1 y limit=-1


        **Parámetros**:

        - page: Número de página (1-based), usar -1 para desactivar paginación

        - limit: Máximo de resultados por página, usar -1 para desactivar paginación


        **Ejemplo de uso**:

        - Mostrar lista paginada en interfaz de usuario

        - Exportar todas las direcciones (usando page=-1)

        '
      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.

          Valor especial -1 devuelve todos los resultados sin paginar.

          '
        required: false
        example: 20
        schema:
          type: integer
          default: 20
      responses:
        '200':
          description: 'Respuesta exitosa que contiene una lista paginada de direcciones.

            La estructura sigue el formato de paginación de MongoDB.

            '
          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.

            Incluye todos los campos del schema Address.

            '
          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.

            Ocurre cuando el usuario no tiene permisos o los datos son inválidos.

            '
          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.

            Incluye todos los campos del schema Address.

            '
          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.

            Ocurre cuando el usuario no tiene permisos o los datos son inválidos.

            '
          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\n\
        2. 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.

          Debe coincidir exactamente con los nombres devueltos por /states o /states/{label}

          Ejemplos válidos:

          - "Madrid"

          - "barcelona" (case-insensitive)

          '
        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

        del sistema, sin filtro por país.


        **Casos de uso**:

        - Búsqueda general de estados sin contexto de país

        - Validación de datos en formularios


        **Notas**:

        - Los estados se devuelven en su formato original como están registrados

        - Para obtener estados filtrados por país, usar /states/{label}

        '
      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.

        El parámetro {label} puede ser:

        - Nombre del país (ej: "españa")

        - Código de país (ej: "ES")


        **Ejemplo de flujo**:

        1. Obtener lista de países con /countries

        2. Seleccionar país y obtener sus estados con este endpoint


        **Notas**:

        - Los nombres de países son case-insensitive

        - Se aceptan nombres completos o códigos ISO

        '
      tags:
      - Address
      parameters:
      - name: label
        in: path
        description: 'Nombre o código del país para filtrar estados.

          Ejemplos válidos:

          - "españa"

          - "ES"

          - "portugal"

          - "PT"

          '
        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.

          Debe ser un ObjectId válido de MongoDB.

          '
        required: true
        example: 64917618ef73c37ccae60bfc
        schema:
          type: string
      responses:
        '200':
          description: 'Respuesta exitosa que contiene los datos completos de una
            dirección.

            Incluye todos los campos del schema Address.

            '
          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.

            Ocurre cuando el usuario no tiene permisos o los datos son inválidos.

            '
          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.

            Ocurre cuando el usuario no tiene permisos o los datos son inválidos.

            '
          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.


        Las subastas devueltas pueden estar en diferentes estados:

        - published: Subastas activas y disponibles para pujar

        - awarded: Subastas asignadas pero pendientes de firma

        - approved: Subastas aprobadas y en curso

        - completed: Subastas finalizadas


        El transportista solo verá subastas donde puede participar según:

        - Su ubicación geográfica

        - Tipo de vehículo

        - Capacidad de carga

        - Historial de servicio


        Ejemplo de uso típico:

        1. Transportista consulta subastas disponibles

        2. Filtra por ubicación/carga

        3. Selecciona una para ver detalles y pujar

        '
      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.

            Ocurre cuando el usuario no tiene permisos o los datos son inválidos.

            '
          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.


        Incluye:

        - Pujas en subastas activas (status: published)

        - Pujas ganadoras pendientes de firma (status: awarded)

        - Pujas rechazadas o canceladas (status: rejected/cancelled)


        Las pujas se ordenan por fecha de actualización descendente.

        '
      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:

          - all: Todas las pujas (valor por defecto)

          - open: Pujas en subastas abiertas

          - to_sign: Pujas ganadoras pendientes de firma

          - awarded: Pujas ganadoras

          - closed: Pujas en subastas cerradas

          - lost: Pujas perdidas

          - won: Pujas ganadas

          '
        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.

            Ocurre cuando el usuario no tiene permisos o los datos son inválidos.

            '
          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.


        Requisitos:

        - Usuario autenticado y activo

        - Cuenta de pago verificada y habilitada

        - Subasta en estado ''published''

        - Monto de puja válido (mayor que 0 y menor que la puja actual más baja)

        - Transportista cumple con los requisitos de la subasta (tipo de vehículo,
        capacidad, etc.)


        Flujo típico:

        1. Transportista consulta subastas disponibles

        2. Obtiene detalles de una subasta específica

        3. Realiza una puja con un monto competitivo

        4. Si la puja es la más baja, se convierte en la puja ganadora temporal


        En caso de puja ganadora:

        - Se notifica al transportista y al cargador

        - Se bloquea la subasta para otras pujas si se alcanza el precio de adjudicació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).

                    Ejemplo: "ABC12345"

                    '
                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).

                    Ejemplo: "Disponible con camión refrigerado"

                    '
            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:

            - Monto de puja inválido

            - Subasta no encontrada o no válida

            - Puja no cumple requisitos

            '
          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:

            - Usuario no activo

            - Cuenta de pago no verificada

            - No tiene permiso para pujar en esta subasta

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/403Error'
          headers: {}
        '409':
          description: 'Conflicto. Posibles causas:

            - Subasta ya adjudicada

            - Puja más baja ya existe

            - Restricciones de tiempo/ubicació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.


        Incluye:

        - Costo base del servicio

        - Tiempo estimado de transporte

        - Distancia entre origen y destino

        - Tarifas y comisiones aplicables

        - Costo total mínimo recomendado

        - Porcentaje sobre el precio base


        Este cálculo ayuda al transportista a:

        1. Determinar un precio competitivo pero rentable

        2. Evaluar la viabilidad económica de la subasta

        3. Planificar sus costos operativos


        Los cálculos consideran:

        - Distancia de la ruta

        - Tipo de vehículo requerido

        - Características especiales de la carga (refrigeración, etc.)

        - Tarifas vigentes

        '
      tags:
      - Bids
      parameters:
      - name: service_code
        in: path
        description: 'Código único de la subasta (8 caracteres alfanuméricos).

          Ejemplo: "ABC12345"

          '
        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:

            - Código de subasta inválido

            - Subasta no disponible para cálculo

            '
          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:

            - Usuario no autenticado

            - No tiene permiso para calcular costos de esta subasta

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/403Error'
          headers: {}
        '404':
          description: 'No encontrado. Posibles causas:

            - Subasta no existe

            - Datos insuficientes para cálculo

            '
          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.


        Requisitos:

        - Usuario autenticado y activo

        - La puja debe pertenecer al usuario o a su compañía

        - La subasta debe estar en un estado que permita eliminar pujas (published)

        - No puede eliminarse si ya es la puja ganadora (status: accepted)


        Flujo típico:

        1. Transportista consulta sus pujas activas

        2. Selecciona una puja para eliminar

        3. Si cumple las condiciones, la puja se marca como cancelled

        4. Si era la puja más baja, se actualiza la puja ganadora de la subasta

        '
      tags:
      - Bids
      parameters:
      - name: serviceCode
        in: path
        description: 'Código único de la subasta (8 caracteres alfanuméricos).

          Ejemplo: "ABC12345"

          '
        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:

            - Subasta no encontrada o no válida

            - Puja no puede eliminarse en el estado actual

            '
          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:

            - Usuario no activo

            - Puja no pertenece al usuario

            - No tiene permiso para eliminar esta puja

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/403Error'
          headers: {}
        '404':
          description: 'No encontrado. Posibles causas:

            - Subasta no existe

            - Puja no existe

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/404Error'
          headers: {}
        '409':
          description: 'Conflicto. Posibles causas:

            - Subasta ya adjudicada

            - Puja ya fue aceptada

            - Restricciones de tiempo

            '
          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.


        Requisitos:

        - Usuario autenticado y activo

        - Debe ser el ganador de la subasta (amWinner: true)

        - La subasta debe estar en estado ''approved'' o ''awarded''

        - El contrato debe estar disponible (signed_by_trucker: true)


        El contrato incluye:

        - Detalles completos de la subasta

        - Información de las partes (cargador y transportista)

        - Términos y condiciones

        - Firma digital del transportista (si aplica)

        - Datos del vehículo asignado


        El PDF se genera dinámicamente en el idioma del usuario (i18n).

        '
      tags:
      - Contracts
      parameters:
      - name: service_code
        in: path
        description: 'Código único de la subasta (8 caracteres alfanuméricos).

          Ejemplo: "ABC12345"

          '
        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:

            - Subasta no encontrada o no válida

            - Estado de subasta no permite descarga

            '
          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:

            - Usuario no es el ganador de la subasta

            - Contrato no disponible para descarga

            - Faltan firmas requeridas

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/403Error'
          headers: {}
        '404':
          description: 'No encontrado. Posibles causas:

            - Subasta no existe

            - Contrato no generado

            '
          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.


        Características:

        - Solo muestra subastas favoritas del usuario autenticado

        - Incluye subastas en todos los estados (activas, adjudicadas, completadas)

        - Mantiene el orden en que fueron marcadas como favoritas (más recientes primero)

        - Soporta paginación para manejar listas largas


        Campos importantes:

        - service_code: Identificador único de la subasta

        - status: Estado actual (published, awarded, approved, completed)

        - is_favorite: Siempre true (ya que son las favoritas del usuario)

        - favorite_date: Fecha cuando se marcó como favorita


        Ejemplo de uso:

        1. Transportista marca subastas como favoritas desde la lista

        2. Consulta esta lista para ver solo sus favoritas

        3. Puede filtrar/ordenar según necesidades

        '
      tags:
      - Auctions
      parameters:
      - name: page
        in: query
        description: 'Número de página a recuperar (por defecto 1).

          Ejemplo: ?page=2 para la segunda página.

          '
        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).

          Ejemplo: ?limit=50 para 50 resultados por página.

          '
        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:

            - Usuario no autenticado

            - Token JWT inválido o expirado

            '
          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.


        La imagen incluye información clave como:

        - Código y estado de la subasta

        - Fechas y ubicaciones de carga/descarga

        - Tipo y cantidad de pallets

        - Peso y dimensiones de la carga

        - Precio actual y puja mínima

        - Indicador visual si es carga refrigerada


        Características:

        - Imagen generada en tiempo real con los últimos datos

        - Formato PNG optimizado para web

        - Diseño responsive que se adapta a diferentes tamaños

        - Cacheada para mejorar el rendimiento


        Uso típico:

        1. Mostrar vista previa visual en listados de subastas

        2. Compartir en redes sociales o mensajería

        3. Incrustar en aplicaciones móviles

        '
      tags:
      - Auctions
      parameters:
      - name: code
        in: path
        description: 'Código único de la subasta (8 caracteres alfanuméricos).

          Ejemplo: "ABC12345"

          '
        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:

            - Código de subasta inválido

            - Formato no soportado

            '
          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:

            - Usuario no autenticado

            - No tiene permiso para ver esta subasta

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/403Error'
          headers: {}
        '404':
          description: 'No encontrado. Posibles causas:

            - Subasta no existe

            - Imagen no disponible

            '
          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.


        Permite buscar por:

        - Ciudad

        - Provincia/Estado

        - País

        - Código postal

        - Barrio


        El parámetro ''from'' determina si se buscan ubicaciones de:

        - etl: Lugares de carga (ETL - Estimated Time of Loading)

        - etd: Lugares de descarga (ETD - Estimated Time of Delivery)


        La búsqueda es insensible a mayúsculas/minúsculas y maneja acentos automáticamente.

        Ejemplo: Buscar "Madrid" encontrará también "MADRID" y "Mádrid".


        La respuesta incluye resultados paginados con información geográfica normalizada.

        '
      tags:
      - Auctions
      parameters:
      - name: search
        in: query
        description: 'Término de búsqueda para ubicaciones. Puede ser:

          - Nombre de ciudad (ej: "Barcelona")

          - Código postal (ej: "08001")

          - Provincia (ej: "Madrid")

          - País (ej: "España")

          - Combinación separada por comas (ej: "Madrid, Barcelona")

          '
        required: true
        schema:
          type: string
      - name: from
        in: query
        description: 'Tipo de ubicación a buscar:

          - etl: Lugares de carga (origen)

          - etd: Lugares de descarga (destino)

          Si no se especifica, busca en ambos tipos.

          '
        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.

            Ocurre cuando el usuario no tiene permisos o los datos son inválidos.

            '
          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).

          Ejemplo: "ABC12345"

          '
        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.

            Ocurre cuando el usuario no tiene permisos o los datos son inválidos.

            '
          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.


        Flujo:

        1. Valida el token de activación

        2. Marca la cuenta como activa y verifica el email

        3. Elimina el token de activación

        4. Envía email de confirmación


        El token es válido por 24 horas desde su generación.


        Errores comunes:

        - INVALID_TOKEN: Token no válido o expirado (400)

        - USER_NOT_FOUND: No existe usuario asociado al token (404)

        '
      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.


        Campos verificados:

        - Nombre completo

        - Teléfono válido

        - NIF/CIF válido

        - Email verificado

        - Datos de empresa completos (si aplica)


        Requiere autenticación JWT válida.

        '
      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.


        Flujo:

        1. Valida email y contraseña

        2. Verifica que la cuenta esté activa

        3. Registra el acceso en el historial

        4. Genera token JWT con datos del usuario


        Errores comunes:

        - WRONG_CREDENTIALS: Credenciales incorrectas (400)

        - USER_NOT_ACTIVE: Cuenta suspendida o no activada (401)

        '
      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\n\
        Proceso:\n1. Valida datos requeridos\n2. Verifica que el email no esté registrado\n\
        3. 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.

            Se debe renovar la autenticació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.

                    '
                  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.

                    '
                  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:

            - Formato de email incorrecto

            - Lista de contratos vacía

            - Más de 10 códigos de contrato

            '
          headers: {}
        '401':
          description: 'No autorizado. El token JWT es inválido o ha expirado.

            Se debe renovar la autenticació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.


        **Funcionalidad:**

        - Devuelve el PDF del contrato como binario

        - Valida que el código de contrato exista

        - Requiere autenticación JWT válida

        - Incluye cabeceras Content-Type y Content-Disposition


        **Casos de uso:**

        - Visualización del contrato en navegador

        - Descarga local para archivo

        - Integración con sistemas de gestión documental


        **Ejemplo de URL:**

        `https://api.demo.cargoffer.com/truckers/contracts/TRANS-2023-001`

        '
      tags:
      - Contracts
      parameters:
      - name: service_code
        in: path
        description: 'Código único del contrato a descargar.

          Debe coincidir exactamente con el código registrado.

          '
        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.

            Se debe renovar la autenticación.

            '
          headers: {}
        '404':
          description: 'Contrato no encontrado. Posibles causas:

            - El código de contrato no existe

            - El contrato no está asociado al transportista

            - El contrato ha sido eliminado

            '
          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.

        Este endpoint es público pero requiere autenticación básica.


        **Casos de uso:**

        - Mostrar lista de países disponibles para selección en formularios

        - Filtrar operaciones por país habilitado

        - Integración con otros sistemas que necesiten referencia de países


        **Notas:**

        - Solo devuelve países con enabled=true

        - Los campos sensibles como _id, deleted, createdAt son omitidos

        '
      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\n\
            con 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.


            **Posibles causas:**

            - País no existe

            - Recurso eliminado

            '
          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.


            **Posibles causas:**

            - Fallo en base de datos

            - Excepción no controlada

            '
          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.

            Ocurre cuando el usuario no tiene permisos o los datos son inválidos.

            '
          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.


        **Restricciones:**

        - Solo puede eliminar entregas asignadas al transportista autenticado

        - No se pueden eliminar entregas en estado ''collected'' o ''delivered''


        **Casos de uso:**

        - Cancelar una entrega que aún no ha comenzado

        - Eliminar una entrega creada por error


        **Notas:**

        - Requiere autenticación JWT válida

        - La eliminación es permanente y no se puede deshacer

        - Devuelve el ID de la entrega eliminada si tiene éxito

        '
      tags:
      - Deliveries
      parameters:
      - name: id
        in: path
        description: 'ID único de MongoDB de la entrega a eliminar.

          Formato: 24 caracteres hexadecimales.

          '
        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.


        **Funcionalidades:**

        - Actualiza la ETA considerando el tiempo de viaje restante

        - Registra nivel de combustible y carga extra si se proporcionan

        - Notifica automáticamente a la empresa contratante del cambio


        **Casos de uso:**

        - Ajustar ETA por retrasos en ruta (tráfico, averías)

        - Actualizar estimación por cambios en velocidad media

        - Reportar estado del vehículo (combustible, carga)


        **Validaciones:**

        - Solo funciona para entregas en estado ''collected''

        - La nueva ETA debe ser posterior a la actual

        - Nivel de combustible debe estar entre 0-100

        '
      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.


        **Características:**

        - Devuelve mensajes ordenados cronológicamente (más recientes primero)

        - Incluye texto, fotos adjuntas y metadatos

        - Muestra información del autor (nombre y ID)


        **Casos de uso:**

        - Consultar el historial de comunicación sobre una entrega

        - Verificar actualizaciones o incidencias reportadas

        - Revisar fotos adjuntas de la carga/descarga


        **Notas:**

        - Requiere autenticación JWT válida

        - Solo devuelve mensajes de entregas asignadas al transportista

        - Los mensajes se almacenan en formato UTC

        '
      tags:
      - Deliveries
      parameters:
      - name: id
        in: path
        description: 'ID único de MongoDB de la entrega.

          Formato: 24 caracteres hexadecimales.

          '
        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.


        **Características:**

        - Soporta texto y adjuntos (hasta 6 imágenes)

        - Registra automáticamente autor (nombre e ID) y timestamp

        - Almacena los mensajes asociados permanentemente a la entrega

        - Valida que la entrega permita mensajes (can_message=true)


        **Casos de uso:**

        - Reportar incidencias durante el transporte (averías, retrasos)

        - Enviar actualizaciones sobre el estado de la entrega

        - Compartir fotos como prueba de carga/descarga

        - Comunicar cambios en horarios o rutas


        **Notas:**

        - Requiere autenticación JWT válida

        - Solo funciona para entregas asignadas al transportista

        - El usuario autenticado se registra automáticamente como autor

        - Las imágenes se suben mediante multipart/form-data

        - El timestamp se genera automáticamente en el servidor (UTC)

        '
      tags:
      - Deliveries
      parameters:
      - name: id
        in: path
        description: 'ID único de MongoDB de la entrega.

          Formato: 24 caracteres hexadecimales.

          '
        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.


        **Características:**

        - Almacena notas en formato texto plano

        - Registra automáticamente timestamp de creación/actualización

        - Las notas son visibles solo para el transportista

        - Soporta formato Markdown básico para organización


        **Casos de uso:**

        - Registrar observaciones importantes sobre la entrega

        - Guardar detalles de contacto o instrucciones especiales

        - Anotar información relevante para futuras referencias


        **Notas:**

        - Requiere autenticación JWT válida

        - Solo funciona para entregas asignadas al transportista

        - Las notas se almacenan en formato UTC

        - Cada actualización sobrescribe las notas anteriores

        '
      tags:
      - Deliveries
      parameters:
      - name: id
        in: path
        description: 'ID único de MongoDB de la entrega.

          Formato: 24 caracteres hexadecimales.

          '
        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

                - Contacto en destino: María López - 612345678

                - Horario restringido de descarga: 8:00-14:00

                - Requiere documentación adicional en puerta

                - Número de pallets confirmados: 12

                '
        required: true
      responses:
        '200':
          description: Notas guardadas correctamente
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Note'
              example:
                content: '# Notas importantes

                  - Contacto en destino: María López - 612345678

                  - Horario restringido de descarga: 8:00-14:00

                  - Requiere documentación adicional en puerta

                  - Número de pallets confirmados: 12

                  '
                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.


        **Información incluida:**

        - Coordenadas de la ruta completa (array de puntos GPS)

        - Distancia total en kilómetros

        - Tiempo estimado de viaje en minutos

        - Instrucciones paso a paso para la navegación

        - Puntos de interés relevantes en la ruta


        **Casos de uso:**

        - Visualizar la ruta óptima en el mapa del transportista

        - Calcular tiempo y distancia estimados

        - Obtener instrucciones de navegación detalladas

        - Identificar puntos de descanso o repostaje


        **Notas:**

        - Requiere autenticación JWT válida

        - Solo funciona para entregas asignadas al transportista

        - La ruta se calcula usando el servicio de mapas interno

        - Los tiempos estimados consideran tráfico en tiempo real

        '
      tags:
      - Deliveries
      parameters:
      - name: service_code
        in: path
        description: 'Código único de identificación de la entrega.

          Formato: 8 caracteres alfanuméricos en mayúsculas.

          '
        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.

            Ocurre cuando el usuario no tiene permisos o los datos son inválidos.

            '
          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.

            Espere antes de realizar nuevas consultas.

            '
          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.


        **Funcionalidades clave:**

        - Registra coordenadas GPS precisas (latitud, longitud)

        - Almacena timestamp exacto de la posición (UTC)

        - Actualiza la última posición conocida del transportista

        - Asocia automáticamente la posición a la entrega especificada

        - Mantiene historial de posiciones para trazabilidad


        **Casos de uso principales:**

        - Seguimiento en tiempo real durante el transporte

        - Generación de rutas optimizadas basadas en posición actual

        - Cálculo de tiempos estimados de llegada (ETA) dinámicos

        - Verificación geográfica para procesos de carga/descarga

        - Monitoreo de flota y gestión de activos


        **Validaciones y requisitos:**

        - Solo funciona para entregas en estado ''collected''

        - Coordenadas GPS deben ser válidas (lat: -90 a 90, lng: -180 a 180)

        - Timestamp debe ser actual (no más de 5 minutos de diferencia)

        - Máximo 1 actualización por minuto para evitar spam


        **Ejemplo de flujo:**

        1. App móvil obtiene posición GPS del dispositivo

        2. Envía posición al endpoint cada 2-5 minutos

        3. Sistema actualiza posición y recalcula ETA

        4. Empresa puede visualizar posición en tiempo real


        **Notas técnicas:**

        - Las coordenadas se almacenan en formato GeoJSON

        - El timestamp se convierte automáticamente a UTC

        - Las posiciones antiguas se archivan después de 30 días

        '
      tags:
      - Deliveries
      parameters:
      - name: service_code
        in: path
        description: 'Código único de identificación de la entrega a trackear.

          Formato: 8 caracteres alfanuméricos en mayúsculas.

          Debe corresponder a una entrega en estado ''collected''.

          '
        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.

            Ocurre cuando el usuario no tiene permisos o los datos son inválidos.

            '
          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.

            Espere antes de enviar nuevas posiciones.

            '
          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.


        **Información incluida:**

        - Datos básicos de la entrega (código, estado, fechas)

        - Direcciones de carga y descarga (etl_address, etd_address)

        - Detalles de la carga (tipo, peso, pallets)

        - Información de la empresa contratante

        - Datos de contacto del responsable

        - Mensajes y notas asociadas


        **Casos de uso:**

        - Visualizar todos los detalles de una entrega específica

        - Consultar información para preparar la recogida/entrega

        - Verificar datos antes de iniciar una operación


        **Notas:**

        - Requiere autenticación JWT válida

        - Solo devuelve entregas asignadas al transportista autenticado

        '
      tags:
      - Deliveries
      parameters:
      - name: service_code
        in: path
        description: 'Código único de identificación de la entrega.

          Formato: 8 caracteres alfanuméricos en mayúsculas.

          '
        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.

          Debe incluir la extensión del archivo (ej: .pdf, .jpg).


          **Ejemplos válidos:**

          - contrato_transporte_2023.pdf

          - perfil_usuario_123.jpg

          - licencia_conducir_456.png

          '
        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.

        La búsqueda es case-insensitive y busca coincidencias parciales en títulos
        y etiquetas.


        **Ejemplo de uso**:

        - Búsqueda rápida de códigos HS por producto

        - Autocompletado en interfaces de usuario

        - Identificación de categorías HS relevantes


        **Notas**:

        - Devuelve un máximo de 20 resultados por tipo (headings y subheadings)

        - Los resultados se ordenan alfabéticamente por título

        - La búsqueda ignora comillas simples en el término

        '
      tags:
      - Hscode
      parameters:
      - name: term
        in: query
        description: 'Término de búsqueda (ej. "animales", "electronicos").

          Puede ser una palabra parcial o completa del título o descripció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.

        Si no se especifica idioma, usa español por defecto.


        Requiere autenticación JWT válida.

        No requiere permisos de administrador.

        '
      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).

        Busca tanto por código directo como por combinación código+idioma.


        Requiere autenticación JWT válida.

        No requiere permisos de administrador.

        '
      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:

            - El código no existe

            - El documento no está visible

            - No existe versión en el idioma solicitado

            '
          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.

            Ocurre cuando el usuario no tiene permisos o los datos son inválidos.

            '
          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.\n\
        Permite 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.

            Ocurre cuando el usuario no tiene permisos o los datos son inválidos.

            '
          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.

            Ocurre cuando el usuario no tiene permisos o los datos son inválidos.

            '
          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.

            Ocurre cuando el usuario no tiene permisos o los datos son inválidos.

            '
          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).\n\
        Las 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.

            Ocurre cuando el usuario no tiene permisos o los datos son inválidos.

            '
          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,

        identificado por su ID único.


        **Parámetros:**

        - id (requerido): ID único del vehículo a eliminar (MongoDB ObjectId)


        **Casos de uso:**

        - Dar de baja un vehículo específico que ya no está en servicio

        - Eliminar registros duplicados o erróneos

        - Limpiar la flota de vehículos obsoletos


        **Notas:**

        - La eliminación es permanente y no se puede deshacer

        - Se eliminarán también las imágenes asociadas de S3

        - Requiere autenticación JWT válida

        '
      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.

            Ocurre cuando el usuario no tiene permisos o los datos son inválidos.

            '
          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:**\n\
        1. 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.

        Contiene información de ubicación geográfica y datos postales.

        '
      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.

        Contiene toda la información necesaria para identificar y localizar la estació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.

        Incluye información básica, imágenes y metadatos de creació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.

            - true: Perfil completo, puede operar normalmente

            - false: Faltan datos obligatorios, necesita completar perfil

            '
          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.

        **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.


            **Transformaciones aplicadas**:

            - Elimina prefijos textuales (ej: "section 01" → "01")

            - Mantiene solo la parte numérica del código

            - Para partidas: 4 dígitos (ej: "0101")

            - Para subpartidas: 6 dígitos (ej: "010121")


            **Ejemplos**:

            - "section 01" → "01"

            - "heading 0101" → "0101"

            - "subheading 010121" → "010121"'
          example: '010121'
        label:
          type: string
          description: 'Descripción legible para humanos, normalizada y limpia.


            **Transformaciones aplicadas**:

            - Combina múltiples etiquetas en un solo string

            - Elimina caracteres especiales como "-", ":", "--"

            - Normaliza espacios (elimina múltiples espacios)

            - Convierte a minúsculas (excepto la primera letra)


            **Ejemplo de transformación**:

            Original: ["Reproductores", "-", "de", "raza", "pura"]

            Resultado: "Reproductores de raza pura"


            **Casos de uso**:

            - Mostrar en interfaces de usuario

            - Búsqueda por texto

            - Generación de documentos'
          example: Reproductores de raza pura
    CategorySection:
      type: object
      description: 'Estructura jerárquica completa de categorías de mercancías.

        Representa la relación padre-hijo entre secciones, capítulos, partidas y subpartidas.


        **Niveles jerárquicos**:

        1. Sección (nivel más alto)

        2. Capítulo (dentro de sección)

        3. Partida (4 dígitos, dentro de capítulo)

        4. Subpartida (6 dígitos, dentro de partida)


        **Notas**:

        - Todos los niveles excepto sección son opcionales

        - La estructura puede terminar en cualquier nivel

        - Cada nivel hereda las propiedades de CategoryItem

        '
      properties: {}
    CategorySearchResult:
      type: array
      items:
        $ref: '#/components/schemas/CategoryItem'
      description: 'Resultados de búsqueda que coinciden con el término proporcionado.

        Ordenados alfabéticamente por título y limitados a 20 resultados por tipo

        (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.

        Se utiliza para normalizar y geolocalizar direcciones físicas.

        '
      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.

        Incluye información básica, dirección y datos bancarios.

        '
      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.

        Todos los campos son opcionales, solo se actualizarán los campos proporcionados.

        '
      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.

            Si se proporciona, se usará para actualizar la dirección física.

            '
        bank:
          type: string
          description: 'Número completo de cuenta bancaria.

            - Se almacenan solo los últimos 4 dígitos (****1234)

            - Si el valor contiene asteriscos (****1234), se ignorará

            - Requiere formato válido según el país

            '
          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

        y reglas de validación para identificadores fiscales.

        '
      properties:
        code:
          type: string
          description: 'Código ISO 3166-1 alpha-2 del país (2 letras mayúsculas).

            Este campo es único y actúa como identificador principal.

            '
          minLength: 2
          maxLength: 2
          pattern: ^[A-Z]{2}$
          example: ES
        name:
          type: string
          description: 'Nombre completo del país en español.

            Debe ser único en el sistema.

            '
          minLength: 3
          maxLength: 100
          example: España
        regex:
          type: string
          description: 'Patrón de expresión regular para validar identificadores fiscales

            (NIF, CIF, VAT) del país. Sigue la sintaxis estándar de regex.

            '
          example: ^[A-Z0-9]{9}$
        enabled:
          type: boolean
          description: 'Indica si el país está habilitado para operaciones.

            Los países deshabilitados no aparecen en listados públicos.

            '
          default: true
          example: true
        createdAt:
          type: string
          format: date-time
          description: 'Fecha y hora de creación del registro en formato ISO 8601.

            Generado automáticamente por el sistema.

            '
          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.

            Actualizado automáticamente por el sistema.

            '
          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\n\
        y 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.

            Puede estar vacío si no hay resultados.

            '
        total:
          type: integer
          description: 'Número total de países que coinciden con la consulta,

            independientemente de la paginación.

            '
          example: 50
        limit:
          type: integer
          description: 'Número máximo de países devueltos por página.

            Coincide con el parámetro `limit` de la consulta o usa el valor por defecto.

            '
          example: 10
        page:
          type: integer
          description: 'Número de página actual (1-based).

            Coincide con el parámetro `page` de la consulta.

            '
          example: 1
        pages:
          type: integer
          description: 'Número total de páginas disponibles según el total de resultados

            y el límite por página.

            '
          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.


            Este código se usará para:

            - Crear el registro DGT

            - Asociar todas las entregas en estado ''collected'' del usuario


            **Nota:** Si no hay entregas en estado ''collected'',

            se crea el registro con deliveries vacío.

            '
          example: DGT2023XYZ123
      required:
      - dgt_code
    DGTEditRequest:
      type: object
      description: 'Request para actualizar un registro DGT existente.


        **Nota importante sobre el parámetro `code`:**

        - El controlador lee el parámetro `code` del request

        - Este valor se asigna al campo `dgt_code` del modelo

        - El valor por defecto de `status` es "resolved" si no se proporciona


        **Campos opcionales:**

        - Ambos campos son opcionales

        - Al menos uno debe proporcionarse para realizar la actualización

        '
      properties:
        code:
          type: string
          description: 'Nuevo código DGT.


            **Importante:** Este parámetro se llama `code` en el request

            pero se asigna al campo `dgt_code` del modelo.


            El controlador hace:

            `const updateData = { status: status, dgt_code: code };`

            '
          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.


            Debe ser una dirección de email válida.

            El idioma del email se determina por la preferencia i18n del usuario.

            '
          example: destinatario@empresa.com
        dgt_code:
          type: string
          description: 'Código DGT existente del usuario autenticado.


            El registro DGT debe existir y pertenecer al usuario autenticado.

            Se buscan las entregas asociadas para generar los documentos.

            '
          example: DGT2023XYZ123
      required:
      - email
      - dgt_code
    Driver:
      type: object
      description: 'Representa un conductor asociado a una compañía de transportistas.

        Contiene toda la información del perfil del conductor y su estado en el sistema.

        '
      properties:
        _id:
          type: string
          description: 'Identificador único del conductor en el sistema.

            Generado automáticamente al crear el conductor.

            '
          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.

            Se utiliza para notificaciones y acceso al sistema.

            '
          format: email
          example: juan.perez@transporte.com
        phone:
          type: string
          description: 'Teléfono de contacto del conductor.

            Formato internacional recomendado.

            '
          example: '+34666555444'
        image:
          type: string
          description: 'URL de la foto de perfil del conductor.

            Almacenada en el servicio de almacenamiento en la nube.

            '
          format: uri
          example: https://bucket.s3.amazonaws.com/drivers/507f1f77bcf86cd799439011.jpg
        status:
          type: string
          description: 'Estado actual del conductor en el sistema.

            Determina qué operaciones puede realizar.

            '
          enum:
          - active
          - inactive
          - pending
          example: active
        createdAt:
          type: string
          format: date-time
          description: 'Fecha y hora de creación del registro.

            Formato ISO 8601.

            '
          example: '2025-01-15T10:30:00.000Z'
        updatedAt:
          type: string
          format: date-time
          description: 'Fecha y hora de la última actualización.

            Actualizado automáticamente en cada modificació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.


        Este objeto controla el acceso y permisos del conductor en el sistema.

        El campo `reason` determina automáticamente el valor booleano del campo `status`.


        **Lógica de negocio:**

        - `reason = "NONE"` → `status = true` (conductor habilitado)

        - Cualquier otro `reason` → `status = false` (conductor deshabilitado)


        **Auditoría:**

        Los campos opcionales (`reasonDate`, `reasonMessage`) permiten documentar

        el motivo y contexto del cambio de estado para fines de auditoría.

        '
      required:
      - id
      - reason
      properties:
        id:
          type: string
          description: 'ID único del conductor (MongoDB ObjectId).

            También se acepta como `_id` en el body o query parameters.

            '
          pattern: ^[0-9a-fA-F]{24}$
          example: 507f1f77bcf86cd799439011
        reason:
          type: string
          description: 'Estado operacional del conductor que determina sus permisos
            de acceso.


            **Valores válidos:**

            - `NONE`: Conductor activo sin restricciones (status=true)

            - `PENDING`: En proceso de validación/aprobación (status=false)

            - `ACTIVE`: Aprobado pero temporalmente inactivo (status=false)

            - `BLOCKED`: Bloqueado por incumplimiento de políticas (status=false)

            - `BAD_USER`: Usuario problemático, acceso permanentemente denegado (status=false)


            **Solo `NONE` habilita completamente al conductor.**

            '
          enum:
          - NONE
          - PENDING
          - ACTIVE
          - BLOCKED
          - BAD_USER
          example: BLOCKED
        reasonDate:
          type: string
          format: date-time
          description: 'Fecha asociada al cambio de estado (opcional).


            **Casos de uso:**

            - Fecha de vencimiento de documentación

            - Fecha de inicio de suspensión

            - Fecha límite para resolver el problema


            **Formato:** ISO 8601 (ej. 2025-10-29T10:30:00Z)


            **Nota:** Se limpia automáticamente si `reason = "NONE"`

            '
          example: '2025-10-29T10:30:00Z'
        reasonMessage:
          type: string
          description: 'Mensaje descriptivo del motivo del cambio (opcional).


            **Buenas prácticas:**

            - Ser específico sobre el problema

            - Incluir acciones requeridas para resolverlo

            - Indicar documentación o requisitos faltantes

            - Mantener tono profesional y constructivo


            **Ejemplos:**

            - "Licencia de conducir vencida. Renovar antes del 2025-12-31"

            - "Pendiente de verificación de antecedentes penales"

            - "Múltiples quejas de clientes. Suspensión temporal por 30 días"


            **Nota:** Se limpia automáticamente si `reason = "NONE"`

            '
          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.

            Este valor se obtiene de la configuración más reciente del sistema (Settings)

            o usa el valor por defecto de 6% si no hay configuració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.

        Puede incluir headings y subheadings que coincidan con el término buscado.

        '
      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).

              Permite identificar el nivel de clasificación.

              '
            example: '0101.21'
          label:
            type: string
            description: 'Descripción completa del resultado.

              Proporciona contexto sobre los productos incluidos.

              '
            example: Caballos para reproducción
          url:
            type: string
            description: 'URL completa para acceder al detalle de este código HS.

              Puede usarse para navegación directa en la jerarquía.

              '
            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.

            Formato: ObjectId hexadecimal de 24 caracteres.

            '
          example: 507f1f77bcf86cd799439011
        number:
          type: string
          description: 'Número único de factura.

            Formato libre pero debe ser único en el sistema.

            '
          minLength: 3
          maxLength: 50
          example: FAC-2023-001
        date:
          type: string
          format: date
          description: 'Fecha de emisión de la factura.

            Formato: YYYY-MM-DD (ISO 8601)

            '
          example: '2023-05-15T00:00:00.000Z'
        amount:
          type: number
          format: float
          description: 'Importe total de la factura.

            Mínimo: 0.01

            Máximo: 9999999.99

            '
          minimum: 0.01
          maximum: 9999999.99
          example: 1250.5
        status:
          type: string
          enum:
          - draft
          - issued
          - paid
          - cancelled
          description: 'Estado actual de la factura.

            - draft: Borrador (editable)

            - issued: Emitida (no editable)

            - paid: Pagada

            - cancelled: Cancelada

            '
          example: issued
        truckerId:
          type: string
          description: 'ID del transportista dueño de la factura.

            Debe coincidir con el JWT de autenticación.

            '
          example: 507f1f77bcf86cd799439011
        createdAt:
          type: string
          format: date-time
          description: 'Fecha de creación automática en el sistema.

            Formato: ISO 8601 (YYYY-MM-DDTHH:MM:SSZ)

            '
          example: '2023-05-15T10:30:00.000Z'
        updatedAt:
          type: string
          format: date-time
          description: 'Fecha de última actualización.

            Actualizada automáticamente al modificar la factura.

            '
          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.).

        Cada documento puede tener múltiples versiones por idioma.

        '
      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).

        Valores permitidos:

        - config: Problemas con configuración de cuenta o perfil

        - contract: Consultas o problemas con contratos

        - address: Errores en direcciones de entrega

        - document: Problemas con documentos subidos

        - delivery: Incidencias en entregas/procesos logísticos

        - auction: Problemas con subastas de carga

        - messages: Consultas sobre mensajes/conversaciones

        - user: Problemas con otros usuarios

        - bid: Consultas sobre ofertas/pujas

        - others: Otros motivos no categorizados

        '
      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

        y capacidades del vehículo para transporte de mercancías.


        **Tipos válidos:** Los definidos en Vehicle.validTypes del modelo

        **Tipos de envío válidos:** Los definidos en Vehicle.validShippingTypes del
        modelo

        '
      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.

            Se utiliza internamente para referencias y validaciones.

            '
          example: TRUCK
        name:
          type: string
          description: 'Nombre legible del tipo de vehículo.

            Se muestra en interfaces de usuario y formularios.

            '
          example: Camión de carga
        description:
          type: string
          description: 'Descripción detallada de las características y capacidades
            del vehículo.

            Incluye información sobre dimensiones, peso máximo, tipo de carga soportada,
            etc.

            '
          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.

            Los tipos ocultos no aparecen en listados ni formularios.

            '
          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.

        Sigue el formato estándar de paginación usado en la API.


        **Estructura:**

        - docs: Array con los documentos solicitados

        - total: Número total de documentos disponibles

        - limit: Límite de documentos por página

        - page: Página actual

        - pages: Número total de páginas

        '
      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
