openapi: 3.0.3
info:
  title: Pymas API
  description: |
    API REST de Pymas (SaaS). Incluye, entre otras áreas:
    - Autenticación y empresas
    - Personal (fichajes, solicitudes)
    - Marketing (listas, plantillas, campañas)
    - Servicios (tareas / tickets de trabajo)
    - Cumplimiento (estado del módulo; recursos adicionales en desarrollo)
    - Ventas (CRM: lectura de clientes y contactos por empresa)
    - Compras (lectura de proveedores por empresa)
    - Finanzas (lecturas por empresa: resumen, bancos, facturas, asientos)
    Portal integradores (landing): `/desarrolladores` en el mismo dominio que la app.
    Documentación interactiva: `/docs/api` (Redoc). Contrato YAML: `/docs/openapi.yaml`.
    Este contrato describe solo rutas expuestas bajo `/api` en la aplicación actual (sin endpoints legacy de «bot»).
  version: 1.0.0
  contact:
    name: Pymas
    url: https://pymas.es
    email: info@pymas.es
  license:
    name: Propietario

servers:
  - url: https://pymas.es/api
    description: Producción
  - url: https://pymas.test/api
    description: Entorno local (Valet / dominio de desarrollo)
  - url: http://127.0.0.1:8000/api
    description: Artisan serve (opcional)

security:
  - BearerAuth: []

