openapi: 3.0.3
info:
  title: Partner Portal API v1
  version: 2.0.0
  description: |-
    Kompletna specyfikacja API Partner Portal 2.0.

    Każdy endpoint zawiera precyzyjne nazwy argumentów body/query, typy pól, ograniczenia walidacji (minLength, maxLength, minimum, maximum, pattern, enum, format) i przykładowe wartości.

    Autoryzacja: JWT Bearer Token (Authorization: Bearer <token>) + nagłówek x-vendor-id (tenant).

    Data generacji: 2026-03-05.
servers:
  - url: http://localhost:4001
    description: Local development (port 4001)
  - url: https://api.partnerzy.zadbano.pl
    description: Production (behind /api proxy)
tags:
  - name: Auth
    description: Logowanie, sesja, role
  - name: Operations
    description: Zamówienia operacyjne, KPI, etapy, aktywność
  - name: Orders
    description: Zamówienia CRUD, wyszukiwanie, tracking, media
  - name: Geo
    description: Miasta i geolokalizacja
  - name: Register
    description: Słowniki rejestracji zamówień
  - name: Finance
    description: Faktury, rozliczenia miesięczne, COD
  - name: Quality
    description: Reklamacje, incydenty, CAPA, media incydentów
  - name: NPS
    description: Ankiety NPS, feedback portalu
  - name: Notifications
    description: Powiadomienia
  - name: Contacts
    description: Kontakty i opiekun handlowy
  - name: Vendor
    description: Profil vendora, opiekun
  - name: Admin
    description: Administracja użytkownikami, konteksty vendorów
  - name: Analytics
    description: Dashboard KPI, trendy, wykresy, sprzedaż, statystyki
  - name: Reports
    description: Generowanie raportów (async)
  - name: Kepler
    description: Orbita — predykcja opóźnień, przesunięcia, powiadomienia
  - name: Support
    description: Procesy BPMN, galeria zdjęć
  - name: AI
    description: Chat AI z załącznikami i ustawieniami
  - name: Guest
    description: Rejestracja gości (publiczny endpoint)
  - name: CMS
    description: Data Lineage, Runtime CMS, tłumaczenia
  - name: Polaris
    description: Dokumenty handlowe partnerów, upload S3, AI summary
  - name: Partners
    description: CRUD partnerów (JSON Schema Fastify validation)
  - name: Vendors
    description: CRUD vendorów — 44 pola (JSON Schema Fastify validation)
  - name: HubDivision
    description: CRUD i wyszukiwanie słowników Tms.Hub / Tms.Division
  - name: People
    description: Zarządzanie osobami — CRUD, role, auth methods, identity keys, sources, lockout, OTP, recovery, sync
  - name: RBAC
    description: Dwuwarstwowy RBAC — role, subroles, permissions, moduły API i aplikacji, przypisania użytkowników
  - name: Audit
    description: Logi audytu i API, aktywność użytkowników, statystyki wydajności
  - name: Infrastructure
    description: Health check
  - name: Dictionaries
    description: Słowniki systemowe — kategorie incydentów i inne
  - name: Documentation
    description: Dokumentacja API (OpenAPI, Postman)
