openapi: 3.0.3
info:
  title: calwebapp API v1 (Produktion)
  description: |
    REST API v1 fuer das calwebapp-Kalibriermanagementsystem (calServer-yii).

    Dies ist die produktive API v1 (Yii-basiert, Header-Auth, codierte Feldnamen).

    > **Hinweis:** API v2 (Laravel, JSON:API, Bearer-Auth) befindet sich in Entwicklung
    > fuer calServer 6.0 und ist als eigenstaendige Spezifikation unter
    > [openapi-v2.yaml](openapi-v2.yaml) verfuegbar (Beta).

    ## Overview
    - **Base URL:** `https://<domain>/api`
    - **Content Type (responses):** `application/json; charset=utf-8`
    - **Content Type (requests):** JSON, form-encoded, or multipart (see each endpoint)
    - **Encoding:** UTF-8

    ## Response Format
    All successful REST responses follow this structure:
    ```json
    {
      "success": true,
      "message": "Record(s) Found",
      "data": {
        "totalCount": 1,
        "<modelName>": [{ ... }]
      }
    }
    ```
    Error responses:
    ```json
    {
      "success": false,
      "message": "<error message>",
      "data": {
        "errorCode": 400,
        "message": "<error message>"
      }
    }
    ```

    ## Pagination, Sorting & Filtering
    All list endpoints support these query parameters:
    - `limit` (default 100)
    - `offset` (default 0)
    - `sort` — JSON array, e.g. `[{"property":"field","direction":"DESC"}]`
    - `filter` — JSON array, e.g. `[{"property":"field","value":"x","operator":"="}]`
    - `scenario` (default `restfullyii`)

    Nested filter syntax is also supported:
    ```json
    {
      "logic": "AND",
      "conditions": [
        {"property":"field1","value":"v1","operator":"="},
        {
          "filter": {
            "logic": "OR",
            "conditions": [
              {"property":"field2","value":"v2","operator":"="}
            ]
          }
        }
      ]
    }
    ```

    ## Permissions
    All API endpoints require `using_API`. Additional per-resource permissions:
    - **Inventory:** `inventory_view`, `inventory_edit`, `inventory_delete`
    - **Calibration:** `calibration_view`, `calibration_edit`, `calibration_delete`
    - **Customer:** `customers_view`, `customers_edit`, `customers_delete`
    - **Standard:** `inventory_details_calibrations_standards_view`
    - **Result:** `inventory_details_calibrations_measurements_edit`
    - **Location:** `location_edit`, `inventory_rental_edit`, `location_delete`, `inventory_rental_delete`
    - **Repair:** `repair_edit`, `inventory_repair_edit`, `repair_delete`, `inventory_repair_delete`

    ## Field Naming
    Many model properties use coded column names (e.g. `I4201`, `C2301`, `K4601`).
    Human-readable labels are configured via the application's translation system
    and may vary by language/tenant.

    User-defined fields added via the AdminPanel field configuration are stored as
    real database columns using the column name as entered (e.g. `I42_Test`).
    They can be used in `filter` and field-based lookups (`/get/{field}/{value}`)
    identically to built-in fields.
    Use `GET /api/{resource}/rules` to discover field metadata at runtime.
  version: 1.0.0
  contact:
    name: calwebapp Support
  license:
    name: Proprietary

servers:
  - url: https://{domain}
    description: Production server
    variables:
      domain:
        default: your-domain.com
        description: Your calwebapp domain
  - url: http://localhost
    description: Local development

security:
  - ApiKeyUsername: []
    ApiKeyPassword: []
    ApiKeyKey: []

tags:
  - name: auth
    description: Authentication endpoints
  - name: app
    description: Application information
  - name: inventory
    description: Inventory / asset management
  - name: calibration
    description: Calibration records
  - name: customer
    description: Customer management
  - name: standard
    description: Calibration standards (reference instruments)
  - name: result
    description: Calibration measurement results
  - name: location
    description: Location / rental tracking
  - name: repair
    description: Repair records
  - name: document
    description: Document management
  - name: report
    description: Report file management
  - name: role
    description: User roles (RBAC)
  - name: group
    description: User groups
  - name: user
    description: User management
  - name: changelog
    description: Application changelog