paths:
  /health:
    get:
      tags:
        - Sistema
      summary: Comprobación de disponibilidad
      description: Público. No expone diagnóstico interno.
      security: []
      responses:
        '200':
          description: API operativa
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PymasApiHealthResponse'

  /system/info:
    get:
      tags:
        - Sistema
      summary: Información pública de la API
      description: |
        Público. Datos mínimos (producto, versión, zona horaria). **No** incluye entorno, PHP, Laravel ni estado de base de datos.
      security: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PymasApiPublicInfoResponse'

  /sistema/info:
    get:
      tags:
        - Sistema
      summary: Alias en español de GET /system/info
      description: Misma respuesta que `/system/info`.
      security: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PymasApiPublicInfoResponse'

  /system/stats:
    get:
      tags:
        - Sistema
      summary: Estadísticas globales (solo desarrollador)
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PymasApiSuccessStats'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'

  /sistema/estadisticas:
    get:
      tags:
        - Sistema
      summary: Alias en español de GET /system/stats
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PymasApiSuccessStats'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'

  /system/diagnostics:
    get:
      tags:
        - Sistema
      summary: Diagnóstico de servidor (solo desarrollador)
      description: Incluye entorno, versiones de runtime y estado de BD. No usar con tokens de integración de clientes.
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PymasApiDiagnosticsResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'

  /sistema/diagnostico:
    get:
      tags:
        - Sistema
      summary: Alias en español de GET /system/diagnostics
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PymasApiDiagnosticsResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'

  /auth/me:
    get:
      tags:
        - Autenticación
      summary: Usuario autenticado (perfil API)
      description: |
        Perfil del token actual. Respuesta estándar Pymas (`success`, `message`, `data.user`).
        Requiere `Authorization: Bearer {token}` (Laravel Sanctum).
      security:
        - BearerAuth: []
      responses:
        '200':
          description: Perfil del usuario
          content:
            application/json:
              schema:
                type: object
                required:
                  - success
                  - data
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                  data:
                    type: object
                    properties:
                      user:
                        type: object
                        properties:
                          id:
                            type: integer
                          name:
                            type: string
                          email:
                            type: string
                            format: email
                          roles:
                            type: array
                            items:
                              type: string
                          empresa_id:
                            type: integer
                            nullable: true
                          empresa_nombre:
                            type: string
                            nullable: true
                          ref_cliente:
                            type: string
                            nullable: true
                          created_at:
                            type: string
                            nullable: true
        '401':
          $ref: '#/components/responses/UnauthorizedError'

  /empresas/{empresa}/trabajadores:
    get:
      tags:
        - Empresas
      summary: Obtener trabajadores de una empresa
      description: Obtiene la lista de trabajadores de una empresa específica con roles de trabajador
      security:
        - BearerAuth: []
      parameters:
        - name: empresa
          in: path
          required: true
          description: ID de la empresa
          schema:
            type: integer
            example: 1
      responses:
        '200':
          description: Lista de trabajadores
          content:
            application/json:
              schema:
                type: object
                properties:
                  trabajadores:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          example: 1
                        name:
                          type: string
                          example: "María García"
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  /ventas/clientes:
    get:
      tags:
        - Ventas
      summary: Listar clientes y contactos (CRM)
      description: |
        Lista paginada de la tabla `clientes` filtrada por la empresa efectiva del usuario autenticado (`getEmpresa()`).
        No admite cruzar datos entre empresas por ID (404 si el registro no pertenece al tenant).
        Requiere permiso funcional de ver **gestion_clientes** o **gestion_contactos** (misma regla OR que en la UI de Ventas).
      security:
        - BearerAuth: []
      parameters:
        - name: per_page
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 15
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
        - name: tipo
          in: query
          description: Filtrar por tipo de ficha (campo `es_cliente`).
          schema:
            type: string
            enum: [contactos, clientes]
        - name: estado
          in: query
          description: Estado comercial (p. ej. activo, archivado).
          schema:
            type: string
        - name: q
          in: query
          description: Búsqueda de texto (alias `search`).
          schema:
            type: string
      responses:
        '200':
          description: Listado paginado
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                  data:
                    type: object
                    properties:
                      items:
                        type: array
                        items:
                          $ref: '#/components/schemas/VentasClienteResumen'
                      meta:
                        type: object
                        properties:
                          current_page:
                            type: integer
                          last_page:
                            type: integer
                          per_page:
                            type: integer
                          total:
                            type: integer
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
    post:
      tags:
        - Ventas
      summary: Crear cliente o contacto (CRM)
      description: |
        Crea una ficha en `clientes` para la empresa efectiva del token.
        Requiere permiso funcional **crear** en `gestion_clientes` si `es_cliente` es true (por defecto), o en `gestion_contactos` si es contacto.
        Política `ClientePolicy::create`.
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VentasClienteCreate'
      responses:
        '201':
          description: Creado
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                  data:
                    $ref: '#/components/schemas/VentasClienteDetalle'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '422':
          $ref: '#/components/responses/ValidationError422'

  /ventas/clientes/{id}:
    get:
      tags:
        - Ventas
      summary: Detalle de cliente o contacto
      description: |
        Devuelve un registro de `clientes` solo si pertenece a la empresa efectiva del token.
        Política `ClientePolicy::view` adicional (p. ej. rol estándar acotado a su empresa).
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Detalle
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                  data:
                    $ref: '#/components/schemas/VentasClienteDetalle'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          $ref: '#/components/responses/NotFoundError'
    put:
      tags:
        - Ventas
      summary: Actualizar cliente o contacto
      description: |
        Actualiza campos permitidos. Requiere permiso funcional **editar** en la funcionalidad acorde a `es_cliente` actual del registro.
        Si se cambia `es_cliente`, además se exige permiso **crear** en la funcionalidad de destino.
        Política `ClientePolicy::update`.
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VentasClienteUpdate'
      responses:
        '200':
          description: Actualizado
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                  data:
                    $ref: '#/components/schemas/VentasClienteDetalle'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '422':
          $ref: '#/components/responses/ValidationError422'

  /cumplimiento/status:
    get:
      tags:
        - Cumplimiento
      summary: Estado del área Cumplimiento
      description: |
        Indica que el módulo está reconocido por la API. Los usuarios con empresa obtienen su `empresa_id`;
        el rol `desarrollador` puede enviar `empresa_id` opcional (query) para anclar la respuesta a un tenant.
        No expone datos internos de diagnóstico.
      security:
        - BearerAuth: []
      parameters:
        - name: empresa_id
          in: query
          required: false
          description: Solo rol desarrollador. ID de empresa existente.
          schema:
            type: integer
            example: 1
      responses:
        '200':
          description: Estado del módulo
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: "Estado del área Cumplimiento (Pymas)."
                  data:
                    type: object
                    properties:
                      area:
                        type: string
                        example: cumplimiento
                      module_state:
                        type: string
                        example: in_development
                      empresa_id:
                        type: integer
                        nullable: true
                      features:
                        type: array
                        items:
                          type: string
                        example: []
                      documentation_url:
                        type: string
                        format: uri
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '422':
          description: Validación (p. ej. empresa_id inexistente para desarrollador)
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  errors:
                    type: object

  /servicios/tareas:
    get:
      tags:
        - Servicios
      summary: Listado de tareas (gestión)
      description: |
        Listado paginado para la empresa en contexto. Los usuarios no desarrollador solo ven su empresa.
        Rol desarrollador debe indicar `empresa_id` en query.
      security:
        - BearerAuth: []
      parameters:
        - name: empresa_id
          in: query
          required: false
          description: Obligatorio en la práctica para rol desarrollador
          schema:
            type: integer
        - name: estado
          in: query
          schema:
            type: string
        - name: pestana_id
          in: query
          schema:
            type: integer
        - name: categoria_id
          in: query
          schema:
            type: integer
        - name: per_page
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
      responses:
        '200':
          description: Listado paginado
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                  data:
                    type: object
                    properties:
                      items:
                        type: array
                        items:
                          $ref: '#/components/schemas/TareaResumen'
                      meta:
                        $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '422':
          description: Empresa no indicada o inválida (típico desarrollador sin empresa_id)
    post:
      tags:
        - Servicios
      summary: Crear tarea
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TareaCreate'
      responses:
        '201':
          description: Tarea creada
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
                  data:
                    $ref: '#/components/schemas/TareaDetalle'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '422':
          description: Validación o empresa no indicada

  /servicios/tareas/mis:
    get:
      tags:
        - Servicios
      summary: Mis tareas
      description: Tareas creadas por el usuario o compartidas con él.
      security:
        - BearerAuth: []
      parameters:
        - name: estado
          in: query
          schema:
            type: string
        - name: pestana_id
          in: query
          schema:
            type: integer
        - name: per_page
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: Listado paginado
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
                  data:
                    type: object
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'

  /servicios/tareas/{tarea}:
    get:
      tags:
        - Servicios
      summary: Detalle de tarea
      security:
        - BearerAuth: []
      parameters:
        - name: tarea
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Detalle
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
                  data:
                    $ref: '#/components/schemas/TareaDetalle'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
    put:
      tags:
        - Servicios
      summary: Actualizar tarea
      security:
        - BearerAuth: []
      parameters:
        - name: tarea
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TareaUpdate'
      responses:
        '200':
          description: Actualizada
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
    delete:
      tags:
        - Servicios
      summary: Eliminar tarea
      security:
        - BearerAuth: []
      parameters:
        - name: tarea
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Eliminada
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'

  /servicios/tareas/{tarea}/notas:
    get:
      tags:
        - Servicios
      summary: Listar notas de una tarea
      security:
        - BearerAuth: []
      parameters:
        - name: tarea
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Listado de notas
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
    post:
      tags:
        - Servicios
      summary: Añadir nota a una tarea
      security:
        - BearerAuth: []
      parameters:
        - name: tarea
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - contenido
              properties:
                titulo:
                  type: string
                contenido:
                  type: string
      responses:
        '201':
          description: Nota creada
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'

  /servicios/categorias:
    get:
      tags:
        - Servicios
      summary: Listar categorías de tareas (empresa)
      description: Requiere permiso funcional gestion_categorias_tareas (ver). Desarrollador debe enviar empresa_id.
      security:
        - BearerAuth: []
      parameters:
        - name: empresa_id
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: Lista de categorías
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '422':
          description: Empresa no indicada o inválida

  /servicios/categorias/{categoriaTarea}:
    get:
      tags:
        - Servicios
      summary: Detalle de categoría
      security:
        - BearerAuth: []
      parameters:
        - name: categoriaTarea
          in: path
          required: true
          schema:
            type: integer
        - name: empresa_id
          in: query
          description: Filtro opcional para desarrollador
          schema:
            type: integer
      responses:
        '200':
          description: Categoría
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'

  /servicios/pestanas:
    get:
      tags:
        - Servicios
      summary: Listar pestañas de tareas (empresa)
      description: Requiere gestion_pestanas (ver). Desarrollador debe enviar empresa_id.
      security:
        - BearerAuth: []
      parameters:
        - name: empresa_id
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: Lista de pestañas
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '422':
          description: Empresa no indicada o inválida

  /servicios/ideas:
    get:
      tags:
        - Servicios
      summary: Listar ideas (tablero)
      description: Ideas compartidas o propias. Requiere sistema_ideas (ver).
      security:
        - BearerAuth: []
      parameters:
        - name: empresa_id
          in: query
          schema:
            type: integer
        - name: per_page
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: Paginado
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
    post:
      tags:
        - Servicios
      summary: Crear idea
      security:
        - BearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - titulo
              properties:
                titulo:
                  type: string
                contenido:
                  type: string
                link:
                  type: string
                  format: uri
                color:
                  type: string
      responses:
        '201':
          description: Creada
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'

  /servicios/ideas/{idea}:
    get:
      tags:
        - Servicios
      summary: Detalle de idea
      security:
        - BearerAuth: []
      parameters:
        - name: idea
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Idea
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
    put:
      tags:
        - Servicios
      summary: Actualizar idea
      security:
        - BearerAuth: []
      parameters:
        - name: idea
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Actualizada
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
    delete:
      tags:
        - Servicios
      summary: Eliminar idea
      security:
        - BearerAuth: []
      parameters:
        - name: idea
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Eliminada
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'

  /compras/proveedores:
    get:
      tags:
        - Compras
      summary: Listar proveedores (por empresa)
      description: |
        Maestro de proveedores (área Compras). Permiso de funcionalidad `proveedores` (ver).
        El rol `desarrollador` debe indicar `empresa_id` en query.
      security:
        - BearerAuth: []
      parameters:
        - name: empresa_id
          in: query
          required: false
          description: Obligatorio para rol desarrollador.
          schema:
            type: integer
            example: 42
        - name: search
          in: query
          schema:
            type: string
        - name: estado
          in: query
          schema:
            type: string
        - name: categoria
          in: query
          schema:
            type: string
        - name: nivel_calidad
          in: query
          schema:
            type: string
        - name: es_tambien_cliente
          in: query
          schema:
            type: boolean
        - name: per_page
          in: query
          schema:
            type: integer
            default: 15
            maximum: 100
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
      responses:
        '200':
          description: Lista paginada
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComprasProveedorListEnvelope'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '422':
          description: Validación
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationError'

  /compras/proveedores/{proveedor}:
    get:
      tags:
        - Compras
      summary: Obtener un proveedor
      description: |
        Detalle; notas internas y datos adicionales solo para roles con privilegio
        (desarrollador, empresa, trabajador_admin), no para trabajador_estandar.
      security:
        - BearerAuth: []
      parameters:
        - name: proveedor
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Proveedor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComprasProveedorDetailEnvelope'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  /finanzas/empresas/{empresa}/resumen:
    get:
      tags:
        - Finanzas
      summary: Resumen Finanzas (perfil contable)
      description: |
        Requiere módulo Finanzas (salvo desarrollador) y permiso de ver al menos una funcionalidad del área
        (p. ej. `dashboard_global`, `configuracion_contable`, `facturas_emitidas`). Sin secretos ni rutas de certificado.
      security:
        - BearerAuth: []
      parameters:
        - name: empresa
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                  data:
                    type: object
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'

  /finanzas/empresas/{empresa}/cuentas-bancarias:
    get:
      tags:
        - Finanzas
      summary: Cuentas bancarias (IBAN/BIC enmascarados)
      description: Permiso `configuracion_contable` (ver).
      security:
        - BearerAuth: []
      parameters:
        - name: empresa
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'

  /finanzas/empresas/{empresa}/facturas-emitidas:
    get:
      tags:
        - Finanzas
      summary: Listar facturas emitidas
      description: Permiso `facturas_emitidas` (ver). Filtros opcionales desde, hasta, estado_pago, paginación.
      security:
        - BearerAuth: []
      parameters:
        - name: empresa
          in: path
          required: true
          schema:
            type: integer
        - name: desde
          in: query
          schema:
            type: string
            format: date
        - name: hasta
          in: query
          schema:
            type: string
            format: date
        - name: estado_pago
          in: query
          schema:
            type: string
        - name: per_page
          in: query
          schema:
            type: integer
        - name: page
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: Lista paginada
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '422':
          description: Validación (rango de fechas)

  /finanzas/empresas/{empresa}/facturas-emitidas/{id}:
    get:
      tags:
        - Finanzas
      summary: Detalle factura emitida
      security:
        - BearerAuth: []
      parameters:
        - name: empresa
          in: path
          required: true
          schema:
            type: integer
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  /finanzas/empresas/{empresa}/facturas-recibidas:
    get:
      tags:
        - Finanzas
      summary: Listar facturas recibidas
      description: Permiso `facturas_recibidas` (ver).
      security:
        - BearerAuth: []
      parameters:
        - name: empresa
          in: path
          required: true
          schema:
            type: integer
        - name: desde
          in: query
          schema:
            type: string
            format: date
        - name: hasta
          in: query
          schema:
            type: string
            format: date
        - name: estado_pago
          in: query
          schema:
            type: string
        - name: per_page
          in: query
          schema:
            type: integer
        - name: page
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: Lista paginada
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'

  /finanzas/empresas/{empresa}/facturas-recibidas/{id}:
    get:
      tags:
        - Finanzas
      summary: Detalle factura recibida
      security:
        - BearerAuth: []
      parameters:
        - name: empresa
          in: path
          required: true
          schema:
            type: integer
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  /finanzas/empresas/{empresa}/asientos-contables:
    get:
      tags:
        - Finanzas
      summary: Listar asientos contables (libro diario)
      description: Permiso `libro_diario` (ver).
      security:
        - BearerAuth: []
      parameters:
        - name: empresa
          in: path
          required: true
          schema:
            type: integer
        - name: desde
          in: query
          schema:
            type: string
            format: date
        - name: hasta
          in: query
          schema:
            type: string
            format: date
        - name: tipo_asiento
          in: query
          schema:
            type: string
        - name: per_page
          in: query
          schema:
            type: integer
        - name: page
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: Lista paginada
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'

  /finanzas/empresas/{empresa}/asientos-contables/{id}:
    get:
      tags:
        - Finanzas
      summary: Detalle asiento con apuntes
      security:
        - BearerAuth: []
      parameters:
        - name: empresa
          in: path
          required: true
          schema:
            type: integer
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: OK
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          $ref: '#/components/responses/NotFoundError'

  /personal/trabajadores:
    get:
      tags:
        - Personal (Pymas)
      summary: Listar trabajadores
      description: |
        Ámbito por empresa. Rol `desarrollador`: query obligatorio `empresa_id`.
        Resto de roles: solo trabajadores de `auth()->user()->empresa_id`.
      security:
        - BearerAuth: []
      parameters:
        - name: empresa_id
          in: query
          required: false
          schema:
            type: integer
          description: Obligatorio si el usuario tiene rol desarrollador (soporte Pymas).
        - name: estado
          in: query
          schema:
            type: string
        - name: search
          in: query
          schema:
            type: string
        - name: per_page
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: Lista paginada (`data.items`, `data.meta`)
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '422':
          description: Validación (p. ej. falta empresa_id para desarrollador)
    post:
      tags:
        - Personal (Pymas)
      summary: Crear trabajador
      description: Requiere rol `desarrollador`, `empresa` o `trabajador_admin`; no se puede asignar otra empresa que no sea la del usuario (salvo desarrollador).
      security:
        - BearerAuth: []
      responses:
        '201':
          description: Creado
        '403':
          $ref: '#/components/responses/ForbiddenError'

  /personal/trabajadores/{trabajador}:
    get:
      tags:
        - Personal (Pymas)
      summary: Ver trabajador
      security:
        - BearerAuth: []
      parameters:
        - name: trabajador
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Detalle
        '403':
          $ref: '#/components/responses/ForbiddenError'
    put:
      tags:
        - Personal (Pymas)
      summary: Actualizar trabajador
      security:
        - BearerAuth: []
      responses:
        '200':
          description: Actualizado
        '403':
          $ref: '#/components/responses/ForbiddenError'
    delete:
      tags:
        - Personal (Pymas)
      summary: Eliminar trabajador
      security:
        - BearerAuth: []
      responses:
        '200':
          description: Eliminado
        '403':
          $ref: '#/components/responses/ForbiddenError'

  /personal/trabajadores/{trabajador}/estado-laboral:
    post:
      tags:
        - Personal (Pymas)
      summary: Actualizar estado laboral
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
        '403':
          $ref: '#/components/responses/ForbiddenError'

  /personal/fichajes:
    get:
      tags:
        - Personal (Pymas)
      summary: Listar fichajes
      description: |
        Trabajador estándar solo ve sus fichajes. `empresa` / `trabajador_admin` ven su empresa;
        query `user_id` solo si el usuario filtrado pertenece a la misma empresa (si no, 403).
        Desarrollador puede filtrar por `user_id` sin restricción de tenant.
      security:
        - BearerAuth: []
      parameters:
        - name: user_id
          in: query
          schema:
            type: integer
        - name: fecha_inicio
          in: query
          schema:
            type: string
            format: date
        - name: fecha_fin
          in: query
          schema:
            type: string
            format: date
        - name: per_page
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: Lista paginada
        '403':
          $ref: '#/components/responses/ForbiddenError'
    post:
      tags:
        - Personal (Pymas)
      summary: Registrar fichaje (entrada/salida)
      description: Siempre para el usuario autenticado; alterna entrada/salida según último fichaje del día.
      security:
        - BearerAuth: []
      responses:
        '201':
          description: Registrado

  /personal/fichajes/estado:
    get:
      tags:
        - Personal (Pymas)
      summary: Estado de fichaje del día
      security:
        - BearerAuth: []
      responses:
        '200':
          description: Estado (`trabajando` / `fuera_turno`, último fichaje, minutos)

  /personal/fichajes/{fichaje}/confirmar:
    post:
      tags:
        - Personal (Pymas)
      summary: Confirmar o sellar fichaje (empresa)
      description: |
        Roles `empresa`, `trabajador_admin` o `desarrollador`. El fichaje debe ser de un usuario
        de la misma empresa que quien confirma (desarrollador sin restricción de tenant).
      security:
        - BearerAuth: []
      parameters:
        - name: fichaje
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Confirmado
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '422':
          description: Ya confirmado u otro estado inválido

  /personal/solicitudes:
    get:
      tags:
        - Personal (Pymas)
      summary: Listar solicitudes de ausencia
      description: Misma regla de filtro `user_id` que en fichajes para admins de empresa.
      security:
        - BearerAuth: []
      parameters:
        - name: user_id
          in: query
          schema:
            type: integer
        - name: estado
          in: query
          schema:
            type: string
        - name: per_page
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: Lista paginada
        '403':
          $ref: '#/components/responses/ForbiddenError'
    post:
      tags:
        - Personal (Pymas)
      summary: Crear solicitud de ausencia
      security:
        - BearerAuth: []
      responses:
        '201':
          description: Creada

  /personal/solicitudes/{solicitud}/estado:
    post:
      tags:
        - Personal (Pymas)
      summary: Aprobar o rechazar solicitud
      description: |
        Body JSON `estado` `aceptada` o `rechazada`. Opcional `observaciones_empresa` (en rechazo se guarda en `motivo_rechazo`).
        Solo `empresa`, `trabajador_admin` o `desarrollador`; ámbito empresa como en la app web.
      security:
        - BearerAuth: []
      parameters:
        - name: solicitud
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Actualizada
        '403':
          $ref: '#/components/responses/ForbiddenError'

  /marketing/listas:
    get:
      tags:
        - Marketing
      summary: Listar listas de mailing
      description: |
        Ámbito por empresa (creador en la misma empresa). Desarrollador puede indicar empresa_id en query.
        Sanctum. Ver xDevDoc/API/07_API_MARKETING.md.
      security:
        - BearerAuth: []
      parameters:
        - name: activas
          in: query
          schema:
            type: boolean
        - name: per_page
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
        - name: empresa_id
          in: query
          description: Solo rol desarrollador
          schema:
            type: integer
      responses:
        '200':
          description: Listado paginado
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
    post:
      tags:
        - Marketing
      summary: Crear lista de mailing
      security:
        - BearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - nombre
              properties:
                nombre:
                  type: string
                descripcion:
                  type: string
                color:
                  type: string
                activo:
                  type: boolean
                empresa_id:
                  type: integer
      responses:
        '201':
          description: Creada
        '422':
          description: Validación

  /marketing/listas/{lista}:
    get:
      tags:
        - Marketing
      summary: Detalle lista
      security:
        - BearerAuth: []
      parameters:
        - name: lista
          in: path
          required: true
          schema:
            type: integer
        - name: empresa_id
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: OK
        '404':
          $ref: '#/components/responses/NotFoundError'

  /marketing/listas/{lista}/clientes:
    post:
      tags:
        - Marketing
      summary: Añadir clientes a la lista
      security:
        - BearerAuth: []
      parameters:
        - name: lista
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - clientes_ids
              properties:
                clientes_ids:
                  type: array
                  items:
                    type: integer
                empresa_id:
                  type: integer
      responses:
        '200':
          description: OK
        '404':
          $ref: '#/components/responses/NotFoundError'

  /marketing/plantillas:
    get:
      tags:
        - Marketing
      summary: Listar plantillas
      security:
        - BearerAuth: []
      parameters:
        - name: tipo
          in: query
          schema:
            type: string
        - name: per_page
          in: query
          schema:
            type: integer
        - name: empresa_id
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: OK
    post:
      tags:
        - Marketing
      summary: Crear plantilla
      security:
        - BearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - nombre
                - asunto
                - contenido
                - tipo
              properties:
                nombre:
                  type: string
                asunto:
                  type: string
                contenido:
                  type: string
                tipo:
                  type: string
                  enum:
                    - general
                    - presupuesto
                    - seguimiento
                    - promocional
                    - informativo
                empresa_id:
                  type: integer
      responses:
        '201':
          description: Creada

  /marketing/plantillas/{plantilla}/preview:
    post:
      tags:
        - Marketing
      summary: Vista previa HTML de plantilla
      security:
        - BearerAuth: []
      parameters:
        - name: plantilla
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                variables:
                  type: object
                  additionalProperties:
                    type: string
                empresa_id:
                  type: integer
      responses:
        '200':
          description: preview_html en data

  /marketing/campanas:
    get:
      tags:
        - Marketing
      summary: Listar campañas (email_mailings)
      security:
        - BearerAuth: []
      parameters:
        - name: estado
          in: query
          schema:
            type: string
        - name: per_page
          in: query
          schema:
            type: integer
        - name: empresa_id
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: OK
    post:
      tags:
        - Marketing
      summary: Crear campaña
      security:
        - BearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - asunto
                - tipo
                - estado
              properties:
                asunto:
                  type: string
                contenido:
                  type: string
                tipo:
                  type: string
                  enum:
                    - masivo
                    - personalizado
                    - presupuesto
                    - seguimiento
                estado:
                  type: string
                  enum:
                    - borrador
                    - programado
                fecha_programada:
                  type: string
                  format: date-time
                plantilla_id:
                  type: integer
                  nullable: true
                lista_id:
                  type: integer
                  nullable: true
                  description: Validado por tenant; no persistido en email_mailings (esquema actual)
                empresa_id:
                  type: integer
      responses:
        '201':
          description: Creada

  /marketing/campanas/{campana}:
    get:
      tags:
        - Marketing
      summary: Detalle campaña y métricas
      security:
        - BearerAuth: []
      parameters:
        - name: campana
          in: path
          required: true
          schema:
            type: integer
        - name: empresa_id
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: OK
        '404':
          $ref: '#/components/responses/NotFoundError'