paths:
  /health:
    get:
      tags:
        - Infrastructure
      summary: Health check (top-level)
      operationId: healthCheck
      security: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'
  /v1/health:
    get:
      tags:
        - Infrastructure
      summary: Health check (v1)
      operationId: healthCheckV1
      security: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'
  /v1/docs:
    get:
      tags:
        - Documentation
      summary: Lista URL dokumentacji API
      operationId: docsIndex
      security: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      title:
                        type: string
                      importUrls:
                        type: object
                        properties:
                          openapiYaml:
                            type: string
                          openapiJson:
                            type: string
                          postmanCollection:
                            type: string
                          swaggerUi:
                            type: string
                      postmanImportHint:
                        type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/docs/openapi.yaml:
    get:
      tags:
        - Documentation
      summary: OpenAPI YAML spec
      operationId: openapiYaml
      security: []
      responses:
        '200':
          description: YAML
          content:
            application/yaml:
              schema:
                type: string
  /v1/docs/openapi.json:
    get:
      tags:
        - Documentation
      summary: OpenAPI JSON spec
      operationId: openapiJson
      security: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/docs/postman.json:
    get:
      tags:
        - Documentation
      summary: Postman Collection JSON
      operationId: postmanJson
      security: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/auth/login:
    post:
      tags:
        - Auth
      summary: Logowanie użytkownika
      description: |-
        Uwierzytelnianie użytkownika. Obsługuje metody: PASSWORD, TOKEN_URL, SMS_OTP, EMAIL_OTP, OAUTH_GOOGLE, OAUTH_APPLE, OAUTH_FACEBOOK, OAUTH_LINKEDIN.

        Brute-force protection: 3 nieudane próby w 5 min → blokada eskalująca (5 min → 30 min → zawieszenie).
      operationId: authLogin
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LoginBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '423':
          description: Locked
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/auth/refresh:
    post:
      tags:
        - Auth
      summary: Odświeżenie tokena JWT
      operationId: authRefresh
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RefreshBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/auth/me:
    get:
      tags:
        - Auth
      summary: Dane zalogowanego użytkownika
      operationId: authMe
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthMeResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/auth/switch-role:
    post:
      tags:
        - Auth
      summary: Zmiana aktywnej roli
      operationId: authSwitchRole
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SwitchRoleBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/auth/logout:
    post:
      tags:
        - Auth
      summary: Wylogowanie (unieważnienie sesji)
      operationId: authLogout
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      success:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/operations/orders:
    get:
      tags:
        - Operations
      summary: Lista zamówień operacyjnych
      description: |-
        Uprawnienie: operations.read

        Dozwolone fields: id, vendor_id, order_id, order_number, order_number_external, order_date, customer, city, status, stage, value, currency, items, customer_service_date, updated_at

        Domyślne: id, order_id, order_number, order_date, customer, city, status, stage, value, currency, items

        Include: metadata (order_number_external, customer_service_date, updated_at), financial (value, currency), customer (customer, city)
      operationId: opsOrders
      security:
        - BearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
          description: Numer strony
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 20
          description: Rozmiar strony (max 50)
        - name: fields
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Wybrane kolumny (CSV)
        - name: include
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Grupy dodatkowych pól (CSV)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        vendor_id:
                          type: integer
                        order_id:
                          type: integer
                        order_number:
                          type: string
                        order_number_external:
                          type: string
                        order_date:
                          type: string
                          format: date-time
                        customer:
                          type: string
                        city:
                          type: string
                        status:
                          type: string
                          enum:
                            - REGISTERED
                            - COLLECTED
                            - IN_TRANSIT
                            - DELIVERED
                        stage:
                          type: string
                          enum:
                            - registered
                            - collected
                            - transit
                            - distribution
                            - delivered
                        value:
                          type: number
                        currency:
                          type: string
                          example: PLN
                        items:
                          type: integer
                        customer_service_date:
                          type: string
                          format: date-time
                        updated_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/operations/kpis:
    get:
      tags:
        - Operations
      summary: KPI operacyjne
      description: |-
        Uprawnienie: operations.read

        Zwraca: all_orders, active_orders, delivered_orders, efficiency_pct, open_complaints
      operationId: opsKpis
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        all_orders:
                          type: integer
                        active_orders:
                          type: integer
                        delivered_orders:
                          type: integer
                        efficiency_pct:
                          type: number
                          format: float
                        open_complaints:
                          type: integer
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/operations/stages:
    get:
      tags:
        - Operations
      summary: Statystyki etapów zamówień
      description: |-
        Uprawnienie: operations.read

        Zwraca: [{stage, count, percentage}] dla: registered, collected, transit, distribution, delivered, problems
      operationId: opsStages
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        stage:
                          type: string
                          enum:
                            - registered
                            - collected
                            - transit
                            - distribution
                            - delivered
                            - problems
                        count:
                          type: integer
                        percentage:
                          type: number
                          format: float
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/operations/activity:
    get:
      tags:
        - Operations
      summary: Ostatnia aktywność operacyjna
      description: |-
        Uprawnienie: operations.read

        Zwraca: [{order_id, from_stage, to_stage, time_ago}]
      operationId: opsActivity
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        order_id:
                          type: integer
                        from_stage:
                          type: string
                        to_stage:
                          type: string
                        time_ago:
                          type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/orders:
    get:
      tags:
        - Orders
      summary: Lista zamówień
      description: |-
        Uprawnienie: orders.read

        Dozwolone fields: id, vendor_id, order_id, order_number, order_number_external, order_date, customer, city, status, stage, value, currency, items, customer_service_date, updated_at

        Include: metadata, financial, customer
      operationId: ordersList
      security:
        - BearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
          description: Numer strony
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 20
          description: Rozmiar strony (max 50)
        - name: fields
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Wybrane kolumny (CSV)
        - name: include
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Grupy dodatkowych pól (CSV)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        vendor_id:
                          type: integer
                        order_id:
                          type: integer
                        order_number:
                          type: string
                        order_number_external:
                          type: string
                        order_date:
                          type: string
                          format: date-time
                        customer:
                          type: string
                        city:
                          type: string
                        status:
                          type: string
                        stage:
                          type: string
                        value:
                          type: number
                        currency:
                          type: string
                        items:
                          type: integer
                        customer_service_date:
                          type: string
                          format: date-time
                        updated_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - Orders
      summary: Utwórz zamówienie
      description: >-
        Uprawnienie: orders.create


        Body JSON z polami: orderType, customerName, customerPhone, customerEmail, deliveryDate, timeSlot, deliveryStandard, warehouse, address{country,street,buildingNo,apartment,city,postalCode},
        codAmount, orderValue, externalDocuments[], packages[], services[], relatedOrders[], additionalNotes
      operationId: ordersCreate
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: Dane zamówienia — walidacja po stronie SQL
              properties:
                orderType:
                  type: string
                  example: standard
                customerName:
                  type: string
                  example: Anna Nowak
                customerPhone:
                  type: string
                  example: '+48600700800'
                customerEmail:
                  type: string
                  example: anna@example.com
                deliveryDate:
                  type: string
                  format: date
                  example: '2026-03-10'
                timeSlot:
                  type: string
                  example: 08:00-12:00
                deliveryStandard:
                  type: string
                  example: standard
                warehouse:
                  type: string
                  example: WAR-01
                address:
                  type: object
                  properties:
                    country:
                      type: string
                      example: PL
                    street:
                      type: string
                      example: ul. Główna
                    buildingNo:
                      type: string
                      example: '15'
                    apartment:
                      type: string
                      example: 3A
                    city:
                      type: string
                      example: Warszawa
                    postalCode:
                      type: string
                      example: 00-001
                codAmount:
                  type: number
                  example: 150
                orderValue:
                  type: number
                  example: 499.99
                packages:
                  type: array
                  items:
                    type: object
                    properties:
                      type:
                        type: string
                      length:
                        type: number
                      width:
                        type: number
                      height:
                        type: number
                      weight:
                        type: number
                      externalNumber:
                        type: string
                additionalNotes:
                  type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      orderId:
                        type: integer
                      orderNumber:
                        type: string
                      status:
                        type: string
                        example: QUEUED
                      source:
                        type: string
                      receivedAt:
                        type: string
                        format: date-time
                      customerName:
                        type: string
                      city:
                        type: string
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/orders/search:
    get:
      tags:
        - Orders
      summary: Wyszukiwanie zamówień
      description: |-
        Uprawnienie: orders.read

        Dozwolone fields: id, order_id, order_number, order_number_external, customer, city, status, stage, order_date, value, currency, items

        Include: financial (value, currency), operations (stage, items)
      operationId: ordersSearch
      security:
        - BearerAuth: []
      parameters:
        - name: q
          in: query
          required: true
          schema:
            type: string
            minLength: 3
            maxLength: 120
          description: Fraza wyszukiwania (min 3 znaki, max 120)
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
          description: Numer strony
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 20
          description: Rozmiar strony (max 50)
        - name: fields
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Wybrane kolumny (CSV)
        - name: include
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Grupy dodatkowych pól (CSV)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        order_id:
                          type: integer
                        order_number:
                          type: string
                        order_number_external:
                          type: string
                        customer:
                          type: string
                        city:
                          type: string
                        status:
                          type: string
                        stage:
                          type: string
                        order_date:
                          type: string
                          format: date-time
                        value:
                          type: number
                        currency:
                          type: string
                        items:
                          type: integer
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/orders/{id}:
    get:
      tags:
        - Orders
      summary: Szczegóły zamówienia
      operationId: ordersDetail
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      vendor_id:
                        type: integer
                      order_id:
                        type: integer
                      order_number:
                        type: string
                      order_number_external:
                        type: string
                      order_date:
                        type: string
                        format: date-time
                      customer:
                        type: string
                      city:
                        type: string
                      status:
                        type: string
                      stage:
                        type: string
                      value:
                        type: number
                      currency:
                        type: string
                      items:
                        type: integer
                      customer_service_date:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
                      sender_name:
                        type: string
                      sender_address:
                        type: string
                      sender_city:
                        type: string
                      receiver_name:
                        type: string
                      receiver_address:
                        type: string
                      receiver_city:
                        type: string
                      receiver_phone:
                        type: string
                      weight_kg:
                        type: number
                      dimensions_cm:
                        type: string
                      service_type:
                        type: string
                      cod_amount:
                        type: number
                      notes:
                        type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/orders/{id}/media:
    get:
      tags:
        - Orders
      summary: Media zamówienia (presigned URL S3)
      description: |-
        Uprawnienie: orders.read

        Zwraca listę [{id, url}] z podpisanymi URL-ami S3. Nagłówek Cache-Control: no-store, private
      operationId: ordersMedia
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        url:
                          type: string
                          nullable: true
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/orders/{id}/tracking:
    get:
      tags:
        - Orders
      summary: Historia śledzenia zamówienia
      description: |-
        Uprawnienie: orders.read

        Zwraca: [{event_time, title, description, location}]
      operationId: ordersTracking
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        event_time:
                          type: string
                          format: date-time
                        title:
                          type: string
                        description:
                          type: string
                        location:
                          type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/geo/cities:
    get:
      tags:
        - Geo
      summary: Lista miast z geolokalizacją
      description: |-
        Uprawnienie: orders.read

        Dozwolone fields: city, lat, lng
      operationId: geoCities
      security:
        - BearerAuth: []
      parameters:
        - name: q
          in: query
          schema:
            type: string
            minLength: 1
            maxLength: 120
          description: Wyszukiwanie nazwy miasta
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
          description: Numer strony
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 20
          description: Rozmiar strony (max 50)
        - name: fields
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Wybrane kolumny (CSV)
        - name: include
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Grupy dodatkowych pól (CSV)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        city:
                          type: string
                        lat:
                          type: number
                          format: float
                        lng:
                          type: number
                          format: float
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/register/dictionaries:
    get:
      tags:
        - Register
      summary: Słowniki rejestracji zamówień
      description: |-
        Uprawnienie: orders.read

        Zwraca: delivery_standards, package_types, related_order_types, cover_types, service_types, time_slots
      operationId: registerDicts
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      delivery_standards:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            label:
                              type: string
                      package_types:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            label:
                              type: string
                      related_order_types:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            label:
                              type: string
                      cover_types:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            label:
                              type: string
                      service_types:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            label:
                              type: string
                      time_slots:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            label:
                              type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/invoices:
    get:
      tags:
        - Finance
      summary: Lista faktur
      description: |-
        Uprawnienie: finance.read

        Dozwolone fields: id, vendor_id, invoice_id, invoice_number, period, issue_date, due_date, status, netto, vat, brutto, orders, pdf_url, xlsx_url

        Domyślne: id, invoice_id, invoice_number, period, issue_date, due_date, status, brutto, orders

        Include: amounts (netto, vat, brutto), documents (pdf_url, xlsx_url)
      operationId: invoicesList
      security:
        - BearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
          description: Numer strony
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 20
          description: Rozmiar strony (max 50)
        - name: fields
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Wybrane kolumny (CSV)
        - name: include
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Grupy dodatkowych pól (CSV)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        vendor_id:
                          type: string
                        invoice_id:
                          type: string
                        invoice_number:
                          type: string
                        period:
                          type: string
                        issue_date:
                          type: string
                          format: date
                        due_date:
                          type: string
                          format: date
                        status:
                          type: string
                        netto:
                          type: number
                        vat:
                          type: number
                        brutto:
                          type: number
                        orders:
                          type: integer
                        pdf_url:
                          type: string
                        xlsx_url:
                          type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/settlements/monthly:
    get:
      tags:
        - Finance
      summary: Rozliczenia miesięczne
      description: 'Uprawnienie: finance.read'
      operationId: settlementsMonthly
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        month:
                          type: string
                        total_orders:
                          type: integer
                        total_value:
                          type: number
                        commission:
                          type: number
                        status:
                          type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/settlements/cod:
    get:
      tags:
        - Finance
      summary: Rozliczenia COD
      description: 'Uprawnienie: finance.read'
      operationId: settlementsCod
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        order_id:
                          type: string
                        cod_amount:
                          type: number
                        settled:
                          type: boolean
                        settlement_date:
                          type: string
                          format: date
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/complaints:
    get:
      tags:
        - Quality
      summary: Lista reklamacji
      description: >-
        Uprawnienie: quality.read


        Dozwolone fields: id, vendor_id, complaint_id, order_id, complaint_number, category, status, claim_date, created_at, cost, description, client_name, client_address, priority, messages_count,
        attachments_count


        Include: details (description, client_address), counters (messages_count, attachments_count)
      operationId: complaintsList
      security:
        - BearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
          description: Numer strony
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 20
          description: Rozmiar strony (max 50)
        - name: fields
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Wybrane kolumny (CSV)
        - name: include
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Grupy dodatkowych pól (CSV)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        vendor_id:
                          type: integer
                        complaint_id:
                          type: integer
                        order_id:
                          type: integer
                        complaint_number:
                          type: string
                        category:
                          type: string
                        status:
                          type: string
                          enum:
                            - OPEN
                            - IN_PROGRESS
                            - RESOLVED
                        claim_date:
                          type: string
                          format: date-time
                        created_at:
                          type: string
                          format: date-time
                        cost:
                          type: number
                        description:
                          type: string
                        client_name:
                          type: string
                        client_address:
                          type: string
                        priority:
                          type: string
                          enum:
                            - P1
                            - P2
                            - P3
                            - P4
                        messages_count:
                          type: integer
                        attachments_count:
                          type: integer
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - Quality
      summary: Utwórz reklamację
      description: |-
        Uprawnienie: quality.create

        Wymagane pola: orderId (niepusty), description (niepusty). Opcjonalne: category, cost/claimAmount, claimNumber, vendorComplaintNumber.
      operationId: complaintsCreate
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ComplaintCreateBody'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      complaint_id:
                        type: integer
                      order_id:
                        type: string
                      complaint_number:
                        type: string
                      category:
                        type: string
                      status:
                        type: string
                        example: OPEN
                      description:
                        type: string
                      cost:
                        type: number
                      created_at:
                        type: string
                        format: date-time
                      source:
                        type: string
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/complaints/{id}:
    get:
      tags:
        - Quality
      summary: Szczegóły reklamacji
      operationId: complaintsDetail
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      vendor_id:
                        type: integer
                      complaint_id:
                        type: integer
                      order_id:
                        type: integer
                      complaint_number:
                        type: string
                      category:
                        type: string
                      status:
                        type: string
                      claim_date:
                        type: string
                        format: date-time
                      created_at:
                        type: string
                        format: date-time
                      cost:
                        type: number
                      description:
                        type: string
                      client_name:
                        type: string
                      client_address:
                        type: string
                      priority:
                        type: string
                      messages_count:
                        type: integer
                      attachments_count:
                        type: integer
                      source:
                        type: string
                      history:
                        type: array
                        items:
                          type: object
                          properties:
                            event:
                              type: string
                            at:
                              type: string
                              format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/complaints/{id}/messages:
    post:
      tags:
        - Quality
      summary: Dodaj wiadomość do reklamacji
      operationId: complaintsAddMessage
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                content:
                  type: string
                  description: Treść wiadomości
                  example: Dodatkowe informacje...
                author:
                  type: string
                  example: Jan Kowalski
                role:
                  type: string
                  example: partner
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/complaints/{id}/attachments:
    post:
      tags:
        - Quality
      summary: Dodaj załącznik do reklamacji
      operationId: complaintsAddAttachment
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  example: foto.jpg
                type:
                  type: string
                  example: image/jpeg
                size:
                  type: integer
                  example: 245000
                url:
                  type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/incidents:
    get:
      tags:
        - Quality
      summary: Lista incydentów
      description: >-
        Uprawnienie: quality.read


        Dozwolone fields: id, vendor_id, incident_id, order_id, order_number, title, priority, status, created, expectation, description, category, assigned_to, vendor_name, reporter_name, log_count,
        capa_actions_count


        Include: details (expectation, description), counters (log_count, capa_actions_count)
      operationId: incidentsList
      security:
        - BearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
          description: Numer strony
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 20
          description: Rozmiar strony (max 50)
        - name: fields
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Wybrane kolumny (CSV)
        - name: include
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Grupy dodatkowych pól (CSV)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        vendor_id:
                          type: integer
                        incident_id:
                          type: integer
                        order_id:
                          type: integer
                        order_number:
                          type: string
                        title:
                          type: string
                        priority:
                          type: string
                          enum:
                            - P1
                            - P2
                            - P3
                            - P4
                        status:
                          type: string
                          enum:
                            - OPEN
                            - IN_PROGRESS
                            - RESOLVED
                        created:
                          type: string
                          format: date-time
                        expectation:
                          type: string
                        description:
                          type: string
                        category:
                          type: string
                        assigned_to:
                          type: string
                        vendor_name:
                          type: string
                        reporter_name:
                          type: string
                        log_count:
                          type: integer
                        capa_actions_count:
                          type: integer
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - Quality
      summary: Utwórz incydent
      operationId: incidentsCreate
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IncidentCreateBody'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      scope:
                        type: string
                      refresh_status:
                        type: string
                        example: QUEUED
                      details:
                        type: object
                        properties:
                          vendorId:
                            type: string
                          body:
                            type: object
                          fallback:
                            type: boolean
                      refreshed_at:
                        type: string
                        format: date-time
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/incidents/{id}:
    get:
      tags:
        - Quality
      summary: Szczegóły incydentu z logiem
      operationId: incidentsDetail
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      vendor_id:
                        type: integer
                      incident_id:
                        type: integer
                      order_id:
                        type: integer
                      order_number:
                        type: string
                      title:
                        type: string
                      priority:
                        type: string
                      status:
                        type: string
                      created:
                        type: string
                        format: date-time
                      expectation:
                        type: string
                      description:
                        type: string
                      category:
                        type: string
                      assigned_to:
                        type: string
                      vendor_name:
                        type: string
                      reporter_name:
                        type: string
                      log_count:
                        type: integer
                      capa_actions_count:
                        type: integer
                      source:
                        type: string
                      logs:
                        type: array
                        items:
                          type: object
                          properties:
                            event:
                              type: string
                            at:
                              type: string
                              format: date-time
                            message:
                              type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    patch:
      tags:
        - Quality
      summary: Aktualizuj incydent (partial)
      operationId: incidentsUpdate
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IncidentUpdateBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      title:
                        type: string
                      priority:
                        type: string
                      status:
                        type: string
                      description:
                        type: string
                      expectation:
                        type: string
                      category:
                        type: string
                      vendor_name:
                        type: string
                      reporter_name:
                        type: string
                      updated_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - Quality
      summary: Usuń incydent
      description: 'Uprawnienie: quality.delete'
      operationId: incidentsDelete
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/incidents/{id}/log:
    post:
      tags:
        - Quality
      summary: Dodaj wpis do logu incydentu
      operationId: incidentsAddLog
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                message:
                  type: string
                  example: Podjęto działania naprawcze
                author:
                  type: string
                  example: Jan Kowalski
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      incident_id:
                        type: integer
                      message:
                        type: string
                      author:
                        type: string
                      created_at:
                        type: string
                        format: date-time
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/incidents/{id}/status:
    patch:
      tags:
        - Quality
      summary: Zmień status incydentu
      operationId: incidentsChangeStatus
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                status:
                  type: string
                  enum:
                    - OPEN
                    - IN_PROGRESS
                    - RESOLVED
                    - CLOSED
                  default: OPEN
                  description: Nowy status
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      status:
                        type: string
                      updated_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/incidents/{id}/capa/actions/{actionId}:
    patch:
      tags:
        - Quality
      summary: Aktualizuj akcję CAPA
      operationId: incidentsUpdateCapaAction
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
        - name: actionId
          in: path
          required: true
          schema:
            type: string
          description: ID akcji CAPA
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                status:
                  type: string
                  enum:
                    - open
                    - in_progress
                    - done
                    - verified
                progress:
                  type: integer
                  minimum: 0
                  maximum: 100
                  example: 100
                evidence:
                  type: string
                  example: Przeprowadzono szkolenie zespołu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      action_id:
                        type: integer
                      status:
                        type: string
                      description:
                        type: string
                      updated_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/incidents/{id}/media:
    post:
      tags:
        - Quality
      summary: Upload mediów incydentu (multipart)
      description: |-
        Uprawnienie: quality.create

        Content-Type: multipart/form-data. Min. 1 plik.

        Automatyczne rozpoznanie mediaType: image/* → photo, video/* → video, application/pdf → document, inne → document.
      operationId: incidentsUploadMedia
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: ID incydentu
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
                  description: Plik(i) do uploadu
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        incident_id:
                          type: integer
                        vendor_id:
                          type: integer
                        file_name:
                          type: string
                        content_type:
                          type: string
                        size_bytes:
                          type: integer
                        s3_bucket:
                          type: string
                        s3_key:
                          type: string
                        s3_uri:
                          type: string
                        media_type:
                          type: string
                          enum:
                            - photo
                            - video
                            - document
                        uploaded_by:
                          type: integer
                          nullable: true
                        created_at:
                          type: string
                          format: date-time
                        presignedUrl:
                          type: string
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    get:
      tags:
        - Quality
      summary: Lista mediów incydentu
      operationId: incidentsListMedia
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: ID incydentu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        incident_id:
                          type: integer
                        vendor_id:
                          type: integer
                        file_name:
                          type: string
                        content_type:
                          type: string
                        size_bytes:
                          type: integer
                        s3_bucket:
                          type: string
                        s3_key:
                          type: string
                        s3_uri:
                          type: string
                        media_type:
                          type: string
                        uploaded_by:
                          type: integer
                          nullable: true
                        created_at:
                          type: string
                          format: date-time
                        presignedUrl:
                          type: string
                          nullable: true
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/incidents/{id}/media/{mediaId}/download:
    get:
      tags:
        - Quality
      summary: Pobierz medium (presigned URL)
      operationId: incidentsDownloadMedia
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: ID incydentu
        - name: mediaId
          in: path
          required: true
          schema:
            type: string
          description: ID medium
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      presignedUrl:
                        type: string
                      media:
                        type: object
                        description: Full IncidentMediaRow
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/incidents/{id}/media/{mediaId}:
    delete:
      tags:
        - Quality
      summary: Usuń medium incydentu
      operationId: incidentsDeleteMedia
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: ID incydentu
        - name: mediaId
          in: path
          required: true
          schema:
            type: string
          description: ID medium
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/nps/surveys:
    get:
      tags:
        - NPS
      summary: Lista ankiet NPS
      description: |-
        Uprawnienie: nps.read

        Dozwolone fields: vendor_id, id, survey_date, score, category, areas, comment, author

        Include: feedback (areas, comment)
      operationId: npsSurveysList
      security:
        - BearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
          description: Numer strony
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 20
          description: Rozmiar strony (max 50)
        - name: fields
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Wybrane kolumny (CSV)
        - name: include
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Grupy dodatkowych pól (CSV)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        vendor_id:
                          type: string
                        id:
                          type: string
                        survey_date:
                          type: string
                          format: date
                        score:
                          type: integer
                          minimum: 0
                          maximum: 10
                        category:
                          type: string
                        areas:
                          type: string
                        comment:
                          type: string
                        author:
                          type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - NPS
      summary: Utwórz ankietę NPS
      operationId: npsSurveysCreate
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NpsSurveyBody'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      score:
                        type: integer
                      category:
                        type: string
                      created_at:
                        type: string
                        format: date-time
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/nps/summary:
    get:
      tags:
        - NPS
      summary: Podsumowanie NPS
      operationId: npsSummary
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      avgScore:
                        type: number
                      totalSurveys:
                        type: integer
                      promoters:
                        type: integer
                      detractors:
                        type: integer
                      passives:
                        type: integer
                      npsScore:
                        type: number
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/portal/feedback:
    post:
      tags:
        - NPS
      summary: Feedback portalu (formularz 1-5)
      operationId: portalFeedback
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PortalFeedbackBody'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      overall:
                        type: integer
                      usability:
                        type: integer
                      design:
                        type: integer
                      speed:
                        type: integer
                      comment:
                        type: string
                        nullable: true
                      created_at:
                        type: string
                        format: date-time
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/portal/survey:
    post:
      tags:
        - NPS
      summary: Ankieta portalu (formularz 1-5)
      operationId: portalSurvey
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PortalFeedbackBody'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      overall:
                        type: integer
                      usability:
                        type: integer
                      design:
                        type: integer
                      speed:
                        type: integer
                      comment:
                        type: string
                        nullable: true
                      created_at:
                        type: string
                        format: date-time
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/notifications:
    get:
      tags:
        - Notifications
      summary: Lista powiadomień
      description: |-
        Uprawnienie: notifications.read

        Dozwolone fields: id, vendor_id, notification_id, type, title, summary, content, published_at, is_pinned, author, author_role, user_id, is_read, read_at

        Include: content (content), audit (author, author_role, read_at)
      operationId: notificationsList
      security:
        - BearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
          description: Numer strony
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 20
          description: Rozmiar strony (max 50)
        - name: fields
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Wybrane kolumny (CSV)
        - name: include
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Grupy dodatkowych pól (CSV)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        vendor_id:
                          type: string
                        notification_id:
                          type: string
                        type:
                          type: string
                        title:
                          type: string
                        summary:
                          type: string
                        content:
                          type: string
                        published_at:
                          type: string
                          format: date-time
                        is_pinned:
                          type: boolean
                        author:
                          type: string
                        author_role:
                          type: string
                        user_id:
                          type: string
                        is_read:
                          type: boolean
                        read_at:
                          type: string
                          format: date-time
                          nullable: true
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/notifications/{id}/read:
    patch:
      tags:
        - Notifications
      summary: Oznacz powiadomienie jako przeczytane
      operationId: notificationsRead
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      ok:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/notifications/read-all:
    patch:
      tags:
        - Notifications
      summary: Oznacz wszystkie jako przeczytane
      operationId: notificationsReadAll
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      updated:
                        type: integer
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/contacts:
    get:
      tags:
        - Contacts
      summary: Lista kontaktów i opiekun
      description: 'Uprawnienie: contacts.read'
      operationId: contactsList
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        name:
                          type: string
                        email:
                          type: string
                        phone:
                          type: string
                        role:
                          type: string
                        department:
                          type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/vendor/profile:
    get:
      tags:
        - Vendor
      summary: Profil vendora
      description: 'Uprawnienie: auth.read'
      operationId: vendorProfile
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      name:
                        type: string
                      display_name:
                        type: string
                      symbol:
                        type: string
                      tax_id_number:
                        type: string
                      email:
                        type: string
                      main_phone_number:
                        type: string
                      is_active:
                        type: boolean
                      color:
                        type: string
                      invoice_address:
                        type: object
                      financial:
                        type: object
                      created_at:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/vendor/caretaker:
    get:
      tags:
        - Vendor
      summary: Opiekun handlowy vendora
      description: 'Zwraca dane opiekuna handlowego (vendor_id, auth_user_id, first_name, last_name, name, email, phone, photo_url, role) lub data: null'
      operationId: vendorCaretaker
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      auth_user_id:
                        type: string
                      first_name:
                        type: string
                      last_name:
                        type: string
                      name:
                        type: string
                      email:
                        type: string
                      phone:
                        type: string
                      photo_url:
                        type: string
                        nullable: true
                      role:
                        type: string
                        example: Opiekun handlowy
                    nullable: true
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/admin/users:
    get:
      tags:
        - Admin
      summary: Lista użytkowników
      description: |-
        Uprawnienie: auth.read

        Dozwolone fields: vendor_id, vendor_name, auth_user_id, email, first_name, last_name, display_name, photo_url, phone, roles, status, login_enabled, last_login_at, created_at, updated_at

        Include: profile (first_name, last_name, photo_url, phone), audit (login_enabled, last_login_at, created_at, updated_at)
      operationId: adminUsers
      security:
        - BearerAuth: []
      parameters:
        - name: q
          in: query
          schema:
            type: string
            minLength: 1
            maxLength: 120
          description: Wyszukiwanie (imię, email)
        - name: status
          in: query
          schema:
            type: string
            minLength: 1
            maxLength: 40
          description: Filtr statusu (active, inactive, pending)
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
          description: Numer strony
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 20
          description: Rozmiar strony (max 50)
        - name: fields
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Wybrane kolumny (CSV)
        - name: include
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Grupy dodatkowych pól (CSV)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        vendor_id:
                          type: string
                        vendor_name:
                          type: string
                        auth_user_id:
                          type: string
                        email:
                          type: string
                        first_name:
                          type: string
                        last_name:
                          type: string
                        display_name:
                          type: string
                        photo_url:
                          type: string
                          nullable: true
                        phone:
                          type: string
                        roles:
                          type: string
                        status:
                          type: string
                        login_enabled:
                          type: boolean
                        last_login_at:
                          type: string
                          format: date-time
                          nullable: true
                        created_at:
                          type: string
                          format: date-time
                        updated_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/admin/vendor-contexts:
    get:
      tags:
        - Admin
      summary: Konteksty vendorów (wymaga SUPER_USER)
      description: |-
        Uprawnienie: auth.read + SUPER_USER

        Dozwolone fields: vendor_id, vendor_name, display_name, is_active, tax_id_number, symbol

        Include: details (vendor_name, tax_id_number, symbol)
      operationId: adminVendorContexts
      security:
        - BearerAuth: []
      parameters:
        - name: q
          in: query
          schema:
            type: string
            minLength: 1
            maxLength: 120
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
          description: Numer strony
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 20
          description: Rozmiar strony (max 50)
        - name: fields
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Wybrane kolumny (CSV)
        - name: include
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Grupy dodatkowych pól (CSV)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        vendor_id:
                          type: string
                        vendor_name:
                          type: string
                        display_name:
                          type: string
                        is_active:
                          type: boolean
                        tax_id_number:
                          type: string
                        symbol:
                          type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/dashboard/kpis:
    get:
      tags:
        - Analytics
      summary: KPI Dashboard
      description: |-
        Uprawnienie: analytics.read

        Zwraca: [{all_orders, active_orders, delivered_orders, efficiency_pct, open_complaints}]
      operationId: dashboardKpis
      security:
        - BearerAuth: []
      parameters:
        - name: dateFrom
          in: query
          schema:
            type: string
            pattern: ^\d{4}-\d{2}-\d{2}$
          description: Data od (ISO YYYY-MM-DD, domyślnie 90 dni wstecz)
        - name: dateTo
          in: query
          schema:
            type: string
            pattern: ^\d{4}-\d{2}-\d{2}$
          description: Data do (ISO YYYY-MM-DD, domyślnie dzisiaj, max 365 dni zakres)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        all_orders:
                          type: integer
                        active_orders:
                          type: integer
                        delivered_orders:
                          type: integer
                        efficiency_pct:
                          type: number
                        open_complaints:
                          type: integer
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/dashboard/trends:
    get:
      tags:
        - Analytics
      summary: Trendy Dashboard
      description: |-
        Uprawnienie: analytics.read

        Zwraca: [{trend_date, orders_count, avg_order_value}]
      operationId: dashboardTrends
      security:
        - BearerAuth: []
      parameters:
        - name: dateFrom
          in: query
          schema:
            type: string
            pattern: ^\d{4}-\d{2}-\d{2}$
          description: Data od (ISO YYYY-MM-DD, domyślnie 90 dni wstecz)
        - name: dateTo
          in: query
          schema:
            type: string
            pattern: ^\d{4}-\d{2}-\d{2}$
          description: Data do (ISO YYYY-MM-DD, domyślnie dzisiaj, max 365 dni zakres)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        trend_date:
                          type: string
                          format: date
                        orders_count:
                          type: integer
                        avg_order_value:
                          type: number
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/dashboard/vendor-charts:
    get:
      tags:
        - Analytics
      summary: Wykresy vendora
      description: |-
        Uprawnienie: analytics.read

        Zwraca: [{month, orders_this_year, orders_last_year, avg_delivery_time_hours, avg_prob_success}]
      operationId: dashboardVendorCharts
      security:
        - BearerAuth: []
      parameters:
        - name: dateFrom
          in: query
          schema:
            type: string
            pattern: ^\d{4}-\d{2}-\d{2}$
          description: Data od (ISO YYYY-MM-DD, domyślnie 90 dni wstecz)
        - name: dateTo
          in: query
          schema:
            type: string
            pattern: ^\d{4}-\d{2}-\d{2}$
          description: Data do (ISO YYYY-MM-DD, domyślnie dzisiaj, max 365 dni zakres)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        month:
                          type: string
                        orders_this_year:
                          type: integer
                        orders_last_year:
                          type: integer
                        avg_delivery_time_hours:
                          type: number
                        avg_prob_success:
                          type: number
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/sales/summary:
    get:
      tags:
        - Analytics
      summary: Podsumowanie sprzedaży
      description: |-
        Uprawnienie: analytics.read

        Zwraca: [{month, orders, gross_value, avg_order_value, active_clients}]
      operationId: salesSummary
      security:
        - BearerAuth: []
      parameters:
        - name: dateFrom
          in: query
          schema:
            type: string
            pattern: ^\d{4}-\d{2}-\d{2}$
          description: Data od (ISO YYYY-MM-DD, domyślnie 90 dni wstecz)
        - name: dateTo
          in: query
          schema:
            type: string
            pattern: ^\d{4}-\d{2}-\d{2}$
          description: Data do (ISO YYYY-MM-DD, domyślnie dzisiaj, max 365 dni zakres)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        month:
                          type: string
                        orders:
                          type: integer
                        gross_value:
                          type: number
                        avg_order_value:
                          type: number
                        active_clients:
                          type: integer
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/sales/categories:
    get:
      tags:
        - Analytics
      summary: Kategorie sprzedaży
      description: |-
        Uprawnienie: analytics.read

        Zwraca: [{name, value, count}]
      operationId: salesCategories
      security:
        - BearerAuth: []
      parameters:
        - name: dateFrom
          in: query
          schema:
            type: string
            pattern: ^\d{4}-\d{2}-\d{2}$
          description: Data od (ISO YYYY-MM-DD, domyślnie 90 dni wstecz)
        - name: dateTo
          in: query
          schema:
            type: string
            pattern: ^\d{4}-\d{2}-\d{2}$
          description: Data do (ISO YYYY-MM-DD, domyślnie dzisiaj, max 365 dni zakres)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                        value:
                          type: number
                        count:
                          type: integer
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/statistics/overview:
    get:
      tags:
        - Analytics
      summary: Przegląd statystyk
      description: 'Uprawnienie: analytics.read'
      operationId: statsOverview
      security:
        - BearerAuth: []
      parameters:
        - name: dateFrom
          in: query
          schema:
            type: string
            pattern: ^\d{4}-\d{2}-\d{2}$
          description: Data od (ISO YYYY-MM-DD, domyślnie 90 dni wstecz)
        - name: dateTo
          in: query
          schema:
            type: string
            pattern: ^\d{4}-\d{2}-\d{2}$
          description: Data do (ISO YYYY-MM-DD, domyślnie dzisiaj, max 365 dni zakres)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        metric:
                          type: string
                        label:
                          type: string
                        value:
                          type: number
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/statistics/dashboard:
    get:
      tags:
        - Analytics
      summary: Dashboard statystyk
      description: |-
        Uprawnienie: analytics.read

        Zwraca: {regionData, performanceData, timeSlots}
      operationId: statsDashboard
      security:
        - BearerAuth: []
      parameters:
        - name: dateFrom
          in: query
          schema:
            type: string
            pattern: ^\d{4}-\d{2}-\d{2}$
          description: Data od (ISO YYYY-MM-DD, domyślnie 90 dni wstecz)
        - name: dateTo
          in: query
          schema:
            type: string
            pattern: ^\d{4}-\d{2}-\d{2}$
          description: Data do (ISO YYYY-MM-DD, domyślnie dzisiaj, max 365 dni zakres)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      regionData:
                        type: array
                        items:
                          type: object
                          properties:
                            region:
                              type: string
                            deliveries:
                              type: integer
                      performanceData:
                        type: array
                        items:
                          type: object
                          properties:
                            metric:
                              type: string
                            value:
                              type: number
                      timeSlots:
                        type: array
                        items:
                          type: object
                          properties:
                            slot:
                              type: string
                            pct:
                              type: number
                            count:
                              type: integer
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/analytics/driver-helper-quality:
    get:
      tags:
        - Analytics
      summary: Raport jakości kierowcy i pomocnika
      description: |-
        Uprawnienie: analytics.read

        Zwraca pełne dane o kierowcy i pomocniku realizującym zlecenie na dany dzień.
        Przydatne przy zgłoszeniach incydentów do oceny pracy załogi.

        Wywołuje: "Analytics".fn_driver_helper_quality(order_id, report_date)
      operationId: analyticsDriverHelperQuality
      security:
        - BearerAuth: []
      parameters:
        - name: orderId
          in: query
          required: true
          schema:
            type: integer
            minimum: 1
          description: ID zlecenia (parametr kanoniczny). Endpoint akceptuje też aliasy `order_id`, `OrderID`, `orderID`.
        - name: reportDate
          in: query
          required: true
          schema:
            type: string
            format: date
          description: Data raportu (ISO YYYY-MM-DD, parametr kanoniczny). Endpoint akceptuje też aliasy `report_date`, `raportDate`, `raport_date`, `date`.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  description: Rekord zwrócony przez fn_driver_helper_quality — struktura zależna od definicji funkcji w bazie danych
        '400':
          description: Bad Request — nieprawidłowe parametry
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: Service Unavailable — błąd bazy danych
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/reports/jobs:
    post:
      tags:
        - Reports
      summary: Utwórz zadanie raportu
      operationId: reportsCreate
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReportJobBody'
      responses:
        '202':
          description: Accepted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      jobId:
                        type: string
                        format: uuid
                      reportType:
                        type: string
                      format:
                        type: string
                        enum:
                          - json
                          - csv
                      status:
                        type: string
                        example: queued
                      submittedAt:
                        type: string
                        format: date-time
                      vendorId:
                        type: string
                      requestedBy:
                        type: string
                      dateFrom:
                        type: string
                        format: date
                      dateTo:
                        type: string
                        format: date
                      links:
                        type: object
                        properties:
                          status:
                            type: string
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/reports/jobs/{jobId}:
    get:
      tags:
        - Reports
      summary: Status zadania raportu
      operationId: reportsStatus
      security:
        - BearerAuth: []
      parameters:
        - name: jobId
          in: path
          required: true
          schema:
            type: string
          description: ID zadania raportu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      jobId:
                        type: string
                        format: uuid
                      reportType:
                        type: string
                      format:
                        type: string
                      status:
                        type: string
                        enum:
                          - queued
                          - processing
                          - done
                          - failed
                      submittedAt:
                        type: string
                        format: date-time
                      vendorId:
                        type: string
                      requestedBy:
                        type: string
                      dateFrom:
                        type: string
                        format: date
                      dateTo:
                        type: string
                        format: date
                      downloadUrl:
                        type: string
                        nullable: true
                      links:
                        type: object
                        properties:
                          status:
                            type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/kepler/at-risk-orders:
    get:
      tags:
        - Kepler
      summary: Zamówienia zagrożone opóźnieniem
      description: >-
        Uprawnienie: kepler.read


        Dozwolone fields: id, vendor_id, order_id, order_number, customer, city, hub, vendor, delivery_standard, promise_to, predicted_delivery, predicted_delay_minutes, prob_success,
        remaining_minutes, status, stage, risk_level, eta_confidence, neighbors, vector_distance, customer_email, customer_phone, parcels, weight, last_event, last_event_at


        Include: prediction (predicted_delay_minutes, remaining_minutes, eta_confidence, neighbors), context (hub, vendor, delivery_standard, status), details (stage, vector_distance, customer_email,
        customer_phone, parcels, weight, last_event, last_event_at)
      operationId: keplerAtRisk
      security:
        - BearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
          description: Numer strony
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 20
          description: Rozmiar strony (max 50)
        - name: fields
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Wybrane kolumny (CSV)
        - name: include
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Grupy dodatkowych pól (CSV)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        vendor_id:
                          type: integer
                        order_id:
                          type: integer
                        order_number:
                          type: string
                        customer:
                          type: string
                        city:
                          type: string
                        hub:
                          type: string
                        vendor:
                          type: string
                        delivery_standard:
                          type: string
                        promise_to:
                          type: string
                          format: date-time
                        predicted_delivery:
                          type: string
                          format: date-time
                        predicted_delay_minutes:
                          type: integer
                        prob_success:
                          type: number
                        remaining_minutes:
                          type: integer
                        status:
                          type: string
                        stage:
                          type: string
                        risk_level:
                          type: string
                          enum:
                            - critical
                            - high
                            - medium
                            - low
                        eta_confidence:
                          type: number
                        neighbors:
                          type: integer
                        vector_distance:
                          type: number
                        customer_email:
                          type: string
                        customer_phone:
                          type: string
                        parcels:
                          type: integer
                        weight:
                          type: number
                        last_event:
                          type: string
                        last_event_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/kepler/reschedule:
    post:
      tags:
        - Kepler
      summary: Przesunięcie dostawy
      operationId: keplerReschedule
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/KeplerRescheduleBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      scope:
                        type: string
                      refresh_status:
                        type: string
                        example: QUEUED
                      details:
                        type: object
                      refreshed_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/kepler/batch-reschedule:
    post:
      tags:
        - Kepler
      summary: Grupowe przesunięcie dostaw
      operationId: keplerBatchReschedule
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/KeplerBatchRescheduleBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      scope:
                        type: string
                      refresh_status:
                        type: string
                        example: QUEUED
                      details:
                        type: object
                      refreshed_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/kepler/notify/sms:
    post:
      tags:
        - Kepler
      summary: Powiadomienie SMS klienta
      operationId: keplerNotifySms
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                orderId:
                  type: string
                  example: ORD-001
                phone:
                  type: string
                  example: '+48600700800'
                message:
                  type: string
                  example: Informujemy o opóźnieniu dostawy
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      scope:
                        type: string
                      refresh_status:
                        type: string
                        example: QUEUED
                      details:
                        type: object
                      refreshed_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/kepler/notify/email:
    post:
      tags:
        - Kepler
      summary: Powiadomienie email klienta
      operationId: keplerNotifyEmail
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                orderId:
                  type: string
                  example: ORD-001
                email:
                  type: string
                  example: klient@example.com
                subject:
                  type: string
                  example: Opóźnienie dostawy
                message:
                  type: string
                  example: Informujemy o opóźnieniu...
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      scope:
                        type: string
                      refresh_status:
                        type: string
                        example: QUEUED
                      details:
                        type: object
                      refreshed_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/kepler/statistics:
    get:
      tags:
        - Kepler
      summary: Statystyki Kepler
      description: 'Zwraca: total_at_risk, critical_orders, high_risk_orders, avg_predicted_delay_minutes, avg_prob_success'
      operationId: keplerStats
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        metric:
                          type: string
                        label:
                          type: string
                        value:
                          type: number
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/process/bpmn/files:
    get:
      tags:
        - Support
      summary: Lista plików BPMN
      description: 'Uprawnienie: support.read'
      operationId: bpmnFiles
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                        baseName:
                          type: string
                        path:
                          type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/process/bpmn/{name}:
    get:
      tags:
        - Support
      summary: Pobierz plik BPMN
      operationId: bpmnFile
      security:
        - BearerAuth: []
      parameters:
        - name: name
          in: path
          required: true
          schema:
            type: string
          description: Nazwa pliku BPMN
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      name:
                        type: string
                      baseName:
                        type: string
                      path:
                        type: string
                      content:
                        type: string
                        description: XML content
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/process/bpmn/{baseName}/description:
    get:
      tags:
        - Support
      summary: Opis procesu BPMN
      operationId: bpmnDesc
      security:
        - BearerAuth: []
      parameters:
        - name: baseName
          in: path
          required: true
          schema:
            type: string
          description: Bazowa nazwa pliku
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      baseName:
                        type: string
                      description:
                        type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/gallery/photos:
    get:
      tags:
        - Support
      summary: Lista zdjęć galerii
      description: 'Uprawnienie: support.read'
      operationId: galleryPhotos
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                        url:
                          type: string
                        size:
                          type: integer
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/ai/chat:
    post:
      tags:
        - AI
      summary: Chat AI z załącznikami
      description: |-
        Uprawnienie: ai.create

        Odpowiedź: {id, role, content, provider, model, sessionId, channel, attachmentsProcessed, usage{promptTokens, completionTokens, totalTokens}, createdAt}
      operationId: aiChat
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AiChatBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      role:
                        type: string
                        example: assistant
                      content:
                        type: string
                      provider:
                        type: string
                      model:
                        type: string
                      sessionId:
                        type: string
                        nullable: true
                      channel:
                        type: string
                        enum:
                          - text
                          - voice
                          - video
                      attachmentsProcessed:
                        type: integer
                      usage:
                        type: object
                        nullable: true
                        properties:
                          promptTokens:
                            type: integer
                          completionTokens:
                            type: integer
                          totalTokens:
                            type: integer
                      createdAt:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/guest/register:
    post:
      tags:
        - Guest
      summary: Rejestracja gościa (publiczny)
      operationId: guestRegister
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GuestRegisterBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      fullName:
                        type: string
                      email:
                        type: string
                      company:
                        type: string
                      source:
                        type: string
                      createdAt:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/guest/list:
    get:
      tags:
        - Guest
      summary: Lista zarejestrowanych gości
      description: 'Uprawnienie: vendor.read'
      operationId: guestList
      security:
        - BearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
          description: Numer strony
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 20
          description: Rozmiar strony (max 50)
        - name: fields
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Wybrane kolumny (CSV)
        - name: include
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z0-9_,-]+$
          description: Grupy dodatkowych pól (CSV)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        fullName:
                          type: string
                        email:
                          type: string
                        company:
                          type: string
                        source:
                          type: string
                        createdAt:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/cms/data-lineage:
    get:
      tags:
        - CMS
      summary: Data Lineage — pobierz dane
      description: 'Dostęp: token serwisowy (nagłówek X-CMS-Sync-Token) LUB auth + support.read + SUPER_USER'
      operationId: cmsLineageGet
      security:
        - BearerAuth: []
      parameters:
        - name: moduleId
          in: query
          schema:
            type: string
            minLength: 1
            maxLength: 120
          description: Filtr po module
        - name: keys
          in: query
          schema:
            type: string
            minLength: 1
            maxLength: 8000
          description: CSV lista kluczy do pobrania
        - name: active
          in: query
          schema:
            type: boolean
            default: true
          description: Tylko aktywne (domyślnie true)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        module_id:
                          type: string
                        key:
                          type: string
                        value:
                          type: string
                        is_active:
                          type: boolean
                        updated_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/cms/data-lineage/sync:
    post:
      tags:
        - CMS
      summary: Data Lineage — synchronizacja
      operationId: cmsLineageSync
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CmsDataLineageSyncBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      synced:
                        type: integer
                      errors:
                        type: integer
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/cms/data-lineage/verify:
    post:
      tags:
        - CMS
      summary: Data Lineage — weryfikacja kluczy
      operationId: cmsLineageVerify
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CmsDataLineageVerifyBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      valid:
                        type: boolean
                      missing:
                        type: array
                        items:
                          type: string
                      extra:
                        type: array
                        items:
                          type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/cms/runtime:
    get:
      tags:
        - CMS
      summary: Runtime CMS — pobierz konfigurację
      operationId: cmsRuntimeGet
      security:
        - BearerAuth: []
      parameters:
        - name: lang
          in: query
          schema:
            type: string
            minLength: 2
            maxLength: 5
          description: Kod języka (np. pl, en)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      activeLang:
                        type: string
                      texts:
                        type: object
                        additionalProperties: true
                      images:
                        type: object
                        additionalProperties: true
                      moduleVisibility:
                        type: object
                        additionalProperties: true
                      translations:
                        type: object
                        additionalProperties: true
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/cms/runtime/active-lang:
    put:
      tags:
        - CMS
      summary: Ustaw aktywny język CMS
      operationId: cmsSetLang
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CmsSetActiveLangBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      ok:
                        type: boolean
                      lang:
                        type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/cms/runtime/text/{lang}/{id}:
    put:
      tags:
        - CMS
      summary: Nadpisz tekst CMS
      operationId: cmsTextOverride
      security:
        - BearerAuth: []
      parameters:
        - name: lang
          in: path
          required: true
          schema:
            type: string
            minLength: 2
            maxLength: 5
          description: Kod języka
        - name: id
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 240
          description: ID tekstu
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CmsTextOverrideBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      ok:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/cms/runtime/image/{id}:
    put:
      tags:
        - CMS
      summary: Nadpisz obraz CMS
      operationId: cmsImageOverride
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 240
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CmsTextOverrideBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      ok:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/cms/runtime/module-visibility/{id}:
    put:
      tags:
        - CMS
      summary: Widoczność modułu CMS
      operationId: cmsModuleVis
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 120
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CmsModuleVisibilityBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      ok:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/cms/runtime/translations/sync:
    post:
      tags:
        - CMS
      summary: Synchronizacja tłumaczeń
      operationId: cmsTransSync
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CmsTranslationsSyncBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      synced:
                        type: integer
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/cms/runtime/migrate-local:
    post:
      tags:
        - CMS
      summary: Migracja lokalna CMS
      operationId: cmsMigrate
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CmsMigrateLocalBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      ok:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/cms/runtime/reset:
    post:
      tags:
        - CMS
      summary: Reset runtime CMS
      operationId: cmsReset
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CmsRuntimeResetBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      ok:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/polaris/document-types:
    get:
      tags:
        - Polaris
      summary: Lista typów dokumentów
      operationId: polarisTypes
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        code:
                          type: string
                        name:
                          type: string
                        description:
                          type: string
                          nullable: true
                        is_active:
                          type: boolean
                        created_at:
                          type: string
                          format: date-time
                        updated_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - Polaris
      summary: Utwórz typ dokumentu
      operationId: polarisTypesCreate
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PolarisDocumentTypeBody'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      code:
                        type: string
                      name:
                        type: string
                      description:
                        type: string
                        nullable: true
                      is_active:
                        type: boolean
                      created_at:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/polaris/partners/{partnerId}/documents:
    get:
      tags:
        - Polaris
      summary: Lista dokumentów partnera
      operationId: polarisDocsList
      security:
        - BearerAuth: []
      parameters:
        - name: partnerId
          in: path
          required: true
          schema:
            type: string
          description: ID partnera
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 20
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        partner_id:
                          type: integer
                        document_type_id:
                          type: integer
                        file_name:
                          type: string
                        content_type:
                          type: string
                        size_bytes:
                          type: integer
                        s3_bucket:
                          type: string
                        s3_key:
                          type: string
                        s3_uri:
                          type: string
                        ai_summary:
                          type: string
                          nullable: true
                        uploaded_by:
                          type: integer
                          nullable: true
                        created_at:
                          type: string
                          format: date-time
                        updated_at:
                          type: string
                          format: date-time
                        document_type_code:
                          type: string
                        document_type_name:
                          type: string
                        presignedUrl:
                          type: string
                          nullable: true
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - Polaris
      summary: Upload dokumentu partnera (multipart)
      description: |-
        Content-Type: multipart/form-data.

        Pole documentTypeId (number) MUSI być PRZED plikami w multipart stream. Min. 1 plik.

        Po uploadzie uruchamiane jest automatyczne streszczenie AI (ai_summary_status: processing).
      operationId: polarisDocsUpload
      security:
        - BearerAuth: []
      parameters:
        - name: partnerId
          in: path
          required: true
          schema:
            type: string
          description: ID partnera
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - documentTypeId
                - file
              properties:
                documentTypeId:
                  type: integer
                  description: ID typu dokumentu (MUSI być PRZED plikami)
                file:
                  type: string
                  format: binary
                  description: Plik dokumentu
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        file_name:
                          type: string
                        content_type:
                          type: string
                        size_bytes:
                          type: integer
                        s3_key:
                          type: string
                        document_type_code:
                          type: string
                        document_type_name:
                          type: string
                        presignedUrl:
                          type: string
                        ai_summary_status:
                          type: string
                          example: processing
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/polaris/partners/{partnerId}/documents/{documentId}:
    get:
      tags:
        - Polaris
      summary: Szczegóły dokumentu
      operationId: polarisDocsDetail
      security:
        - BearerAuth: []
      parameters:
        - name: partnerId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      partner_id:
                        type: integer
                      document_type_id:
                        type: integer
                      file_name:
                        type: string
                      content_type:
                        type: string
                      size_bytes:
                        type: integer
                      s3_bucket:
                        type: string
                      s3_key:
                        type: string
                      s3_uri:
                        type: string
                      ai_summary:
                        type: string
                        nullable: true
                      uploaded_by:
                        type: integer
                        nullable: true
                      created_at:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
                      document_type_code:
                        type: string
                      document_type_name:
                        type: string
                      presignedUrl:
                        type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - Polaris
      summary: Usuń dokument
      operationId: polarisDocsDelete
      security:
        - BearerAuth: []
      parameters:
        - name: partnerId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/polaris/partners/{partnerId}/documents/{documentId}/download:
    get:
      tags:
        - Polaris
      summary: Pobierz dokument (presigned URL)
      operationId: polarisDocsDownload
      security:
        - BearerAuth: []
      parameters:
        - name: partnerId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      presignedUrl:
                        type: string
                      document:
                        type: object
                        description: Full PartnerDocumentRow
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/polaris/partners/{partnerId}/documents/{documentId}/summarize:
    post:
      tags:
        - Polaris
      summary: Wygeneruj AI summary dokumentu
      operationId: polarisDocsSummarize
      security:
        - BearerAuth: []
      parameters:
        - name: partnerId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      ai_summary:
                        type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/partners:
    get:
      tags:
        - Partners
      summary: Lista partnerów
      description: 'Uprawnienie: partners.read. additionalProperties: false na query.'
      operationId: partnersList
      security:
        - BearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 20
        - name: q
          in: query
          schema:
            type: string
            minLength: 1
            maxLength: 120
          description: Wyszukiwanie (nazwa, symbol, NIP)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        name:
                          type: string
                        symbol30:
                          type: string
                          nullable: true
                        phone:
                          type: string
                          nullable: true
                        email:
                          type: string
                          nullable: true
                        tax_id:
                          type: string
                          nullable: true
                        bank_account:
                          type: string
                          nullable: true
                        contact_person:
                          type: string
                          nullable: true
                        pays_vat_tax:
                          type: boolean
                          nullable: true
                        is_active:
                          type: boolean
                        responsible_regional_director:
                          type: string
                          nullable: true
                        responsible_regional_director_email:
                          type: string
                          nullable: true
                        responsible_hub_manager:
                          type: string
                          nullable: true
                        responsible_hub_manager_email:
                          type: string
                          nullable: true
                        invoice_address:
                          type: string
                          nullable: true
                        paymodel:
                          type: string
                          nullable: true
                        hubs:
                          type: string
                          nullable: true
                        last_route_date:
                          type: string
                          nullable: true
                        last_activity_year:
                          type: integer
                          nullable: true
                        tms_link:
                          type: string
                          nullable: true
                        ftl_fuel_surcharge:
                          type: boolean
                          nullable: true
                        wysylka_rozliczen:
                          type: boolean
                          nullable: true
                        created_at:
                          type: string
                          format: date-time
                        updated_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - Partners
      summary: Utwórz partnera
      description: 'Uprawnienie: partners.create'
      operationId: partnersCreate
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePartnerBody'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      name:
                        type: string
                      symbol30:
                        type: string
                        nullable: true
                      phone:
                        type: string
                        nullable: true
                      email:
                        type: string
                        nullable: true
                      tax_id:
                        type: string
                        nullable: true
                      bank_account:
                        type: string
                        nullable: true
                      contact_person:
                        type: string
                        nullable: true
                      pays_vat_tax:
                        type: boolean
                        nullable: true
                      is_active:
                        type: boolean
                      created_at:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/partners/{id}:
    get:
      tags:
        - Partners
      summary: Szczegóły partnera
      operationId: partnersDetail
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 40
          description: ID zasobu (max 40 zn.)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      name:
                        type: string
                      symbol30:
                        type: string
                        nullable: true
                      phone:
                        type: string
                        nullable: true
                      email:
                        type: string
                        nullable: true
                      tax_id:
                        type: string
                        nullable: true
                      bank_account:
                        type: string
                        nullable: true
                      contact_person:
                        type: string
                        nullable: true
                      pays_vat_tax:
                        type: boolean
                        nullable: true
                      is_active:
                        type: boolean
                      invoice_address:
                        type: string
                        nullable: true
                      paymodel:
                        type: string
                        nullable: true
                      hubs:
                        type: string
                        nullable: true
                      tms_link:
                        type: string
                        nullable: true
                      ftl_fuel_surcharge:
                        type: boolean
                        nullable: true
                      wysylka_rozliczen:
                        type: boolean
                        nullable: true
                      created_at:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    patch:
      tags:
        - Partners
      summary: Aktualizuj partnera (partial)
      operationId: partnersUpdate
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 40
          description: ID zasobu (max 40 zn.)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdatePartnerBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      name:
                        type: string
                      is_active:
                        type: boolean
                      updated_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - Partners
      summary: Usuń partnera
      operationId: partnersDelete
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 40
          description: ID zasobu (max 40 zn.)
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/vendors:
    get:
      tags:
        - Vendors
      summary: Lista vendorów
      description: 'Uprawnienie: vendor.read'
      operationId: vendorsList
      security:
        - BearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          description: Max 100
        - name: q
          in: query
          schema:
            type: string
            minLength: 1
            maxLength: 120
        - name: isActive
          in: query
          schema:
            type: boolean
          description: Filtr aktywności
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        name:
                          type: string
                          nullable: true
                        display_name:
                          type: string
                          nullable: true
                        symbol:
                          type: string
                          nullable: true
                        symbol_external:
                          type: string
                          nullable: true
                        tax_id_number:
                          type: string
                          nullable: true
                        is_active:
                          type: boolean
                          nullable: true
                        color:
                          type: string
                          nullable: true
                        email:
                          type: string
                          nullable: true
                        complaints_email:
                          type: string
                          nullable: true
                        accounting_email:
                          type: string
                          nullable: true
                        main_phone_number:
                          type: string
                          nullable: true
                        email_domain:
                          type: string
                          nullable: true
                        invoice_address:
                          type: object
                          nullable: true
                        financial:
                          type: object
                          nullable: true
                        created_at:
                          type: string
                          format: date-time
                        updated_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - Vendors
      summary: Utwórz vendora
      description: 'Uprawnienie: vendor.create. Body z 44 polami — jedyne wymagane: name.'
      operationId: vendorsCreate
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateVendorBody'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      name:
                        type: string
                      display_name:
                        type: string
                        nullable: true
                      symbol:
                        type: string
                        nullable: true
                      is_active:
                        type: boolean
                      created_at:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/vendors/autocomplete:
    get:
      tags:
        - Vendors
      summary: Autouzupełnianie vendorów
      description: 'Uprawnienie: vendor.read. Walidacja: po trim q.length < 1 → 400 VALIDATION_ERROR.'
      operationId: vendorsAutocomplete
      security:
        - BearerAuth: []
      parameters:
        - name: q
          in: query
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 120
          description: Fraza (wymagana, min 1 zn. po trim)
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 15
          description: Max wyników (max 50)
        - name: isActive
          in: query
          schema:
            type: boolean
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        name:
                          type: string
                          nullable: true
                        display_name:
                          type: string
                          nullable: true
                        symbol:
                          type: string
                          nullable: true
                        tax_id_number:
                          type: string
                          nullable: true
                        is_active:
                          type: boolean
                          nullable: true
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/vendors/{id}:
    get:
      tags:
        - Vendors
      summary: Szczegóły vendora
      operationId: vendorsDetail
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 40
          description: ID zasobu (max 40 zn.)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      name:
                        type: string
                        nullable: true
                      display_name:
                        type: string
                        nullable: true
                      symbol:
                        type: string
                        nullable: true
                      tax_id_number:
                        type: string
                        nullable: true
                      is_active:
                        type: boolean
                      email:
                        type: string
                        nullable: true
                      main_phone_number:
                        type: string
                        nullable: true
                      invoice_address:
                        type: object
                        nullable: true
                      financial:
                        type: object
                        nullable: true
                      billing_config:
                        type: object
                        nullable: true
                      created_at:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    patch:
      tags:
        - Vendors
      summary: Aktualizuj vendora (partial)
      description: 'Uprawnienie: vendor.update. Identyczne pola jak POST, ale żadne nie jest wymagane (partial update).'
      operationId: vendorsUpdate
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 40
          description: ID zasobu (max 40 zn.)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateVendorBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      name:
                        type: string
                      is_active:
                        type: boolean
                      updated_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - Vendors
      summary: Usuń vendora
      description: 'Uprawnienie: vendor.delete'
      operationId: vendorsDelete
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 40
          description: ID zasobu (max 40 zn.)
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people:
    get:
      tags:
        - People
      summary: Lista osób
      operationId: peopleList
      security:
        - BearerAuth: []
      parameters:
        - name: q
          in: query
          schema:
            type: string
          description: Wyszukiwanie (nazwisko, email, telefon). Pełny email lub telefon 9-cyfrowy uruchamia precyzyjne wyszukiwanie.
        - name: is_active
          in: query
          schema:
            type: string
            enum:
              - 'true'
              - 'false'
        - name: is_customer
          in: query
          schema:
            type: string
            enum:
              - 'true'
          description: Szukaj także wśród klientów
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            default: 20
            maximum: 200
          description: Max 200
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                        first_name:
                          type: string
                          nullable: true
                        last_name:
                          type: string
                          nullable: true
                        full_name:
                          type: string
                          nullable: true
                        email_normalized:
                          type: string
                          nullable: true
                        phone_normalized:
                          type: string
                          nullable: true
                        phone_prefix:
                          type: string
                          nullable: true
                        is_active:
                          type: boolean
                        preferred_language:
                          type: string
                          nullable: true
                        photo_url:
                          type: string
                          nullable: true
                        tags:
                          type: array
                          items:
                            type: string
                        metadata:
                          type: object
                          additionalProperties: true
                        profile:
                          type: object
                          additionalProperties: true
                        auth_user_ids:
                          type: array
                          items:
                            type: string
                        created_at:
                          type: string
                          format: date-time
                        updated_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - People
      summary: Utwórz osobę
      operationId: peopleCreate
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePersonBody'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      first_name:
                        type: string
                        nullable: true
                      last_name:
                        type: string
                        nullable: true
                      email_normalized:
                        type: string
                        nullable: true
                      is_active:
                        type: boolean
                      tags:
                        type: array
                        items:
                          type: string
                      metadata:
                        type: object
                      profile:
                        type: object
                      created_at:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/{id}:
    get:
      tags:
        - People
      summary: Szczegóły osoby z rolami i auth
      operationId: peopleDetail
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      first_name:
                        type: string
                        nullable: true
                      last_name:
                        type: string
                        nullable: true
                      full_name:
                        type: string
                        nullable: true
                      email_normalized:
                        type: string
                        nullable: true
                      phone_normalized:
                        type: string
                        nullable: true
                      is_active:
                        type: boolean
                      photo_url:
                        type: string
                        nullable: true
                      tags:
                        type: array
                        items:
                          type: string
                      metadata:
                        type: object
                      profile:
                        type: object
                      created_at:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
                      roles:
                        type: array
                        items:
                          type: object
                          properties:
                            role_id:
                              type: integer
                            symbol:
                              type: string
                            name:
                              type: string
                            context:
                              type: object
                            is_active:
                              type: boolean
                            granted_at:
                              type: string
                              format: date-time
                      subroles:
                        type: array
                        items:
                          type: object
                          properties:
                            subrole_symbol:
                              type: string
                            is_active:
                              type: boolean
                            granted_at:
                              type: string
                              format: date-time
                      auth_methods:
                        type: array
                        items:
                          type: object
                          properties:
                            method_symbol:
                              type: string
                            is_active:
                              type: boolean
                            login_identifier:
                              type: string
                            last_used_at:
                              type: string
                              nullable: true
                      identity_keys:
                        type: array
                        items:
                          type: object
                          properties:
                            key_type:
                              type: string
                            key_value:
                              type: string
                            is_primary:
                              type: boolean
                            source:
                              type: string
                              nullable: true
                      sources:
                        type: array
                        items:
                          type: object
                          properties:
                            source_schema:
                              type: string
                            source_table:
                              type: string
                            source_id:
                              type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    patch:
      tags:
        - People
      summary: Aktualizuj osobę (partial)
      description: Partial update. Email i phone normalizowane automatycznie.
      operationId: peopleUpdate
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePersonBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      first_name:
                        type: string
                        nullable: true
                      last_name:
                        type: string
                        nullable: true
                      is_active:
                        type: boolean
                      updated_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - People
      summary: Usuń osobę
      operationId: peopleDelete
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/views/with-roles:
    get:
      tags:
        - People
      summary: Osoby z przypisanymi rolami
      operationId: peopleWithRoles
      security:
        - BearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            default: 20
            maximum: 200
          description: Max 200
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                        first_name:
                          type: string
                        last_name:
                          type: string
                        email_normalized:
                          type: string
                        is_active:
                          type: boolean
                        roles:
                          type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/{id}/auth-summary:
    get:
      tags:
        - People
      summary: Podsumowanie auth osoby
      operationId: peopleAuthSummary
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      person_id:
                        type: string
                      auth_methods:
                        type: array
                        items:
                          type: object
                      identity_keys:
                        type: array
                        items:
                          type: object
                      lockout_status:
                        type: object
                        nullable: true
                      last_login:
                        type: string
                        nullable: true
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/{id}/roles:
    get:
      tags:
        - People
      summary: Role przypisane do osoby
      operationId: peopleRolesList
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        person_id:
                          type: string
                          format: uuid
                        role_id:
                          type: integer
                        context:
                          type: object
                        is_active:
                          type: boolean
                        granted_at:
                          type: string
                          format: date-time
                        revoked_at:
                          type: string
                          format: date-time
                          nullable: true
                        symbol:
                          type: string
                        role_name:
                          type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - People
      summary: Przypisz rolę osobie
      operationId: peopleRolesAssign
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - role
              properties:
                role:
                  type: string
                  description: Symbol roli
                  example: partner
                context:
                  type: object
                  description: Kontekst roli (JSON)
                  example:
                    vendor_id: v-100
                  additionalProperties: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      person_id:
                        type: string
                      role_id:
                        type: integer
                      context:
                        type: object
                      is_active:
                        type: boolean
                      granted_at:
                        type: string
                        format: date-time
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/{id}/roles/{roleId}:
    patch:
      tags:
        - People
      summary: Aktualizuj rolę osoby
      operationId: peopleRolesUpdate
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
        - name: roleId
          in: path
          required: true
          schema:
            type: string
          description: ID roli
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                is_active:
                  type: boolean
                context:
                  type: object
                  additionalProperties: true
                  description: 'Kontekst roli (JSON, np. { vendor_id: "v-100" })'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      person_id:
                        type: string
                      is_active:
                        type: boolean
                      context:
                        type: object
                      granted_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - People
      summary: Usuń rolę osoby
      operationId: peopleRolesDelete
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
        - name: roleId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/auth-methods/catalog:
    get:
      tags:
        - People
      summary: Katalog metod auth
      operationId: peopleAuthCatalog
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        symbol:
                          type: string
                        name:
                          type: string
                        priority:
                          type: integer
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/{id}/auth-methods:
    get:
      tags:
        - People
      summary: Metody auth osoby
      operationId: peopleAuthMethods
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        person_id:
                          type: string
                          format: uuid
                        method_symbol:
                          type: string
                        is_active:
                          type: boolean
                        login_identifier:
                          type: string
                          nullable: true
                        provider_uid:
                          type: string
                          nullable: true
                        last_used_at:
                          type: string
                          format: date-time
                          nullable: true
                        created_at:
                          type: string
                          format: date-time
                        updated_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/{personId}/auth-methods/{methodId}:
    patch:
      tags:
        - People
      summary: Aktualizuj metodę auth
      operationId: peopleAuthMethodUpdate
      security:
        - BearerAuth: []
      parameters:
        - name: personId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
        - name: methodId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                is_active:
                  type: boolean
                login_identifier:
                  type: string
                  description: Identyfikator logowania
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      person_id:
                        type: string
                      method_symbol:
                        type: string
                      is_active:
                        type: boolean
                      login_identifier:
                        type: string
                        nullable: true
                      last_used_at:
                        type: string
                        nullable: true
                      created_at:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/{id}/identity-keys:
    get:
      tags:
        - People
      summary: Klucze tożsamości osoby
      operationId: peopleIdKeys
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        person_id:
                          type: string
                          format: uuid
                        key_type:
                          type: string
                        key_value:
                          type: string
                        is_primary:
                          type: boolean
                        source:
                          type: string
                          nullable: true
                        created_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - People
      summary: Dodaj klucz tożsamości
      operationId: peopleIdKeysCreate
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - key_type
                - key_value
              properties:
                key_type:
                  type: string
                  description: Typ klucza (email, phone, etc.)
                  example: email
                key_value:
                  type: string
                  description: Wartość klucza
                  example: jan@firma.pl
                is_primary:
                  type: boolean
                  description: Czy klucz główny
                source:
                  type: string
                  description: Źródło
                  example: registration
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      person_id:
                        type: string
                      key_type:
                        type: string
                      key_value:
                        type: string
                      is_primary:
                        type: boolean
                      source:
                        type: string
                        nullable: true
                      created_at:
                        type: string
                        format: date-time
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/identity-keys/{keyId}:
    delete:
      tags:
        - People
      summary: Usuń klucz tożsamości
      operationId: peopleIdKeysDelete
      security:
        - BearerAuth: []
      parameters:
        - name: keyId
          in: path
          required: true
          schema:
            type: string
          description: ID klucza (konwertowany na Number)
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/{id}/sources:
    get:
      tags:
        - People
      summary: Źródła osoby
      operationId: peopleSources
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        person_id:
                          type: string
                          format: uuid
                        source_schema:
                          type: string
                        source_table:
                          type: string
                        source_id:
                          type: string
                        source_id_type:
                          type: string
                        linked_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - People
      summary: Dodaj źródło osoby
      operationId: peopleSourcesCreate
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - source_schema
                - source_table
                - source_id
              properties:
                source_schema:
                  type: string
                  example: Auth
                source_table:
                  type: string
                  example: User
                source_id:
                  type: string
                  example: auth-user-001
                source_id_type:
                  type: string
                  example: uuid
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      person_id:
                        type: string
                      source_schema:
                        type: string
                      source_table:
                        type: string
                      source_id:
                        type: string
                      source_id_type:
                        type: string
                      linked_at:
                        type: string
                        format: date-time
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/sources/{sourceId}:
    delete:
      tags:
        - People
      summary: Usuń źródło
      operationId: peopleSourcesDelete
      security:
        - BearerAuth: []
      parameters:
        - name: sourceId
          in: path
          required: true
          schema:
            type: string
          description: ID źródła (Number)
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/{id}/lockout:
    get:
      tags:
        - People
      summary: Stan blokady osoby
      operationId: peopleLockout
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      person_id:
                        type: string
                        format: uuid
                      locked_at:
                        type: string
                        format: date-time
                      locked_until:
                        type: string
                        format: date-time
                        nullable: true
                      failed_attempts:
                        type: integer
                      reason:
                        type: string
                        nullable: true
                    nullable: true
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/{id}/unlock:
    post:
      tags:
        - People
      summary: Odblokuj osobę
      operationId: peopleUnlock
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      result:
                        type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/{id}/auth-attempts:
    get:
      tags:
        - People
      summary: Próby logowania osoby
      operationId: peopleAuthAttempts
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            default: 20
            maximum: 200
          description: Max 200
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        person_id:
                          type: string
                        method:
                          type: string
                        ip_address:
                          type: string
                        user_agent:
                          type: string
                        success:
                          type: boolean
                        created_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/{id}/otp-codes:
    get:
      tags:
        - People
      summary: Kody OTP osoby
      operationId: peopleOtpCodes
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        person_id:
                          type: string
                        channel:
                          type: string
                        destination:
                          type: string
                        expires_at:
                          type: string
                          format: date-time
                        verified_at:
                          type: string
                          format: date-time
                          nullable: true
                        attempts:
                          type: integer
                        max_attempts:
                          type: integer
                        created_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/{id}/recoveries:
    get:
      tags:
        - People
      summary: Procesy odzyskiwania osoby
      operationId: peopleRecoveries
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        person_id:
                          type: string
                        channel:
                          type: string
                        destination:
                          type: string
                        expires_at:
                          type: string
                          format: date-time
                        used_at:
                          type: string
                          format: date-time
                          nullable: true
                        ip_address:
                          type: string
                          nullable: true
                        created_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/sync-logs:
    get:
      tags:
        - People
      summary: Logi synchronizacji osób
      operationId: peopleSyncLogs
      security:
        - BearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            default: 20
            maximum: 200
          description: Max 200
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        sync_type:
                          type: string
                        status:
                          type: string
                        records_processed:
                          type: integer
                        errors:
                          type: integer
                        started_at:
                          type: string
                          format: date-time
                        completed_at:
                          type: string
                          format: date-time
                          nullable: true
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/sync-logs/{id}:
    get:
      tags:
        - People
      summary: Szczegóły logu synchronizacji
      operationId: peopleSyncLogDetail
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: ID logu (Number)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      sync_type:
                        type: string
                      status:
                        type: string
                      records_processed:
                        type: integer
                      errors:
                        type: integer
                      started_at:
                        type: string
                        format: date-time
                      completed_at:
                        type: string
                        format: date-time
                        nullable: true
                      details:
                        type: object
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/role-catalog:
    get:
      tags:
        - People
      summary: Katalog ról
      operationId: peopleRoleCatalog
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        symbol:
                          type: string
                        name:
                          type: string
                        description:
                          type: string
                          nullable: true
                        is_assignable:
                          type: boolean
                        is_system:
                          type: boolean
                        category:
                          type: string
                          nullable: true
                        created_at:
                          type: string
                          format: date-time
                        updated_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/role-catalog/{symbol}:
    get:
      tags:
        - People
      summary: Szczegóły roli z katalogu
      operationId: peopleRoleCatalogDetail
      security:
        - BearerAuth: []
      parameters:
        - name: symbol
          in: path
          required: true
          schema:
            type: string
          description: Symbol roli
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      symbol:
                        type: string
                      name:
                        type: string
                      description:
                        type: string
                        nullable: true
                      is_assignable:
                        type: boolean
                      is_system:
                        type: boolean
                      category:
                        type: string
                        nullable: true
                      created_at:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/app-modules:
    get:
      tags:
        - People
      summary: Moduły aplikacji
      operationId: peopleAppModules
      security:
        - BearerAuth: []
      parameters:
        - name: include_inactive
          in: query
          schema:
            type: string
            enum:
              - 'true'
          description: Pokaż nieaktywne
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        module_id:
                          type: string
                        name:
                          type: string
                        description:
                          type: string
                          nullable: true
                        icon:
                          type: string
                          nullable: true
                        sort_order:
                          type: integer
                        platform:
                          type: string
                        is_active:
                          type: boolean
                        created_at:
                          type: string
                          format: date-time
                        updated_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/people/app-modules/{moduleId}:
    get:
      tags:
        - People
      summary: Szczegóły modułu aplikacji
      operationId: peopleAppModuleDetail
      security:
        - BearerAuth: []
      parameters:
        - name: moduleId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      module_id:
                        type: string
                      name:
                        type: string
                      description:
                        type: string
                        nullable: true
                      icon:
                        type: string
                        nullable: true
                      sort_order:
                        type: integer
                      platform:
                        type: string
                      is_active:
                        type: boolean
                      created_at:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/roles:
    get:
      tags:
        - RBAC
      summary: Lista ról
      operationId: rbacRoles
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        symbol:
                          type: string
                        name:
                          type: string
                        description:
                          type: string
                          nullable: true
                        is_assignable:
                          type: boolean
                        is_system:
                          type: boolean
                        category:
                          type: string
                          nullable: true
                        created_at:
                          type: string
                          format: date-time
                        updated_at:
                          type: string
                          format: date-time
                        subroles:
                          type: array
                          items:
                            type: object
                            properties:
                              symbol:
                                type: string
                              name:
                                type: string
                              is_admin:
                                type: boolean
                              description:
                                type: string
                                nullable: true
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - RBAC
      summary: Utwórz rolę
      operationId: rbacRoleCreate
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateRoleBody'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      symbol:
                        type: string
                      name:
                        type: string
                      description:
                        type: string
                        nullable: true
                      is_assignable:
                        type: boolean
                      is_system:
                        type: boolean
                      category:
                        type: string
                        nullable: true
                      created_at:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/roles/{symbol}:
    get:
      tags:
        - RBAC
      summary: Szczegóły roli
      operationId: rbacRoleDetail
      security:
        - BearerAuth: []
      parameters:
        - name: symbol
          in: path
          required: true
          schema:
            type: string
          description: Symbol roli
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      symbol:
                        type: string
                      name:
                        type: string
                      description:
                        type: string
                        nullable: true
                      is_assignable:
                        type: boolean
                      is_system:
                        type: boolean
                      category:
                        type: string
                        nullable: true
                      created_at:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
                      subroles:
                        type: array
                        items:
                          type: object
                          properties:
                            symbol:
                              type: string
                            name:
                              type: string
                            is_admin:
                              type: boolean
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    patch:
      tags:
        - RBAC
      summary: Aktualizuj rolę
      operationId: rbacRoleUpdate
      security:
        - BearerAuth: []
      parameters:
        - name: symbol
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
                is_assignable:
                  type: boolean
                category:
                  type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      symbol:
                        type: string
                      name:
                        type: string
                      description:
                        type: string
                        nullable: true
                      is_assignable:
                        type: boolean
                      category:
                        type: string
                        nullable: true
                      updated_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - RBAC
      summary: Usuń rolę
      operationId: rbacRoleDelete
      security:
        - BearerAuth: []
      parameters:
        - name: symbol
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/subroles:
    get:
      tags:
        - RBAC
      summary: Lista subról
      operationId: rbacSubroles
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        symbol:
                          type: string
                        parent_role:
                          type: string
                        name:
                          type: string
                        description:
                          type: string
                          nullable: true
                        is_admin:
                          type: boolean
                        is_assignable:
                          type: boolean
                        is_system:
                          type: boolean
                        created_at:
                          type: string
                          format: date-time
                        updated_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - RBAC
      summary: Utwórz subrolę
      operationId: rbacSubroleCreate
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSubroleBody'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      symbol:
                        type: string
                      parent_role:
                        type: string
                      name:
                        type: string
                      description:
                        type: string
                        nullable: true
                      is_admin:
                        type: boolean
                      is_assignable:
                        type: boolean
                      is_system:
                        type: boolean
                      created_at:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/subroles/{symbol}:
    get:
      tags:
        - RBAC
      summary: Szczegóły subroli
      operationId: rbacSubroleDetail
      security:
        - BearerAuth: []
      parameters:
        - name: symbol
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      symbol:
                        type: string
                      parent_role:
                        type: string
                      name:
                        type: string
                      description:
                        type: string
                        nullable: true
                      is_admin:
                        type: boolean
                      is_assignable:
                        type: boolean
                      is_system:
                        type: boolean
                      created_at:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
                      permissions:
                        type: array
                        items:
                          type: string
                      app_modules:
                        type: array
                        items:
                          type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    patch:
      tags:
        - RBAC
      summary: Aktualizuj subrolę
      operationId: rbacSubroleUpdate
      security:
        - BearerAuth: []
      parameters:
        - name: symbol
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
                is_admin:
                  type: boolean
                is_assignable:
                  type: boolean
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      symbol:
                        type: string
                      name:
                        type: string
                      description:
                        type: string
                        nullable: true
                      is_admin:
                        type: boolean
                      is_assignable:
                        type: boolean
                      updated_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - RBAC
      summary: Usuń subrolę
      operationId: rbacSubroleDelete
      security:
        - BearerAuth: []
      parameters:
        - name: symbol
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/subroles/{symbol}/permissions:
    get:
      tags:
        - RBAC
      summary: Uprawnienia subroli
      operationId: rbacSubrolePerms
      security:
        - BearerAuth: []
      parameters:
        - name: symbol
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        permission:
                          type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    put:
      tags:
        - RBAC
      summary: Ustaw uprawnienia subroli
      operationId: rbacSubrolePermsSet
      security:
        - BearerAuth: []
      parameters:
        - name: symbol
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - permissions
              properties:
                permissions:
                  type: array
                  items:
                    type: string
                  example:
                    - orders.read
                    - quality.read
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      symbol:
                        type: string
                      permissions:
                        type: array
                        items:
                          type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/subroles/{symbol}/app-modules:
    get:
      tags:
        - RBAC
      summary: Moduły subroli
      operationId: rbacSubroleModules
      security:
        - BearerAuth: []
      parameters:
        - name: symbol
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        module_id:
                          type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    put:
      tags:
        - RBAC
      summary: Ustaw moduły subroli
      operationId: rbacSubroleModulesSet
      security:
        - BearerAuth: []
      parameters:
        - name: symbol
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - modules
              properties:
                modules:
                  type: array
                  items:
                    type: string
                  example:
                    - mod-orders
                    - mod-invoices
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      symbol:
                        type: string
                      modules:
                        type: array
                        items:
                          type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/subroles/{symbol}/app-modules/{moduleId}:
    post:
      tags:
        - RBAC
      summary: Dodaj moduł do subroli
      operationId: rbacSubroleModuleAdd
      security:
        - BearerAuth: []
      parameters:
        - name: symbol
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
        - name: moduleId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      subrole_symbol:
                        type: string
                      module_id:
                        type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - RBAC
      summary: Usuń moduł z subroli
      operationId: rbacSubroleModuleRemove
      security:
        - BearerAuth: []
      parameters:
        - name: symbol
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
        - name: moduleId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/permissions:
    get:
      tags:
        - RBAC
      summary: Katalog uprawnień
      operationId: rbacPermissions
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        permission:
                          type: string
                        name:
                          type: string
                        module:
                          type: string
                        operation:
                          type: string
                        description:
                          type: string
                          nullable: true
                        is_system:
                          type: boolean
                        created_at:
                          type: string
                          format: date-time
                        updated_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - RBAC
      summary: Utwórz uprawnienie
      operationId: rbacPermCreate
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePermissionBody'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      permission:
                        type: string
                      name:
                        type: string
                      module:
                        type: string
                      operation:
                        type: string
                      description:
                        type: string
                        nullable: true
                      is_system:
                        type: boolean
                      created_at:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/permissions/{permission}:
    patch:
      tags:
        - RBAC
      summary: Aktualizuj uprawnienie
      operationId: rbacPermUpdate
      security:
        - BearerAuth: []
      parameters:
        - name: permission
          in: path
          required: true
          schema:
            type: string
          description: Symbol uprawnienia
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      permission:
                        type: string
                      name:
                        type: string
                      description:
                        type: string
                        nullable: true
                      updated_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - RBAC
      summary: Usuń uprawnienie
      operationId: rbacPermDelete
      security:
        - BearerAuth: []
      parameters:
        - name: permission
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/api-modules:
    get:
      tags:
        - RBAC
      summary: Moduły API
      operationId: rbacApiModules
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        module_id:
                          type: string
                        name:
                          type: string
                        description:
                          type: string
                          nullable: true
                        is_active:
                          type: boolean
                        created_at:
                          type: string
                          format: date-time
                        updated_at:
                          type: string
                          format: date-time
                        endpoints:
                          type: array
                          items:
                            type: object
                            properties:
                              method:
                                type: string
                              path:
                                type: string
                              permission:
                                type: string
                              description:
                                type: string
                                nullable: true
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - RBAC
      summary: Utwórz moduł API
      operationId: rbacApiModCreate
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - module_id
                - name
              properties:
                module_id:
                  type: string
                  example: mod-warehouse
                name:
                  type: string
                  example: Moduł magazynowy
                description:
                  type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      module_id:
                        type: string
                      name:
                        type: string
                      description:
                        type: string
                        nullable: true
                      is_active:
                        type: boolean
                      created_at:
                        type: string
                        format: date-time
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/api-modules/{moduleId}:
    patch:
      tags:
        - RBAC
      summary: Aktualizuj moduł API
      operationId: rbacApiModUpdate
      security:
        - BearerAuth: []
      parameters:
        - name: moduleId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
                is_active:
                  type: boolean
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      module_id:
                        type: string
                      name:
                        type: string
                      description:
                        type: string
                        nullable: true
                      is_active:
                        type: boolean
                      updated_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - RBAC
      summary: Usuń moduł API
      operationId: rbacApiModDelete
      security:
        - BearerAuth: []
      parameters:
        - name: moduleId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/api-modules/visibility:
    get:
      tags:
        - RBAC
      summary: Widoczność modułów API (dla zalogowanego)
      operationId: rbacApiModVisibility
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        module_id:
                          type: string
                        name:
                          type: string
                        is_visible:
                          type: boolean
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/api-endpoints:
    get:
      tags:
        - RBAC
      summary: Lista endpointów API
      operationId: rbacApiEndpoints
      security:
        - BearerAuth: []
      parameters:
        - name: module_id
          in: query
          schema:
            type: string
          description: Filtr po module
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        endpoint_id:
                          type: integer
                        module_id:
                          type: string
                        method:
                          type: string
                        path:
                          type: string
                        permission:
                          type: string
                        description:
                          type: string
                          nullable: true
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - RBAC
      summary: Zarejestruj endpoint API
      operationId: rbacApiEndpCreate
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - module_id
                - method
                - path
                - permission
              properties:
                module_id:
                  type: string
                method:
                  type: string
                  example: GET
                path:
                  type: string
                  example: /v1/orders
                permission:
                  type: string
                  example: orders.read
                description:
                  type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      endpoint_id:
                        type: integer
                      module_id:
                        type: string
                      method:
                        type: string
                      path:
                        type: string
                      permission:
                        type: string
                      description:
                        type: string
                        nullable: true
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/api-endpoints/{endpointId}:
    patch:
      tags:
        - RBAC
      summary: Aktualizuj endpoint API
      operationId: rbacApiEndpUpdate
      security:
        - BearerAuth: []
      parameters:
        - name: endpointId
          in: path
          required: true
          schema:
            type: string
          description: ID endpointu (Number)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                module_id:
                  type: string
                method:
                  type: string
                path:
                  type: string
                permission:
                  type: string
                description:
                  type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      endpoint_id:
                        type: integer
                      module_id:
                        type: string
                      method:
                        type: string
                      path:
                        type: string
                      permission:
                        type: string
                      description:
                        type: string
                        nullable: true
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - RBAC
      summary: Usuń endpoint API
      operationId: rbacApiEndpDelete
      security:
        - BearerAuth: []
      parameters:
        - name: endpointId
          in: path
          required: true
          schema:
            type: string
          description: ID endpointu (Number)
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/app-modules:
    get:
      tags:
        - RBAC
      summary: Moduły aplikacji (RBAC)
      operationId: rbacAppModules
      security:
        - BearerAuth: []
      parameters:
        - name: include_inactive
          in: query
          schema:
            type: string
            enum:
              - 'true'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        module_id:
                          type: string
                        name:
                          type: string
                        description:
                          type: string
                          nullable: true
                        icon:
                          type: string
                          nullable: true
                        sort_order:
                          type: integer
                        platform:
                          type: string
                        is_active:
                          type: boolean
                        created_at:
                          type: string
                          format: date-time
                        updated_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - RBAC
      summary: Utwórz moduł aplikacji
      operationId: rbacAppModCreate
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - module_id
                - name
              properties:
                module_id:
                  type: string
                  example: mod-reports
                name:
                  type: string
                  example: Raporty
                description:
                  type: string
                icon:
                  type: string
                  example: chart-bar
                sort_order:
                  type: number
                  example: 10
                platform:
                  type: string
                  example: web
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      module_id:
                        type: string
                      name:
                        type: string
                      description:
                        type: string
                        nullable: true
                      icon:
                        type: string
                        nullable: true
                      sort_order:
                        type: integer
                      platform:
                        type: string
                      is_active:
                        type: boolean
                      created_at:
                        type: string
                        format: date-time
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/app-modules/{moduleId}:
    get:
      tags:
        - RBAC
      summary: Szczegóły modułu aplikacji
      operationId: rbacAppModDetail
      security:
        - BearerAuth: []
      parameters:
        - name: moduleId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      module_id:
                        type: string
                      name:
                        type: string
                      description:
                        type: string
                        nullable: true
                      icon:
                        type: string
                        nullable: true
                      sort_order:
                        type: integer
                      platform:
                        type: string
                      is_active:
                        type: boolean
                      created_at:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
                      subroles:
                        type: array
                        items:
                          type: object
                          properties:
                            subrole_symbol:
                              type: string
                            subrole_name:
                              type: string
                            parent_role:
                              type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    patch:
      tags:
        - RBAC
      summary: Aktualizuj moduł aplikacji
      operationId: rbacAppModUpdate
      security:
        - BearerAuth: []
      parameters:
        - name: moduleId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
                icon:
                  type: string
                sort_order:
                  type: number
                platform:
                  type: string
                is_active:
                  type: boolean
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      module_id:
                        type: string
                      name:
                        type: string
                      is_active:
                        type: boolean
                      updated_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - RBAC
      summary: Usuń moduł aplikacji
      operationId: rbacAppModDelete
      security:
        - BearerAuth: []
      parameters:
        - name: moduleId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/app-modules/{moduleId}/subroles:
    get:
      tags:
        - RBAC
      summary: Subrole modułu
      operationId: rbacAppModSubroles
      security:
        - BearerAuth: []
      parameters:
        - name: moduleId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        subrole_symbol:
                          type: string
                        subrole_name:
                          type: string
                        parent_role:
                          type: string
                        is_admin:
                          type: boolean
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/app-modules/my:
    get:
      tags:
        - RBAC
      summary: Moje moduły (zalogowany)
      operationId: rbacMyModules
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        module_id:
                          type: string
                        name:
                          type: string
                        icon:
                          type: string
                          nullable: true
                        sort_order:
                          type: integer
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/app-modules/user/{userId}:
    get:
      tags:
        - RBAC
      summary: Moduły użytkownika
      operationId: rbacUserModules
      security:
        - BearerAuth: []
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        module_id:
                          type: string
                        name:
                          type: string
                        icon:
                          type: string
                          nullable: true
                        sort_order:
                          type: integer
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/users/{userId}/roles:
    get:
      tags:
        - RBAC
      summary: Role użytkownika
      operationId: rbacUserRoles
      security:
        - BearerAuth: []
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        role_symbol:
                          type: string
                        role_name:
                          type: string
                        subroles:
                          type: array
                          items:
                            type: object
                            properties:
                              symbol:
                                type: string
                              name:
                                type: string
                              is_admin:
                                type: boolean
                              is_active:
                                type: boolean
                              granted_at:
                                type: string
                                format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/users/{userId}/subroles:
    get:
      tags:
        - RBAC
      summary: Subrole użytkownika
      operationId: rbacUserSubroles
      security:
        - BearerAuth: []
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        person_id:
                          type: string
                        subrole_symbol:
                          type: string
                        is_active:
                          type: boolean
                        granted_at:
                          type: string
                          format: date-time
                        revoked_at:
                          type: string
                          format: date-time
                          nullable: true
                        subrole_name:
                          type: string
                        parent_role:
                          type: string
                        is_admin:
                          type: boolean
                        subrole_description:
                          type: string
                          nullable: true
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    put:
      tags:
        - RBAC
      summary: Ustaw subrole użytkownika
      operationId: rbacUserSubrolesSet
      security:
        - BearerAuth: []
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - subroles
              properties:
                subroles:
                  type: array
                  items:
                    type: string
                  example:
                    - partner_viewer
                    - partner_editor
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      userId:
                        type: string
                      subroles:
                        type: array
                        items:
                          type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/users/{userId}/subroles/{symbol}:
    post:
      tags:
        - RBAC
      summary: Dodaj subrolę użytkownikowi
      operationId: rbacUserSubroleAdd
      security:
        - BearerAuth: []
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
        - name: symbol
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      userId:
                        type: string
                      symbol:
                        type: string
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - RBAC
      summary: Usuń subrolę użytkownika
      operationId: rbacUserSubroleRemove
      security:
        - BearerAuth: []
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
        - name: symbol
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/users/{userId}/permissions:
    get:
      tags:
        - RBAC
      summary: Uprawnienia użytkownika
      operationId: rbacUserPerms
      security:
        - BearerAuth: []
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        permission:
                          type: string
                        source:
                          type: string
                        subrole_symbol:
                          type: string
                          nullable: true
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/users/{userId}/app-modules:
    get:
      tags:
        - RBAC
      summary: Moduły aplikacji użytkownika
      operationId: rbacUserAppMods
      security:
        - BearerAuth: []
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        module_id:
                          type: string
                        name:
                          type: string
                        icon:
                          type: string
                          nullable: true
                        sort_order:
                          type: integer
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/users/{userId}/permission-overrides:
    get:
      tags:
        - RBAC
      summary: Override uprawnień użytkownika
      operationId: rbacUserOverrides
      security:
        - BearerAuth: []
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        person_id:
                          type: string
                        permission:
                          type: string
                        is_grant:
                          type: boolean
                        expires_at:
                          type: string
                          format: date-time
                          nullable: true
                        created_at:
                          type: string
                          format: date-time
                        permission_name:
                          type: string
                          nullable: true
                        module:
                          type: string
                          nullable: true
                        operation:
                          type: string
                          nullable: true
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    put:
      tags:
        - RBAC
      summary: Ustaw override uprawnień
      operationId: rbacUserOverridesSet
      security:
        - BearerAuth: []
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PermissionOverridesBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      userId:
                        type: string
                      overrides:
                        type: array
                        items:
                          type: object
                          properties:
                            permission:
                              type: string
                            is_grant:
                              type: boolean
                            expires_at:
                              type: string
                              nullable: true
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/users/{userId}/access-profile:
    get:
      tags:
        - RBAC
      summary: Profil dostępu użytkownika
      operationId: rbacUserAccessProfile
      security:
        - BearerAuth: []
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      roles:
                        type: array
                        items:
                          type: object
                      subroles:
                        type: array
                        items:
                          type: object
                      permissions:
                        type: array
                        items:
                          type: object
                      app_modules:
                        type: array
                        items:
                          type: object
                      permission_overrides:
                        type: array
                        items:
                          type: object
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/audit-log:
    get:
      tags:
        - Audit
      summary: Log audytu bezpieczeństwa
      operationId: auditLog
      security:
        - BearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            default: 50
            maximum: 200
          description: Max 200
        - name: person_id
          in: query
          schema:
            type: string
          description: Filtr po osobie
        - name: event_type
          in: query
          schema:
            type: string
          description: Typ zdarzenia
        - name: path
          in: query
          schema:
            type: string
          description: Ścieżka URL
        - name: status
          in: query
          schema:
            type: integer
          description: Kod HTTP
        - name: api_module
          in: query
          schema:
            type: string
          description: Moduł API
        - name: from
          in: query
          schema:
            type: string
            format: date
          description: Data od (ISO)
        - name: to
          in: query
          schema:
            type: string
            format: date
          description: Data do (ISO)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        person_id:
                          type: string
                        event_type:
                          type: string
                        ip_address:
                          type: string
                        user_agent:
                          type: string
                        details:
                          type: object
                          nullable: true
                        created_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/api-log:
    get:
      tags:
        - Audit
      summary: Log wywołań API
      operationId: apiLog
      security:
        - BearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            default: 50
            maximum: 200
          description: Max 200
        - name: person_id
          in: query
          schema:
            type: string
          description: Filtr po osobie
        - name: event_type
          in: query
          schema:
            type: string
          description: Typ zdarzenia
        - name: path
          in: query
          schema:
            type: string
          description: Ścieżka URL
        - name: status
          in: query
          schema:
            type: integer
          description: Kod HTTP
        - name: api_module
          in: query
          schema:
            type: string
          description: Moduł API
        - name: from
          in: query
          schema:
            type: string
            format: date
          description: Data od (ISO)
        - name: to
          in: query
          schema:
            type: string
            format: date
          description: Data do (ISO)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        person_id:
                          type: string
                        method:
                          type: string
                        path:
                          type: string
                        status:
                          type: integer
                        api_module:
                          type: string
                          nullable: true
                        duration_ms:
                          type: integer
                        ip_address:
                          type: string
                        user_agent:
                          type: string
                        created_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/users/{userId}/activity:
    get:
      tags:
        - Audit
      summary: Aktywność użytkownika
      operationId: userActivity
      security:
        - BearerAuth: []
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
          description: Identyfikator zasobu
        - name: days
          in: query
          schema:
            type: integer
            default: 30
          description: Liczba dni wstecz
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      total_requests:
                        type: integer
                      last_active:
                        type: string
                        format: date-time
                        nullable: true
                      most_used_modules:
                        type: array
                        items:
                          type: object
                      activity_by_day:
                        type: array
                        items:
                          type: object
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/stats/api-performance:
    get:
      tags:
        - Audit
      summary: Statystyki wydajności API
      operationId: statsApiPerf
      security:
        - BearerAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        api_module:
                          type: string
                        avg_response_ms:
                          type: number
                        p95_ms:
                          type: number
                        p99_ms:
                          type: number
                        total_requests:
                          type: integer
                        error_rate:
                          type: number
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/rbac/stats/user-modules:
    get:
      tags:
        - Audit
      summary: Statystyki modułów użytkowników
      operationId: statsUserMods
      security:
        - BearerAuth: []
      parameters:
        - name: person_id
          in: query
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        person_id:
                          type: string
                        module_id:
                          type: string
                        request_count:
                          type: integer
                        last_used_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/ListMeta'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/dict/incidents-category:
    get:
      tags:
        - Dictionaries
      summary: Lista kategorii incydentów
      description: Zwraca listę wszystkich kategorii incydentów (słownik). Domyślnie tylko aktywne wpisy.
      operationId: getDictIncidentsCategories
      security:
        - BearerAuth: []
      parameters:
        - name: x-vendor-id
          in: header
          required: true
          schema:
            type: string
          description: Identyfikator vendora (tenant)
        - name: includeInactive
          in: query
          required: false
          schema:
            type: boolean
            default: false
          description: Jeśli true, zwraca również nieaktywne kategorie
      responses:
        '200':
          description: Lista kategorii incydentów
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/DictIncidentsCategory'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - Dictionaries
      summary: Utwórz nową kategorię incydentu
      description: Dodaje nowy wpis do słownika kategorii incydentów. Pole code musi być unikalne i składać się z wielkich liter, cyfr i podkreśleń.
      operationId: createDictIncidentsCategory
      security:
        - BearerAuth: []
      parameters:
        - name: x-vendor-id
          in: header
          required: true
          schema:
            type: string
          description: Identyfikator vendora (tenant)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - code
                - label
                - sortOrder
              properties:
                code:
                  type: string
                  minLength: 1
                  maxLength: 60
                  pattern: ^[A-Z][A-Z0-9_]*$
                  description: Unikalny kod kategorii (wielkie litery, cyfry, podkreślenia)
                  example: DAMAGED_SHIPMENT
                label:
                  type: string
                  minLength: 1
                  maxLength: 200
                  description: Etykieta wyświetlana użytkownikowi
                  example: Uszkodzenie przesyłki
                sortOrder:
                  type: integer
                  minimum: 0
                  description: Kolejność sortowania
                  example: 1
                isActive:
                  type: boolean
                  description: Czy kategoria jest aktywna (domyślnie true)
                  example: true
      responses:
        '201':
          description: Kategoria utworzona
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/DictIncidentsCategory'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Conflict — wpis z tym kodem już istnieje
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/dict/incidents-category/{code}:
    get:
      tags:
        - Dictionaries
      summary: Pobierz kategorię incydentu po kodzie
      description: Zwraca szczegóły jednej kategorii incydentu na podstawie kodu.
      operationId: getDictIncidentsCategoryByCode
      security:
        - BearerAuth: []
      parameters:
        - name: x-vendor-id
          in: header
          required: true
          schema:
            type: string
          description: Identyfikator vendora (tenant)
        - name: code
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 60
          description: Kod kategorii incydentu
          example: DAMAGED_SHIPMENT
      responses:
        '200':
          description: Szczegóły kategorii incydentu
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/DictIncidentsCategory'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    patch:
      tags:
        - Dictionaries
      summary: Aktualizuj kategorię incydentu
      description: Częściowa aktualizacja wpisu słownikowego. Można zmienić etykietę, kolejność sortowania i flagę aktywności.
      operationId: updateDictIncidentsCategory
      security:
        - BearerAuth: []
      parameters:
        - name: x-vendor-id
          in: header
          required: true
          schema:
            type: string
          description: Identyfikator vendora (tenant)
        - name: code
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 60
          description: Kod kategorii incydentu
          example: DAMAGED_SHIPMENT
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                label:
                  type: string
                  minLength: 1
                  maxLength: 200
                  description: Nowa etykieta kategorii
                  example: Uszkodzenie przesyłki (zmieniona)
                sortOrder:
                  type: integer
                  minimum: 0
                  description: Nowa kolejność sortowania
                  example: 2
                isActive:
                  type: boolean
                  description: Czy kategoria jest aktywna
                  example: true
      responses:
        '200':
          description: Zaktualizowana kategoria
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/DictIncidentsCategory'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - Dictionaries
      summary: Usuń kategorię incydentu
      description: Trwale usuwa wpis ze słownika kategorii incydentów.
      operationId: deleteDictIncidentsCategory
      security:
        - BearerAuth: []
      parameters:
        - name: x-vendor-id
          in: header
          required: true
          schema:
            type: string
          description: Identyfikator vendora (tenant)
        - name: code
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 60
          description: Kod kategorii incydentu do usunięcia
          example: OTHER
      responses:
        '204':
          description: Kategoria usunięta
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/hubs-divisions:
    get:
      tags:
        - HubDivision
      summary: Lista HUBów z przypisanymi Division
      description: |-
        Pobiera listę HUBów z zagnieżdżoną listą `divisions`.
        Uprawnienie: `hub.read`.

        Filtrowanie:
        - `hubIsActive` filtruje tylko rekordy HUB,
        - `divisionIsActive` filtruje tylko rekordy Division,
        - `isActive` jest aliasem legacy ustawiającym oba filtry naraz.
      operationId: hubsDivisionsList
      security:
        - BearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          description: Maksymalna wielkość strony (100)
        - name: q
          in: query
          schema:
            type: string
            minLength: 1
            maxLength: 120
          description: Wyszukiwanie po nazwie/symbolu/adresie HUB i Division
        - name: all
          in: query
          schema:
            type: boolean
          description: Jeśli `true`, zwraca wszystkie rekordy bez paginacji
        - name: isActive
          in: query
          schema:
            type: boolean
          description: Legacy alias - ustawia jednocześnie `hubIsActive` i `divisionIsActive`
        - name: hubIsActive
          in: query
          schema:
            type: boolean
          description: Filtr aktywności tylko dla HUB
        - name: divisionIsActive
          in: query
          schema:
            type: boolean
          description: Filtr aktywności tylko dla Division
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HubWithDivisionsListResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Database configuration error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: SQL unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/hubs-divisions/autocomplete:
    get:
      tags:
        - HubDivision
      summary: Autocomplete dla HUB i Division
      description: |-
        Wyszukiwanie autocomplete po nazwie/symbolu/adresie.
        Uprawnienie: `hub.read`.
      operationId: hubsDivisionsAutocomplete
      security:
        - BearerAuth: []
      parameters:
        - name: q
          in: query
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 120
          description: Fraza wyszukiwania
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 15
          description: Maksymalna liczba wyników
        - name: scope
          in: query
          schema:
            type: string
            enum:
              - hub
              - division
              - all
            default: all
          description: Zakres wyszukiwania
        - name: isActive
          in: query
          schema:
            type: boolean
          description: Legacy alias - ustawia jednocześnie `hubIsActive` i `divisionIsActive`
        - name: hubIsActive
          in: query
          schema:
            type: boolean
          description: Filtr aktywności tylko dla HUB
        - name: divisionIsActive
          in: query
          schema:
            type: boolean
          description: Filtr aktywności tylko dla Division
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HubDivisionAutocompleteResponse'
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Database configuration error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: SQL unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/hubs:
    get:
      tags:
        - HubDivision
      summary: Lista HUB
      description: Pobiera paginowaną listę HUB. Uprawnienie `hub.read`.
      operationId: hubsList
      security:
        - BearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: q
          in: query
          schema:
            type: string
            minLength: 1
            maxLength: 120
        - name: isActive
          in: query
          schema:
            type: boolean
          description: Filtr aktywności HUB
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HubListResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Database configuration error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: SQL unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - HubDivision
      summary: Utwórz HUB
      description: Tworzy nowy HUB. Uprawnienie `hub.create`.
      operationId: hubsCreate
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateHubBody'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HubWriteResponse'
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Database configuration error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: SQL unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/hubs/{id}:
    get:
      tags:
        - HubDivision
      summary: Szczegóły HUB
      description: Zwraca HUB wraz z listą jego Division. Uprawnienie `hub.read`.
      operationId: hubsDetail
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 40
          description: ID HUB
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HubDetailResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Hub not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Database configuration error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: SQL unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    patch:
      tags:
        - HubDivision
      summary: Aktualizuj HUB (partial)
      description: Częściowa aktualizacja danych HUB. Uprawnienie `hub.update`.
      operationId: hubsUpdate
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 40
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateHubBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HubWriteResponse'
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Hub not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Database configuration error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: SQL unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - HubDivision
      summary: Usuń HUB
      description: Trwale usuwa HUB. Uprawnienie `hub.delete`.
      operationId: hubsDelete
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 40
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Hub not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Database configuration error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: SQL unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/hubs/{id}/location:
    patch:
      tags:
        - HubDivision
      summary: Aktualizuj lokalizację HUB jako POINT
      description: |-
        Aktualizuje pozycję HUB na mapie i zapisuje lokalizację jako `POINT` (`geometry(Point,4326)`).
        Uprawnienie `hub.update`.
      operationId: hubsUpdateLocation
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 40
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateHubLocationBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HubWriteResponse'
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Hub not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Database configuration error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: SQL unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/divisions:
    get:
      tags:
        - HubDivision
      summary: Lista Division
      description: Pobiera paginowaną listę Division. Uprawnienie `hub.read`.
      operationId: divisionsList
      security:
        - BearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: q
          in: query
          schema:
            type: string
            minLength: 1
            maxLength: 120
        - name: isActive
          in: query
          schema:
            type: boolean
          description: Filtr aktywności Division
        - name: hubId
          in: query
          schema:
            type: string
            minLength: 1
            maxLength: 40
          description: Ogranicz listę do Division wskazanego HUB
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DivisionListResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Database configuration error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: SQL unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - HubDivision
      summary: Utwórz Division
      description: Tworzy nową Division. Uprawnienie `hub.create`.
      operationId: divisionsCreate
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateDivisionBody'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DivisionWriteResponse'
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Database configuration error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: SQL unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/divisions/{id}:
    get:
      tags:
        - HubDivision
      summary: Szczegóły Division
      description: Zwraca szczegóły pojedynczej Division. Uprawnienie `hub.read`.
      operationId: divisionsDetail
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 40
          description: ID Division
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DivisionDetailResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Division not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Database configuration error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: SQL unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    patch:
      tags:
        - HubDivision
      summary: Aktualizuj Division (partial)
      description: Częściowa aktualizacja Division. Uprawnienie `hub.update`.
      operationId: divisionsUpdate
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 40
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateDivisionBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DivisionWriteResponse'
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Division not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Database configuration error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: SQL unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - HubDivision
      summary: Usuń Division
      description: Trwale usuwa Division. Uprawnienie `hub.delete`.
      operationId: divisionsDelete
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 40
      responses:
        '204':
          description: No Content
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Division not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Database configuration error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: SQL unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: 'JWT Bearer Token. Nagłówek: Authorization: Bearer <token>. Większość endpointów wymaga także nagłówka x-vendor-id (identyfikator tenanta/vendora).'
  schemas:
    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              example: NOT_FOUND
            message:
              type: string
              example: Resource not found
        traceId:
          type: string
          format: uuid
        timestamp:
          type: string
          format: date-time
    ListMeta:
      type: object
      properties:
        page:
          type: integer
          example: 1
        pageSize:
          type: integer
          example: 20
        total:
          type: integer
          example: 157
        totalPages:
          type: integer
          example: 8
        requestId:
          type: string
          format: uuid
        generatedAt:
          type: string
          format: date-time
    DictIncidentsCategory:
      type: object
      properties:
        code:
          type: string
          description: Unikalny kod kategorii incydentu
          example: DAMAGED_SHIPMENT
        label:
          type: string
          description: Etykieta wyświetlana użytkownikowi
          example: Uszkodzenie przesyłki
        sort_order:
          type: integer
          description: Kolejność sortowania
          example: 1
        is_active:
          type: boolean
          description: Czy kategoria jest aktywna
          example: true
    HealthResponse:
      type: object
      properties:
        service:
          type: string
          example: partner-portal-api
        status:
          type: string
          example: ok
        git:
          type: object
          properties:
            branch:
              type: string
              example: main
            commit:
              type: string
              example: abc1234
        environment:
          type: string
          example: production
        databaseUrlConfigured:
          type: boolean
          example: true
        redisUrlConfigured:
          type: boolean
          example: true
        timestamp:
          type: string
          format: date-time
    LoginBody:
      type: object
      required:
        - email
        - password
      additionalProperties: false
      properties:
        email:
          type: string
          description: Adres email użytkownika
          example: jan.kowalski@firma.pl
        password:
          type: string
          minLength: 1
          description: Hasło użytkownika
          example: MojeHaslo123!
        method:
          type: string
          default: PASSWORD
          description: 'Metoda auth: PASSWORD (domyślna), TOKEN_URL, SMS_OTP, EMAIL_OTP, OAUTH_GOOGLE, OAUTH_APPLE, OAUTH_FACEBOOK, OAUTH_LINKEDIN'
          example: PASSWORD
    LoginResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            accessToken:
              type: string
              example: eyJhbGciOiJIUzI1NiIs...
            refreshToken:
              type: string
              example: eyJhbGciOiJIUzI1NiIs...
            user:
              type: object
              properties:
                id:
                  type: string
                  example: usr-001
                email:
                  type: string
                  example: jan.kowalski@firma.pl
                displayName:
                  type: string
                  example: Jan Kowalski
                roles:
                  type: array
                  items:
                    type: string
                  example:
                    - partner
                vendorId:
                  type: string
                  example: v-100
                activeRole:
                  type: string
                  example: partner
                permissions:
                  type: array
                  items:
                    type: string
                  example:
                    - orders.read
                    - quality.read
    AuthMeResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            id:
              type: string
              example: usr-001
            email:
              type: string
              example: jan.kowalski@firma.pl
            firstName:
              type: string
              example: Jan
            lastName:
              type: string
              example: Kowalski
            displayName:
              type: string
              example: Jan Kowalski
            photoUrl:
              type: string
              nullable: true
            phone:
              type: string
              example: '+48500100200'
            roles:
              type: array
              items:
                type: string
            permissions:
              type: array
              items:
                type: string
            vendorId:
              type: string
            status:
              type: string
              enum:
                - active
                - inactive
                - pending
            activeRole:
              type: string
            lastLoginAt:
              type: string
              format: date-time
              nullable: true
            createdAt:
              type: string
              format: date-time
    RefreshBody:
      type: object
      required:
        - refreshToken
      additionalProperties: false
      properties:
        refreshToken:
          type: string
          minLength: 10
          description: Refresh token otrzymany przy logowaniu
          example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
    SwitchRoleBody:
      type: object
      required:
        - role
      additionalProperties: false
      properties:
        role:
          type: string
          minLength: 1
          description: Symbol nowej aktywnej roli
          example: admin
    PortalFeedbackBody:
      type: object
      required:
        - overall
        - usability
        - design
        - speed
      additionalProperties: false
      properties:
        overall:
          type: integer
          minimum: 1
          maximum: 5
          description: Ogólna ocena
          example: 4
        usability:
          type: integer
          minimum: 1
          maximum: 5
          description: Ocena użyteczności
          example: 5
        design:
          type: integer
          minimum: 1
          maximum: 5
          description: Ocena designu
          example: 4
        speed:
          type: integer
          minimum: 1
          maximum: 5
          description: Ocena szybkości
          example: 3
        comment:
          type: string
          maxLength: 2000
          description: Opcjonalny komentarz
          example: Bardzo intuicyjny interfejs
    ReportJobBody:
      type: object
      required:
        - reportType
      additionalProperties: false
      properties:
        reportType:
          type: string
          enum:
            - dashboard_trends
            - sales_summary
            - vendor_charts
            - statistics_overview
          description: Typ raportu
          example: sales_summary
        format:
          type: string
          enum:
            - json
            - csv
          default: json
          description: Format wyjściowy
          example: csv
        dateFrom:
          type: string
          pattern: ^\d{4}-\d{2}-\d{2}$
          description: Data od (ISO)
          example: '2026-01-01'
        dateTo:
          type: string
          pattern: ^\d{4}-\d{2}-\d{2}$
          description: Data do (ISO)
          example: '2026-02-28'
    AiChatBody:
      type: object
      required:
        - message
      additionalProperties: false
      properties:
        message:
          type: string
          minLength: 1
          maxLength: 8000
          description: Wiadomość użytkownika
          example: Jakie zamówienia są zagrożone opóźnieniem?
        channel:
          type: string
          enum:
            - text
            - voice
            - video
          default: text
          description: Kanał komunikacji
        history:
          type: array
          maxItems: 24
          description: Historia konwersacji (max 24 wiadomości)
          items:
            type: object
            required:
              - role
              - content
            additionalProperties: false
            properties:
              role:
                type: string
                enum:
                  - user
                  - assistant
                  - system
                description: Rola autora
              content:
                type: string
                minLength: 1
                maxLength: 8000
                description: Treść wiadomości
        attachments:
          type: array
          maxItems: 8
          description: Załączniki (max 8, max 10MB każdy)
          items:
            type: object
            required:
              - name
              - mimeType
              - kind
              - sizeBytes
              - contentBase64
            additionalProperties: false
            properties:
              id:
                type: string
                minLength: 1
                maxLength: 120
              name:
                type: string
                minLength: 1
                maxLength: 200
                description: Nazwa pliku
                example: raport.pdf
              mimeType:
                type: string
                minLength: 1
                maxLength: 120
                description: MIME type
                example: application/pdf
              kind:
                type: string
                enum:
                  - image
                  - file
                  - audio
                  - video
                description: Rodzaj załącznika
              sizeBytes:
                type: integer
                minimum: 1
                maximum: 10485760
                description: Rozmiar w bajtach (max 10MB)
              contentBase64:
                type: string
                minLength: 1
                maxLength: 12000000
                description: Zawartość zakodowana w base64
        settings:
          type: object
          additionalProperties: false
          description: Ustawienia AI
          properties:
            responseLanguage:
              type: string
              minLength: 1
              maxLength: 40
              example: pl
            responseStyle:
              type: string
              minLength: 1
              maxLength: 40
              example: concise
            notificationsEnabled:
              type: boolean
            privacyMode:
              type: string
              minLength: 1
              maxLength: 80
              example: standard
            historyRetentionDays:
              type: integer
              minimum: 1
              maximum: 365
              example: 30
            voiceEnabled:
              type: boolean
            videoEnabled:
              type: boolean
            microphoneEnabled:
              type: boolean
            cameraEnabled:
              type: boolean
            screenShareEnabled:
              type: boolean
            translationEnabled:
              type: boolean
    GuestRegisterBody:
      type: object
      required:
        - fullName
        - email
        - company
      additionalProperties: false
      properties:
        fullName:
          type: string
          minLength: 1
          maxLength: 200
          description: Imię i nazwisko
          example: Anna Wiśniewska
        email:
          type: string
          format: email
          maxLength: 320
          description: Adres email
          example: anna@firma.com
        company:
          type: string
          minLength: 1
          maxLength: 300
          description: Nazwa firmy
          example: Logistics Corp Sp. z o.o.
        source:
          type: string
          maxLength: 100
          default: trade_fair
          description: Źródło rejestracji
          example: trade_fair
    ComplaintCreateBody:
      type: object
      required:
        - orderId
        - description
      properties:
        orderId:
          type: string
          description: ID zamówienia (wymagane, niepuste po trim)
          example: ORD-12345
        description:
          type: string
          description: Opis reklamacji (wymagane, niepuste po trim)
          example: Paczka dotarła z widocznym uszkodzeniem
        category:
          type: string
          description: Kategoria reklamacji
          example: Uszkodzenie paczki
        cost:
          type: number
          description: Kwota reklamacji
          example: 450
        claimAmount:
          type: number
          description: Kwota roszczenia (alternatywa dla cost)
          example: 450
        claimNumber:
          type: string
          description: Numer roszczenia
          example: CLM-2026-001
        vendorComplaintNumber:
          type: string
          description: Numer reklamacji vendora
          example: VRK-001
    IncidentCreateBody:
      type: object
      properties:
        orderId:
          type: string
          description: ID zamówienia
          example: ORD-600
        orderNumber:
          type: string
          description: Numer zamówienia
          example: ZAM/2026/03/600
        title:
          type: string
          description: Tytuł incydentu
          example: Uszkodzony towar przy dostawie
        priority:
          type: string
          enum:
            - P0
            - P1
            - P2
            - P3
          description: Priorytet
          example: P2
        description:
          type: string
          description: Opis incydentu
          example: Klient zgłosił uszkodzony produkt
        category:
          type: string
          description: Kategoria
          example: Uszkodzenie
        expectation:
          type: string
          description: Oczekiwanie rozwiązania
          example: Wymiana produktu w ciągu 3 dni
        vendorName:
          type: string
          description: Nazwa vendora
          example: Firma Transportowa XYZ
        reporterName:
          type: string
          description: Nazwa zgłaszającego
          example: Jan Kowalski
    IncidentUpdateBody:
      type: object
      description: Partial update — wszystkie pola opcjonalne
      properties:
        title:
          type: string
        description:
          type: string
        expectation:
          type: string
        priority:
          type: string
          enum:
            - P0
            - P1
            - P2
            - P3
        status:
          type: string
          enum:
            - OPEN
            - IN_PROGRESS
            - RESOLVED
            - CLOSED
        orderId:
          type: string
        orderNumber:
          type: string
        vendorName:
          type: string
        reporterName:
          type: string
    CmsDataLineageSyncBody:
      type: object
      required:
        - rows
      properties:
        rows:
          type: array
          maxItems: 5000
          description: Wiersze danych (max 5000)
          items:
            type: object
            additionalProperties: true
        source:
          type: string
          minLength: 1
          maxLength: 120
          example: cms-admin
        updatedBy:
          type: string
          minLength: 1
          maxLength: 120
          example: admin@firma.pl
    CmsDataLineageVerifyBody:
      type: object
      required:
        - expectedKeys
      properties:
        expectedKeys:
          type: array
          minItems: 1
          maxItems: 5000
          items:
            type: string
            minLength: 1
            maxLength: 200
          description: Lista oczekiwanych kluczy
        moduleId:
          type: string
          minLength: 1
          maxLength: 120
    CmsSetActiveLangBody:
      type: object
      required:
        - lang
      properties:
        lang:
          type: string
          minLength: 2
          maxLength: 5
          example: pl
    CmsTextOverrideBody:
      type: object
      required:
        - override
      properties:
        override:
          type: object
          additionalProperties: true
          description: Dane nadpisania
    CmsModuleVisibilityBody:
      type: object
      required:
        - visible
      properties:
        visible:
          type: boolean
          description: Widoczność modułu
    CmsTranslationsSyncBody:
      type: object
      required:
        - rows
      properties:
        rows:
          type: array
          maxItems: 20000
          items:
            type: object
            additionalProperties: true
        updatedBy:
          type: string
          minLength: 1
          maxLength: 120
    CmsMigrateLocalBody:
      type: object
      required:
        - snapshot
      properties:
        snapshot:
          type: object
          additionalProperties: true
        updatedBy:
          type: string
          minLength: 1
          maxLength: 120
    CmsRuntimeResetBody:
      type: object
      properties:
        resetActiveLang:
          type: boolean
          default: true
    PolarisDocumentTypeBody:
      type: object
      required:
        - code
        - name
      properties:
        code:
          type: string
          description: Unikalny kod
          example: CONTRACT
        name:
          type: string
          description: Nazwa
          example: Umowa
        description:
          type: string
          example: Umowa partnerska
    CreatePartnerBody:
      type: object
      required:
        - name
      additionalProperties: false
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 300
          description: Nazwa partnera
          example: Logistics Partner Sp. z o.o.
        symbol30:
          type: string
          nullable: true
          maxLength: 30
          description: Symbol (max 30 zn.)
          example: LOGPART
        phone:
          type: string
          nullable: true
          example: '+48221234567'
        email:
          type: string
          nullable: true
          example: biuro@partner.pl
        taxId:
          type: string
          nullable: true
          description: NIP
          example: '1234567890'
        bankAccount:
          type: string
          nullable: true
          description: IBAN
          example: PL61109010140000071219812874
        contactPerson:
          type: string
          nullable: true
          example: Jan Kowalski
        paysVatTax:
          type: boolean
          nullable: true
        isActive:
          type: boolean
          nullable: true
          default: true
        invoiceAddress:
          type: string
          nullable: true
          example: ul. Logistyczna 15, 00-001 Warszawa
        paymodel:
          type: string
          nullable: true
          example: monthly
        hubs:
          type: string
          nullable: true
          description: Lista hubów CSV
          example: WAR-01,KRK-02
        tmsLink:
          type: string
          nullable: true
          example: https://tms.example.com/partner/123
        ftlFuelSurcharge:
          type: boolean
          nullable: true
        wysylkaRozliczen:
          type: boolean
          nullable: true
    UpdatePartnerBody:
      type: object
      additionalProperties: false
      description: Partial update — żadne pole nie jest wymagane
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 300
        symbol30:
          type: string
          nullable: true
          maxLength: 30
        phone:
          type: string
          nullable: true
        email:
          type: string
          nullable: true
        taxId:
          type: string
          nullable: true
        bankAccount:
          type: string
          nullable: true
        contactPerson:
          type: string
          nullable: true
        paysVatTax:
          type: boolean
          nullable: true
        isActive:
          type: boolean
          nullable: true
        invoiceAddress:
          type: string
          nullable: true
        paymodel:
          type: string
          nullable: true
        hubs:
          type: string
          nullable: true
        tmsLink:
          type: string
          nullable: true
        ftlFuelSurcharge:
          type: boolean
          nullable: true
        wysylkaRozliczen:
          type: boolean
          nullable: true
    CreateVendorBody:
      type: object
      required:
        - name
      additionalProperties: false
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 500
          example: Zadbano Logistics Sp. z o.o.
        displayName:
          type: string
          nullable: true
          example: Zadbano Logistics
        symbol:
          type: string
          nullable: true
          maxLength: 50
          example: ZADB
        symbolExternal:
          type: string
          nullable: true
          maxLength: 50
        shortVendorDesc:
          type: string
          nullable: true
          maxLength: 20
          example: ZadbanoLog
        taxIdNumber:
          type: string
          nullable: true
          maxLength: 30
          example: '1234567890'
        isActive:
          type: boolean
          nullable: true
        color:
          type: string
          nullable: true
          maxLength: 20
          example: '#FF6B00'
        email:
          type: string
          nullable: true
          maxLength: 500
          example: kontakt@zadbano.pl
        complaintsEmail:
          type: string
          nullable: true
          maxLength: 500
          example: reklamacje@zadbano.pl
        accountingEmail:
          type: string
          nullable: true
          maxLength: 400
          example: ksiegowosc@zadbano.pl
        mainPhoneNumber:
          type: string
          nullable: true
          maxLength: 20
          example: '+48221234567'
        emailDomain:
          type: string
          nullable: true
          maxLength: 100
          example: zadbano.pl
        invoiceAddress:
          type: object
          nullable: true
          additionalProperties: true
          description: Adres do faktur (JSON)
        financial:
          type: object
          nullable: true
          additionalProperties: true
          description: Dane finansowe (JSON)
        bankAccountIbanCod:
          type: string
          nullable: true
          maxLength: 200
          example: PL61109010140000071219812874
        salesmanId:
          type: integer
          nullable: true
          description: ID opiekuna handlowego
        vendorGroupId:
          type: integer
          nullable: true
        regionId:
          type: integer
          nullable: true
        tradeId:
          type: integer
          nullable: true
        segmentName:
          type: string
          nullable: true
          example: premium
        tradeLimit:
          type: number
          nullable: true
          example: 500000
        codSettlementPlusDays:
          type: integer
          nullable: true
          description: Dni do rozliczenia COD
          example: 7
        codName:
          type: string
          nullable: true
          maxLength: 300
        maxDaysToReturnToWarehouse:
          type: integer
          nullable: true
          example: 14
        daysToSettleInvoice:
          type: integer
          nullable: true
          example: 30
        defaultDensity:
          type: integer
          nullable: true
        prepaymentsEnabled:
          type: boolean
          nullable: true
        prepaymentsMinBalance:
          type: number
          nullable: true
          example: 10000
        apiRegistrationIsOn:
          type: boolean
          nullable: true
        warehousing:
          type: boolean
          nullable: true
        isInternalVendor:
          type: boolean
          nullable: true
        requiresBilling:
          type: boolean
          nullable: true
        billingContact:
          type: string
          nullable: true
        billingContactEmail:
          type: string
          nullable: true
        billingContactPhone:
          type: string
          nullable: true
        billingConfig:
          type: object
          nullable: true
          additionalProperties: true
          description: Konfiguracja billingowa (JSON)
        factoringActive:
          type: boolean
          nullable: true
        factoringName:
          type: string
          nullable: true
          maxLength: 200
        factoringAddress:
          type: string
          nullable: true
          maxLength: 200
        factoringTaxId:
          type: string
          nullable: true
          maxLength: 20
        factoringBankAccountNo:
          type: string
          nullable: true
          maxLength: 50
        factoringNote:
          type: string
          nullable: true
        activeFrom:
          type: string
          nullable: true
          format: date
          description: Data aktywacji (YYYY-MM-DD)
          example: '2025-01-01'
        invoiceAdditionalRemarks:
          type: string
          nullable: true
          maxLength: 100
        discountPolicyId:
          type: integer
          nullable: true
    CreatePersonBody:
      type: object
      properties:
        first_name:
          type: string
          nullable: true
          example: Marta
        last_name:
          type: string
          nullable: true
          example: Zielińska
        email:
          type: string
          nullable: true
          format: email
          description: Email (normalizowany automatycznie)
          example: marta@firma.pl
        phone:
          type: string
          nullable: true
          description: Telefon (normalizowany automatycznie)
          example: '+48500600700'
        phone_prefix:
          type: string
          nullable: true
          example: '+48'
        is_active:
          type: boolean
          default: true
        preferred_language:
          type: string
          nullable: true
          example: pl
        photo_url:
          type: string
          nullable: true
        tags:
          type: array
          items:
            type: string
          example:
            - partner
            - logistics
        metadata:
          type: object
          additionalProperties: true
          description: Dowolne metadane (JSON)
          example:
            source: manual
            region: mazowieckie
        profile:
          type: object
          additionalProperties: true
          description: Profil (JSON)
          example:
            department: Logistyka
    CreateRoleBody:
      type: object
      required:
        - symbol
        - name
      properties:
        symbol:
          type: string
          description: Unikalny symbol roli
          example: warehouse_manager
        name:
          type: string
          description: Nazwa wyświetlana
          example: Kierownik magazynu
        description:
          type: string
          example: Zarządzanie operacjami magazynowymi
        is_assignable:
          type: boolean
          example: true
        category:
          type: string
          example: operations
    CreateSubroleBody:
      type: object
      required:
        - symbol
        - parent_role
        - name
      properties:
        symbol:
          type: string
          example: partner_viewer
        parent_role:
          type: string
          description: Symbol roli nadrzędnej
          example: partner
        name:
          type: string
          example: Partner - tylko odczyt
        description:
          type: string
        is_admin:
          type: boolean
    CreatePermissionBody:
      type: object
      required:
        - permission
        - name
        - module
        - operation
      properties:
        permission:
          type: string
          description: Symbol uprawnienia
          example: reports.export
        name:
          type: string
          example: Eksport raportów
        module:
          type: string
          example: reports
        operation:
          type: string
          example: export
        description:
          type: string
          example: Uprawnienie do eksportu raportów
    PermissionOverridesBody:
      type: object
      required:
        - overrides
      properties:
        overrides:
          type: array
          items:
            type: object
            required:
              - permission
              - is_grant
            properties:
              permission:
                type: string
                description: Symbol uprawnienia
                example: reports.export
              is_grant:
                type: boolean
                description: true = nadanie, false = odebranie
              expires_at:
                type: string
                format: date-time
                description: Data wygaśnięcia (opcjonalna)
    NpsSurveyBody:
      type: object
      properties:
        score:
          type: integer
          minimum: 0
          maximum: 10
          description: Wynik NPS (0-10)
          example: 9
        areas:
          type: array
          items:
            type: string
          description: Obszary oceny
          example:
            - szybkość dostawy
            - obsługa klienta
        comment:
          type: string
          example: Bardzo dobra współpraca
        contactConsent:
          type: boolean
          description: Zgoda na kontakt
          example: true
    KeplerRescheduleBody:
      type: object
      properties:
        orderId:
          type: string
          example: ORD-AT-RISK-001
        newDeliveryDate:
          type: string
          format: date
          example: '2026-03-08'
        reason:
          type: string
          example: Klient niedostępny w pierwotnym terminie
    KeplerBatchRescheduleBody:
      type: object
      properties:
        orderIds:
          type: array
          items:
            type: string
          example:
            - ORD-001
            - ORD-002
        newDeliveryDate:
          type: string
          format: date
          example: '2026-03-08'
        reason:
          type: string
          example: Warunki pogodowe
    HubRecord:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
          nullable: true
        code:
          type: string
          nullable: true
        symbol:
          type: string
          nullable: true
        address:
          type: string
          nullable: true
        street:
          type: string
          nullable: true
        street_no:
          type: string
          nullable: true
        city:
          type: string
          nullable: true
        postal_code:
          type: string
          nullable: true
        country:
          type: string
          nullable: true
        latitude:
          type: number
          nullable: true
        longitude:
          type: number
          nullable: true
        location_wkt:
          type: string
          nullable: true
          example: POINT(21.0122 52.2297)
        location_type:
          type: string
          nullable: true
          enum:
            - POINT
        is_active:
          type: boolean
          nullable: true
        email:
          type: string
          nullable: true
        phone:
          type: string
          nullable: true
        manager_name:
          type: string
          nullable: true
        manager_email:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    DivisionRecord:
      type: object
      properties:
        id:
          type: string
        hub_id:
          type: string
          nullable: true
        name:
          type: string
          nullable: true
        code:
          type: string
          nullable: true
        symbol:
          type: string
          nullable: true
        address:
          type: string
          nullable: true
        street:
          type: string
          nullable: true
        street_no:
          type: string
          nullable: true
        city:
          type: string
          nullable: true
        postal_code:
          type: string
          nullable: true
        country:
          type: string
          nullable: true
        latitude:
          type: number
          nullable: true
        longitude:
          type: number
          nullable: true
        location_wkt:
          type: string
          nullable: true
          example: POINT(21.0122 52.2297)
        location_type:
          type: string
          nullable: true
          enum:
            - POINT
        position:
          type: integer
          nullable: true
        color:
          type: string
          nullable: true
        is_active:
          type: boolean
          nullable: true
        email:
          type: string
          nullable: true
        symbol_external:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
        hub_name:
          type: string
          nullable: true
        hub_code:
          type: string
          nullable: true
        hub_symbol:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    HubWithDivisionsRecord:
      allOf:
        - $ref: '#/components/schemas/HubRecord'
        - type: object
          properties:
            divisions:
              type: array
              items:
                $ref: '#/components/schemas/DivisionRecord'
    HubDivisionAutocompleteItem:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
          nullable: true
        code:
          type: string
          nullable: true
        symbol:
          type: string
          nullable: true
        is_active:
          type: boolean
          nullable: true
        type:
          type: string
          enum:
            - hub
            - division
        hub_id:
          type: string
          nullable: true
        address:
          type: string
          nullable: true
    HubListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/HubRecord'
        meta:
          $ref: '#/components/schemas/ListMeta'
    HubWithDivisionsListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/HubWithDivisionsRecord'
        meta:
          $ref: '#/components/schemas/ListMeta'
    DivisionListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/DivisionRecord'
        meta:
          $ref: '#/components/schemas/ListMeta'
    HubDetailResponse:
      type: object
      properties:
        data:
          $ref: '#/components/schemas/HubWithDivisionsRecord'
    DivisionDetailResponse:
      type: object
      properties:
        data:
          $ref: '#/components/schemas/DivisionRecord'
    HubWriteResponse:
      type: object
      properties:
        data:
          $ref: '#/components/schemas/HubRecord'
    DivisionWriteResponse:
      type: object
      properties:
        data:
          $ref: '#/components/schemas/DivisionRecord'
    HubDivisionAutocompleteResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/HubDivisionAutocompleteItem'
    CreateHubBody:
      type: object
      additionalProperties: false
      required:
        - name
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 500
        code:
          type: string
          nullable: true
          maxLength: 50
        symbol:
          type: string
          nullable: true
          maxLength: 50
        address:
          type: string
          nullable: true
        street:
          type: string
          nullable: true
          maxLength: 400
        streetNo:
          type: string
          nullable: true
          maxLength: 20
        city:
          type: string
          nullable: true
        postalCode:
          type: string
          nullable: true
          maxLength: 20
        country:
          type: string
          nullable: true
          maxLength: 10
        latitude:
          type: number
          nullable: true
        longitude:
          type: number
          nullable: true
        isActive:
          type: boolean
          nullable: true
        phone:
          type: string
          nullable: true
          maxLength: 30
        email:
          type: string
          nullable: true
          maxLength: 500
        managerName:
          type: string
          nullable: true
        managerEmail:
          type: string
          nullable: true
          maxLength: 500
        description:
          type: string
          nullable: true
    UpdateHubBody:
      type: object
      additionalProperties: false
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 500
        code:
          type: string
          nullable: true
          maxLength: 50
        symbol:
          type: string
          nullable: true
          maxLength: 50
        address:
          type: string
          nullable: true
        street:
          type: string
          nullable: true
          maxLength: 400
        streetNo:
          type: string
          nullable: true
          maxLength: 20
        city:
          type: string
          nullable: true
        postalCode:
          type: string
          nullable: true
          maxLength: 20
        country:
          type: string
          nullable: true
          maxLength: 10
        isActive:
          type: boolean
          nullable: true
        phone:
          type: string
          nullable: true
          maxLength: 30
        email:
          type: string
          nullable: true
          maxLength: 500
        managerName:
          type: string
          nullable: true
        managerEmail:
          type: string
          nullable: true
          maxLength: 500
        description:
          type: string
          nullable: true
    UpdateHubLocationBody:
      type: object
      additionalProperties: false
      required:
        - latitude
        - longitude
      properties:
        latitude:
          type: number
          minimum: -90
          maximum: 90
        longitude:
          type: number
          minimum: -180
          maximum: 180
        address:
          type: string
          nullable: true
        street:
          type: string
          nullable: true
          maxLength: 400
        streetNo:
          type: string
          nullable: true
          maxLength: 20
        city:
          type: string
          nullable: true
        postalCode:
          type: string
          nullable: true
          maxLength: 20
        country:
          type: string
          nullable: true
          maxLength: 10
    CreateDivisionBody:
      type: object
      additionalProperties: false
      required:
        - hubId
        - name
      properties:
        hubId:
          oneOf:
            - type: string
              minLength: 1
              maxLength: 40
            - type: integer
        name:
          type: string
          minLength: 1
          maxLength: 500
        code:
          type: string
          nullable: true
          maxLength: 50
        symbol:
          type: string
          nullable: true
          maxLength: 50
        address:
          type: string
          nullable: true
        street:
          type: string
          nullable: true
          maxLength: 400
        streetNo:
          type: string
          nullable: true
          maxLength: 20
        city:
          type: string
          nullable: true
        postalCode:
          type: string
          nullable: true
          maxLength: 20
        country:
          type: string
          nullable: true
          maxLength: 10
        latitude:
          type: number
          nullable: true
        longitude:
          type: number
          nullable: true
        position:
          type: integer
          nullable: true
        color:
          type: string
          nullable: true
          maxLength: 20
        email:
          type: string
          nullable: true
          maxLength: 500
        symbolExternal:
          type: string
          nullable: true
          maxLength: 200
        isActive:
          type: boolean
          nullable: true
        description:
          type: string
          nullable: true
    UpdateDivisionBody:
      type: object
      additionalProperties: false
      properties:
        hubId:
          nullable: true
          oneOf:
            - type: string
              minLength: 1
              maxLength: 40
            - type: integer
        name:
          type: string
          minLength: 1
          maxLength: 500
        code:
          type: string
          nullable: true
          maxLength: 50
        symbol:
          type: string
          nullable: true
          maxLength: 50
        address:
          type: string
          nullable: true
        street:
          type: string
          nullable: true
          maxLength: 400
        streetNo:
          type: string
          nullable: true
          maxLength: 20
        city:
          type: string
          nullable: true
        postalCode:
          type: string
          nullable: true
          maxLength: 20
        country:
          type: string
          nullable: true
          maxLength: 10
        latitude:
          type: number
          nullable: true
        longitude:
          type: number
          nullable: true
        position:
          type: integer
          nullable: true
        color:
          type: string
          nullable: true
          maxLength: 20
        email:
          type: string
          nullable: true
          maxLength: 500
        symbolExternal:
          type: string
          nullable: true
          maxLength: 200
        isActive:
          type: boolean
          nullable: true
        description:
          type: string
          nullable: true