paths:
  # ──────────────────────────────────────────────
  # AUTH
  # ──────────────────────────────────────────────
  /api/auth/login:
    post:
      tags: [auth]
      summary: Authenticate user credentials
      description: |
        Authenticate with username and password to create a session.
        No API key headers required for this endpoint.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [username, password]
              properties:
                username:
                  type: string
                  description: User email address
                  example: user@example.com
                password:
                  type: string
                  format: password
                  description: User password
      responses:
        '200':
          description: Authentication successful
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: "Authenticated User: John Doe"
        '401':
          description: Authentication failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/auth:
    get:
      tags: [auth]
      summary: List auth records
      description: Standard REST list endpoint for auth resource.
      parameters:
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/sort'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/scenario'
      responses:
        '200':
          description: Records found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestListResponse'
    options:
      tags: [auth]
      summary: CORS preflight
      security: []
      responses:
        '200':
          description: CORS headers returned
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: string
            Access-Control-Allow-Methods:
              schema:
                type: string
                example: "GET, POST, PUT, DELETE, OPTIONS"

  /api/auth/rules:
    get:
      tags: [auth]
      summary: Get auth validation rules
      description: Returns validation rules for the auth model.
      responses:
        '200':
          description: Validation rules
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RulesResponse'

  # ──────────────────────────────────────────────
  # APP
  # ──────────────────────────────────────────────
  /api/app/version:
    get:
      tags: [app]
      summary: Get application changelog
      description: Returns changelog data. Use `version` query param to filter.
      parameters:
        - name: version
          in: query
          schema:
            type: string
            default: all
            enum: [all, latest]
          description: Version filter — `all` or `latest`
      responses:
        '200':
          description: Changelog data
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      version:
                        type: string
                      changes:
                        type: array
                        items:
                          type: string

  /api/app:
    get:
      tags: [app]
      summary: List app records
      parameters:
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/sort'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/scenario'
      responses:
        '200':
          description: Records found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestListResponse'
    options:
      tags: [app]
      summary: CORS preflight
      security: []
      responses:
        '200':
          description: CORS headers returned

  # ──────────────────────────────────────────────
  # INVENTORY
  # ──────────────────────────────────────────────
  /api/inventory:
    get:
      tags: [inventory]
      summary: List inventory items
      description: |
        Returns paginated list of inventory items.
        Requires `inventory_view` permission.
        Results are scoped to the authenticated user's assigned customers.
      parameters:
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/sort'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/scenario'
      responses:
        '200':
          description: Inventory records found
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/RestSuccessBase'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          totalCount:
                            type: integer
                          inventory:
                            type: array
                            items:
                              $ref: '#/components/schemas/Inventory'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags: [inventory]
      summary: Create an inventory item
      description: Requires `inventory_edit` permission.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Inventory'
      responses:
        '200':
          description: Record created
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/RestSuccessBase'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          totalCount:
                            type: integer
                            example: 1
                          inventory:
                            $ref: '#/components/schemas/Inventory'
        '400':
          $ref: '#/components/responses/BadRequest'
    options:
      tags: [inventory]
      summary: CORS preflight
      security: []
      responses:
        '200':
          description: CORS headers returned

  /api/inventory/{id}:
    get:
      tags: [inventory]
      summary: Get inventory item by ID
      description: Requires `inventory_view` permission.
      parameters:
        - $ref: '#/components/parameters/resourceId'
      responses:
        '200':
          description: Record found
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/RestSuccessBase'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          totalCount:
                            type: integer
                            example: 1
                          inventory:
                            $ref: '#/components/schemas/Inventory'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [inventory]
      summary: Update inventory item
      description: Requires `inventory_edit` permission.
      parameters:
        - $ref: '#/components/parameters/resourceId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Inventory'
      responses:
        '200':
          description: Record updated
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/RestSuccessBase'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          totalCount:
                            type: integer
                          inventory:
                            $ref: '#/components/schemas/Inventory'
        '400':
          $ref: '#/components/responses/BadRequest'
    delete:
      tags: [inventory]
      summary: Delete inventory item
      description: Requires `inventory_delete` permission.
      parameters:
        - $ref: '#/components/parameters/resourceId'
      responses:
        '200':
          description: Record deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteResponse'
        '404':
          $ref: '#/components/responses/NotFound'

  /api/inventory/rules:
    get:
      tags: [inventory]
      summary: Get inventory validation rules
      responses:
        '200':
          description: Validation rules
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RulesResponse'

  /api/inventory/get/{field}/{value}:
    get:
      tags: [inventory]
      summary: Find inventory by field value
      description: |
        Field-based lookup — find records matching a single field/value pair.
        Returns a `fields` map plus matched data.
      parameters:
        - name: field
          in: path
          required: true
          schema:
            type: string
          description: Field name (e.g. I4202)
        - name: value
          in: path
          required: true
          schema:
            type: string
          description: Value to search for
      responses:
        '200':
          description: Records found
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
                  fields:
                    type: object
                    additionalProperties:
                      type: string
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Inventory'

  /api/inventory/insert:
    post:
      tags: [inventory]
      summary: Custom insert inventory item
      description: Custom create endpoint using model attributes.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Inventory'
      responses:
        '200':
          description: Record created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'
        '400':
          $ref: '#/components/responses/BadRequest'

  /api/inventory/update:
    post:
      tags: [inventory]
      summary: Custom update inventory item
      description: Custom update using model attributes. Identify record by MTAG.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Inventory'
      responses:
        '200':
          description: Record updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'
        '400':
          $ref: '#/components/responses/BadRequest'

  # ──────────────────────────────────────────────
  # CALIBRATION
  # ──────────────────────────────────────────────
  /api/calibration:
    get:
      tags: [calibration]
      summary: List calibration records
      description: |
        Returns paginated list of calibrations.
        Requires `calibration_view` permission.
        Scoped to user's assigned customers via inventory ownership.
      parameters:
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/sort'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/scenario'
      responses:
        '200':
          description: Records found
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/RestSuccessBase'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          totalCount:
                            type: integer
                          calibration:
                            type: array
                            items:
                              $ref: '#/components/schemas/Calibration'
    post:
      tags: [calibration]
      summary: Create a calibration record
      description: Requires `calibration_edit` permission.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Calibration'
      responses:
        '200':
          description: Record created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'
        '400':
          $ref: '#/components/responses/BadRequest'
    options:
      tags: [calibration]
      summary: CORS preflight
      security: []
      responses:
        '200':
          description: CORS headers returned

  /api/calibration/{id}:
    get:
      tags: [calibration]
      summary: Get calibration by ID
      parameters:
        - $ref: '#/components/parameters/resourceId'
      responses:
        '200':
          description: Record found
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/RestSuccessBase'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          totalCount:
                            type: integer
                          calibration:
                            $ref: '#/components/schemas/Calibration'
    put:
      tags: [calibration]
      summary: Update calibration record
      parameters:
        - $ref: '#/components/parameters/resourceId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Calibration'
      responses:
        '200':
          description: Record updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'
    delete:
      tags: [calibration]
      summary: Delete calibration record
      description: Requires `calibration_delete` permission.
      parameters:
        - $ref: '#/components/parameters/resourceId'
      responses:
        '200':
          description: Record deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteResponse'

  /api/calibration/rules:
    get:
      tags: [calibration]
      summary: Get calibration validation rules
      responses:
        '200':
          description: Validation rules
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RulesResponse'

  /api/calibration/get/{field}/{value}:
    get:
      tags: [calibration]
      summary: Find calibrations by field value
      parameters:
        - name: field
          in: path
          required: true
          schema:
            type: string
        - name: value
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Records found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FieldLookupResponse'

  /api/calibration/insert:
    post:
      tags: [calibration]
      summary: Custom insert calibration
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Calibration'
      responses:
        '200':
          description: Record created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'

  /api/calibration/update:
    post:
      tags: [calibration]
      summary: Custom update calibration
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Calibration'
      responses:
        '200':
          description: Record updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'

  # ──────────────────────────────────────────────
  # CUSTOMER
  # ──────────────────────────────────────────────
  /api/customer:
    get:
      tags: [customer]
      summary: List customers
      description: Requires `customers_view` permission.
      parameters:
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/sort'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/scenario'
      responses:
        '200':
          description: Records found
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/RestSuccessBase'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          totalCount:
                            type: integer
                          customer:
                            type: array
                            items:
                              $ref: '#/components/schemas/Customer'
    post:
      tags: [customer]
      summary: Create a customer
      description: Requires `customers_edit` permission.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Customer'
      responses:
        '200':
          description: Record created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'
    options:
      tags: [customer]
      summary: CORS preflight
      security: []
      responses:
        '200':
          description: CORS headers returned

  /api/customer/{id}:
    get:
      tags: [customer]
      summary: Get customer by ID
      parameters:
        - $ref: '#/components/parameters/resourceId'
      responses:
        '200':
          description: Record found
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/RestSuccessBase'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          totalCount:
                            type: integer
                          customer:
                            $ref: '#/components/schemas/Customer'
    put:
      tags: [customer]
      summary: Update customer
      description: Requires `customers_edit` permission.
      parameters:
        - $ref: '#/components/parameters/resourceId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Customer'
      responses:
        '200':
          description: Record updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'
    delete:
      tags: [customer]
      summary: Delete customer
      description: Requires `customers_delete` permission.
      parameters:
        - $ref: '#/components/parameters/resourceId'
      responses:
        '200':
          description: Record deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteResponse'

  /api/customer/rules:
    get:
      tags: [customer]
      summary: Get customer validation rules
      responses:
        '200':
          description: Validation rules
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RulesResponse'

  /api/customer/get/{field}/{value}:
    get:
      tags: [customer]
      summary: Find customers by field value
      parameters:
        - name: field
          in: path
          required: true
          schema:
            type: string
        - name: value
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Records found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FieldLookupResponse'

  /api/customer/insert:
    post:
      tags: [customer]
      summary: Custom insert customer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Customer'
      responses:
        '200':
          description: Record created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'

  /api/customer/update:
    post:
      tags: [customer]
      summary: Custom update customer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Customer'
      responses:
        '200':
          description: Record updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'

  # ──────────────────────────────────────────────
  # STANDARD
  # ──────────────────────────────────────────────
  /api/standard:
    get:
      tags: [standard]
      summary: List standards
      description: |
        Returns calibration standards (reference instruments).
        Requires `inventory_details_calibrations_standards_view` permission.
      parameters:
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/sort'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/scenario'
      responses:
        '200':
          description: Records found
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/RestSuccessBase'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          totalCount:
                            type: integer
                          standard:
                            type: array
                            items:
                              $ref: '#/components/schemas/Standard'
    post:
      tags: [standard]
      summary: Create a standard
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Standard'
      responses:
        '200':
          description: Record created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'
    delete:
      tags: [standard]
      summary: Bulk delete standards
      description: |
        Delete standards by query attributes.
        At least one of `CTAG`, `MTAG`, or `C2430` must be provided.
      parameters:
        - name: CTAG
          in: query
          schema:
            type: string
          description: Calibration tag
        - name: MTAG
          in: query
          schema:
            type: string
          description: Inventory tag
        - name: C2430
          in: query
          schema:
            type: string
          description: Standard reference
      responses:
        '200':
          description: Records deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
                    example: "1 Record(s) Deleted"
        '400':
          $ref: '#/components/responses/BadRequest'
    options:
      tags: [standard]
      summary: CORS preflight
      security: []
      responses:
        '200':
          description: CORS headers returned

  /api/standard/{id}:
    get:
      tags: [standard]
      summary: Get standard by ID
      parameters:
        - $ref: '#/components/parameters/resourceId'
      responses:
        '200':
          description: Record found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'
    put:
      tags: [standard]
      summary: Update standard
      parameters:
        - $ref: '#/components/parameters/resourceId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Standard'
      responses:
        '200':
          description: Record updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'
    delete:
      tags: [standard]
      summary: Delete standard by ID
      parameters:
        - $ref: '#/components/parameters/resourceId'
      responses:
        '200':
          description: Record deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteResponse'

  /api/standard/rules:
    get:
      tags: [standard]
      summary: Get standard validation rules
      responses:
        '200':
          description: Validation rules
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RulesResponse'

  /api/standard/get/{field}/{value}:
    get:
      tags: [standard]
      summary: Find standards by field value
      parameters:
        - name: field
          in: path
          required: true
          schema:
            type: string
        - name: value
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Records found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FieldLookupResponse'

  /api/standard/insert:
    post:
      tags: [standard]
      summary: Custom insert standard
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Standard'
      responses:
        '200':
          description: Record created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'

  /api/standard/update:
    post:
      tags: [standard]
      summary: Custom update standard
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Standard'
      responses:
        '200':
          description: Record updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'

  # ──────────────────────────────────────────────
  # RESULT
  # ──────────────────────────────────────────────
  /api/result:
    get:
      tags: [result]
      summary: List measurement results
      description: |
        Returns calibration measurement results.
        Requires `inventory_details_calibrations_measurements_edit` permission.
      parameters:
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/sort'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/scenario'
      responses:
        '200':
          description: Records found
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/RestSuccessBase'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          totalCount:
                            type: integer
                          result:
                            type: array
                            items:
                              $ref: '#/components/schemas/Result'
    post:
      tags: [result]
      summary: Create a result record
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Result'
      responses:
        '200':
          description: Record created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'
    options:
      tags: [result]
      summary: CORS preflight
      security: []
      responses:
        '200':
          description: CORS headers returned

  /api/result/{id}:
    get:
      tags: [result]
      summary: Get result by ID
      parameters:
        - $ref: '#/components/parameters/resourceId'
      responses:
        '200':
          description: Record found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'
    put:
      tags: [result]
      summary: Update result
      parameters:
        - $ref: '#/components/parameters/resourceId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Result'
      responses:
        '200':
          description: Record updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'
    delete:
      tags: [result]
      summary: Delete result
      parameters:
        - $ref: '#/components/parameters/resourceId'
      responses:
        '200':
          description: Record deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteResponse'

  /api/result/rules:
    get:
      tags: [result]
      summary: Get result validation rules
      responses:
        '200':
          description: Validation rules
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RulesResponse'

  /api/result/get/{field}/{value}:
    get:
      tags: [result]
      summary: Find results by field value
      parameters:
        - name: field
          in: path
          required: true
          schema:
            type: string
        - name: value
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Records found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FieldLookupResponse'

  /api/result/update:
    post:
      tags: [result]
      summary: Bulk update results
      description: |
        Update multiple result rows for a calibration (CTAG) in one call.
        Provide the CTAG and an array of result rows to update.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [CTAG, Results]
              properties:
                CTAG:
                  type: string
                  description: Calibration tag identifying which calibration's results to update
                Results:
                  type: array
                  items:
                    type: object
                    properties:
                      row_num:
                        type: integer
                    additionalProperties:
                      type: string
            example:
              CTAG: "CTAG-1"
              Results:
                - row_num: 1
                  measurement: "1.234"
                  pass_fail: "pass"
      responses:
        '200':
          description: Records updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
                    example: "Set 1 Records Successfully."

  # ──────────────────────────────────────────────
  # LOCATION
  # ──────────────────────────────────────────────
  /api/location:
    get:
      tags: [location]
      summary: List locations
      description: Requires `location_edit` or `inventory_rental_edit` permission.
      parameters:
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/sort'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/scenario'
      responses:
        '200':
          description: Records found
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/RestSuccessBase'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          totalCount:
                            type: integer
                          location:
                            type: array
                            items:
                              $ref: '#/components/schemas/Location'
    post:
      tags: [location]
      summary: Create a location record
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Location'
      responses:
        '200':
          description: Record created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'
    options:
      tags: [location]
      summary: CORS preflight
      security: []
      responses:
        '200':
          description: CORS headers returned

  /api/location/{id}:
    get:
      tags: [location]
      summary: Get location by ID
      parameters:
        - $ref: '#/components/parameters/resourceId'
      responses:
        '200':
          description: Record found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'
    put:
      tags: [location]
      summary: Update location
      parameters:
        - $ref: '#/components/parameters/resourceId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Location'
      responses:
        '200':
          description: Record updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'
    delete:
      tags: [location]
      summary: Delete location
      description: Requires `location_delete` or `inventory_rental_delete` permission.
      parameters:
        - $ref: '#/components/parameters/resourceId'
      responses:
        '200':
          description: Record deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteResponse'

  /api/location/rules:
    get:
      tags: [location]
      summary: Get location validation rules
      responses:
        '200':
          description: Validation rules
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RulesResponse'

  /api/location/get/{field}/{value}:
    get:
      tags: [location]
      summary: Find locations by field value
      parameters:
        - name: field
          in: path
          required: true
          schema:
            type: string
        - name: value
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Records found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FieldLookupResponse'

  /api/location/insert:
    post:
      tags: [location]
      summary: Custom insert location
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Location'
      responses:
        '200':
          description: Record created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'

  /api/location/update:
    post:
      tags: [location]
      summary: Custom update location
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Location'
      responses:
        '200':
          description: Record updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'

  # ──────────────────────────────────────────────
  # REPAIR
  # ──────────────────────────────────────────────
  /api/repair:
    get:
      tags: [repair]
      summary: List repair records
      description: Requires `repair_edit` or `inventory_repair_edit` permission.
      parameters:
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/sort'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/scenario'
      responses:
        '200':
          description: Records found
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/RestSuccessBase'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          totalCount:
                            type: integer
                          repair:
                            type: array
                            items:
                              $ref: '#/components/schemas/Repair'
    post:
      tags: [repair]
      summary: Create a repair record
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Repair'
      responses:
        '200':
          description: Record created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'
    options:
      tags: [repair]
      summary: CORS preflight
      security: []
      responses:
        '200':
          description: CORS headers returned

  /api/repair/{id}:
    get:
      tags: [repair]
      summary: Get repair by ID
      parameters:
        - $ref: '#/components/parameters/resourceId'
      responses:
        '200':
          description: Record found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'
    put:
      tags: [repair]
      summary: Update repair record
      parameters:
        - $ref: '#/components/parameters/resourceId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Repair'
      responses:
        '200':
          description: Record updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'
    delete:
      tags: [repair]
      summary: Delete repair record
      description: Requires `repair_delete` or `inventory_repair_delete` permission.
      parameters:
        - $ref: '#/components/parameters/resourceId'
      responses:
        '200':
          description: Record deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteResponse'

  /api/repair/rules:
    get:
      tags: [repair]
      summary: Get repair validation rules
      responses:
        '200':
          description: Validation rules
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RulesResponse'

  /api/repair/get/{field}/{value}:
    get:
      tags: [repair]
      summary: Find repairs by field value
      parameters:
        - name: field
          in: path
          required: true
          schema:
            type: string
        - name: value
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Records found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FieldLookupResponse'

  /api/repair/insert:
    post:
      tags: [repair]
      summary: Custom insert repair
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Repair'
      responses:
        '200':
          description: Record created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'

  /api/repair/update:
    post:
      tags: [repair]
      summary: Custom update repair
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Repair'
      responses:
        '200':
          description: Record updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'

  # ──────────────────────────────────────────────
  # DOCUMENT
  # ──────────────────────────────────────────────
  /api/document:
    get:
      tags: [document]
      summary: List documents
      parameters:
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/sort'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/scenario'
      responses:
        '200':
          description: Records found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestListResponse'
    options:
      tags: [document]
      summary: CORS preflight
      security: []
      responses:
        '200':
          description: CORS headers returned

  /api/document/{id}:
    get:
      tags: [document]
      summary: Download document
      description: |
        Downloads a document file as binary stream.
        Returns JSON error if file does not exist.
      parameters:
        - $ref: '#/components/parameters/resourceId'
      responses:
        '200':
          description: File download
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '404':
          description: File not found
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: false
                  message:
                    type: string
                    example: "File doesn't exist"

  # ──────────────────────────────────────────────
  # REPORT
  # ──────────────────────────────────────────────
  /api/report:
    get:
      tags: [report]
      summary: List reports
      parameters:
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/sort'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/scenario'
      responses:
        '200':
          description: Records found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestListResponse'
    options:
      tags: [report]
      summary: CORS preflight
      security: []
      responses:
        '200':
          description: CORS headers returned

  /api/report/{id}:
    get:
      tags: [report]
      summary: Download report file
      description: Downloads a report as binary stream.
      parameters:
        - $ref: '#/components/parameters/resourceId'
      responses:
        '200':
          description: File download
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '404':
          description: Report not found
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: false
                  message:
                    type: string
                    example: "Report File doesn't exist"
    post:
      tags: [report]
      summary: Upload report file
      description: Upload a report file via multipart form data.
      parameters:
        - $ref: '#/components/parameters/resourceId'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
      responses:
        '200':
          description: Upload successful
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'

  # ──────────────────────────────────────────────
  # ROLE
  # ──────────────────────────────────────────────
  /api/role:
    get:
      tags: [role]
      summary: List roles
      parameters:
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/sort'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/scenario'
      responses:
        '200':
          description: Records found
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/RestSuccessBase'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          totalCount:
                            type: integer
                          role:
                            type: array
                            items:
                              $ref: '#/components/schemas/Role'
    options:
      tags: [role]
      summary: CORS preflight
      security: []
      responses:
        '200':
          description: CORS headers returned

  /api/role/{id}:
    get:
      tags: [role]
      summary: Get role by name
      parameters:
        - $ref: '#/components/parameters/resourceId'
      responses:
        '200':
          description: Record found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'

  /api/role/rules:
    get:
      tags: [role]
      summary: Get role validation rules
      responses:
        '200':
          description: Validation rules
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RulesResponse'

  # ──────────────────────────────────────────────
  # GROUP
  # ──────────────────────────────────────────────
  /api/group:
    get:
      tags: [group]
      summary: List groups
      parameters:
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/sort'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/scenario'
      responses:
        '200':
          description: Records found
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/RestSuccessBase'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          totalCount:
                            type: integer
                          group:
                            type: array
                            items:
                              $ref: '#/components/schemas/Group'
    options:
      tags: [group]
      summary: CORS preflight
      security: []
      responses:
        '200':
          description: CORS headers returned

  /api/group/{id}:
    get:
      tags: [group]
      summary: Get group by ID
      parameters:
        - $ref: '#/components/parameters/resourceId'
      responses:
        '200':
          description: Record found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'

  /api/group/rules:
    get:
      tags: [group]
      summary: Get group validation rules
      responses:
        '200':
          description: Validation rules
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RulesResponse'

  # ──────────────────────────────────────────────
  # USER
  # ──────────────────────────────────────────────
  /api/user:
    get:
      tags: [user]
      summary: List users
      parameters:
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/sort'
        - $ref: '#/components/parameters/filter'
        - $ref: '#/components/parameters/scenario'
      responses:
        '200':
          description: Records found
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/RestSuccessBase'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          totalCount:
                            type: integer
                          user:
                            type: array
                            items:
                              $ref: '#/components/schemas/User'
    post:
      tags: [user]
      summary: Create a user
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/User'
      responses:
        '200':
          description: Record created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'
    options:
      tags: [user]
      summary: CORS preflight
      security: []
      responses:
        '200':
          description: CORS headers returned

  /api/user/{id}:
    get:
      tags: [user]
      summary: Get user by ID
      parameters:
        - $ref: '#/components/parameters/resourceId'
      responses:
        '200':
          description: Record found
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/RestSuccessBase'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          totalCount:
                            type: integer
                          user:
                            $ref: '#/components/schemas/User'
    put:
      tags: [user]
      summary: Update user
      parameters:
        - $ref: '#/components/parameters/resourceId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/User'
      responses:
        '200':
          description: Record updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RestSuccessBase'
    delete:
      tags: [user]
      summary: Delete user
      parameters:
        - $ref: '#/components/parameters/resourceId'
      responses:
        '200':
          description: Record deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteResponse'

  /api/user/rules:
    get:
      tags: [user]
      summary: Get user validation rules
      responses:
        '200':
          description: Validation rules
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RulesResponse'

  /api/user/{id}/roles:
    post:
      tags: [user]
      summary: Assign roles to user
      description: Assign one or more roles to a user by user ID.
      parameters:
        - $ref: '#/components/parameters/resourceId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                role:
                  oneOf:
                    - type: string
                      description: Single role name
                    - type: array
                      items:
                        type: string
                      description: Array of role names
            examples:
              single:
                summary: Single role
                value:
                  role: Admin
              multiple:
                summary: Multiple roles
                value:
                  role: ["Admin", "Operator"]
      responses:
        '200':
          description: Role(s) assigned
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
                    example: "Role Assigned To User"

  /api/user/{id}/groups:
    post:
      tags: [user]
      summary: Assign groups to user
      description: Assign one or more groups to a user by user ID.
      parameters:
        - $ref: '#/components/parameters/resourceId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                group:
                  oneOf:
                    - type: string
                    - type: array
                      items:
                        type: string
      responses:
        '200':
          description: Group(s) assigned
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
                    example: "Group Assigned To User"

  /api/user/{id}/roles/{role}:
    delete:
      tags: [user]
      summary: Remove role from user
      parameters:
        - $ref: '#/components/parameters/resourceId'
        - name: role
          in: path
          required: true
          schema:
            type: string
          description: Role name to remove
      responses:
        '200':
          description: Role removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
                    example: "Role Removed from User"

  /api/user/{id}/groups/{group}:
    delete:
      tags: [user]
      summary: Remove group from user
      parameters:
        - $ref: '#/components/parameters/resourceId'
        - name: group
          in: path
          required: true
          schema:
            type: string
          description: Group name to remove
      responses:
        '200':
          description: Group removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
                    example: "Group Removed from User"

  # ──────────────────────────────────────────────
  # CHANGELOG (adminpanel)
  # ──────────────────────────────────────────────
  /adminpanel/adminInformation/changeLogApi:
    get:
      tags: [changelog]
      summary: Get full changelog
      description: Returns changelog for all versions. Requires session authentication.
      security:
        - SessionAuth: []
      responses:
        '200':
          description: Changelog data
          content:
            application/json:
              schema:
                type: object
                properties:
                  version:
                    type: string
                  changes:
                    type: array
                    items:
                      type: string

  /adminpanel/adminInformation/changeLogApi/version/latest:
    get:
      tags: [changelog]
      summary: Get latest version changelog
      security:
        - SessionAuth: []
      responses:
        '200':
          description: Latest version changelog
          content:
            application/json:
              schema:
                type: object
                properties:
                  version:
                    type: string
                  changes:
                    type: array
                    items:
                      type: string

  /adminpanel/adminInformation/changeLogApi/version/{version}:
    get:
      tags: [changelog]
      summary: Get changelog for specific version
      security:
        - SessionAuth: []
      parameters:
        - name: version
          in: path
          required: true
          schema:
            type: string
          description: Version number (e.g. 4.0.0)
          example: "4.0.0"
      responses:
        '200':
          description: Version-specific changelog
          content:
            application/json:
              schema:
                type: object
                properties:
                  version:
                    type: string
                  changes:
                    type: array
                    items:
                      type: string