components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Token de autenticación Laravel Sanctum

  schemas:
    PymasApiHealthResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message:
          type: string
        version:
          type: string
          example: "1.0"
        timestamp:
          type: string
          format: date-time

    PymasApiPublicInfoResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message:
          type: string
        data:
          type: object
          properties:
            product:
              type: string
              example: Pymas
            app:
              type: string
            version:
              type: string
            api_version:
              type: string
              example: "1"
            timezone:
              type: string
            timestamp:
              type: string
              format: date-time

    PymasApiSuccessStats:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message:
          type: string
        data:
          type: object
          properties:
            empresas:
              type: integer
            usuarios:
              type: integer
            timestamp:
              type: string
              format: date-time

    PymasApiDiagnosticsResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message:
          type: string
        data:
          type: object
          properties:
            app:
              type: string
            version:
              type: string
            environment:
              type: string
            php:
              type: string
            laravel:
              type: string
            timezone:
              type: string
            db_status:
              type: string
              enum: [ok, error]
            timestamp:
              type: string
              format: date-time

    ValidationError:
      type: object
      properties:
        success:
          type: boolean
          example: false
        mensaje:
          type: string
          example: "Datos de validación incorrectos"
        errores:
          type: object
          additionalProperties:
            type: array
            items:
              type: string

    ApiResponse:
      type: object
      properties:
        success:
          type: boolean
          description: Indica si la operación fue exitosa
        mensaje:
          type: string
          description: Mensaje descriptivo del resultado
        data:
          type: object
          description: Datos específicos de la respuesta (opcional)
        errores:
          type: array
          items:
            type: string
          description: Lista de errores (opcional)

    VentasClienteResumen:
      type: object
      description: Campos públicos de listado (sin tokens ni notas internas).
      properties:
        id:
          type: integer
        nombre_empresa:
          type: string
          nullable: true
        razon_social:
          type: string
          nullable: true
        cif_nif:
          type: string
          nullable: true
        email_principal:
          type: string
          nullable: true
        telefono_principal:
          type: string
          nullable: true
        telefono_secundario:
          type: string
          nullable: true
        estado:
          type: string
        es_cliente:
          type: boolean
        localidad:
          type: string
          nullable: true
        provincia:
          type: string
          nullable: true
        codigo_postal:
          type: string
          nullable: true
        categoria_id:
          type: integer
          nullable: true
        trabajador_asignado_id:
          type: integer
          nullable: true
        fecha_ultimo_contacto:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
          nullable: true
        updated_at:
          type: string
          format: date-time
          nullable: true

    VentasClienteCreate:
      type: object
      required:
        - nombre_empresa
        - email_principal
        - telefono_principal
        - localidad
        - provincia
      properties:
        nombre_empresa:
          type: string
        razon_social:
          type: string
          nullable: true
        cif_nif:
          type: string
          nullable: true
        email_principal:
          type: string
          format: email
        telefono_principal:
          type: string
        localidad:
          type: string
        provincia:
          type: string
        codigo_postal:
          type: string
          nullable: true
        direccion_completa:
          type: string
          nullable: true
        direccion_fiscal:
          type: string
          nullable: true
        es_cliente:
          type: boolean
          default: true
          description: true = cliente oficial; false = contacto comercial

    VentasClienteUpdate:
      type: object
      properties:
        nombre_empresa:
          type: string
        razon_social:
          type: string
          nullable: true
        cif_nif:
          type: string
          nullable: true
        email_principal:
          type: string
          format: email
        telefono_principal:
          type: string
        localidad:
          type: string
        provincia:
          type: string
        codigo_postal:
          type: string
          nullable: true
        direccion_completa:
          type: string
          nullable: true
        direccion_fiscal:
          type: string
          nullable: true
        es_cliente:
          type: boolean
        estado:
          type: string
          description: Estado comercial (p. ej. activo, archivado)

    VentasClienteDetalle:
      allOf:
        - $ref: '#/components/schemas/VentasClienteResumen'
        - type: object
          properties:
            direccion_fiscal:
              type: string
              nullable: true
            direccion_completa:
              type: string
              nullable: true
            email_secundario:
              type: string
              nullable: true
            web:
              type: string
              nullable: true
            nombre_responsable:
              type: string
              nullable: true
            persona_contacto:
              type: string
              nullable: true
            comercial_id:
              type: integer
              nullable: true

    ComprasProveedorListEnvelope:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message:
          type: string
        data:
          type: object
          properties:
            items:
              type: array
              items:
                $ref: '#/components/schemas/ComprasProveedorResumen'
            meta:
              type: object
              properties:
                current_page:
                  type: integer
                last_page:
                  type: integer
                per_page:
                  type: integer
                total:
                  type: integer

    ComprasProveedorResumen:
      type: object
      properties:
        id:
          type: integer
        empresa_id:
          type: integer
        nombre_empresa:
          type: string
        cif_nif:
          type: string
        razon_social:
          type: string
          nullable: true
        estado:
          type: string
          nullable: true
        categoria:
          type: string
          nullable: true
        email:
          type: string
          nullable: true
        telefono:
          type: string
          nullable: true
        nivel_calidad:
          type: string
          nullable: true
        es_tambien_cliente:
          type: boolean

    ComprasProveedorDetailEnvelope:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message:
          type: string
        data:
          $ref: '#/components/schemas/ComprasProveedorDetalle'

    ComprasProveedorDetalle:
      type: object
      description: |
        Incluye más campos fiscales y de calidad que el resumen.
        notas_internas, recomendaciones y datos_adicionales solo si el rol tiene privilegio.
      properties:
        id:
          type: integer
        empresa_id:
          type: integer
        nombre_empresa:
          type: string
        cif_nif:
          type: string
        notas_internas:
          type: string
          nullable: true
        datos_adicionales:
          type: object
          nullable: true
        estado:
          type: string
          nullable: true

    PaginationMeta:
      type: object
      properties:
        current_page:
          type: integer
        last_page:
          type: integer
        per_page:
          type: integer
        total:
          type: integer

    TareaResumen:
      type: object
      properties:
        id:
          type: integer
        titulo:
          type: string
        estado:
          type: string
        prioridad:
          type: string
          enum: [baja, media, alta, urgente]
        fecha_inicio:
          type: string
          format: date
          nullable: true
        fecha_fin:
          type: string
          format: date
          nullable: true
        empresa_id:
          type: integer
        categoria_id:
          type: integer
          nullable: true
        pestana_id:
          type: integer
          nullable: true
        creador_id:
          type: integer
        asignado_id:
          type: integer
          nullable: true
        updated_at:
          type: string
          format: date-time
          nullable: true

    TareaDetalle:
      allOf:
        - $ref: '#/components/schemas/TareaResumen'
        - type: object
          properties:
            descripcion:
              type: string
              nullable: true
            categoria:
              type: object
              nullable: true
            pestana:
              type: object
              nullable: true
            creador:
              type: object
              nullable: true
            asignado:
              type: object
              nullable: true

    TareaCreate:
      type: object
      required:
        - titulo
        - prioridad
      properties:
        titulo:
          type: string
          maxLength: 255
        descripcion:
          type: string
          nullable: true
        prioridad:
          type: string
          enum: [baja, media, alta, urgente]
        estado:
          type: string
        fecha_inicio:
          type: string
          format: date
        fecha_fin:
          type: string
          format: date
        categoria_id:
          type: integer
        pestana_id:
          type: integer
        asignado_id:
          type: integer

    TareaUpdate:
      type: object
      properties:
        titulo:
          type: string
        descripcion:
          type: string
          nullable: true
        prioridad:
          type: string
          enum: [baja, media, alta, urgente]
        estado:
          type: string
        fecha_inicio:
          type: string
          format: date
        fecha_fin:
          type: string
          format: date
        categoria_id:
          type: integer
        pestana_id:
          type: integer
        asignado_id:
          type: integer

  responses:
    UnauthorizedError:
      description: No autenticado o token inválido
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
                example: "Unauthenticated."

    ForbiddenError:
      description: Sin permisos para acceder al recurso
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
                example: "This action is unauthorized."

    ValidationError422:
      description: Datos de entrada no válidos (validación Laravel)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ValidationError'

    NotFoundError:
      description: Recurso no encontrado
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
                example: "No query results for model"

    InternalServerError:
      description: Error interno del servidor
      content:
        application/json:
          schema:
            type: object
            properties:
              success:
                type: boolean
                example: false
              mensaje:
                type: string
                example: "Error interno del servidor"

tags:
  - name: Autenticación
    description: Endpoints relacionados con la autenticación de usuarios
  - name: Empresas
    description: Gestión de empresas y trabajadores
  - name: Marketing
    description: |
      API REST Pymas — listas de mailing, plantillas y campañas (email_mailings).
      Requiere Sanctum; ámbito por empresa. Ver xDevDoc/API/07_API_MARKETING.md.
  - name: Ventas
    description: |
      CRM Pymas — lectura de clientes y contactos por empresa (`/api/ventas/clientes`).
      Ver xDevDoc/API/02_API_VENTAS.md.
  - name: Fichajes
    description: Módulo de gestión de fichajes y horarios
  - name: Servicios
    description: Tareas, proyectos y servicios internos
  - name: Clientes
    description: Clientes, contactos y CRM
  - name: Compras
    description: |
      Proveedores y compras por empresa (`/api/compras/*`). Ver documentación de API Compras en xDevDoc/API.
  - name: Finanzas
    description: |
      Lecturas por empresa (`/api/finanzas/empresas/{empresa}/*`): resumen, cuentas bancarias enmascaradas,
      facturas emitidas/recibidas, asientos contables. Sanctum, módulo Finanzas y permisos funcionales. Ver xDevDoc/API/05_API_FINANZAS.md.
  - name: Personal (Pymas)
    description: |
      RRHH — trabajadores, fichajes y solicitudes (`/api/personal/*`). Ver xDevDoc/API/04_API_PERSONAL.md.
  - name: Cumplimiento
    description: Cumplimiento normativo (módulo en desarrollo; contrato de estado)