components:
  # ──────────────────────────────────────────────
  # SECURITY SCHEMES
  # ──────────────────────────────────────────────
  securitySchemes:
    ApiKeyUsername:
      type: apiKey
      in: header
      name: X_REST_USERNAME
      description: API username (typically the user's email)
    ApiKeyPassword:
      type: apiKey
      in: header
      name: X_REST_PASSWORD
      description: API password
    ApiKeyKey:
      type: apiKey
      in: header
      name: X_REST_API_KEY
      description: API key assigned to the user
    SessionAuth:
      type: apiKey
      in: cookie
      name: PHPSESSID
      description: Session-based authentication via browser cookie

  # ──────────────────────────────────────────────
  # PARAMETERS
  # ──────────────────────────────────────────────
  parameters:
    resourceId:
      name: id
      in: path
      required: true
      schema:
        type: string
      description: Resource identifier (UUID or numeric ID depending on resource)
    limit:
      name: limit
      in: query
      schema:
        type: integer
        default: 100
        minimum: 1
      description: Maximum number of records to return
    offset:
      name: offset
      in: query
      schema:
        type: integer
        default: 0
        minimum: 0
      description: Number of records to skip
    sort:
      name: sort
      in: query
      schema:
        type: string
      description: |
        JSON array of sort directives.
        Example: `[{"property":"I4201","direction":"ASC"}]`
    filter:
      name: filter
      in: query
      schema:
        type: string
      description: |
        JSON array of filter conditions or nested filter object.

        **Simple filter:**
        `[{"property":"I4202","value":"FLUKE","operator":"="}]`

        **User-defined field filter:**
        Fields added via the AdminPanel field configuration (e.g. `I42_Test`) are real
        database columns and can be used in filters identically to standard fields:
        `[{"property":"I42_Test","value":"Yes","operator":"="}]`

        Without an explicit `operator` the API defaults to `LIKE` (partial match).
        Always specify `"operator": "="` for exact matches.

        **Nested filter with AND/OR logic:**
        ```json
        {
          "logic": "AND",
          "conditions": [
            {"property": "C2339", "value": "1", "operator": "="},
            {
              "filter": {
                "logic": "OR",
                "conditions": [
                  {"property": "inventory.I4209", "value": "B", "operator": "="},
                  {"property": "inventory.I4209", "value": "H", "operator": "="}
                ]
              }
            }
          ]
        }
        ```

        **Filtering on related fields (dot notation):**
        Related model fields can be filtered using dot notation (`relation.field`).
        The API automatically joins the related table.
        ```json
        [{"property": "categories.name", "value": "Messgeraet", "operator": "like"}]
        ```

        For inventory categories specifically, the shorthand `categoryname` is also
        supported — the API resolves matching categories and translates the filter
        into an MTAG-based condition internally:
        ```json
        [{"property": "categoryname", "value": "Messgeraet", "operator": "like"}]
        ```

        Supported operators: `=`, `!=`, `<>`, `<`, `<=`, `>`, `>=`, `like`, `in`, `not in`.
      examples:
        simple:
          summary: Simple filter (exact match)
          value: '[{"property":"I4202","value":"FLUKE","operator":"="}]'
        like:
          summary: Partial match (LIKE)
          value: '[{"property":"I4202","value":"FLUKE","operator":"like"}]'
        categoryDot:
          summary: Related field — dot notation (categories.name)
          value: '[{"property":"categories.name","value":"Messgeraet","operator":"like"}]'
        categoryShorthand:
          summary: Category shorthand (categoryname)
          value: '[{"property":"categoryname","value":"Messgeraet","operator":"like"}]'
        nestedAndOr:
          summary: Nested AND/OR logic
          value: '{"logic":"AND","conditions":[{"property":"C2339","value":"1","operator":"="},{"filter":{"logic":"OR","conditions":[{"property":"inventory.I4209","value":"B","operator":"="},{"property":"inventory.I4209","value":"H","operator":"="}]}}]}'
        userDefinedField:
          summary: User-defined field
          value: '[{"property":"I42_Test","value":"Yes","operator":"="}]'
    scenario:
      name: scenario
      in: query
      schema:
        type: string
        default: restfullyii
      description: Model scenario for validation rules

  # ──────────────────────────────────────────────
  # RESPONSES
  # ──────────────────────────────────────────────
  responses:
    BadRequest:
      description: Bad request — invalid parameters or validation failure
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Forbidden:
      description: Forbidden — missing required permissions
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Unauthorized:
      description: Unauthorized — fehlende oder ungültige Authentifizierung
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

  # ──────────────────────────────────────────────
  # SCHEMAS
  # ──────────────────────────────────────────────
  schemas:
    # ── Response wrappers ──────────────────────
    RestSuccessBase:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message:
          type: string
          example: "Record(s) Found"

    RestListResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message:
          type: string
          example: "Record(s) Found"
        data:
          type: object
          properties:
            totalCount:
              type: integer
          additionalProperties: true

    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          example: false
        message:
          type: string
          example: "Error message"
        data:
          type: object
          properties:
            errorCode:
              type: integer
              example: 400
            message:
              type: string

    DeleteResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message:
          type: string
          example: "Record Deleted"
        data:
          type: object
          properties:
            totalCount:
              type: integer
              example: 1

    RulesResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        rules:
          type: array
          items:
            type: object
            properties:
              field:
                type: string
              required:
                type: boolean
            additionalProperties: true

    FieldLookupResponse:
      type: object
      properties:
        success:
          type: boolean
        message:
          type: string
        fields:
          type: object
          additionalProperties:
            type: string
        data:
          type: array
          items:
            type: object
            additionalProperties: true

    # ── Inventory ──────────────────────────────
    # Table: inventory | PK: MTAG (UUID, max 80)
    # Column names are coded identifiers; human-readable labels
    # are configured via the translation system and may vary.
    # Use GET /api/inventory/rules to discover field metadata.
    Inventory:
      type: object
      description: |
        Inventory / asset record. Column names use coded identifiers
        (I4201, I4202, etc.). Use `GET /api/inventory/rules` for field metadata.
      properties:
        MTAG:
          type: string
          maxLength: 80
          description: Primary key — UUID auto-generated on insert
          example: "ea79b775-8b4b-8e17-98c9-b33e7065ff6f"
        I4201:
          type: string
          maxLength: 60
          description: Asset number (required)
          example: "Test-API-Asset"
        I4202:
          type: string
          maxLength: 30
          description: Manufacturer
          example: "FLUKE"
        I4203:
          type: string
          maxLength: 30
          description: Model / type designation
          example: "179"
        I4204:
          type: string
          maxLength: 60
          description: Description
          example: "Digital Multimeter"
        I4205:
          type: string
          maxLength: 1
        I4206:
          type: string
          maxLength: 30
          description: Serial number
          example: "1234567890"
        I4207:
          type: string
          maxLength: 24
        I4208:
          type: string
          maxLength: 29
        I4209:
          type: string
          maxLength: 1
          description: Status code
          example: "B"
        I4210:
          type: string
          maxLength: 12
        I4211:
          type: string
          maxLength: 15
          description: Location / group
          example: "CALSERVICE"
        I4212:
          type: string
        I4213:
          type: string
          maxLength: 10
        I4214:
          type: string
          maxLength: 8
        I4215:
          type: string
          maxLength: 5
        I4216:
          type: string
          maxLength: 5
        I4217:
          type: string
          maxLength: 60
        I4218:
          type: string
          maxLength: 4
        I4219:
          type: string
          maxLength: 12
        I4220:
          type: integer
        I4221:
          type: string
        I4222:
          type: string
          maxLength: 12
        I4223:
          type: string
          maxLength: 16
        I4224:
          type: string
        I4225:
          type: string
          maxLength: 8
        I4226:
          type: string
          maxLength: 1
        I4228:
          type: string
          maxLength: 1
          description: Calibration interval unit (M=months, W=weeks, D=days)
          example: "M"
        I4229:
          type: integer
          description: Calibration interval value
          example: 12
        I4230:
          type: integer
        I4231:
          type: string
          maxLength: 32
        I4232:
          type: string
          maxLength: 32
        I4233:
          type: string
          maxLength: 32
        I4234:
          type: string
          maxLength: 32
        I4235:
          type: string
          maxLength: 32
        I4236:
          type: string
        I4237:
          type: string
          maxLength: 16
        I4238:
          type: string
        I4239:
          type: string
          maxLength: 16
        I4240:
          type: string
          maxLength: 8
        I4299:
          type: string
          maxLength: 1
        I4241:
          type: string
          maxLength: 80
        I4242:
          type: string
          maxLength: 80
        I4243:
          type: string
          maxLength: 80
        idsrc:
          type: string
          maxLength: 3
        IPUBLISH:
          type: string
          maxLength: 32
        ktag:
          type: string
          maxLength: 80
          description: Customer tag (FK to customers.KTAG)
        replic:
          type: string
          maxLength: 1
        I4258:
          type: string
          maxLength: 1
        I4244:
          type: string
          maxLength: 48
        I4245:
          type: string
          maxLength: 48
        I4246:
          type: string
          maxLength: 48
        I4247:
          type: string
          maxLength: 48
        I4248:
          type: string
          maxLength: 48
        I4249:
          type: string
          maxLength: 48
        I4250:
          type: string
        I4251:
          type: string
        I4252:
          type: string
        I4253:
          type: string
        I4254:
          type: string
          maxLength: 12
        I4255:
          type: string
          maxLength: 12
        I4256:
          type: string
          maxLength: 12
        I4257:
          type: string
          maxLength: 12
        I4259:
          type: integer
        I4260:
          type: string
        I4261:
          type: string
          maxLength: 128
        I4262:
          type: string
          maxLength: 128
        categories:
          type: array
          description: |
            **GET:** Zugeordnete Kategorien (type=inventory) als Objektliste.
            **POST/PUT:** Array von category_uIDs (UUIDs). Replace-Semantik —
            fehlt das Feld im PUT, bleiben bestehende Zuordnungen unverändert.
            Leeres Array `[]` entfernt alle Zuordnungen.
          items:
            $ref: '#/components/schemas/CategoryItem'
      required:
        - I4201
      additionalProperties: true

    # ── CategoryItem ────────────────────────────
    CategoryItem:
      type: object
      description: Kategorie, die einem Inventarobjekt zugeordnet ist
      properties:
        uID:
          type: string
          format: uuid
          example: "3fa85f64-5717-4562-b3fc-2c963f66afa6"
        name:
          type: string
          example: "Messgerät"
        short_name:
          type: string
          nullable: true
        type:
          type: string
          enum: [inventory, booking, repair, calibration]
        color:
          type: string
          nullable: true
          example: "#FF5733"
        text_color:
          type: string
          nullable: true
        sort_num:
          type: integer
          nullable: true
        rentable:
          type: integer
          nullable: true

    # ── Calibration ────────────────────────────
    # Table: calibration | PK: CTAG (UUID, max 80)
    # Required: C2301, MTAG, CTAG
    Calibration:
      type: object
      description: |
        Calibration record. Links to inventory via MTAG.
        Column names use coded identifiers (C2301, C2302, etc.).
      properties:
        CTAG:
          type: string
          maxLength: 80
          description: Primary key — UUID auto-generated on insert
          example: "ba2bdcdc-391a-24d3-c1de-0501619668c4"
        C2301:
          type: string
          description: Calibration date (required)
          example: "2044-03-05"
        C2302:
          type: integer
        C2303:
          type: string
          description: Due date
          example: "2017-02-15"
        C2304:
          type: integer
        C2306:
          type: string
        C2307:
          type: string
          maxLength: 20
          description: Technician / operator
          example: "oly"
        C2308:
          type: string
          maxLength: 10
        C2309:
          type: string
          maxLength: 12
        C2310:
          type: string
          maxLength: 5
        C2311:
          type: string
          maxLength: 6
        C2312:
          type: string
          maxLength: 6
        C2313:
          type: string
          maxLength: 15
        C2314:
          type: string
          maxLength: 16
        C2315:
          type: string
          maxLength: 5
        C2316:
          type: string
        C2317:
          type: string
          maxLength: 5
        C2318:
          type: string
          maxLength: 13
        C2319:
          type: string
          maxLength: 1
        C2320:
          type: string
          maxLength: 128
        C2321:
          type: string
          maxLength: 55
        C2322:
          type: string
          maxLength: 1
        C2323:
          type: string
          maxLength: 1
        C2324:
          type: string
          maxLength: 5
        C2325:
          type: string
          maxLength: 1
        C2326:
          type: string
          maxLength: 44
        C2327:
          type: string
          maxLength: 20
        C2328:
          type: string
          maxLength: 5
        C2329:
          type: string
          maxLength: 44
        C2330:
          type: string
          maxLength: 10
        C2331:
          type: string
          maxLength: 1
        C2332:
          type: integer
        C2333:
          type: string
          maxLength: 8
          description: Time of calibration
          example: "12:30:00"
        C2334:
          type: string
          maxLength: 8
        C2335:
          type: string
        C2336:
          type: string
          maxLength: 16
        C2337:
          type: string
        C2338:
          type: string
          maxLength: 16
        C2339:
          type: integer
          description: Active calibration flag (1 = active)
          example: 1
        C2341:
          type: string
          maxLength: 254
        MTAG:
          type: string
          maxLength: 80
          description: Inventory tag (FK to inventory.MTAG, required)
        C2342:
          type: string
        C2343:
          type: string
          maxLength: 8
        C2344:
          type: integer
        C2345:
          type: integer
        C2346:
          type: integer
        C2347:
          type: integer
        C2350:
          type: integer
        C2351:
          type: integer
        C2352:
          type: integer
        C2353:
          type: integer
        C2354:
          type: string
          maxLength: 64
        C2355:
          type: string
          maxLength: 64
        C2356:
          type: string
          maxLength: 64
        C2357:
          type: string
          maxLength: 64
        C2358:
          type: string
          maxLength: 64
        C2359:
          type: string
          maxLength: 64
        C2360:
          type: string
          maxLength: 64
        C2361:
          type: string
          maxLength: 64
        C2362:
          type: integer
        C2363:
          type: string
          maxLength: 1
        C2364:
          type: string
          maxLength: 48
        C2365:
          type: string
          maxLength: 48
        C2366:
          type: string
          maxLength: 48
        C2367:
          type: string
          maxLength: 48
        C2368:
          type: string
          maxLength: 48
        C2369:
          type: string
          maxLength: 48
        C2370:
          type: string
          maxLength: 48
        C2371:
          type: string
        C2372:
          type: string
        C2373:
          type: string
        C2374:
          type: string
        C2375:
          type: string
          maxLength: 12
        C2376:
          type: string
          maxLength: 12
        C2377:
          type: string
          maxLength: 12
        C2378:
          type: string
          maxLength: 12
        C2379:
          type: string
          maxLength: 12
        C2380:
          type: string
        C2381:
          type: string
          maxLength: 8
        C2382:
          type: string
          maxLength: 1
        C2383:
          type: integer
        C2384:
          type: string
        C2385:
          type: string
          maxLength: 128
        C2386:
          type: string
          maxLength: 5
        C2387:
          type: string
          maxLength: 5
        C2388:
          type: string
          maxLength: 9
        C2389:
          type: string
          maxLength: 7
        C2390:
          type: string
          maxLength: 9
        C2391:
          type: string
          maxLength: 7
        C2392:
          type: string
          maxLength: 2
        C2393:
          type: string
          maxLength: 1
        C2394:
          type: string
          maxLength: 1
        C2395:
          type: string
          maxLength: 10
        C2396:
          type: string
          maxLength: 80
        cdsrc:
          type: string
          maxLength: 3
        CPUBLISH:
          type: string
          maxLength: 32
        combine:
          type: string
          maxLength: 3
      required:
        - C2301
        - MTAG
      additionalProperties: true

    # ── Customer ───────────────────────────────
    # Table: customers | PK: KTAG (UUID, max 80)
    Customer:
      type: object
      description: |
        Customer record. Column names use coded identifiers (K4601, K4602, etc.).
      properties:
        KTAG:
          type: string
          maxLength: 80
          description: Primary key — UUID auto-generated on insert
          example: "45f09720-082f-5c74-9cfa-a605e2863904"
        KPUBLISH:
          type: string
          maxLength: 32
        K4601:
          type: string
          maxLength: 48
          description: Customer number
          example: "0013"
        K4602:
          type: string
          maxLength: 48
          description: Customer name (required)
          example: "Friedrich"
        K4603:
          type: string
          maxLength: 48
          description: Street address
          example: "Friedrich-Ebert-Strasse 699"
        K4604:
          type: string
          maxLength: 48
          description: City
          example: "Bergisch Gladbach"
        K4605:
          type: string
          maxLength: 48
        K4606:
          type: string
          maxLength: 48
        K4607:
          type: string
          maxLength: 48
        K4608:
          type: string
          maxLength: 48
        K4609:
          type: string
          maxLength: 48
        K4610:
          type: string
          maxLength: 48
        K4611:
          type: string
          maxLength: 48
        K4612:
          type: string
          maxLength: 48
        K4613:
          type: string
          maxLength: 48
        K4614:
          type: string
          maxLength: 48
        K4615:
          type: string
          maxLength: 48
        K4616:
          type: string
          maxLength: 48
        K4617:
          type: string
          maxLength: 48
        K4618:
          type: string
          maxLength: 48
        K4619:
          type: string
          maxLength: 48
        K4620:
          type: string
          maxLength: 48
        K4621:
          type: string
        K4622:
          type: string
        K4623:
          type: string
        K4624:
          type: string
        K4625:
          type: string
          maxLength: 12
        K4626:
          type: string
          maxLength: 12
        K4627:
          type: string
          maxLength: 12
        K4628:
          type: string
          maxLength: 12
        K4640:
          type: string
          maxLength: 8
        K4629:
          type: string
        K4630:
          type: string
          maxLength: 16
        K4631:
          type: string
        K4632:
          type: string
          maxLength: 16
        KDSRC:
          type: string
          maxLength: 3
        kreplic:
          type: string
          maxLength: 1
        K4633:
          type: string
          maxLength: 40
        K4634:
          type: string
          maxLength: 30
        K4635:
          type: string
          maxLength: 20
        K4636:
          type: string
          maxLength: 20
        K4637:
          type: string
          maxLength: 60
      required:
        - K4602
      additionalProperties: true

    # ── Standard ───────────────────────────────
    # Table: standards | Composite key (C2430 + CTAG + MTAG)
    Standard:
      type: object
      description: Calibration standard (reference instrument linking).
      properties:
        C2430:
          type: string
          maxLength: 80
          description: Standard reference identifier
        CTAG:
          type: string
          maxLength: 80
          description: Calibration tag (FK to calibration.CTAG)
        MTAG:
          type: string
          maxLength: 80
          description: Inventory tag (FK to inventory.MTAG)
      additionalProperties: true

    # ── Result ─────────────────────────────────
    # Table: results | Composite key (row_num + ctag)
    # This model has 250+ columns for calibration measurement data.
    # Only key representative fields are listed; use additionalProperties
    # for the full set. Use GET /api/result/rules for complete metadata.
    Result:
      type: object
      description: |
        Calibration measurement result row. Contains 250+ columns for
        measurement data, uncertainty budgets, guardbands, and signal parameters.
        Use `GET /api/result/rules` for the complete field list.
      properties:
        row_num:
          type: integer
          description: Row number (unique per ctag)
        ctag:
          type: string
          maxLength: 80
          description: Calibration tag (FK to calibration.CTAG)
        mtag:
          type: string
          maxLength: 80
          description: Inventory tag (FK to inventory.MTAG)
        test_desc:
          type: string
          maxLength: 254
          description: Test description
        test_step:
          type: string
          maxLength: 128
          description: Test step identifier
        test_status:
          type: string
          maxLength: 32
          description: Test status
        pass_fail:
          type: string
          maxLength: 50
          description: Pass/fail result
        result_type:
          type: string
          maxLength: 32
          description: Result type
        fixq:
          type: string
          maxLength: 32
          description: Fixed quantity value
        fixq_p:
          type: string
          maxLength: 1
          description: Fixed quantity SI prefix
        fixq_u:
          type: string
          maxLength: 16
          description: Fixed quantity unit
        varq:
          type: string
          maxLength: 32
          description: Variable quantity value
        varq_p:
          type: string
          maxLength: 1
          description: Variable quantity SI prefix
        varq_u:
          type: string
          maxLength: 16
          description: Variable quantity unit
        measurement:
          type: string
          maxLength: 32
          description: Measurement value
        measurement_p:
          type: string
          maxLength: 1
          description: Measurement SI prefix
        measurement_u:
          type: string
          maxLength: 16
          description: Measurement unit
        nominal:
          type: string
          maxLength: 32
          description: Nominal value
        lower_limit:
          type: string
          maxLength: 32
          description: Lower tolerance limit
        upper_limit:
          type: string
          maxLength: 32
          description: Upper tolerance limit
        exp_uncert:
          type: string
          maxLength: 32
          description: Expanded uncertainty
        std_uncert:
          type: string
          maxLength: 32
          description: Standard uncertainty
        cov_fac:
          type: string
          maxLength: 32
          description: Coverage factor
        error:
          type: string
          maxLength: 32
          description: Error value
        dev:
          type: string
          maxLength: 32
          description: Deviation
        corr:
          type: string
          maxLength: 32
          description: Correction
        tol:
          type: string
          maxLength: 32
          description: Tolerance
        tur:
          type: string
          maxLength: 32
          description: Test uncertainty ratio
        remark:
          type: string
          maxLength: 254
          description: Remark / comment
        fix_flag:
          type: integer
          description: Fix flag
        corr_flag:
          type: integer
          description: Correction flag
        meas_flag:
          type: integer
          description: Measurement flag
        accred:
          type: integer
          description: Accreditation flag
        num_meas:
          type: integer
          description: Number of measurements
        created:
          type: string
          description: Creation timestamp
        createdby:
          type: string
          maxLength: 16
          description: Created by user
        editlogtime:
          type: string
          description: Last edit timestamp
        modifiedby:
          type: string
          maxLength: 16
          description: Modified by user
      additionalProperties: true

    # ── User ───────────────────────────────────
    # Table: user | PK: uID (UUID, max 36)
    User:
      type: object
      description: User account.
      properties:
        uID:
          type: string
          maxLength: 36
          description: Primary key — UUID auto-generated on insert
          example: "4782a8df-4ebb-5a29-092a-c972b676e1ad"
        firstname:
          type: string
          maxLength: 100
          description: First name
          example: "Alina"
        lastname:
          type: string
          maxLength: 100
          description: Last name
          example: "Abner"
        password:
          type: string
          maxLength: 60
          description: Password (hashed)
          writeOnly: true
        email:
          type: string
          maxLength: 255
          description: Email address (required)
          example: "alina@calhelp.de"
        phone:
          type: string
          maxLength: 40
          description: Phone number
          example: "1234567890"
        firm:
          type: string
          maxLength: 40
          description: Company / firm name
          example: "KodePlus"
        photo:
          type: string
          maxLength: 100
          description: Avatar photo path
        api_key:
          type: string
          maxLength: 255
          description: API key for authentication
        signature:
          type: string
          description: Digital signature data
        cert_info:
          type: string
          description: Certificate information
        cert_file:
          type: string
          maxLength: 100
          description: Self-signed certificate file
        signature_details:
          type: string
          maxLength: 500
          description: Signature details
        page_size:
          type: integer
          description: Default page size for lists
        inventory_filter:
          type: string
          maxLength: 255
          description: Inventory filter condition
        customer_filter:
          type: string
          maxLength: 255
          description: Customer filter condition
        createdate:
          type: string
          description: Account creation date
        enable_email_support:
          type: integer
          description: Follow support email flag
        enable_email_support_group:
          type: integer
          description: Follow group support email flag
        reminder_change_password:
          type: integer
          description: Password change reminder flag
        role:
          type: string
          maxLength: 255
          description: User role name
        active:
          type: integer
          description: Active flag (1 = active)
        type:
          type: integer
          description: User type
        locked_minute:
          type: integer
          description: Account lock duration in minutes
      required:
        - password
        - email
        - phone
        - firm
        - createdate
        - role
        - inventory_filter
        - customer_filter
        - active
      additionalProperties: true

    # ── Role ───────────────────────────────────
    # Table: authitem | PK: name
    Role:
      type: object
      description: RBAC role (filtered to type=2 roles only).
      properties:
        name:
          type: string
          description: Role name (primary key)
        type:
          type: integer
          description: Auth item type (always 2 for roles)
          example: 2
        description:
          type: string
          description: Role description
        group_uID:
          type: string
          description: Associated group ID
        sort_order:
          type: integer
          description: Display sort order
        bizrule:
          type: string
          description: Business rule expression
        data:
          type: string
          description: Serialized data
      additionalProperties: true

    # ── Group ──────────────────────────────────
    # Table: group | PK: id (auto-increment)
    Group:
      type: object
      description: User group.
      properties:
        id:
          type: integer
          description: Primary key (auto-increment)
        name:
          type: string
          description: Group name
        support_ticket:
          type: integer
          description: Support ticket integration flag
        created_time:
          type: integer
          description: Creation timestamp (Unix epoch)
        updated_time:
          type: integer
          description: Last update timestamp (Unix epoch)
      additionalProperties: true

    # ── Location ───────────────────────────────
    # Table: location | PK: LTAG (UUID, max 80)
    Location:
      type: object
      description: |
        Location / rental record. Column names use coded identifiers (L2801, etc.).
      properties:
        LTAG:
          type: string
          maxLength: 80
          description: Primary key — UUID auto-generated on insert
          example: "9757f448-7c8c-6274-f7bf-caa753136d1f"
        MTAG:
          type: string
          maxLength: 80
          description: Inventory tag (FK to inventory.MTAG)
          example: "Dummy:1080815227"
        status:
          type: string
          maxLength: 80
          description: Location status
          example: "Reservation"
        customer_KTAG:
          type: string
          maxLength: 80
          description: Customer tag
          example: "000000-000000:1073727785"
        delivery_customer_KTAG:
          type: string
          maxLength: 80
          description: Delivery customer tag
      additionalProperties: true

    # ── Repair ─────────────────────────────────
    # Table: repair | PK: RTAG (UUID, max 80)
    Repair:
      type: object
      description: |
        Repair record. Column names use coded identifiers (R3201, etc.).
      properties:
        RTAG:
          type: string
          maxLength: 80
          description: Primary key — UUID auto-generated on insert
          example: "33a73f60-861e-6abe-7eba-313d9fdfccb8"
        MTAG:
          type: string
          maxLength: 80
          description: Inventory tag (FK to inventory.MTAG, required)
          example: "Dummy:1080815227"
        R3230:
          type: string
          description: Repair date
          example: "2025-04-01"
        status:
          type: string
          maxLength: 80
          description: Repair status
          example: "Wartung geplant"
        RPUBLISH:
          type: string
          maxLength: 32
        rdsrc:
          type: string
          maxLength: 3
        R3201:
          type: string
          maxLength: 102
          description: Repair description
          example: "Routine maintenance"
        R3204:
          type: string
          maxLength: 102
          description: Repair notes
          example: "Cleaned case and replaced battery"
        R3246:
          type: integer
        R3249:
          type: integer
        R3250:
          type: string
          maxLength: 1
        R3251:
          type: string
          maxLength: 1
          description: Repair type
          example: "A"
        R3270:
          type: string
          maxLength: 128
      required:
        - R3230
        - MTAG
      additionalProperties: true
