{
  "openapi": "3.0.1",
  "info": {
    "title": "SoftLanding.ApiService | office",
    "version": "1.0.0"
  },
  "paths": {
    "/office/teachers": {
      "get": {
        "tags": [
          "Teacher (Office)"
        ],
        "summary": "List teachers (office).",
        "description": "Returns the full teacher records (office view â€” not row-restricted), each as a `TeacherGet` with\npersonal and contact details, address, the complete employment/insurance/tax and payment data (AHV,\nALV, NBU, KTG, BVG, Quellensteuer, IBAN, flat salary), teaching capabilities, teaching and spoken\nlanguages with levels, children, weekly availability and absence periods, and completed trainings.\nSoft-deleted teachers are excluded unless you pass `showRemoved=true`. Filter with `searchTerm`\n(matches first name, last name, address, city and private email), `lessonsToChildren`,\n`challengingStudents`, `teachingLanguageId` (with an optional `teachingLanguageMinLevel`),\n`spokenLanguageId` (with an optional `spokenLanguageMinLevel`) and `inactive`.\n\n`inactive` is the retirement state: `false` lists only the teachers who can still be given new\ncourses and lessons (what the assignment pickers ask for), `true` only the retired ones, and\nomitting it lists both. It is a plain flag on the teacher, not a lifecycle status.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (a property name\nof the returned object, case-insensitive) and `desc` query parameters. The body is a plain JSON\narray; the total count is returned in the `Content-Range` response header (`items {start}-{end}/{total}`).\nStatus is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetTeachersOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "LessonsToChildren",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "ChallengingStudents",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "TeachingLanguageId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "TeachingLanguageMinLevel",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/NullableOfETeachingLevel"
            }
          },
          {
            "name": "SpokenLanguageId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "SpokenLanguageMinLevel",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/NullableOfETeachingLevel"
            }
          },
          {
            "name": "AccountStatus",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/NullableOfEAccountStatus"
            }
          },
          {
            "name": "Inactive",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "ExcludeIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TeacherGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TeacherGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "Teacher (Office)"
        ],
        "summary": "Create a teacher.",
        "description": "Creates a new teacher from the `TeacherPost` body and returns the created `TeacherGet` with its\ngenerated id. Note that the work email is immutable after creation. Returns `400` with validation\ndetails if the body is invalid.",
        "operationId": "CreateTeacherOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TeacherPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeacherGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teachers/{id}": {
      "get": {
        "tags": [
          "Teacher (Office)"
        ],
        "summary": "Get a single teacher by id.",
        "description": "Returns a single teacher by its id as a `TeacherGet`, including the full employment, languages,\navailability and training details. Returns `404` if no teacher with that id exists.",
        "operationId": "GetTeacherOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeacherGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Teacher (Office)"
        ],
        "summary": "Partially update a teacher.",
        "description": "Partially updates an existing teacher: only the fields present in the `TeacherPatch` body are changed,\nthe rest stay as they are. Returns the updated `TeacherGet`. `404` if it does not exist; `400` on\nvalidation errors, `409` if the email is already in use by another teacher or by a login of the\nother portal.\n\n`inactive` is how a teacher is retired: they take on no NEW course or lesson (the pickers stop\noffering them) while everything they already hold stays with them. This is the intended way to\nremove a teacher from service â€” deleting one who still holds a course or a lesson is refused.",
        "operationId": "UpdateTeacherOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TeacherPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeacherGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Teacher (Office)"
        ],
        "summary": "Delete a teacher (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a teacher. By default this is a soft delete â€” the teacher is hidden from normal listings but\ncan be restored; pass `?hard=true` to remove them permanently. Returns `204`, or `404` if they do\nnot exist.\n\nA teacher who is still ASSIGNED cannot be deleted at all, neither soft nor hard: being the main or\nan additional teacher of a course, or holding a lesson, is answered with `409` naming what blocks\nit (soft-deleted courses and lessons count too â€” they can be restored). Deleting them would change\nwho taught what, which the salaries and every billed position are based on. Retire them with\n`PATCH { \"inactive\": true }` instead: they keep their courses and lessons and are no longer offered\nfor new assignments.",
        "operationId": "DeleteTeacherOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teachers/{id}/resend-invitation": {
      "post": {
        "tags": [
          "Teacher (Office)"
        ],
        "summary": "Re-send the teacher's password-setup mail.",
        "description": "Triggers the onboarding (\"magic link\") mail again for a teacher who has not completed sign-up, and\nrepairs a provisioning run that did not finish â€” a missing login account or a missing realm role is\ncreated on the way, which is how a teacher whose `accountStatus` reads `Failed` is fixed.\n\nReturns `204` on success. `404` if no such teacher exists, `400` if the teacher has no email\naddress, `409` if the account already completed onboarding (a forgotten password is reset by the\nuser through the sign-in page, not from here) or if the email belongs to a login of the other\nportal, and `503` when no identity provider is configured for this environment.",
        "operationId": "ResendTeacherInvitationOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teachers/{id}/restore": {
      "post": {
        "tags": [
          "Teacher (Office)"
        ],
        "summary": "Restore a soft-deleted teacher.",
        "description": "Restores a previously soft-deleted teacher so they appear in normal listings again. Returns `204`,\nor `404` if no such teacher exists.",
        "operationId": "RestoreTeacherOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/locations": {
      "get": {
        "tags": [
          "Location (Office)"
        ],
        "summary": "List locations (office).",
        "description": "Returns all locations in the system (office view — not row-restricted), each as a `LocationGet`.\nA location is a teaching venue with a name, address, and optional arrival and parking instructions.\nSoft-deleted locations are excluded unless you pass `showRemoved=true`. Filter with `searchTerm`\n(matches name, description, city, street and postal code) and by `cantonId` (the Swiss canton,\nresolvable via the matching `/lookups/...` endpoint).\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (any returned\nproperty name, case-insensitive) and `desc` query parameters. The response body is a plain JSON\narray and the total count is returned in the `Content-Range` header (`items {start}-{end}/{total}`);\nthe status is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetLocationsOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "CantonId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/LocationGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/LocationGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "Location (Office)"
        ],
        "summary": "Create a location.",
        "description": "Creates a new location from the `LocationPost` body (name and address, with optional arrival and\nparking notes) and returns the created `LocationGet` with its generated id. Returns `400` with\nvalidation details if the body is invalid.",
        "operationId": "CreateLocationOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LocationPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LocationGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/locations/{id}": {
      "get": {
        "tags": [
          "Location (Office)"
        ],
        "summary": "Get a single location by id.",
        "description": "Returns a single location by its id as a `LocationGet`, including its address and arrival/parking\ninformation. Returns `404` if no location with that id exists.",
        "operationId": "GetLocationOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LocationGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Location (Office)"
        ],
        "summary": "Partially update a location.",
        "description": "Partially updates an existing location: only the fields present in the `LocationPatch` body are\nchanged, the rest stay as they are. Returns the updated `LocationGet`. Returns `404` if the location\ndoes not exist and `400` on validation errors.",
        "operationId": "UpdateLocationOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LocationPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LocationGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Location (Office)"
        ],
        "summary": "Delete a location (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a location. By default this is a soft delete — the location is hidden from normal listings\nbut can be restored; pass `?hard=true` to remove it permanently. Returns `204`, or `404` if the\nlocation does not exist.",
        "operationId": "DeleteLocationOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/locations/{id}/restore": {
      "post": {
        "tags": [
          "Location (Office)"
        ],
        "summary": "Restore a soft-deleted location.",
        "description": "Restores a previously soft-deleted location so it appears in normal listings again. Returns `204`,\nor `404` if no such location exists.",
        "operationId": "RestoreLocationOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/rooms": {
      "get": {
        "tags": [
          "Room (Office)"
        ],
        "summary": "List rooms (office).",
        "description": "Returns all rooms (office view — not row-restricted), each as a `RoomGet` with its name, size in\nsquare meters, maximum student capacity, operational status and the location it belongs to.\nSoft-deleted rooms are excluded unless you pass `showRemoved=true`. Filter with `searchTerm`\n(matches the room name), `statusId` (resolvable via the room-status lookup), `locationId`, and the\n`minSizeInSquareMeters`/`maxSizeInSquareMeters` and `minMaxStudents`/`maxMaxStudents` ranges.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (a property name\nof the returned object, case-insensitive) and `desc` query parameters. The body is a plain JSON\narray; the total count is returned in the `Content-Range` response header (`items {start}-{end}/{total}`).\nStatus is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetRoomsOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "StatusId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "LocationId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "MinSizeInSquareMeters",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "MaxSizeInSquareMeters",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "MinMaxStudents",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "MaxMaxStudents",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "IsFixed",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/RoomGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/RoomGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "Room (Office)"
        ],
        "summary": "Create a room.",
        "description": "Creates a new room from the `RoomPost` body (including the location it belongs to) and returns the\ncreated `RoomGet` with its generated id. Returns `400` with validation details if the body is invalid.",
        "operationId": "CreateRoomOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RoomPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoomGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/rooms/{id}": {
      "get": {
        "tags": [
          "Room (Office)"
        ],
        "summary": "Get a single room by id.",
        "description": "Returns a single room by its id as a `RoomGet`. Returns `404` if no room with that id exists.",
        "operationId": "GetRoomOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoomGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Room (Office)"
        ],
        "summary": "Partially update a room.",
        "description": "Partially updates an existing room: only the fields present in the `RoomPatch` body are changed, the\nrest stay as they are. Returns the updated `RoomGet`. `404` if it does not exist; `400` on validation\nerrors.",
        "operationId": "UpdateRoomOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RoomPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoomGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Room (Office)"
        ],
        "summary": "Delete a room (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a room. By default this is a soft delete — the room is hidden from normal listings but can be\nrestored; pass `?hard=true` to remove it permanently. Returns `204`, or `404` if it does not exist.",
        "operationId": "DeleteRoomOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/rooms/{id}/occupancy": {
      "get": {
        "tags": [
          "Room (Office)"
        ],
        "summary": "What is already booked in this room, for a window of days.",
        "description": "Returns the lessons occupying the room between `from` and `to` (inclusive, `yyyy-MM-dd`), earliest\nfirst, each as a `RoomOccupancySlotGet` with the lesson id, its course id and name, the date, the\nstart and end times, and `hasOwnRoom` — whether the lesson books this room itself or inherits it\nfrom its course (an inherited booking moves if the course's room is changed).\n\nThis is the read-only side of the double-booking rule, and it uses the same definition: the\nEFFECTIVE room (`Lesson.RoomId ?? Course.RoomId`), cancelled and soft-deleted lessons excluded. Use\nit to show the occupancy while a lesson is being scheduled — a `409` on save is the same rule\narriving too late.\n\nThe two FIXED rooms (External / Online) always return an empty list: they cannot be double-booked,\nso an occupancy for them would suggest a constraint that does not exist.\n\n`400` if `to` is before `from` or the window exceeds one year. Not paginated: the window bounds it.",
        "operationId": "GetRoomOccupancyOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/RoomOccupancySlotGet"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/RoomOccupancySlotGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      }
    },
    "/office/rooms/{id}/restore": {
      "post": {
        "tags": [
          "Room (Office)"
        ],
        "summary": "Restore a soft-deleted room.",
        "description": "Restores a previously soft-deleted room so it appears in normal listings again. Returns `204`,\nor `404` if no such room exists.",
        "operationId": "RestoreRoomOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/materials": {
      "get": {
        "tags": [
          "Material (Office)"
        ],
        "summary": "List materials (office).",
        "description": "Returns all materials in the system (office view — not row-restricted), each as a `MaterialGet`.\nA material is a teaching resource (e.g. a book) with a title, ISBN, language, difficulty level,\ntype, price, citizenship-preparation suitability and current stock amount. Soft-deleted materials\nare excluded unless you pass `showRemoved=true`. Filter with `searchTerm` (matches title,\ndescription and ISBN) and by `languageId`, `levelId`, `typeId` and `citizenship`; the enum ids come\nfrom the matching `/lookups/...` endpoints.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (any returned\nproperty name, case-insensitive) and `desc` query parameters. The response body is a plain JSON\narray and the total count is returned in the `Content-Range` header (`items {start}-{end}/{total}`);\nthe status is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetMaterialsOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "LanguageId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "LevelId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "TypeId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Citizenship",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MaterialGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MaterialGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "Material (Office)"
        ],
        "summary": "Create a material.",
        "description": "Creates a new material from the `MaterialPost` body and returns the created `MaterialGet` with its\ngenerated id. Returns `400` with validation details if the body is invalid.",
        "operationId": "CreateMaterialOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MaterialPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MaterialGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/materials/{id}": {
      "get": {
        "tags": [
          "Material (Office)"
        ],
        "summary": "Get a single material by id.",
        "description": "Returns a single material by its id as a `MaterialGet`. Returns `404` if no material with that id\nexists.",
        "operationId": "GetMaterialOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MaterialGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Material (Office)"
        ],
        "summary": "Partially update a material.",
        "description": "Partially updates an existing material: only the fields present in the `MaterialPatch` body are\nchanged, the rest stay as they are (e.g. adjusting the stock amount or price). Returns the updated\n`MaterialGet`. Returns `404` if the material does not exist and `400` on validation errors.",
        "operationId": "UpdateMaterialOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MaterialPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MaterialGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Material (Office)"
        ],
        "summary": "Delete a material (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a material. By default this is a soft delete — the material is hidden from normal listings\nbut can be restored; pass `?hard=true` to remove it permanently. Returns `204`, or `404` if the\nmaterial does not exist.",
        "operationId": "DeleteMaterialOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/materials/{id}/restore": {
      "post": {
        "tags": [
          "Material (Office)"
        ],
        "summary": "Restore a soft-deleted material.",
        "description": "Restores a previously soft-deleted material so it appears in normal listings again. Returns `204`,\nor `404` if no such material exists.",
        "operationId": "RestoreMaterialOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/training-courses": {
      "get": {
        "tags": [
          "TrainingCourse (Office)"
        ],
        "summary": "List training courses (office).",
        "description": "Returns all training courses that teachers can complete (office view — not row-restricted), each as a\n`TrainingCourseGet` with its name and the number of points awarded for completing it.\nSoft-deleted training courses are excluded unless you pass `showRemoved=true`. Filter with `searchTerm`\n(matches the name) and the `minPoints`/`maxPoints` range.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (a property name\nof the returned object, case-insensitive) and `desc` query parameters. The body is a plain JSON\narray; the total count is returned in the `Content-Range` response header (`items {start}-{end}/{total}`).\nStatus is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetTrainingCoursesOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "MinPoints",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "MaxPoints",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TrainingCourseGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TrainingCourseGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "TrainingCourse (Office)"
        ],
        "summary": "Create a training course.",
        "description": "Creates a new training course from the `TrainingCoursePost` body (name and points) and returns the\ncreated `TrainingCourseGet` with its generated id. Returns `400` with validation details if the body\nis invalid.",
        "operationId": "CreateTrainingCourseOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TrainingCoursePost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrainingCourseGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/training-courses/{id}": {
      "get": {
        "tags": [
          "TrainingCourse (Office)"
        ],
        "summary": "Get a single training course by id.",
        "description": "Returns a single training course by its id as a `TrainingCourseGet`. Returns `404` if no training\ncourse with that id exists.",
        "operationId": "GetTrainingCourseOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrainingCourseGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "TrainingCourse (Office)"
        ],
        "summary": "Partially update a training course.",
        "description": "Partially updates an existing training course: only the fields present in the `TrainingCoursePatch`\nbody are changed, the rest stay as they are. Returns the updated `TrainingCourseGet`. `404` if it does\nnot exist; `400` on validation errors.",
        "operationId": "UpdateTrainingCourseOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TrainingCoursePatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrainingCourseGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "TrainingCourse (Office)"
        ],
        "summary": "Delete a training course (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a training course. By default this is a soft delete — the training course is hidden from\nnormal listings but can be restored; pass `?hard=true` to remove it permanently. Returns `204`, or\n`404` if it does not exist.",
        "operationId": "DeleteTrainingCourseOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/training-courses/{id}/restore": {
      "post": {
        "tags": [
          "TrainingCourse (Office)"
        ],
        "summary": "Restore a soft-deleted training course.",
        "description": "Restores a previously soft-deleted training course so it appears in normal listings again. Returns\n`204`, or `404` if no such training course exists.",
        "operationId": "RestoreTrainingCourseOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/lessons": {
      "get": {
        "tags": [
          "Lesson (Office)"
        ],
        "summary": "List lessons (office).",
        "description": "Returns all lessons in the system (office view — not row-restricted), each as a `LessonGet`.\nA lesson is a single scheduled session within a course: its date and start/end times, the course it\nbelongs to, the ONE teacher holding it, attendance records, homework, the computed teaching salary and\noptional travel allowance, and whether it was cancelled (and why). Soft-deleted lessons are excluded\nunless you pass `showRemoved=true`. Filter with `searchTerm` (matches the course display and\nhomework descriptions) and by `courseId`, `roomId`, a `dateFrom`/`dateTo` range, `isCancelled`,\n`teacherId` (who HOLDS the lesson, not the course's teacher pool), `studentId` (lessons of the\ncourses that student is enrolled in), `customerId` (lessons whose course has an enrolled student of\nthat customer) and `attendanceComplete` (see `isAttendanceComplete` on the response).\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (any returned\nproperty name, case-insensitive) and `desc` query parameters. The response body is a plain JSON\narray and the total count is returned in the `Content-Range` header (`items {start}-{end}/{total}`);\nthe status is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetLessonsOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "CourseId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "RoomId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "DateFrom",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "DateTo",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "IsCancelled",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "AttendanceComplete",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "CustomerId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "TeacherId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "StudentId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/LessonGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/LessonGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "Lesson (Office)"
        ],
        "summary": "Create a lesson.",
        "description": "Creates a new lesson from the `LessonPost` body (the owning course, date, times and its teacher) and returns the created `LessonGet` with its generated id. `teacherId` names the ONE teacher who holds it and must be a teacher of\nthe owning course (its main teacher or one of its additional teachers) — otherwise the request is\nrejected with `400`. Returns `400` with validation details if the body is invalid.\n\n`roomId` is optional and unusual: left unset the lesson takes place in the COURSE's room, which is\nthe normal case. Set it only for a session that has to move; an unknown room is a `400`.\n\n`attendanceRecords` may only name students ENROLLED in the owning course; any other student is\nrejected with `400` naming them. Recording attendance for somebody who is not in the course would\nbe a record nobody can reach from that student, whose lessons are the lessons of their courses.",
        "operationId": "CreateLessonOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LessonPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LessonGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/lessons/{id}": {
      "get": {
        "tags": [
          "Lesson (Office)"
        ],
        "summary": "Get a single lesson by id.",
        "description": "Returns a single lesson by its id as a `LessonGet`, including its teacher, attendance records and homework. Returns `404` if no lesson with that id exists.",
        "operationId": "GetLessonOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LessonGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Lesson (Office)"
        ],
        "summary": "Partially update a lesson.",
        "description": "Partially updates an existing lesson: only the fields present in the `LessonPatch` body are changed,\nthe rest stay as they are. Returns the updated `LessonGet`. The teacher is changed with `teacherId`, which REPLACES the assignment (a lesson has\nexactly one); it must be a teacher of the lesson's course (main or additional) — otherwise the request\nis rejected with `400`. Returns `404` if the\nlesson does not exist and `400` on validation errors.\n\nThe room of a single lesson is set with `roomId` and cleared with `removeRoom`, which returns it to\nthe COURSE's room rather than leaving the lesson without one. An unknown room is a `400`.\n\n`attendanceRecordsToAdd` may only name students ENROLLED in the lesson's course; any other student\nis rejected with `400` naming them. Records already stored are never re-validated — taking a\nstudent off the course later leaves their recorded attendance standing.",
        "operationId": "UpdateLessonOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LessonPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LessonGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Lesson (Office)"
        ],
        "summary": "Delete a lesson (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a lesson. By default this is a soft delete — the lesson is hidden from normal listings but\ncan be restored; pass `?hard=true` to remove it permanently. Returns `204`, or `404` if the lesson\ndoes not exist.",
        "operationId": "DeleteLessonOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/lessons/{id}/restore": {
      "post": {
        "tags": [
          "Lesson (Office)"
        ],
        "summary": "Restore a soft-deleted lesson.",
        "description": "Restores a previously soft-deleted lesson so it appears in normal listings again. Returns `204`,\nor `404` if no such lesson exists.",
        "operationId": "RestoreLessonOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/attendance-documents": {
      "post": {
        "tags": [
          "AttendanceDocument (Office)"
        ],
        "summary": "Upload an absence document (multipart/form-data).",
        "description": "Stores the file and returns its `id`, `fileName` and `sizeBytes`. Put that id on the attendance record\nas `absenceDocumentId` in the following lesson `PATCH`.\n\nThe document is stored UNATTACHED on purpose: while the user is picking the file, the attendance record\nmay not exist yet (a lesson's rows are written by the lesson patch), so requiring the record here would\nmake the normal case impossible.\n\nAccepts PDF, JPEG and PNG up to 10 MB. An empty file, a larger one or another type is `400`. The bytes\nare stored in the database — the API's filesystem is ephemeral and per-instance, so a path would\nsurvive neither a redeploy nor a second replica.",
        "operationId": "UploadAttendanceDocumentOffice",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "required": [
                  "file"
                ],
                "type": "object",
                "properties": {
                  "file": {
                    "$ref": "#/components/schemas/IFormFile"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadedDocument"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/attendance-documents/{id}": {
      "get": {
        "tags": [
          "AttendanceDocument (Office)"
        ],
        "summary": "Download an absence document.",
        "description": "Streams the stored file with its original name and content type. Returns `404` if no document with that\nid exists.",
        "operationId": "DownloadAttendanceDocumentOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "AttendanceDocument (Office)"
        ],
        "summary": "Permanently delete a stored absence document.",
        "description": "Removes the stored file. Any attendance record referencing it keeps its excused status and simply loses\nthe document (the foreign key is `SET NULL`) — deleting a certificate must not silently undo the\nattendance decision that was made on it.\n\nReturns `204`, or `404` if no document with that id exists.",
        "operationId": "DeleteAttendanceDocumentOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/payment-documents": {
      "post": {
        "tags": [
          "PaymentDocument (Office)"
        ],
        "summary": "Upload a receipt for a free payment position (multipart/form-data).",
        "description": "Stores the file and returns its `id`, `fileName` and `sizeBytes`. Put that id on the free position as\n`documentId` when creating or patching the teacher payment.\n\nThe document is stored UNATTACHED on purpose: while the user is picking the file the position may not\nexist yet (a new payment's rows are written by the create), so requiring it here would make the normal\ncase impossible. An upload that is never referenced is cleaned up by the nightly sweep.\n\nAccepts PDF, JPEG and PNG up to 10 MB. An empty file, a larger one or another type is `400`. The bytes\nare stored in the database — the API's filesystem is ephemeral and per-instance, so a path would\nsurvive neither a redeploy nor a second replica.",
        "operationId": "UploadPaymentDocumentOffice",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "required": [
                  "file"
                ],
                "type": "object",
                "properties": {
                  "file": {
                    "$ref": "#/components/schemas/IFormFile"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadedPaymentDocument"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/payment-documents/{id}": {
      "get": {
        "tags": [
          "PaymentDocument (Office)"
        ],
        "summary": "Download a payment receipt.",
        "description": "Streams the stored file with its original name and content type. Returns `404` if no document with that\nid exists.",
        "operationId": "DownloadPaymentDocumentOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "PaymentDocument (Office)"
        ],
        "summary": "Permanently delete a stored payment receipt.",
        "description": "Removes the stored file. A free position referencing it keeps its description and amount (the foreign\nkey is `SET NULL`) — removing the paperwork must not silently remove what is owed.\n\nReturns `204`, or `404` if no document with that id exists.",
        "operationId": "DeletePaymentDocumentOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/homework-documents": {
      "post": {
        "tags": [
          "HomeworkDocument (Office)"
        ],
        "summary": "Upload a file for a homework (multipart/form-data).",
        "description": "Stores the file and returns its `id`, `fileName` and `sizeBytes`. Pass that id in `documentIdsToAdd` on\nthe homework create or patch — or on a homework carried by a lesson create (`homeworksToAdd`).\n\nThe document is stored UNATTACHED on purpose: only the upload can mint the id, and a homework created\ntogether with its lesson does not exist yet while the file is being picked. An upload that is never\nattached is removed by the nightly sweep.\n\nAccepts PDF, JPEG and PNG up to 10 MB. An empty file, a larger one or another type is `400`. The bytes\nare stored in the database — the API's filesystem is ephemeral and per-instance, so a path would survive\nneither a redeploy nor a second replica.",
        "operationId": "UploadHomeworkDocumentOffice",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "required": [
                  "file"
                ],
                "type": "object",
                "properties": {
                  "file": {
                    "$ref": "#/components/schemas/IFormFile"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadedHomeworkDocument"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/homework-documents/{id}": {
      "get": {
        "tags": [
          "HomeworkDocument (Office)"
        ],
        "summary": "Download a homework file.",
        "description": "Streams the stored file with its original name and content type. The office reads every one of them.",
        "operationId": "DownloadHomeworkDocumentOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "HomeworkDocument (Office)"
        ],
        "summary": "Discard an upload that was never attached.",
        "description": "Removes an upload that has NOT been attached to a homework — what a dialog calls when the user takes a\nfile back before saving, or leaves without saving.\n\nAn ATTACHED document is `404`, and so is somebody else's upload: without that, this route would be a way\nto delete a worksheet off another person's homework. To remove an attached file, send its id in\n`documentIdsToRemove` on the homework patch.",
        "operationId": "DiscardHomeworkDocumentOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/mail-attachments": {
      "post": {
        "tags": [
          "MailAttachment (Office)"
        ],
        "summary": "Upload a file to enclose with a mail (multipart/form-data).",
        "description": "Stores the file and returns its `id`, `fileName`, `contentType` and `sizeBytes`. Pass that id in\n`attachmentDocumentIds` on `POST /office/mail-history/{id}/resend` to enclose it.\n\nThe document is stored UNATTACHED on purpose: only the upload can mint the id, and the file is chosen\nwhile the dialog is open and the correction does not exist yet. An upload that is never enclosed with a\nmail is removed by the nightly sweep.\n\nAccepts PDF, JPEG and PNG up to 10 MB. An empty file, a larger one or another type is `400`. The bytes\nare stored in the database — the API's filesystem is ephemeral and per-instance, so a path would survive\nneither a redeploy nor a second replica.",
        "operationId": "UploadMailAttachmentOffice",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "required": [
                  "file"
                ],
                "type": "object",
                "properties": {
                  "file": {
                    "$ref": "#/components/schemas/IFormFile"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadedMailAttachment"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/mail-attachments/{id}": {
      "get": {
        "tags": [
          "MailAttachment (Office)"
        ],
        "summary": "Download a mail attachment.",
        "description": "Streams the stored file with its original name and content type.\n\nOnly files stored through this route are served here. An attachment a trigger enclosed from\nsomewhere else — an invoice PDF, say — lives in its own table and is read through its own route;\nwhich table a document id belongs to is resolved at delivery, not here.",
        "operationId": "DownloadMailAttachmentOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "MailAttachment (Office)"
        ],
        "summary": "Discard an upload that is not enclosed with any mail.",
        "description": "Removes an upload that is enclosed with NO mail — what the dialog calls when the user takes a file back\nbefore saving, or leaves without saving.\n\nA document that IS enclosed with a mail answers `404`: it is part of the record of what went out, and a\nmail whose enclosure has gone refuses to be delivered at all. Take it off the mail instead, by leaving\nits id out of `attachmentDocumentIds` on the next re-send — that keeps the file.",
        "operationId": "DiscardMailAttachmentOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/payment-imports": {
      "post": {
        "tags": [
          "PaymentImport (Office)"
        ],
        "summary": "Read a camt.054 bank statement in and settle the invoices it covers.",
        "description": "Takes the statement as `multipart/form-data` and answers with the run: every payment the file\ncontained, each with the verdict the import reached, plus the derived counts.\n\n**What is settled automatically.** Only a payment whose structured creditor reference (QRR or\nSCOR) names an invoice of ours. The reference is the one printed on the QR bill, so this is the\nreverse of what was sent out. Nothing is guessed from the amount or the payer's name: two open\ninvoices of the same size for one customer are ordinary, and picking the wrong one would put a\ncustomer in the dunning run for an invoice they paid. An invoice is moved to `Paid` once the\npayments booked against it cover its total.\n\n**Importing the same file twice is a no-op.** A payment is identified by its UETR where the\nstatement carries one, otherwise by a hash of what it IS (the bank's booking reference, amount,\ncurrency, value date, reference, payer account). The MESSAGE id is deliberately not part of that:\nbanks deliver the same payment in more than one statement, and treating those as different\npayments would report the customer as having paid twice. Such rows come back as\n`alreadyImported` and change nothing.\n\n**The special cases come back in the answer**, each as an `outcome`: `alreadySettled` (a genuine\nsecond payment — deliberately NOT booked, since what happens to the money is a person's\ndecision), `noReference`, `unknownReference`, `invoiceNotPayable` (a draft or cancelled invoice),\n`underpaid` and `overpaid` (both booked, both reported). `needsReview` on a row is what the\nconflict list is made of.\n\n`400` when the document is not a camt.054, naming what was expected. Debits and reversed credits\nare skipped: neither is money that arrived.",
        "operationId": "ImportBankStatementOffice",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "required": [
                  "file"
                ],
                "type": "object",
                "properties": {
                  "file": {
                    "$ref": "#/components/schemas/IFormFile"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentImportGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "PaymentImport (Office)"
        ],
        "summary": "The history of imported bank statements.",
        "description": "Returns the import runs, newest first by default. Filter with `searchTerm` (file name, the bank's\nmessage id, the account), `accountIban`, `importedFrom`/`importedTo` and `hasConflicts` — the last\none narrows to runs that still hold something for a person to look at. Soft-deleted runs are\nexcluded unless `showRemoved=true`. Paged and sorted through the usual `Range` / `orderBy`\nmechanism.",
        "operationId": "GetPaymentImportsOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "AccountIban",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ImportedFrom",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "ImportedTo",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "HasConflicts",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PaymentImportGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PaymentImportGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      }
    },
    "/office/payment-imports/{id}": {
      "get": {
        "tags": [
          "PaymentImport (Office)"
        ],
        "summary": "One import run with its payments.",
        "description": "Returns the run and every payment it read, with each verdict. `404` if unknown.",
        "operationId": "GetPaymentImportOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentImportGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "PaymentImport (Office)"
        ],
        "summary": "Remove an import run (soft by default; ?hard=true to remove it permanently).",
        "description": "Removes the run and the payments read in it. A soft delete hides both; `?hard=true` removes them\npermanently and needs the `hard-delete` realm role.\n\n**The hard delete is the correction path for a wrong file.** A payment is recognised by its\nfingerprint whether or not its row is hidden, so a SOFT-deleted run still makes every payment in\nit read as `alreadyImported` on the next attempt — hiding a run is not the same as never having\nread it. Removing it permanently frees those fingerprints and the file can be read afresh.\n\n**The invoices are left where they are.** An invoice this run moved to `Paid` stays `Paid`:\nun-paying one is a person's decision, the same rule that keeps the import itself from ever doing\nit. Returns `204`, or `404` if there is no such run.",
        "operationId": "DeletePaymentImportOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/payment-imports/{id}/restore": {
      "post": {
        "tags": [
          "PaymentImport (Office)"
        ],
        "summary": "Restore a soft-deleted import run.",
        "description": "Brings a soft-deleted run and its payments back into the ordinary listings. Returns `204`, or\n`404` if there is no such run.",
        "operationId": "RestorePaymentImportOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/bank-payments": {
      "get": {
        "tags": [
          "BankPayment (Office)"
        ],
        "summary": "The imported bank payments, with what the import decided about each.",
        "description": "One row per payment a statement contained — booked or not. The rows that were NOT settled are the\npoint: `needsReview=true` narrows to exactly what a person still has to decide, which is the\nreview list.\n\nFilter with `searchTerm` (the quoted reference, the payer's name, their IBAN), `importId`,\n`invoiceId`, `outcomeId`, `needsReview` and `valueDateFrom`/`valueDateTo`. Soft-deleted rows are\nexcluded unless `showRemoved=true`. Paged and sorted through the usual `Range` / `orderBy`\nmechanism.\n\n`needsReview` is answered IN SQL over the same set of outcomes the row's own flag reads\n(`BankPaymentOutcomes`), so a filtered page can never contradict the rows in it.",
        "operationId": "GetBankPaymentsOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "ImportId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "OutcomeId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "NeedsReview",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "InvoiceId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "ValueDateFrom",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "ValueDateTo",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/BankPaymentGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/BankPaymentGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      }
    },
    "/office/bank-payments/{id}": {
      "get": {
        "tags": [
          "BankPayment (Office)"
        ],
        "summary": "One imported payment.",
        "description": "Returns the payment and its verdict. `404` if unknown.",
        "operationId": "GetBankPaymentOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BankPaymentGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/payroll-account": {
      "get": {
        "tags": [
          "PayrollAccount (Office)"
        ],
        "summary": "The account salaries are paid from — our own IBAN and address.",
        "description": "Returns the single payroll-account record: the IBAN salaries are debited from, an optional BIC, the\naccount holder and the STRUCTURED address (street, house number, postal code, town, two-letter\ncountry code), plus how the run is produced (`payoutDayOffset`, `confidential`).\n\nAlso returns what is DERIVED from the IBAN and never stored:\n\n* `ibanFormatted` — grouped in fours, the only form comparable with a bank statement.\n* `isIbanValid` — 21 characters, CH or LI, correct ISO 13616 check digits.\n* `isQrIban` — whether the IID falls in 30000–31999. A QR-IBAN here is almost certainly a MISTAKE:\n  it is a virtual account number for matching incoming payments, not something salaries are\n  debited from. Reported rather than refused — only the bank can say for certain what an account\n  is.\n* `institutionNumber` — the IID read out of the IBAN, which is how the bank is identified in the\n  payment message when no BIC is entered.\n* `isConfigured` — whether a payment file can be produced at all.\n\nNever `404`: the record always exists (the migration creates it empty), so an unconfigured system\nanswers `200` with `isConfigured: false` rather than an absence.",
        "operationId": "GetPayrollAccountOffice",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PayrollAccountGet"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "PayrollAccount (Office)"
        ],
        "summary": "Partially update the payroll account.",
        "description": "Applies a `PayrollAccountPatch`: only the fields present are changed. Returns the record with its\nderived facts recomputed.\n\nNormalisation on the way in: the IBAN is stripped of spaces (it is pasted off a bank document as\noften as typed), the postal code loses any country prefix (`CH-6300` is stored as `6300`), and the\nBIC and country code are upper-cased.\n\nClearing an optional field needs its flag — `removeBic`, `removeStreet`, `removeBuildingNumber` —\nbecause a null value alone means \"not part of this patch\". Sending a value and its flag together\nis `400`. The MANDATORY fields have no flag: emptying the IBAN, the account holder or the town is\na payroll run that stops working, not something to express.\n\n`400` on an IBAN that fails its check digits, on a text carrying a character a Swiss payment\nmessage may not hold, or on a length the pain.001 schema refuses.",
        "operationId": "UpdatePayrollAccountOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PayrollAccountPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PayrollAccountGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/salary-exports/preview": {
      "get": {
        "tags": [
          "SalaryExport (Office)"
        ],
        "summary": "What a salary run for one period would contain, and what is in the way.",
        "description": "Computes the whole run WITHOUT writing anything: every teacher payment of `period`, the payout each\nwould receive, the account it would go to, and — per row — what has to be corrected first.\n\nThe amount is the payslip's own **payout** (net pay plus travel, expenses and the child allowance),\ncomposed with the same code the teacher's payslip is built from, so the figure on their screen and\nthe figure the bank is asked for cannot differ.\n\n`problems` on a row is a list of plain sentences (\"no IBAN on the teacher record\", \"no town\"),\nbecause nothing branches on the KIND of problem — it is read, and then a person opens that\nteacher's record. `payrollAccountProblems` is separate: ONE broken payroll account blocks the whole\nrun, while a broken teacher record blocks only that teacher.\n\n`previouslyExportedAt` names every payment that has ALREADY been in a file, with the run it was in.\nThat is a warning and not a refusal: a file can be lost before it ever reaches e-banking, and only\na person knows whether this is that case or a second payment nobody wants.\n\n`period` is any day of the month — `2026-09-01` and `2026-09-17` mean the same run.",
        "operationId": "PreviewSalaryExportOffice",
        "parameters": [
          {
            "name": "period",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SalaryExportPreviewGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/salary-exports": {
      "post": {
        "tags": [
          "SalaryExport (Office)"
        ],
        "summary": "Produce the salary payment file for a period.",
        "description": "Writes a **pain.001.001.09** credit transfer (Swiss Payment Standards) and records the run with one\nitem per transfer. Returns the run; the file itself is fetched from `GET /{id}/file`.\n\n**The message version matters.** The parallel phase in which Swiss banks still accept the 2009\nversion (`pain.001.001.03`) ends on **13 November 2026**; this produces the 2019 version only, and\nvalidates against the Swiss schema `pain.001.001.09.ch.03.xsd`.\n\n**A salary run is CONFIDENTIAL by default** (`confidential` on the payroll account): the file then\ncarries `CtgyPurp/Cd = SALA`, `BtchBookg = true` and a `CND` debit advice together, which produces\nONE collective booking whose individual amounts and payees do not appear on the account statement.\nAny two of those three without the third produce an ordinary payment, so they are never set\nseparately.\n\n**Which payments go in.** With no `teacherPaymentIds`, every payable payment of the period. With\nids, exactly those — for the second run after correcting one teacher's record, so the others are\nnot paid twice. An id that is not of this period, or that cannot be paid, is refused BY NAME rather\nthan dropped: the office asked for that person, and silence would read as \"paid\".\n\n**Producing the same period twice is allowed** and is what `GET /preview` warns about. What stops a\nfile being submitted twice is the bank: each run carries its own `messageId`, and a bank refuses one\nit has already accepted.\n\n`400` when the payroll account is not configured, when the period holds nothing payable, when a\nnamed payment cannot be paid, or when something cannot be expressed in a legal message (an\nincomplete address, an unusable IBAN) — each with the reason and the name.",
        "operationId": "CreateSalaryExportOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SalaryExportPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SalaryExportGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "SalaryExport (Office)"
        ],
        "summary": "The history of salary payment files.",
        "description": "Returns the runs, newest first by default, each with its transfers and the derived totals\n(`paymentCount`, `totalAmount`). The file's CONTENT is not included — a page of runs would\notherwise carry a whole payment message per row.\n\nFilter with `searchTerm` (file name, message id, booking id, account holder), `periodYear` +\n`periodMonth`, `exportedFrom`/`exportedTo` and `debtorIban`. Soft-deleted runs are excluded unless\n`showRemoved=true`. Paged and sorted through the usual `Range` / `orderBy` mechanism.",
        "operationId": "GetSalaryExportsOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "PeriodYear",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "PeriodMonth",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "ExportedFrom",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "ExportedTo",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "DebtorIban",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SalaryExportGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SalaryExportGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      }
    },
    "/office/salary-exports/{id}": {
      "get": {
        "tags": [
          "SalaryExport (Office)"
        ],
        "summary": "One salary run with its transfers.",
        "description": "Returns the run and every transfer it contained. `404` if unknown.",
        "operationId": "GetSalaryExportOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SalaryExportGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "SalaryExport (Office)"
        ],
        "summary": "Remove a salary run (soft by default; ?hard=true to remove it permanently).",
        "description": "Removes the run and its transfers. A soft delete hides both; `?hard=true` removes them permanently\nand needs the `hard-delete` realm role.\n\n**This does NOT recall the payment.** Once the file has been uploaded to e-banking the money is on\nits way and nothing here can stop it — deleting the run removes OUR record of it, which is right\nfor a file that was produced and never submitted, and wrong for one that was.\n\nReturns `204`, or `404` if there is no such run.",
        "operationId": "DeleteSalaryExportOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/salary-exports/{id}/file": {
      "get": {
        "tags": [
          "SalaryExport (Office)"
        ],
        "summary": "The pain.001 file of one run, as it was produced.",
        "description": "Streams the stored XML under the run's own file name, `application/xml`, UTF-8 without a byte-order\nmark — which the standard requires.\n\n**Read back, never regenerated.** A payslip is COMPOSED from the payment, the expense claims and\nthe teacher's children, so re-deriving the file a week later can produce a different amount than\nthe bank was actually asked to transfer. The file that was downloaded is the file that is kept, and\nan ARCHIVED run still answers — somebody who needs to prove what was sent is exactly who asks.\n\n`404` if there is no such run.",
        "operationId": "GetSalaryExportFileOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "200": {
            "description": "OK",
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/office/salary-exports/{id}/restore": {
      "post": {
        "tags": [
          "SalaryExport (Office)"
        ],
        "summary": "Restore a soft-deleted salary run.",
        "description": "Brings a soft-deleted run and its transfers back. Returns `204`, or `404` if unknown.",
        "operationId": "RestoreSalaryExportOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/expense-claim-receipts/{id}": {
      "get": {
        "tags": [
          "Office Expense Receipts"
        ],
        "summary": "Download the receipt behind an expense claim.",
        "description": "Streams the stored file. The office may read ANY receipt — it decides the claims, and a decision\nmade without the evidence is a guess. `404` if no such file exists.",
        "operationId": "DownloadOfficeExpenseReceipt",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/expense-claims": {
      "get": {
        "tags": [
          "Office Expense Claims"
        ],
        "summary": "List the expense claims teachers have filed.",
        "description": "Returns every teacher's expense claims — what was claimed (date, amount, reason, whether a\nreceipt is attached), who claimed it, and what has been decided so far.\n\nFilter with `searchTerm` (the reason, the office's note and the claimant's name), `teacherId`,\n`status`, `dateFrom`/`dateTo` (when the cost was incurred), `settledPeriodYear`/`settledPeriodMonth` /\n`hasSettledPeriod` (which period settled it), `minClaimedAmount`/`maxClaimedAmount` and\n`hasReceipt`. Withdrawn claims are excluded unless you pass `showRemoved=true`. Paged and sorted\nthrough the usual `Range` / `orderBy` mechanism.\n\nThe natural entry filter is `status=Submitted` — \"what is waiting for a decision\" is the\nquestion this list exists to answer.",
        "operationId": "GetOfficeExpenseClaims",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "TeacherId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/NullableOfEExpenseClaimStatus"
            }
          },
          {
            "name": "DateFrom",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "DateTo",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "SettledPeriodYear",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "SettledPeriodMonth",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "HasSettledPeriod",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "MinClaimedAmount",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "MaxClaimedAmount",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "HasReceipt",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ExpenseClaimGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ExpenseClaimGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      }
    },
    "/office/expense-claims/{id}": {
      "get": {
        "tags": [
          "Office Expense Claims"
        ],
        "summary": "One expense claim.",
        "description": "Returns one claim with the claimant and the decision so far. `404` if it does not exist.",
        "operationId": "GetOfficeExpenseClaimById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExpenseClaimGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Office Expense Claims"
        ],
        "summary": "Decide an expense claim.",
        "description": "Sets the status, what will actually be paid, and the note explaining it. Nothing the teacher\nwrote can be changed here.\n\nWhich of the two figures the office supplies depends on the DECISION, and the server settles\neach of them rather than trusting the payload:\n\n- `Approved` pays what was CLAIMED. The amount is not an input — anything sent is ignored and\n  the claimed figure is written, so a mask that locks the field cannot disagree with the record.\n- `Adjusted` and `AlreadyPaid` are the two the office picks the figure for, and both MUST carry\n  an approved amount greater than zero (`400`).\n- `Rejected` must NOT carry one (`400`), and an amount left over from an earlier approval is\n  cleared: a rejection that still had a figure would be a payment nobody can explain.\n- `Adjusted` and `Rejected` need a note (`400`) — either in this request or already stored. The\n  teacher only sees the decision, and a figure that came back different with no word is\n  indistinguishable from a mistake.\n\n`settledPeriodYear`/`settledPeriodMonth` belong to exactly ONE decision. `AlreadyPaid` means the claim was\nreimbursed outside the payroll (cash at the desk, an advance), so it never reaches a payslip and\nnaming the month IS the statement — it is REQUIRED there (`400` without it). On every other\ndecision it is REFUSED (`400`) and a stored value is cleared, because there the period is a\nfinding the payroll close arrives at when the claim goes onto a payslip. It must be the FIRST of\na month (`400`): the month is the unit, and a 14th would make two claims of one month look like\ntwo periods.\n\nA claim whose payslip has already been PAID OUT\n(`POST /office/teacher-payments/{id}/mark-paid-out`) can no longer be changed at all: `409`,\nnaming the month. A payslip is composed live out of its claims, so re-deciding one would rewrite\na document the teacher has been paid on. Reopen that payslip first.\n\nSetting the status back to `Submitted` reopens a claim answered too early — deliberately\npossible, because the alternative is asking the teacher to file it again, which loses the date\nit was handed in on.",
        "operationId": "UpdateOfficeExpenseClaim",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExpenseClaimPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExpenseClaimGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Office Expense Claims"
        ],
        "summary": "Archive an expense claim.",
        "description": "Archives the claim (a soft delete), or removes it permanently with `?hard=true` — which needs\nthe `hard-delete` realm role like every other permanent delete.\n\nThis is NOT how a claim is refused: rejecting it leaves the record and the reason where the\nteacher can see them, and that is almost always what is wanted. Archiving is for a claim that\nshould never have existed — a duplicate, one filed against the wrong person.\n\nIt exists because the office is the only actor who CAN remove a decided claim: the teacher's own\nwithdrawal is closed the moment the office answers, so without this route an answered claim\ncould be removed by nobody at all.\n\nA claim whose payslip has already been PAID OUT cannot be archived either (`409`): removing it\nwould take the position off the office's list while the payslip goes on paying it.",
        "operationId": "DeleteOfficeExpenseClaim",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/dashboard": {
      "get": {
        "tags": [
          "Dashboard (Office)"
        ],
        "summary": "Get the office dashboard aggregate.",
        "description": "Returns a single read-only snapshot object that powers the office home screen — everything needed\nto render it in one call, so you do not have to assemble it from the individual list endpoints.\n\nIt bundles the operationally relevant highlights for internal staff:\n- upcoming teacher birthdays and work anniversaries (next 30 days),\n- upcoming student exams (next 30 days),\n- a billable-hours and revenue overview for the current month,\n- customers whose students have been inactive for more than 18 months.\n\nThis is a company-wide view (not row-restricted). The figures are point-in-time and computed on\neach request from the current data.",
        "operationId": "GetOfficeDashboard",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OfficeDashboardGet"
                }
              }
            }
          }
        }
      }
    },
    "/office/courses": {
      "get": {
        "tags": [
          "Course (Office)"
        ],
        "summary": "List courses (office).",
        "description": "Returns all courses in the system (office view — not row-restricted), each as a `CourseGet` with\nits type (group or private), status, subject/language, teaching level, schedule (start/end dates\nand weekly slots), assigned teacher and room, enrolled students, required materials and the price\nper lesson. Soft-deleted courses are excluded unless you pass `showRemoved=true`. Filter with\n`searchTerm` (matches name and description) and by `courseTypeId`/`courseTypeIds`, `statusId`,\n`subjectId`, `levelId`, `roomId`, `teacherId` (matches courses where the teacher is the MAIN or an\nADDITIONAL teacher), `customerId` (courses with an enrolled student of that customer), start/end\ndate ranges and `isForChildren`. Enum-based filter ids come from the matching `/lookups/...`\nendpoints. Each `CourseGet` carries `teacherId`/`teacher` (the main teacher) plus `teacherIds`/\n`teachers` (ALL teachers of the course = main ∪ additional).\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (any returned\nproperty name, case-insensitive) and `desc` query parameters. The response body is a plain JSON\narray and the total count is returned in the `Content-Range` header (`items {start}-{end}/{total}`);\nthe status is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetCoursesOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "CourseTypeId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "CourseTypeIds",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "StatusId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "IsCancelled",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "SubjectId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "LevelId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "RoomId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "TeacherId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "CustomerId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "StartDateFrom",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "StartDateTo",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "EndDateFrom",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "EndDateTo",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "IsForChildren",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "MinStudentsFrom",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "MinStudentsTo",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CourseGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CourseGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "Course (Office)"
        ],
        "summary": "Create a course.",
        "description": "Creates a new course from the `CoursePost` body and returns the created `CourseGet` with its\ngenerated id. `teacherId` is the main (primary responsible) teacher; `additionalTeacherIds` assigns\nfurther teachers to the course (the main teacher need not be repeated). The price is captured from\nthe price table at creation time and stored on the course, so later price-table changes do not\naffect it. Returns `400` with validation details if the body is invalid (e.g. duplicate additional\nteacher ids, or the main teacher repeated among the additional teachers).\n\n**`endDate` is required for a GROUP course and optional for Private/Duo.** A term has a published\nspan; a standing private arrangement does not know its end when it is set up, so leave it null\nthere. A missing end date on a group course is `400`.\n\n**`weeklySchedule` slots must not OVERLAP** — two slots on the same weekday whose times overlap\nwould generate two lessons in the same room at the same moment. Refused with `400`, naming the two\nslots. Touching ends (`09:00-11:00` and `11:00-12:00`) are two consecutive lessons and fine.\n\n**`interestIds` links the INTERESTS this course matches, and that link sends invitations.** Every\nstudent a linked interest names — its primary student plus the further ones the office attached —\nis invited to the course as soon as it is not a draft: immediately when it is created with any\nother status, otherwise on the save that takes it out of `Draft`. A student already enrolled, or\nalready holding an invitation for this course, is skipped, and an interest whose students are all\nserved is closed (`Done`). A student who is currently paused is left out and the interest stays\nopen, so the omission is visible; the create itself is not refused for it.",
        "operationId": "CreateCourseOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CoursePost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CourseGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/courses/{id}": {
      "get": {
        "tags": [
          "Course (Office)"
        ],
        "summary": "Get a single course by id.",
        "description": "Returns a single course by its id as a `CourseGet`, including its schedule, assigned teacher and\nroom, enrolled students, required materials and enrollment requests. Returns `404` if no course\nwith that id exists.",
        "operationId": "GetCourseOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CourseGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Course (Office)"
        ],
        "summary": "Partially update a course (the price is VERSIONED, and FROZEN once billed).",
        "description": "Partially updates an existing course: only the fields present in the `CoursePatch` body are changed,\nthe rest stay as they are. Returns the updated `CourseGet`. `teacherId` changes the MAIN teacher;\n`additionalTeacherIdsToAdd`/`additionalTeacherIdsToRemove` manage the additional teachers.\nRemoving a teacher (or replacing the main teacher) is rejected with `400` if that teacher is still\nassigned to any lesson of this course — reassign those lessons first.\n\n**`statusId` cannot be CHANGED here — `409`, naming the way out.** A course reaches its status\nthrough the actions only: `POST /{id}/activate` takes a draft live (and sends the invitations it\nhas recorded), `POST /{id}/cancel` cancels it (and its lessons, and writes to everybody\ninvolved). Neither is reversible and neither is \"set a property\", which is what a dropdown made\nthem look like. Sending the status the course ALREADY has is accepted, so a form that binds the\nfield is not blocked from saving anything else — only a change is refused. There is no way back\nto `Draft` at all.\n\n**`additionalTeacherIdsToDeactivate` takes a co-teacher off the FUTURE of the course** — the answer\nto \"stop giving them lessons here\" when removing them is impossible because they already hold\nlessons of it. They stay listed and keep every lesson already assigned to them; what stops is\neverything further: no new lesson may name them (`400`), the course is read-only for them, and its\nstudents leave their `/teacher/my-students`. **Nothing is reassigned automatically** — lesson\nassignments are billing-relevant and only a person may move them. Refused with `409`, naming the\nlessons, while the teacher is still entered on an UPCOMING lesson of the course.\n`additionalTeacherIdsToReactivate` is the plain reverse. `courseGet.additionalTeachers` reports the\nstate of each link, `deactivatedTeacherIds` the short form.\n\n**`interestIdsToAdd`/`interestIdsToRemove` manage the matched interests**, and linking one sends\ninvitations: on this same save when the course is not a draft, otherwise on the save that takes it\nout of `Draft`. Nobody is invited twice (already enrolled, or already holding an invitation for\nthis course, is skipped), and an interest whose students are all served is closed. UNLINKING does\nNOT withdraw invitations that already went out — the invitation is its own record and the invitee\nhas been told.\n\n**`endDate` / `removeEndDate`.** A null `endDate` means \"not part of this patch\"; clearing it needs\n`removeEndDate: true`, and doing both in one request is `400`. A GROUP course may not be left\nwithout an end date (`400`); Private/Duo may. Overlapping `weeklyScheduleToAdd` slots — with each\nother or with the slots the course already has — are `400`, naming the two slots.\n\n`studentIdsToAdd` is rejected with `409` when one of those students is currently INACTIVE (paused);\nthe response names them. Only what is NEW is refused — students already on the course stay, and\n`studentIdsToRemove` keeps working for a paused student.\n\n**`studentIdsToRemove` is rejected with `409` once a lesson of the course has TAKEN PLACE**, naming\nthe dates. The enrolment is what those lessons are billed from (a position is student × lesson), so\nremoving it would delete positions in the past. The boundary is the lesson, not whether the\nattendance was recorded. To end somebody's participation, mark them `Excused` on the lessons they no\nlonger attend — an excused lesson is not charged. Students who are not enrolled can still be listed\n(removing them changes nothing and is not an error).\n\n**`price` is versioned.** A change opens a new validity period from `effectiveFrom`, which defaults to\nTOMORROW — so the course read back today still reports the OLD price, and lessons before that day keep\nthe amount they were billed at. `effectiveFrom` may not fall into a past month, and a stichtag of today\nor earlier is RETROACTIVE and needs `confirmRetroactive: true`; both are `400`.\n\n**`price` is FROZEN once the course has been billed** — that is, once an invoice item exists for any of\nits lessons (`isPriceLocked` on `CourseGet` reports this). The invoice item stores the amount it was\ncharged with, so re-pricing the course would make a recalculation disagree with what was invoiced:\nsuch a change is refused with `409`. A patch carrying the UNCHANGED price still succeeds, and every\nother field of a billed course stays editable.\n\nReturns `404` if the course does not exist, `400` on validation errors and `409` when the price is\nfrozen.",
        "operationId": "UpdateCourseOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CoursePatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CourseGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Course (Office)"
        ],
        "summary": "Delete a course (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a course. By default this is a soft delete — the course is hidden from normal listings but\ncan be restored; pass `?hard=true` to remove it permanently. Returns `204`, or `404` if the course\ndoes not exist.",
        "operationId": "DeleteCourseOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/courses/{id}/price-history": {
      "get": {
        "tags": [
          "Course (Office)"
        ],
        "summary": "Preisverlauf of a course's price per lesson — the amounts billing charged.",
        "description": "Returns the validity periods of THIS course's price per lesson as `PriceVersionGet` entries, newest\nfirst: what the amount was, from which day (`validFrom`, inclusive) until which day (`validUntil`,\nEXCLUSIVE — null means still in force), who changed it and why.\n\n**This is the price invoices are built from.** A billable position (one student at one lesson) is\ncharged the amount that was valid on the DAY of that lesson, so this history is the audit trail behind\nevery invoice line. The first period starts at the earlier of the course's start date and its creation\nday, so every lesson of the course is covered.\n\nExactly one entry has `isCurrent=true`; `isScheduled=true` marks a change announced for a future day.\nFilter with `onDay` (at most one entry — \"what did this course cost on that day\") and `validFromMin`;\n`searchTerm`/`showRemoved` are ignored. Supports the usual `Range`/`orderBy`/`desc` pagination\n(`206` + `Content-Range`, `416` on a bad range). Returns `404` if no course with that id exists.",
        "operationId": "GetCoursePriceHistoryForCourseOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "ValidFromMin",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "OnDay",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PriceVersionGet"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PriceVersionGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      }
    },
    "/office/courses/{id}/billing-impact": {
      "get": {
        "tags": [
          "Course (Office)"
        ],
        "summary": "Which invoices a group-course change would affect.",
        "description": "Read-only preview: which automatic invoices already exist for this course's charge, split into the\nOPEN ones (not yet sent, so they can be deleted with the change) and the SENT ones (which never are).\n\nA GROUP course is charged as a whole the moment it becomes real, and that charge may already have\nproduced an invoice. Putting the course back into its draft (`toDraft=true`) or removing students\nfrom it (`studentIdsToRemove`) takes the charge back, so the office has to decide what happens to\nthe invoice. Ask here to show WHICH documents are involved; the decision then travels on the PATCH\nas `deleteOpenChargeInvoices`, and the PATCH is refused with `409` until it does — a direct API call\nhas to answer the same question.\n\nReturns an empty result when nothing has been charged yet. Single and duo courses are never\naffected: they are still billed lesson by lesson and have no course-wide charge.",
        "operationId": "GetCourseBillingImpactOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "studentIdsToRemove",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "toDraft",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CourseBillingImpactGet"
                }
              }
            }
          }
        }
      }
    },
    "/office/courses/{id}/activate": {
      "post": {
        "tags": [
          "Course (Office)"
        ],
        "summary": "Take a DRAFT course live (and send the invitations it has recorded).",
        "description": "Moves a `Draft` course to `Created`, which is the moment it starts existing outside the office:\nevery student a linked interest names is invited, every recorded invite intent goes out, and a\nGROUP course freezes its fee on each enrolment. All of it in the one save, so a course can never\nbe live without its invitations having been sent.\n\nAn action rather than a status patch, and since 2026-09-10 the status cannot be moved through\n`PATCH` at all — that is refused with `409` and points here. A dropdown offered this as one of\nfour equal-looking values while none of its effects can be taken back.\n\n**A GROUP course needs at least one lesson** (`409`): its invitations point the invitee at a\ncourse they can open, and an empty calendar there promises something nobody has planned.\nSingle, private and duo courses are activated without lessons — their appointments are arranged\nwith the one student afterwards.\n\nReturns the updated `CourseGet`, `404` if no such course exists, and `409` when the course is\nnot a draft (already live, or cancelled) or when a group course has no lesson yet.",
        "operationId": "ActivateCourseOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CourseGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/courses/{id}/cancel": {
      "post": {
        "tags": [
          "Course (Office)"
        ],
        "summary": "Cancel a course (and every lesson of it).",
        "description": "Cancels the course: its status becomes `Cancelled`, **every one of its lessons is cancelled with\nit**, and everybody invited to or enrolled in it is written to (mail template `course.cancelled`).\nAll of it happens in one save, so the course can never be cancelled without its lessons.\n\nAn action rather than a status patch, because it is not \"set a property\": setting `Cancelled`\nthrough `PATCH` is refused with `409` and points here.\n\n**Only while no lesson has taken place.** A lesson in the past is a billable position and a\nteacher's paid work, so a course that has already run cannot be un-run — `409`, naming the dates.\nCancel the remaining lessons individually instead.\n\nAfterwards the course is a dead end: it disappears from the customer, student and teacher portals\n(like a draft), it is offered by no picker, nothing new can be linked to it (`409`), its lessons\ncan no longer be edited — only deleted — and the status cannot be moved back.\n\nReturns the updated `CourseGet`, `404` if no such course exists, and `409` when it has already\nbeen cancelled or a lesson has taken place.",
        "operationId": "CancelCourseOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CourseGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/courses/{id}/restore": {
      "post": {
        "tags": [
          "Course (Office)"
        ],
        "summary": "Restore a soft-deleted course.",
        "description": "Restores a previously soft-deleted course so it appears in normal listings again. Returns `204`,\nor `404` if no such course exists.",
        "operationId": "RestoreCourseOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/course-prices": {
      "get": {
        "tags": [
          "CoursePrice (Office)"
        ],
        "summary": "List course prices (office).",
        "description": "Returns all course-price entries (office view — not row-restricted), each as a `CoursePriceGet`.\nThese make up the pricing matrix: a price per lesson keyed by teaching level, course type and\nwhether the course is for children. Rows can be standard level rows or custom product rows (with\ntheir own product name). New courses copy their price from this matrix at creation time. Soft-deleted\nentries are excluded unless you pass `showRemoved=true`. Filter with `searchTerm` (matches the\ndescription) and by `levelId`, `courseTypeId`, `isForChildren` and a `priceMin`/`priceMax` range;\nthe enum ids come from the matching `/lookups/...` endpoints.\n\nPrices are VERSIONED. Each row reports the amount valid TODAY together with `effectiveFrom` (the day\nthat amount started to apply) and, when a change is already announced, `pendingChangeFrom` and\n`pendingChangeAmount`. Pass **`asOf=YYYY-MM-DD`** to see the matrix as it was — or, with a future day,\nas it WILL be: only cells that had a price on that day are returned, each reporting the amount that\napplied then. Because a cell removed since then still had a price on a past day, `asOf` includes\nsoft-deleted cells regardless of `showRemoved`, and `priceMin`/`priceMax` then apply to that day's\namount. `asOf` is honoured here only; reading a single row by id always reports today.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (any returned\nproperty name, case-insensitive) and `desc` query parameters. The response body is a plain JSON\narray and the total count is returned in the `Content-Range` header (`items {start}-{end}/{total}`);\nthe status is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetCoursePricesOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "LevelId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "CourseTypeId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "IsForChildren",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "PriceMin",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "PriceMax",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "AsOf",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CoursePriceGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CoursePriceGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "CoursePrice (Office)"
        ],
        "summary": "Create a course price.",
        "description": "Creates a new course-price entry from the `CoursePricePost` body and returns the created\n`CoursePriceGet` with its generated id. Returns `400` with validation details if the body is invalid.",
        "operationId": "CreateCoursePriceOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CoursePricePost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoursePriceGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/course-prices/{id}": {
      "get": {
        "tags": [
          "CoursePrice (Office)"
        ],
        "summary": "Get a single course price by id.",
        "description": "Returns a single course-price entry by its id as a `CoursePriceGet`. Returns `404` if no entry with\nthat id exists.",
        "operationId": "GetCoursePriceOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoursePriceGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "CoursePrice (Office)"
        ],
        "summary": "Partially update a course price (a price change is VERSIONED — see effectiveFrom).",
        "description": "Partially updates an existing course-price entry: only the fields present in the `CoursePricePatch`\nbody are changed, the rest stay as they are. Existing courses keep the price they were created with.\n\n**A change to `price` does not overwrite the amount — it opens a new validity period.**\n`effectiveFrom` says from which day the new amount applies and defaults to **TOMORROW**, so the row\nread back today still reports the OLD amount, with `pendingChangeFrom`/`pendingChangeAmount` naming\nthe announced change. Use `GET {id}/history` for the full Preisverlauf.\n\nTwo rules constrain the stichtag, both `400` when broken:\n* it may not fall into a PAST MONTH (that is what the month-end close freezes);\n* if it is today or earlier it is RETROACTIVE — it restates amounts that were already used — and must\n  carry `confirmRetroactive: true`. Enforced here, not only in the UI, so the confirmation cannot be\n  bypassed by calling this endpoint directly. `changeReason` is stored on the period.\n\nReturns the updated `CoursePriceGet`. Returns `404` if the entry does not exist and `400` on\nvalidation errors.",
        "operationId": "UpdateCoursePriceOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CoursePricePatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoursePriceGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "CoursePrice (Office)"
        ],
        "summary": "Delete a course price (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a course-price entry. By default this is a soft delete — the entry is hidden from normal\nlistings but can be restored; pass `?hard=true` to remove it permanently. Returns `204`, or `404`\nif the entry does not exist.",
        "operationId": "DeleteCoursePriceOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/course-prices/{id}/history": {
      "get": {
        "tags": [
          "CoursePrice (Office)"
        ],
        "summary": "Preisverlauf: the validity periods of one price-matrix cell.",
        "description": "Returns the price history of a single matrix cell as `PriceVersionGet` entries, newest first: what the\namount was, from which day (`validFrom`, inclusive) until which day (`validUntil`, EXCLUSIVE — null\nmeans still in force), who changed it and the note they gave.\n\nA price change never overwrites the amount, it opens a new period, so this is the audit trail behind\nevery billed lesson. Exactly one entry has `isCurrent=true`; entries with `isScheduled=true` are\nannounced changes that have not started yet. Soft-deleted cells still report their history — that is\nthe point of keeping it.\n\nFilter with `onDay` (returns only the period in force on that day, so at most one entry) and\n`validFromMin`. `searchTerm` and `showRemoved` are not supported and are ignored: a period carries no\ntext, and periods are never soft-deleted — a correction is a new period.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (any returned\nproperty name, case-insensitive) and `desc` query parameters. The response body is a plain JSON\narray and the total count is returned in the `Content-Range` header (`items {start}-{end}/{total}`);\nthe status is `206` when a `Range` is sent, `416` if the range is invalid.\n\nReturns `404` if no cell with that id exists.",
        "operationId": "GetCoursePriceHistoryOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "ValidFromMin",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "OnDay",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PriceVersionGet"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PriceVersionGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      }
    },
    "/office/course-prices/{id}/restore": {
      "post": {
        "tags": [
          "CoursePrice (Office)"
        ],
        "summary": "Restore a soft-deleted course price.",
        "description": "Restores a previously soft-deleted course-price entry so it appears in normal listings again.\nReturns `204`, or `404` if no such entry exists.",
        "operationId": "RestoreCoursePriceOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/price-keys/activate-due": {
      "post": {
        "tags": [
          "PriceKeys (Office)"
        ],
        "summary": "Bring price changes announced for today into force.",
        "description": "Recomputes the CURRENT amount of every course price, salary rate and course from its validity periods,\nso a change announced for a future day becomes the current one once that day arrives. Returns how many\nrows each part touched.\n\n**This changes no money.** Every calculation resolves its amount from the validity periods by the day\nthe thing was used, and the read paths re-derive the displayed amount as well — the denormalised column\nthis refreshes exists only so the list filters and `orderBy` keep working. A run that is late or missed\ntherefore leaves nothing wrong.\n\nIt runs automatically shortly after midnight inside the API process. This endpoint exists because that\nprocess is not reachable from a test (and because \"wait until tomorrow\" is a poor way to check a price\nlist). It is CONVERGENT — it recomputes rather than steps forward — so calling it repeatedly, at any\ntime, is safe and the second call reports zero.\n\nIt also normalises what the best-effort delete hooks may have missed: a REMOVED matrix cell must not\nkeep an open period, or an `asOf` view of a later day would report a price the cell no longer had.\nCourses are deliberately left alone there — a removed course still has lessons that were held.\n\nRequires the `office` role; `401` when unauthenticated, `403` for any other portal role.",
        "operationId": "ActivateDuePriceVersionsOffice",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PriceActivationResult"
                }
              }
            }
          }
        }
      }
    },
    "/office/orders": {
      "get": {
        "tags": [
          "Order (Office)"
        ],
        "summary": "List orders (office).",
        "description": "Returns all material orders in the system (office view — not row-restricted), each as an `OrderGet`\nwith the ordering student and their customer, the current status, whether it has been paid (`isPaid`),\nthe ordered material items and computed totals (item count and total cost).\nSoft-deleted orders are excluded unless you pass `showRemoved=true`. Filter with `searchTerm`\n(matches student name, customer name and comment), `statusId` (resolvable via the order-status\nlookup), `studentId`, `customerId`, `isPaid`, and a `createdDateFrom`/`createdDateTo` date range.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (a property name\nof the returned object, case-insensitive) and `desc` query parameters. The body is a plain JSON\narray; the total count is returned in the `Content-Range` response header (`items {start}-{end}/{total}`).\nStatus is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetOrdersOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "StatusId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "StudentId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "CustomerDisplayFilter",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "IsPaid",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "CreatedDateFrom",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "CreatedDateTo",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "CustomerId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "CourseId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "ItemStatusId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "DeliveryMethodId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "BilledSeparately",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/OrderGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/OrderGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "Order (Office)"
        ],
        "summary": "Create an order.",
        "description": "Creates a new material order from the `OrderPost` body (the ordering student and the list of material\nitems) and returns the created `OrderGet` with its generated id and computed totals.\nReturns `400` with validation details if the body is invalid.",
        "operationId": "CreateOrderOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/orders/{id}": {
      "get": {
        "tags": [
          "Order (Office)"
        ],
        "summary": "Get a single order by id.",
        "description": "Returns a single order by its id as an `OrderGet`, including its material items and computed totals.\nReturns `404` if no order with that id exists.",
        "operationId": "GetOrderOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Order (Office)"
        ],
        "summary": "Partially update an order.",
        "description": "Partially updates an existing order: only the fields present in the `OrderPatch` body are changed,\nthe rest stay as they are (for example to update the status, payment flag or comment). Returns the\nupdated `OrderGet`. `404` if no such order exists; `400` on validation errors.",
        "operationId": "UpdateOrderOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Order (Office)"
        ],
        "summary": "Delete an order (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes an order. By default this is a soft delete — the order is hidden from normal listings but can\nbe restored; pass `?hard=true` to remove it permanently. Returns `204`, or `404` if it does not exist.",
        "operationId": "DeleteOrderOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/orders/{id}/restore": {
      "post": {
        "tags": [
          "Order (Office)"
        ],
        "summary": "Restore a soft-deleted order.",
        "description": "Restores a previously soft-deleted order so it appears in normal listings again. Returns `204`,\nor `404` if no such order exists.",
        "operationId": "RestoreOrderOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/packages": {
      "get": {
        "tags": [
          "Package (Office)"
        ],
        "summary": "List packages (office).",
        "description": "Returns all sellable packages (office view — not row-restricted), each as a `PackageGet` with its\ntitle, billing title, description, sales price and the credit amount (in CHF) it grants for billing.\nSoft-deleted packages are excluded unless you pass `showRemoved=true`. Filter with `searchTerm`\n(matches title, billing title and description), `statusId` (Draft or Active — see\n`/lookups/package-status`) and the `minPrice`/`maxPrice` and `minCredit`/`maxCredit` range parameters.\n\nEvery package carries a `status`: a new one is a **Draft** unless the POST states otherwise, and\nonly an **Active** package is served to the customer portal (a draft is absent from\n`/customer/packages` and answers `404` by id there). Setting the status to Active publishes it;\nsetting it back to Draft withdraws it from sale without deleting anything.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (a property name\nof the returned object, case-insensitive) and `desc` query parameters. The body is a plain JSON\narray; the total count is returned in the `Content-Range` response header (`items {start}-{end}/{total}`).\nStatus is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetPackagesOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "MinPrice",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "MaxPrice",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "MinCredit",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "MaxCredit",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "StatusId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "IsPrivate",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PackageGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PackageGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "Package (Office)"
        ],
        "summary": "Create a package.",
        "description": "Creates a new package from the `PackagePost` body and returns the created `PackageGet` with its\ngenerated id. Returns `400` with validation details if the body is invalid.",
        "operationId": "CreatePackageOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PackagePost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PackageGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/packages/{id}": {
      "get": {
        "tags": [
          "Package (Office)"
        ],
        "summary": "Get a single package by id.",
        "description": "Returns a single package by its id as a `PackageGet`. Returns `404` if no package with that id exists.",
        "operationId": "GetPackageOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PackageGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Package (Office)"
        ],
        "summary": "Partially update a package.",
        "description": "Partially updates an existing package: only the fields present in the `PackagePatch` body are changed,\nthe rest stay as they are. Returns the updated `PackageGet`. `404` if it does not exist; `400` on\nvalidation errors.",
        "operationId": "UpdatePackageOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PackagePatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PackageGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Package (Office)"
        ],
        "summary": "Delete a package (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a package. By default this is a soft delete — the package is hidden from normal listings but\ncan be restored; pass `?hard=true` to remove it permanently. Returns `204`, or `404` if it does not exist.",
        "operationId": "DeletePackageOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/packages/{id}/restore": {
      "post": {
        "tags": [
          "Package (Office)"
        ],
        "summary": "Restore a soft-deleted package.",
        "description": "Restores a previously soft-deleted package so it appears in normal listings again. Returns `204`,\nor `404` if no such package exists.",
        "operationId": "RestorePackageOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/package-orders": {
      "get": {
        "tags": [
          "PackageOrder (Office)"
        ],
        "summary": "List package orders (office).",
        "description": "Returns all package purchases in the system (office view — not row-restricted), each as a\n`PackageOrderGet`: the purchasing customer, the purchased package, current status, any refund\ndetails, the pass-on assignment (personal / passed on to another customer / received), per-student\ncredit usage and the resulting used and remaining credit.\nSoft-deleted package orders are excluded unless you pass `showRemoved=true`. Filter with `searchTerm`\n(matches customer name and package title), `statusId`, `packageId`, `customerId`, and the\n`minUsedCredit`/`maxUsedCredit` range.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (a property name\nof the returned object, case-insensitive) and `desc` query parameters. The body is a plain JSON\narray; the total count is returned in the `Content-Range` response header (`items {start}-{end}/{total}`).\nStatus is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetPackageOrdersOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "StatusId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "PackageId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "CustomerId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "MinUsedCredit",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "MaxUsedCredit",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "IsPrivate",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "IsOverdrawn",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "OnlyOpenCredits",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PackageOrderGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PackageOrderGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      }
    },
    "/office/package-orders/{id}": {
      "get": {
        "tags": [
          "PackageOrder (Office)"
        ],
        "summary": "Get a single package order by id.",
        "description": "Returns a single package order by its id as a `PackageOrderGet`, including its credit usage breakdown\nand remaining credit. Returns `404` if no package order with that id exists.",
        "operationId": "GetPackageOrderOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PackageOrderGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "PackageOrder (Office)"
        ],
        "summary": "Partially update a package order.",
        "description": "Partially updates an existing package order: only the fields present in the `PackageOrderPatch` body\nare changed, the rest stay as they are (for example to change the status or record a refund). Returns\nthe updated `PackageOrderGet`. `404` if it does not exist; `400` on validation errors.\n\n`paidAt` is the day the payment arrived, and it decides what this package pays for: it covers\nlessons FROM that day on, nothing before it. Settable and correctable, because a payment arrives on\none day and is booked on another — the days in between would otherwise lose their coverage. Setting\nthe status to Paid WITHOUT a date stamps today. A date in the future is rejected with `400`.\n\n**`Settled` closes the package for booking, and its remainder becomes a CREDIT.** Only whole\nlessons and amounts are ever charged, never a fraction, so a package regularly ends with a\nremainder too small to carry the next lesson — that remainder is neither lost nor refunded, it is\ncredited on the next invoice. Settling is what fixes the amount: from `settledAt` on the package\npays for nothing further, so the split between \"still consumed by this package\" and \"left over\" is\na reproducible fact instead of one that keeps moving as lessons are recorded. `settledAt` is\nsettable and correctable for the same reason as `paidAt`; setting the status to Settled WITHOUT a\ndate stamps today, and a future date is `400`. `residualCredit` on `PackageOrderGet` reports what\nis owed back (null while the package is still open).\n\nThis is NOT a refund: what the package already paid for stays paid for, and re-opening a settled\norder (any other status) clears `settledAt` so it can pay again.\n\nThe BENEFICIARY has two shapes and only ever one at a time. `assignedToCustomerEmail` is the address\nwhile no customer has been resolved for it; saving one that DOES belong to a customer assigns the\norder to them (`assignedToCustomerId`) and clears the address, which is what makes the package\nvisible in that customer's portal. `removeAssignedToCustomerId` undoes the assignment (the address\nis not restored — it was consumed when the assignment was resolved), `removeAssignedToCustomerEmail`\nclears a pending address. The assignment type (`passedOnType`) is DERIVED from the result on every\nwrite, so a value sent for it is overwritten unless it agrees. Assigning the order to the BUYING\ncustomer's own address is rejected with `400`.",
        "operationId": "UpdatePackageOrderOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PackageOrderPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PackageOrderGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "PackageOrder (Office)"
        ],
        "summary": "Delete a package order (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a package order. By default this is a soft delete — the package order is hidden from normal\nlistings but can be restored; pass `?hard=true` to remove it permanently. Returns `204`, or `404`\nif it does not exist.",
        "operationId": "DeletePackageOrderOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/package-orders/{id}/restore": {
      "post": {
        "tags": [
          "PackageOrder (Office)"
        ],
        "summary": "Restore a soft-deleted package order.",
        "description": "Restores a previously soft-deleted package order so it appears in normal listings again. Returns\n`204`, or `404` if no such package order exists.",
        "operationId": "RestorePackageOrderOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/package-purchases": {
      "get": {
        "tags": [
          "PackagePurchase (Office)"
        ],
        "summary": "List package purchases (office).",
        "description": "Returns the package purchases in the system (office view — not row-restricted), each as a\n`PackagePurchaseGet`: the buying customer, when it was purchased, whether the bracket is FINISHED\n(`isCompleted`) and the member package orders (the line items bought together in one checkout)\nwith computed item count and total price. A purchase has no status of its own — `isCompleted` is\nderived from its packages and means none of them is `Ordered` or `Invoiced` any more (a refunded,\nsettled or cancelled package counts as done). Because packages are prepaid, one purchase corresponds to\nexactly one invoice. Purchases with no remaining orders (all removed) are\nnever listed. Soft-deleted purchases are excluded unless you pass `showRemoved=true`. Filter with\n`searchTerm` (matches the buying customer name), `isCompleted` (`false` = still open, `true` =\nfinished, omit for both), `customerId`, `packageId` (purchases containing an order for\nthat package), `orderStatusId` (purchases containing a package in that state, from\n`/lookups/package-order-status` — e.g. everything with a refunded package) and a\n`purchasedFrom`/`purchasedTo` date range (ISO-8601; matches on the purchase date).\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (a property name\nof the returned object, case-insensitive) and `desc` query parameters. The body is a plain JSON\narray; the total count is returned in the `Content-Range` response header (`items {start}-{end}/{total}`).\nStatus is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetPackagePurchasesOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "IsCompleted",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "CustomerId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "PackageId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "OrderStatusId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "PurchasedFrom",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "PurchasedTo",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PackagePurchaseGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PackagePurchaseGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "PackagePurchase (Office)"
        ],
        "summary": "Create a package purchase on behalf of a customer (checkout of one or more packages).",
        "description": "Creates a package purchase for the customer named in the `PackagePurchasePost` body from its list\nof items (one per package, each optionally assigned to a beneficiary by email). All package orders\nare created atomically under the new purchase header, which starts in the `Draft` status; the\nprepayment invoice is raised out of band. Returns the created `PackagePurchaseGet` with its\ngenerated id and member orders. Returns `400` with validation details if the body is invalid.\n\nAn item's `beneficiaryCustomerEmail` is RESOLVED at checkout: if the address belongs to a customer,\nthe order is assigned to them (`assignedToCustomerId`, which is what makes the package visible in\nthat customer's portal) and the address is cleared; if it belongs to nobody, it is kept as a\npending note and completed later, when a customer is created with — or renamed to — that address.\nThe assignment type (`passedOnType`) is DERIVED from the result and needs not be sent.\nAssigning an item to the BUYING customer's own address is rejected with `400`.",
        "operationId": "CreatePackagePurchaseOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PackagePurchasePost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PackagePurchaseGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/package-purchases/{id}": {
      "get": {
        "tags": [
          "PackagePurchase (Office)"
        ],
        "summary": "Get a single package purchase by id.",
        "description": "Returns a single package purchase by its id as a `PackagePurchaseGet`, including its member\npackage orders. Returns `404` if no purchase with that id exists.",
        "operationId": "GetPackagePurchaseOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PackagePurchaseGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "PackagePurchase (Office)"
        ],
        "summary": "Delete a package purchase (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a package purchase. By default this is a soft delete — the purchase is hidden from normal\nlistings but can be restored; pass `?hard=true` to remove it permanently (only possible once its\nmember package orders have been removed). Returns `204`, or `404` if it does not exist.",
        "operationId": "DeletePackagePurchaseOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/package-purchases/{id}/mark-invoiced": {
      "post": {
        "tags": [
          "PackagePurchase (Office)"
        ],
        "summary": "Invoice a package purchase (moves its not-yet-invoiced packages).",
        "description": "Raises the prepayment invoice for the purchase as a whole: every member package order still in the\n`Ordered` state becomes `Invoiced`. Packages that have moved on by themselves — `Paid`,\n`RefundRequested`, `Refunded`, `Settled`, `Cancelled` — are left untouched, because the bracket may\nnot undo a decision taken on an individual package.\n\nA purchase carries no status of its own; `isCompleted` on the returned `PackagePurchaseGet` is\nDERIVED from its packages (finished = none of them is `Ordered` or `Invoiced` any more), so this\ncall does not make the purchase \"invoiced\" as a state — it invoices its packages.\n\nReturns the updated `PackagePurchaseGet`. `404` if no such purchase exists, `409` when it has no\npackage left to invoice (the caller was acting on a stale view).",
        "operationId": "MarkPackagePurchaseInvoicedOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PackagePurchaseGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/package-purchases/{id}/mark-paid": {
      "post": {
        "tags": [
          "PackagePurchase (Office)"
        ],
        "summary": "Book the payment for a package purchase (moves its invoiced packages).",
        "description": "Books the prepayment for the purchase as a whole: every member package order in the `Invoiced`\nstate becomes `Paid` and is stamped with the payment day (`paidAt` in the\n`PackagePurchaseMarkPaidPost` body; omit it for today). Deliberately NOT the `Ordered` ones — a\npayment answers an invoice, so use `mark-invoiced` first. `RefundRequested`, `Refunded`, `Settled`\nand `Cancelled` packages are left untouched.\n\nThe date matters beyond bookkeeping: it is the day from which each package starts covering lessons\n(only lessons that already took place are billed), so a payment that arrived last week must be\nbooked with last week's date or those days stay uncovered. It may not be in the future, and a\npackage that already carries a payment date keeps it — the stamp is never moved, because that would\nsilently un-cover lessons the package has already paid for.\n\nReturns the updated `PackagePurchaseGet`. `404` if no such purchase exists, `409` when none of its\npackages is invoiced, `400` if the date is in the future.",
        "operationId": "MarkPackagePurchasePaidOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PackagePurchaseMarkPaidPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PackagePurchaseGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/package-purchases/{id}/cancel": {
      "post": {
        "tags": [
          "PackagePurchase (Office)"
        ],
        "summary": "Cancel what has not been paid for in a package purchase.",
        "description": "Cancels the purchase as far as it can still be cancelled: every member package order in the\n`Ordered` or `Invoiced` state becomes `Cancelled`. A cancelled package is never delivered and never\npays for a lesson.\n\nA PAID package is deliberately not touched: undoing a payment is a REFUND, which is a different\nrecord with a different meaning and is decided on the package itself (the customer may also request\none). A purchase whose packages have all been paid for therefore cannot be cancelled at all.\n\nReturns the updated `PackagePurchaseGet`. `404` if no such purchase exists, `409` when there is\nnothing left to cancel.",
        "operationId": "CancelPackagePurchaseOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PackagePurchaseGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/package-purchases/{id}/restore": {
      "post": {
        "tags": [
          "PackagePurchase (Office)"
        ],
        "summary": "Restore a soft-deleted package purchase.",
        "description": "Restores a previously soft-deleted package purchase so it appears in normal listings again. Returns\n`204`, or `404` if no such purchase exists.",
        "operationId": "RestorePackagePurchaseOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/students": {
      "get": {
        "tags": [
          "Student (Office)"
        ],
        "summary": "List students (office).",
        "description": "Returns all students (office view â€” not row-restricted), each as a `StudentGet` with the customer they\nbelong to, personal and contact details, address, lifecycle status, the subjects (languages and\nlevels) they learn, their enrolled courses, attended lessons, received materials, office notes, exam\nrecords and computed values such as age and whether they are a child.\nSoft-deleted students are excluded unless you pass `showRemoved=true`. Filter with `searchTerm`\n(matches first name, last name and email), `customerId`, `nativeLanguageId`, `subjectLanguageId`,\n`isChild`, and `status` (Active / Inactive / Invited). `status` is COMPOSED, not stored — the\nfilter reproduces that composition in SQL, so paging stays exact — and for the same reason the\nlist cannot be ORDERED by it.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (a property name\nof the returned object, case-insensitive) and `desc` query parameters. The body is a plain JSON\narray; the total count is returned in the `Content-Range` response header (`items {start}-{end}/{total}`).\nStatus is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetStudentsOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "CustomerId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "NativeLanguageId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "IsChild",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "SubjectLanguageId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/NullableOfEStudentStatus"
            }
          },
          {
            "name": "AccountStatus",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/NullableOfEAccountStatus"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/StudentGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/StudentGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "Student (Office)"
        ],
        "summary": "Create a student.",
        "description": "Creates a new student from the `StudentPost` body (including the owning customer and the subjects to\nlearn) and returns the created `StudentGet` with its generated id. Returns `400` with validation\ndetails if the body is invalid.",
        "operationId": "CreateStudentOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StudentPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StudentGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/students/{id}": {
      "get": {
        "tags": [
          "Student (Office)"
        ],
        "summary": "Get a single student by id.",
        "description": "Returns a single student by its id as a `StudentGet`, including subjects, courses, notes and exam\nrecords. Returns `404` if no student with that id exists.",
        "operationId": "GetStudentOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StudentGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Student (Office)"
        ],
        "summary": "Partially update a student.",
        "description": "Partially updates an existing student: only the fields present in the `StudentPatch` body are changed,\nthe rest stay as they are. Returns the updated `StudentGet`. `404` if it does not exist; `400` on\nvalidation errors.\n\n`inactive` PAUSES the student (`true`) or resumes them (`false`) — it replaces the former\n`statusId`, because `status` is now COMPOSED on read: `inactive` → `Inactive`, otherwise `Invited`\nwhile the login has not been used yet, otherwise `Active`. Pausing WITHDRAWS the student's\nunanswered course invitations and makes every new assignment refuse with `409` (course enrolment,\ninvitation, enquiry, enrolment request); their notifications and mails stop. Everything that\nalready exists is untouched — enrolled courses keep running, past lessons stay billed and the\nlogin keeps working.",
        "operationId": "UpdateStudentOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StudentPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StudentGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Student (Office)"
        ],
        "summary": "Delete a student (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a student. By default this is a soft delete â€” the student is hidden from normal listings but\ncan be restored; pass `?hard=true` to remove them permanently. Returns `204`, or `404` if they do\nnot exist.",
        "operationId": "DeleteStudentOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/students/{id}/resend-invitation": {
      "post": {
        "tags": [
          "Student (Office)"
        ],
        "summary": "Re-send the student's password-setup mail.",
        "description": "Triggers the onboarding (\"magic link\") mail again for a student who has not completed sign-up, and\nrepairs a provisioning run that did not finish â€” a missing login account or a missing realm role is\ncreated on the way, which is how a student whose `accountStatus` reads `Failed` is fixed. The mail\nis rendered in the student's correspondence language.\n\nReturns `204` on success. `404` if no such student exists, `400` if the student has no email\naddress (children often have none), `409` if the account already completed onboarding (a forgotten\npassword is reset by the user through the sign-in page, not from here) or if the email belongs to a\nlogin of the other portal, and `503` when no identity provider is configured for this environment.",
        "operationId": "ResendStudentInvitationOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/students/{id}/set-password": {
      "post": {
        "tags": [
          "Student (Office)"
        ],
        "summary": "Set a one-time password for the student's login.",
        "description": "Sets a one-time password on the student's login account and forces them to choose their own at the\nnext sign-in. This is the way IN for a student who has **no email address** — no magic link can\nreach them, so they sign in with their `loginNumber` and the password set here — and it is the\npassword RESET for any student, since the identity provider's self-service reset needs a mailbox.\n\nType the password and pass it on to the student directly; nothing is mailed. Works for every\nstudent, not only the ones without an address.\n\nReturns `204` on success. `400` if the password is missing or shorter than 8 characters (the\nrealm's own policy applies on top), `404` if no such student exists, `409` if the student has no\nlogin account yet, and `502` if the identity provider could not be reached.",
        "operationId": "SetStudentPasswordOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetPasswordPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "502": {
            "description": "Bad Gateway",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/students/{id}/restore": {
      "post": {
        "tags": [
          "Student (Office)"
        ],
        "summary": "Restore a soft-deleted student.",
        "description": "Restores a previously soft-deleted student so they appear in normal listings again. Returns `204`,\nor `404` if no such student exists.",
        "operationId": "RestoreStudentOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/interests": {
      "get": {
        "tags": [
          "Interest (Office)"
        ],
        "summary": "List interests (office).",
        "description": "Returns all interests in the system (office view — not row-restricted), each as an `InterestGet`.\nAn interest is a learning request raised by a customer for one or more students: the desired\nlanguage, teaching level and interest type, the requested time period and lesson frequency, the\nstudent's weekly availability slots, an optional preferred location, an estimated price and the\nteacher applications received for it. Soft-deleted interests are excluded unless you pass\n`showRemoved=true`. Filter with `searchTerm` (matches student, language and level) and by\n`languageId`, `levelId`, `interestTypeId`, `statusId`, `studentId`, and a `dateFrom`/`dateTo`\nrange (interests whose period overlaps it); the enum ids come from the matching `/lookups/...`\nendpoints.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (any returned\nproperty name, case-insensitive) and `desc` query parameters. The response body is a plain JSON\narray and the total count is returned in the `Content-Range` header (`items {start}-{end}/{total}`);\nthe status is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetInterestsOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "LanguageId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "LevelId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "InterestTypeId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "StatusId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "StudentId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "DateFrom",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "DateTo",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/InterestGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/InterestGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "Interest (Office)"
        ],
        "summary": "Create an interest.",
        "description": "Creates a new interest from the `InterestPost` body (requesting customer and students, language,\nlevel, interest type, period, frequency and availability) and returns the created `InterestGet`\nwith its generated id. Returns `400` with validation details if the body is invalid.",
        "operationId": "CreateInterestOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InterestPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InterestGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/interests/{id}": {
      "get": {
        "tags": [
          "Interest (Office)"
        ],
        "summary": "Get a single interest by id.",
        "description": "Returns a single interest by its id as an `InterestGet`, including its availability time slots and\nthe teacher applications received for it. Returns `404` if no interest with that id exists.",
        "operationId": "GetInterestOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InterestGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Interest (Office)"
        ],
        "summary": "Partially update an interest.",
        "description": "Partially updates an existing interest: only the fields present in the `InterestPatch` body are\nchanged, the rest stay as they are (this is also how the office records a decline, with status and\ndecline reason). Returns the updated `InterestGet`. Returns `404` if the interest does not exist\nand `400` on validation errors.",
        "operationId": "UpdateInterestOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InterestPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InterestGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Interest (Office)"
        ],
        "summary": "Delete an interest (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes an interest. By default this is a soft delete — the interest is hidden from normal listings\nbut can be restored; pass `?hard=true` to remove it permanently. Returns `204`, or `404` if the\ninterest does not exist.",
        "operationId": "DeleteInterestOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/interests/{id}/restore": {
      "post": {
        "tags": [
          "Interest (Office)"
        ],
        "summary": "Restore a soft-deleted interest.",
        "description": "Restores a previously soft-deleted interest so it appears in normal listings again. Returns `204`,\nor `404` if no such interest exists.",
        "operationId": "RestoreInterestOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/matching-interests": {
      "get": {
        "tags": [
          "MatchingInterest (Office)"
        ],
        "summary": "List matching interests (office).",
        "description": "Returns open interests matched against a teacher's qualifications, each as an `InterestGet` — the\ncandidate assignments a teacher could apply for. This view is teacher-driven: pass a `teacherId` to\nget the interests matching that teacher (without it the office route has no teacher to match against\nand returns no results). Soft-deleted interests are excluded unless you pass `showRemoved=true`.\nFilter further with `searchTerm` (matches student, language and level) and by `languageId`,\n`levelId` and `interestTypeId`, resolvable via the matching `/lookups/...` endpoints.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (any returned\nproperty name, case-insensitive) and `desc` query parameters. The response body is a plain JSON\narray and the total count is returned in the `Content-Range` header (`items {start}-{end}/{total}`);\nthe status is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetMatchingInterestsOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "LanguageId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "LevelId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "InterestTypeId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "TeacherId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/InterestGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/InterestGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "MatchingInterest (Office)"
        ],
        "summary": "Create a matching interest.",
        "description": "Creates a new interest from the `InterestPost` body and returns the created `InterestGet` with its\ngenerated id. Returns `400` with validation details if the body is invalid.",
        "operationId": "CreateMatchingInterestOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InterestPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InterestGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/matching-interests/{id}": {
      "get": {
        "tags": [
          "MatchingInterest (Office)"
        ],
        "summary": "Get a single matching interest by id.",
        "description": "Returns a single matching interest by its id as an `InterestGet`, including its availability time\nslots and teacher applications. Returns `404` if no interest with that id exists.",
        "operationId": "GetMatchingInterestOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InterestGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "MatchingInterest (Office)"
        ],
        "summary": "Partially update a matching interest.",
        "description": "Partially updates an existing interest: only the fields present in the `InterestPatch` body are\nchanged, the rest stay as they are. Returns the updated `InterestGet`. Returns `404` if the interest\ndoes not exist and `400` on validation errors.",
        "operationId": "UpdateMatchingInterestOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InterestPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InterestGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "MatchingInterest (Office)"
        ],
        "summary": "Delete a matching interest (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes an interest. By default this is a soft delete — the interest is hidden from normal listings\nbut can be restored; pass `?hard=true` to remove it permanently. Returns `204`, or `404` if the\ninterest does not exist.",
        "operationId": "DeleteMatchingInterestOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/matching-interests/{id}/restore": {
      "post": {
        "tags": [
          "MatchingInterest (Office)"
        ],
        "summary": "Restore a soft-deleted matching interest.",
        "description": "Restores a previously soft-deleted interest so it appears in normal listings again. Returns `204`,\nor `404` if no such interest exists.",
        "operationId": "RestoreMatchingInterestOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/enrollment-requests": {
      "get": {
        "tags": [
          "EnrollmentRequest (Office)"
        ],
        "summary": "List course enrollment requests (office).",
        "description": "Returns all customer-submitted course enrollment requests (office view — not row-restricted), each a\n`CourseEnrollmentRequestGet` with its status (Requested, Accepted, Declined, Withdrawn), request\ndate, the requesting customer, the requested students, any notes and decline reason, and the parent\ncourse. This is the office review queue: filter to `statusId` = Requested to see what awaits a\ndecision. Soft-deleted requests are excluded unless you pass `showRemoved=true`. Filter with\n`searchTerm` (matches notes and the requester name), `courseId`, `requestingCustomerId` and\n`statusId` (resolvable via `/lookups/course-enrollment-status`).\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50, `-50`\n= last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (a property name of the\nreturned object, case-insensitive) and `desc` query parameters. The body is a plain JSON array; the\ntotal count is returned in the `Content-Range` response header (`items {start}-{end}/{total}`). Status\nis `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetEnrollmentRequestsOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "CourseId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "RequestingCustomerId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "StatusId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CourseEnrollmentRequestGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CourseEnrollmentRequestGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      }
    },
    "/office/enrollment-requests/{id}": {
      "get": {
        "tags": [
          "EnrollmentRequest (Office)"
        ],
        "summary": "Get a single course enrollment request by id (office).",
        "description": "Returns a single course enrollment request by its id as a `CourseEnrollmentRequestGet`, including the\nrequesting customer, the requested students and the parent course. Returns `404` if no request with\nthat id exists.",
        "operationId": "GetEnrollmentRequestOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CourseEnrollmentRequestGet2"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "EnrollmentRequest (Office)"
        ],
        "summary": "Accept or decline an enrollment request (office decision).",
        "description": "Records the office decision on a pending request via the `CourseEnrollmentRequestPatch` body: set\n`statusId` to the Accepted or Declined option (from `/lookups/course-enrollment-status`), and — when\ndeclining — a `declineReason`. Only the fields present are changed. Returns the updated\n`CourseEnrollmentRequestGet`; `404` if the request does not exist; `400` on validation errors.\n\nACCEPTING ISSUES ONE INVITATION PER REQUESTED STUDENT, in the same save as the status change (with\nits material list and the notifications to the student and the customer). It is idempotent per\n(course, student), so accepting twice adds nothing. Declining issues nothing.\n\n`409` when a requested student is currently INACTIVE (paused): a paused student is not invited to\nanything, and the response names them. Resume them, or remove them from the request, and accept\nagain.",
        "operationId": "UpdateEnrollmentRequestOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CourseEnrollmentRequestPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CourseEnrollmentRequestGet2"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/customers": {
      "get": {
        "tags": [
          "Customer (Office)"
        ],
        "summary": "List customers (office).",
        "description": "Returns all customers in the system (office view â€” not row-restricted), each as a `CustomerGet`.\nA customer is a paying party â€” a company or a private person â€” with contact information, billing\naddress, active status and the students that belong to them. Soft-deleted customers are excluded\nunless you pass `showRemoved=true`. Filter with `searchTerm` (matches company name, contact first/\nlast name, email and phone) and by `customerTypeId`, `isActive`, and `city`/`postalCode`/`country`\nof the billing address.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (any returned\nproperty name, case-insensitive) and `desc` query parameters. The response body is a plain JSON\narray and the total count is returned in the `Content-Range` header (`items {start}-{end}/{total}`);\nthe status is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetCustomersOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "CustomerTypeId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "IsActive",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "City",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "PostalCode",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Country",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "AccountStatus",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/NullableOfEAccountStatus"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CustomerGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CustomerGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "Customer (Office)"
        ],
        "summary": "Create a customer.",
        "description": "Creates a new customer from the `CustomerPost` body (company or private person, with contact and\nbilling details) and returns the created `CustomerGet` with its generated id. Returns `400` with\nvalidation details if the body is invalid.",
        "operationId": "CreateCustomerOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/customers/{id}": {
      "get": {
        "tags": [
          "Customer (Office)"
        ],
        "summary": "Get a single customer by id.",
        "description": "Returns a single customer by its id as a `CustomerGet`, including contact info, billing address and\nthe customer's students. Returns `404` if no customer with that id exists.",
        "operationId": "GetCustomerOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Customer (Office)"
        ],
        "summary": "Partially update a customer.",
        "description": "Partially updates an existing customer: only the fields present in the `CustomerPatch` body are\nchanged, the rest stay as they are. Returns the updated `CustomerGet`. Returns `404` if the customer\ndoes not exist and `400` on validation errors.",
        "operationId": "UpdateCustomerOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Customer (Office)"
        ],
        "summary": "Delete a customer (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a customer. By default this is a soft delete â€” the customer is hidden from normal listings\nbut can be restored; pass `?hard=true` to remove it permanently. Returns `204`, or `404` if the\ncustomer does not exist.",
        "operationId": "DeleteCustomerOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/customers/{id}/resend-invitation": {
      "post": {
        "tags": [
          "Customer (Office)"
        ],
        "summary": "Re-send the customer's password-setup mail.",
        "description": "Triggers the onboarding (\"magic link\") mail again for a customer who has not completed sign-up, and\nrepairs a provisioning run that did not finish â€” a missing login account or a missing realm role is\ncreated on the way, which is how a customer whose `accountStatus` reads `Failed` is fixed. The mail\nis rendered in the customer's correspondence language.\n\nReturns `204` on success. `404` if no such customer exists, `400` if the contact has no email\naddress, `409` if the account already completed onboarding (a forgotten password is reset by the\nuser through the sign-in page, not from here) or if the email belongs to a login of the other\nportal, and `503` when no identity provider is configured for this environment.",
        "operationId": "ResendCustomerInvitationOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/customers/invite": {
      "post": {
        "tags": [
          "Customer (Office)"
        ],
        "summary": "Invite somebody who is not a customer yet to the portal.",
        "description": "Creates a customer from an EMAIL ALONE, provisions the login and sends the same onboarding\n(\"magic link\") mail every new customer receives. The person then completes their own record on the\nportal's \"Confirm your details\" screen - so the registration IS the profile confirmation, and there\nis no second onboarding mechanism and no public endpoint.\n\nThe body is a `CustomerInvitePost`: `email`, `customerTypeId` (company or private person - the portal's\nonboarding branches on it) and `correspondenceLanguageId` (the language the letter is written in).\nEverything else is left empty on purpose and stays that way until the person fills it in; the office\nsees the row immediately with `accountStatus` = `Invited` and `profileConfirmed` = false.\n\nReturns the created `CustomerGet`. `409` if a customer with that email already exists (use that\ncustomer's own \"re-send invitation\" instead) or if the address belongs to a login of the other portal;\n`400` if the body is invalid or a lookup id does not exist; `503` when no identity provider is\nconfigured for this environment.\n\nA PRIVATE person invited this way gets their matching student record as soon as they confirm their\ndetails - not now: a student needs a name and a birthdate, and one without a birthdate would count\nas a minor and could not confirm their own course invitations.",
        "operationId": "InviteCustomerOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerInvitePost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/customers/{id}/restore": {
      "post": {
        "tags": [
          "Customer (Office)"
        ],
        "summary": "Restore a soft-deleted customer.",
        "description": "Restores a previously soft-deleted customer so it appears in normal listings again. Returns `204`,\nor `404` if no such customer exists.",
        "operationId": "RestoreCustomerOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/invoices": {
      "get": {
        "tags": [
          "Invoice (Office)"
        ],
        "summary": "List invoices (office).",
        "description": "Returns all invoices in the system (office view — not row-restricted), each as an `InvoiceGet`.\nAn invoice bills a customer for courses and lessons: it carries a title, invoice number, invoice and\ndue dates, status, line items and a computed total amount. By default results are ordered with the\noverdue invoices first, then the actionable statuses (Draft, Sent, Paid, Cancelled) and newest\ninvoice date first. Every row carries `isOverdue` — past its due date and not yet settled, derived\non read rather than stored — and the list can be narrowed to those with `onlyOverdue=true`.\nSoft-deleted invoices are excluded unless you pass `showRemoved=true`. Filter with\n`searchTerm` (matches title, invoice number, the customer's purchase-order number and the customer\nname), by `statusId`, `customerId`, `invoiceNumberPrefix`, `customerTypeId` (company or private\nperson - the invoiced customer's own type), `purchaseOrderNumber` (a CONTAINS match on the\ncustomer's own reference), by the billing period via `periodYear` and/or `periodMonth` (each applied\nindependently - e.g. all invoices of a year, or of a given month), and by invoice-date, due-date\nand total-amount ranges; `statusId` and `customerTypeId` values come from the matching\n`/lookups/...` endpoints.\n\nAdditive fields on the response: `purchaseOrderNumber` (the customer's own order reference, optional\nand printed on the invoice), `notes` (OFFICE-INTERNAL, never printed and absent from the customer's\nview), `periodDisplay` (the billing period as one value, `2026/08`) and `customerType`.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (any returned\nproperty name, case-insensitive) and `desc` query parameters. The response body is a plain JSON\narray and the total count is returned in the `Content-Range` header (`items {start}-{end}/{total}`);\nthe status is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetInvoicesOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "StatusId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "OnlyOverdue",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "CustomerId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "InvoiceNumberPrefix",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "InvoiceDateFrom",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "InvoiceDateTo",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "DueDateFrom",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "DueDateTo",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "PeriodYear",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "PeriodMonth",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "CustomerTypeId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "PurchaseOrderNumber",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "MinTotalAmount",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "MaxTotalAmount",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/InvoiceGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/InvoiceGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "Invoice (Office)"
        ],
        "summary": "Create an invoice.",
        "description": "Creates a new invoice from the `InvoicePost` body (the billed customer, dates, the billing period\nas `periodYear` + `periodMonth`, and line items) and returns the created `InvoiceGet` with its\ngenerated id and computed total. The billing period (which month/year is billed, distinct from the\ninvoice date) is required: `periodYear` 2000-2100 and `periodMonth` 1-12. Returns `400` with\nvalidation details if the body is invalid.\n\nOptional: `purchaseOrderNumber` (the CUSTOMER's own order reference, printed on the invoice so their\naccounts department can match it - max 100 characters) and `notes` (a free text PRINTED on the invoice -\nthe customer reads it - max 2000 characters). Both appear on the customer-facing invoice DTO.",
        "operationId": "CreateInvoiceOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoicePost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/invoices/{id}": {
      "get": {
        "tags": [
          "Invoice (Office)"
        ],
        "summary": "Get a single invoice by id.",
        "description": "Returns a single invoice by its id as an `InvoiceGet`, including its line items and computed total\namount. Returns `404` if no invoice with that id exists.",
        "operationId": "GetInvoiceOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Invoice (Office)"
        ],
        "summary": "Partially update an invoice.",
        "description": "Partially updates an existing invoice: only the fields present in the `InvoicePatch` body are\nchanged, the rest stay as they are (e.g. moving the status to Sent or Paid). Returns the updated\n`InvoiceGet`. Returns `404` if the invoice does not exist and `400` on validation errors.\n\nThe optional texts follow the Remove-flag convention, because a null value alone means \"not part of\nthis patch\": clear the invoice number with `removeInvoiceNumber`, the customer's reference with\n`removePurchaseOrderNumber` and the internal note with `removeNotes`.",
        "operationId": "UpdateInvoiceOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoicePatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Invoice (Office)"
        ],
        "summary": "Delete an invoice (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes an invoice. By default this is a soft delete — the invoice is hidden from normal listings\nbut can be restored; pass `?hard=true` to remove it permanently. Returns `204`, or `404` if the\ninvoice does not exist.",
        "operationId": "DeleteInvoiceOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/invoices/{id}/restore": {
      "post": {
        "tags": [
          "Invoice (Office)"
        ],
        "summary": "Restore a soft-deleted invoice.",
        "description": "Restores a previously soft-deleted invoice so it appears in normal listings again. Returns `204`,\nor `404` if no such invoice exists.",
        "operationId": "RestoreInvoiceOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/invoices/{id}/send": {
      "post": {
        "tags": [
          "Invoice (Office)"
        ],
        "summary": "Send an invoice to its customer.",
        "description": "Sends the invoice: moves it `Draft` → `Sent`, records the moment on `lastEmailSentDate`, raises the\ncustomer's `customer.invoice_created` notification and stages the `invoice.tuition` letter. All of it\nis written in ONE save, so the list can never claim a send the outbox never received; delivery\nitself is asynchronous and its result appears in `/office/mail-history`.\n\nSending is a MANUAL decision and always has to be. Recording an invoice used to mail it as a side\neffect, which was tolerable while a person typed every one — and wrong as soon as the system\ncreates them itself (a confirmed material order, a package purchase, a group course leaving its\ndraft). Creating an invoice therefore writes nothing but the invoice, and this endpoint is the only\nway one reaches the customer.\n\nReturns `200` with the moment it was sent, `404` if no such invoice exists, and `409` when it may\nnot be sent: already `Sent` (use `/send-reminder` to chase it), `Paid`, `Cancelled`, or without a\ncustomer to send it to.",
        "operationId": "SendInvoiceOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceSentResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/invoices/send-period": {
      "post": {
        "tags": [
          "Invoice (Office)"
        ],
        "summary": "Send every unsent invoice of one period.",
        "description": "Sends every invoice of `{ year, month }` that is still a `Draft`, and returns an\n`InvoiceSendPeriodResultGet` naming each one with its own outcome.\n\n**Why the period is the unit.** A month-end close lays down one draft per billing target, so\nthirty is an ordinary number — pressing the per-invoice button thirty times is not a\nconfirmation. Each invoice goes through the same `POST /{id}/send`, so a series and a single\npress can never disagree about what sending does.\n\n**Selected by STATE**, never from a list the caller brings: an invoice that has moved on (sent,\npaid, cancelled) is never dragged out again, and a second run has nothing to do.\n\n**One failure does not stop the rest.** A customer with no address is reported with the API's\nown sentence in `reason` and the run finishes — thirty letters are thirty separate answers. This\nis the deliberate difference from the month-end close, which writes one database state\nall-or-nothing.\n\n`409` when the period holds no unsent invoice at all — the office only presses this while there\nIS something, so an empty run means they acted on a stale view. `400` for a month outside 1-12.",
        "operationId": "SendInvoicePeriodOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoiceSendPeriodPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceSendPeriodResultGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/invoices/{id}/credit-package-order": {
      "post": {
        "tags": [
          "Invoice (Office)"
        ],
        "summary": "Give a settled package's residual credit back on this invoice.",
        "description": "Puts the RESIDUAL CREDIT of a settled package order onto this invoice as a negative free position,\nand stamps the order as credited — both in one save, so a credit can never be printed on an invoice\nwhile the package still reads as owing it (or the other way round).\n\nWhy it exists: only whole lessons and amounts are ever charged, so a package regularly ends with a\nremainder too small to carry the next lesson. That remainder is neither lost nor refunded — it is\ncredited on the next invoice. Nothing gave it back before, because there is no invoice run to notice\na residual; the office finds the open ones on the customer's record (`isCreditOpen` on the package\norders, and `onlyOpenCredits=true` on `/office/package-orders`).\n\nThe amount comes from the live ledger, so it is the same figure shown next to it. Returns `200` with\nthe credited amount, `404` for an unknown invoice or order, and `409` when there is nothing to\ncredit: the package is not settled, nothing is left over, it has already been credited, or the\ninvoice belongs to a different customer.",
        "operationId": "CreditPackageOrderOnInvoiceOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreditPackageOrderRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PackageCreditGivenResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/invoices/{id}/send-reminder": {
      "post": {
        "tags": [
          "Invoice (Office)"
        ],
        "summary": "Send the payment reminder for an invoice.",
        "description": "Sends the customer the payment reminder for this invoice (mail template `invoice.reminder`) and\nrecords the moment on `lastEmailSentDate`. The mail row and the stamp are written together, so the\nlist can never claim a reminder the outbox never received; delivery itself is asynchronous and its\nresult appears in `/office/mail-history`.\n\nChasing is a MANUAL decision: there is no dunning run, no escalation levels and no rule about when\nan invoice becomes chaseable. The `isOverdue` flag on the list says which invoices are late; the\noffice decides which of them to remind, and may do so as often as it likes.\n\nAn invoice reaches the customer only through `/send`, so this is always a reminder and never\na first send. Returns `200` with the moment it was sent, `404` if no such invoice exists, and `409`\nwhen there is nothing to chase: a `Draft` was never sent, a `Paid` one is settled and a `Cancelled`\none is not owed.",
        "operationId": "SendInvoiceReminderOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceReminderSentResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/invoice-notices": {
      "get": {
        "tags": [
          "Invoice Notice (Office)"
        ],
        "summary": "List invoice notices (office).",
        "description": "Returns the office working notes on customers that have to be dealt with on one of their invoices,\neach as an `InvoiceNoticeGet`. A note carries free text, the customer it is about, and — once it has\nbeen ticked off — the invoice that dealt with it (`settledInvoiceId`, `settledInvoice`, `settledAt`)\nplus the derived `isSettled`.\n\nThese notes are OFFICE-ONLY. They are never printed on the invoice and never served to the customer\nportal: they are the office's own reminders about a customer, and the place they have to surface is\nthe invoice mask, while an invoice for that customer is being written.\n\nOrdered outstanding first, then newest. Soft-deleted notes are excluded unless you pass\n`showRemoved=true`. Filter with `searchTerm` (matches the note text), `customerId` (what both the\ncustomer dialog and the invoice mask ask for), `isSettled` (`false` = still outstanding, `true` =\nalready ticked off, omitted = both) and `settledInvoiceId` (the notes ticked off on one particular\ninvoice — the second half of what the invoice mask shows).",
        "operationId": "GetInvoiceNoticesOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "CustomerId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "IsSettled",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "SettledInvoiceId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/InvoiceNoticeGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/InvoiceNoticeGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "Invoice Notice (Office)"
        ],
        "summary": "Record a new invoice notice on a customer.",
        "description": "Creates an OUTSTANDING note on a customer: `customerId` and `text` are required, and there is\ndeliberately no way to create one already ticked off — a note exists because something still has to\nhappen. Returns `201` with the created note, or `400` when the payload is incomplete.",
        "operationId": "CreateInvoiceNoticeOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoiceNoticePost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceNoticeGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/invoice-notices/{id}": {
      "get": {
        "tags": [
          "Invoice Notice (Office)"
        ],
        "summary": "Get one invoice notice by id (office).",
        "description": "Returns a single `InvoiceNoticeGet`, including the settling invoice reference when the note has\nbeen ticked off. `404` when no such note exists.",
        "operationId": "GetInvoiceNoticeByIdOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceNoticeGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Invoice Notice (Office)"
        ],
        "summary": "Correct an invoice notice, or tick it off.",
        "description": "Updates the wording (`text`), or ticks the note off by naming the invoice that dealt with it\n(`settledInvoiceId`) — the settlement moment is stamped server-side, because it is the audit trail.\n`removeSettledInvoice: true` reopens it and clears both halves; sending both in one request is a\ncontradiction and answers `400`.\n\nThe invoice has to belong to the SAME customer as the note, otherwise the tick would point at a\nstranger's document — `400`. Returns `404` when no such note exists.",
        "operationId": "UpdateInvoiceNoticeOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoiceNoticePatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceNoticeGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Invoice Notice (Office)"
        ],
        "summary": "Delete an invoice notice.",
        "description": "Deletes a note — soft by default (restorable), permanently with `hard=true`, which needs the\n`hard-delete` role.\n\nOnly an OUTSTANDING note can be deleted. One that has been ticked off is part of the record of what\nwas done on a specific invoice, so it answers `409` and says to reopen it first. Returns `204` on\nsuccess and `404` when no such note exists.",
        "operationId": "DeleteInvoiceNoticeOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/invoice-notices/{id}/restore": {
      "post": {
        "tags": [
          "Invoice Notice (Office)"
        ],
        "summary": "Restore a soft-deleted invoice notice.",
        "description": "Undoes a soft delete. Returns `204`, or `404` when no such note exists.",
        "operationId": "RestoreInvoiceNoticeOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/qr-bill-creditor": {
      "get": {
        "tags": [
          "Office QR-Bill"
        ],
        "summary": "Get the QR-bill creditor — our own IBAN and address.",
        "description": "Returns the single creditor record used as the \"Account / Payable to\" of every QR-bill: the IBAN, the\naccount holder's name and the structured address (street, building number, postal code, town,\ntwo-letter country code), plus our VAT number when one is recorded.\n\nAlso returns what the system DERIVES from the IBAN and does not store, because none of it is\nvisible by looking at the number:\n\n* `ibanFormatted` — the IBAN in groups of four, the only form comparable with a bank statement.\n* `isIbanValid` — 21 characters, CH or LI, correct ISO 13616 check digits.\n* `isQrIban` — whether the IID (positions 5–9) falls in 30000–31999, which makes it a QR-IBAN.\n* `referenceType` — the reference every invoice will then carry: `Qrr` for a QR-IBAN, otherwise\n  `Scor`. This is a consequence of the account, never a choice.\n* `isConfigured` — whether a QR-bill can be issued at all.\n\nNever `404`: the record always exists (the migration creates it empty), so an unconfigured system\nanswers `200` with `isConfigured: false` rather than an absence. That is what lets the office mask\nopen on an empty form and say what is missing.",
        "operationId": "GetOfficeQrBillCreditor",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QrBillCreditorGet"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Office QR-Bill"
        ],
        "summary": "Partially update the QR-bill creditor.",
        "description": "Partially updates the creditor from the `QrBillCreditorPatch` body: only the fields present are\nchanged. Returns the updated record with its derived facts recomputed, so a caller that has just\nentered an IBAN immediately learns which reference type it implies.\n\nNormalisation on the way in: the IBAN is stripped of spaces (it may be pasted off a bank document as\n`CH44 3199 …`) and stored upper-case; a country prefix on the postal code is removed (`CH-8000`\nbecomes `8000`, which the standard requires); the country code is upper-cased.\n\n`street`, `buildingNumber` and `vatNumber` are cleared with `removeStreet` / `removeBuildingNumber` /\n`removeVatNumber` — a null value alone means \"not part of this patch\". Sending both a value and its\nremove flag is `400`. The MANDATORY fields have no remove flag: an IBAN or a name that could be\nblanked is an invoice run that stops working.\n\n`400` when the IBAN's check digits are wrong (the check that makes a transposed pair of digits\nimpossible to save), when a field exceeds the length the Swiss QR Code permits\n(name 70, street 70, building number 16, postal code 16, town 35, country 2), when the country code\nis not two letters, or when a value contains a character a Swiss QR Code cannot carry — the message\nnames the character.\n\nNote that a QR-IBAN is NOT required and never refused: both kinds are legitimate and the bank\ndecides which one we hold. The consequence is reported, not enforced.",
        "operationId": "UpdateOfficeQrBillCreditor",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QrBillCreditorPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QrBillCreditorGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/invoices/{id}/qr-bill": {
      "get": {
        "tags": [
          "Office QR-Bill"
        ],
        "summary": "What this invoice's QR-bill says — the payload, the reference, and any faults.",
        "description": "Returns the Swiss QR Code payload for one invoice WITHOUT rendering it, together with the reference\n(raw and grouped for reading), the formatted IBAN, the amount and the debtor's name.\n\n`payload` is the exact text encoded into the QR code, element separators included. It is carried\nverbatim rather than as parsed fields because the ORDER of the 31-plus elements is the\nspecification — a reader identifies each one by counting lines, so a shifted line changes what every\nlater element means.\n\nThe amount is the invoice's line items PLUS its free positions — the same total the document itself\nshows. The reference is derived from the invoice number: a QR reference (27 digits, mod-10 recursive\ncheck digit) for a QR-IBAN, an ISO 11649 Creditor Reference otherwise.\n\n`isValid` and `errors` report what the standard objects to, one sentence each — an unconfigured\ncreditor, an incomplete address, a reference that does not match the account type, a payload over\n997 characters. This endpoint always answers for an invoice that exists: an unconfigured creditor is\na preview with errors, not an absence, which is what lets a mask explain a refusal instead of\nshowing a bare conflict.\n\n`warnings` is the separate list of things that do NOT stop the bill: the DEBTOR had to be left off\n(an unresolvable country on the customer, or an incomplete billing address — the payer then writes\ntheir address into the blank box the standard provides), or the reference was dropped because the\ninvoice number does not fit a Creditor Reference, so the payment will have to be matched by hand.\nKept apart from `errors` because the two have opposite consequences: `errors` makes `isValid` false\nand the PDF endpoint refuse, a warning describes a bill that will be issued.\n\n`404` when the invoice does not exist.",
        "operationId": "GetOfficeInvoiceQrBill",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QrBillPreviewGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/invoices/{id}/qr-bill.pdf": {
      "get": {
        "tags": [
          "Office QR-Bill"
        ],
        "summary": "The QR-bill payment part and receipt as a print-ready PDF.",
        "description": "Returns the payment part and its receipt as a PDF — 210 × 105 mm (DIN long: a 62 mm receipt and a\n148 mm A6-landscape payment part), with the Swiss QR Code at exactly 46 mm carrying the 7 mm Swiss\ncross, and the prescribed headings in the customer's correspondence language (German, French,\nItalian or English; anything else falls back to German).\n\nThe document is vector throughout and embeds no font — it uses Helvetica, one of the 14 standard\nPDF fonts and one the Implementation Guidelines permit — so it is a few kilobytes and prints\nidentically everywhere. `Content-Disposition` offers it as `qr-bill-<invoice number>.pdf`.\n\n`409` when the invoice cannot be issued as a QR-bill, with the standard's own complaints in the\nmessage. This is a REFUSAL and not a best effort on purpose: an invalid payment part is\nindistinguishable from a valid one on paper and fails at the bank, weeks later. Use\n`GET /office/invoices/{id}/qr-bill` to find out what to fix before pressing the button.\n\n`404` when the invoice does not exist.",
        "operationId": "GetOfficeInvoiceQrBillPdf",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/billing-settings": {
      "get": {
        "tags": [
          "Office Billing Settings"
        ],
        "summary": "Get the billing settings — payment term, contact block and invoice texts.",
        "description": "Returns the single record that decides how an invoice is produced:\n\n* `paymentTermDays` — what a new invoice's due date is PREFILLED with, counted from its invoice\n  date. It is never applied afterwards: the due date is stored on the invoice and stays editable\n  there, so changing this default never moves an open document.\n* `phone`, `email`, `website` — the contact block printed in the letterhead.\n* `texts` — the intro, closing and footer paragraphs, one entry per correspondence language. A\n  language without an entry falls back to English; without that either, the paragraph is left off.\n* `logoDocumentId` / `logoFileName` / `logoContentType` — the letterhead logo, or null when\n  none. Fetch the file from `GET /office/billing-documents/{id}`. The content type matters\n  beyond display: the PDF writer will embed a PNG or JPEG and can never draw an SVG.\n\nPlus what is DERIVED and not stored:\n\n* `isConfigured` — whether an invoice can be produced from these settings.\n* `isIssuerConfigured` — whether the ISSUER record (`/office/qr-bill-creditor`) holds an IBAN, a\n  name and an address. Carried here although it belongs to that record, so a mask can say \"the\n  invoice cannot be produced yet, and the reason is over there\".\n\nThe issuer's name, address and IBAN are deliberately NOT part of this record: they are read from\nthe QR-bill creditor, so the letterhead and the QR code cannot drift apart.\n\nNever `404`: the record always exists (the migration creates it with its defaults), so a fresh\nsystem answers `200` rather than an absence.",
        "operationId": "GetOfficeBillingSettings",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingSettingsGet"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Office Billing Settings"
        ],
        "summary": "Partially update the billing settings.",
        "description": "Partially updates the record from the `BillingSettingsPatch` body: only the fields present are\nchanged. Returns the updated record with its derived facts recomputed.\n\n`phone`, `email` and `website` are cleared with `removePhone` / `removeEmail` / `removeWebsite` —\na null value alone means \"not part of this patch\". Sending both a value and its remove flag is\n`400`. `paymentTermDays` has no remove flag: an invoice run without a\npayment term is a run that stops working.\n\n**The texts are an UPSERT PER LANGUAGE, never a replace of the set.** A language that is not in\n`texts` is left exactly as it is, so saving the German paragraphs cannot silently drop the English\nones. Within one entry all three paragraphs are written as sent, empty included — that is how a\nsingle paragraph is cleared. Removing a language's row entirely is `textLanguageIdsToRemove`, and\nthe recipient then falls back to English.\n\n**The only thing this patch does to the logo is `removeLogoDocumentId`**, which takes it off the\nletterhead and DESTROYS the file; invoices are then printed without one, which is a legitimate\nstate. There is no field for choosing a logo because there is nothing to choose from — exactly\none file exists at a time and it arrives through `POST /office/billing-settings/logo`.\n\n`400` when the payment term is outside 1–365, the e-mail address is\nnot one, a paragraph exceeds its length (intro/closing 2000, footer 500), the same language\nappears twice in `texts`, or a language is written and removed in the same request.",
        "operationId": "UpdateOfficeBillingSettings",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BillingSettingsPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingSettingsGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/billing-settings/logo": {
      "post": {
        "tags": [
          "Office Billing Settings"
        ],
        "summary": "Upload the invoice letterhead logo.",
        "description": "Stores the logo printed top left on an invoice and points the billing settings at it. Multipart,\nfield name `file`.\n\nAccepted: **SVG, PNG, JPEG**, at most 2 MB. The cap is an eighth of what a scanned certificate\ngets, and deliberately: this file is drawn on EVERY invoice.\n\n**The format decides where the file can be used.** The office UI shows any of the three. The PDF\nwriter embeds a JPEG verbatim (`/DCTDecode`) and cannot draw an SVG at all — so an SVG is the\nright thing to keep for the screen and the wrong thing to expect on the document. The response\ncarries the content type back so a mask can say which of the two applies.\n\n**IRREVERSIBLE.** Replacing DESTROYS the previous file — exactly one exists at a time and no\nhistory is kept. An invoice PDF is stored with the logo inside its own bytes, so a later upload\ncannot restate a document that has gone out; keeping predecessors bought nothing the design\ncould name.\n\n`400` for an empty file, one over the limit, or a format that is not one of the three — the\nmessage names which.",
        "operationId": "UploadOfficeInvoiceLogo",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "required": [
                  "file"
                ],
                "type": "object",
                "properties": {
                  "file": {
                    "$ref": "#/components/schemas/IFormFile"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadedLogo"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/billing-documents/{id}": {
      "get": {
        "tags": [
          "Office Billing Settings"
        ],
        "summary": "Download an invoicing configuration file (the logo).",
        "description": "Streams the file out of the database straight into the response — never materialised as a byte\narray, so a download costs a buffer rather than a copy of the file per concurrent request.\n\nThere is only ever one such file — the logo currently on the letterhead. Its id is\n`logoDocumentId` on the billing settings, and a replacement gets a NEW id, which is what makes\nthis address safe to cache.\n\n`404` when no such file exists.",
        "operationId": "GetOfficeBillingDocument",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/payroll-settings": {
      "get": {
        "tags": [
          "Office Billing Settings"
        ],
        "summary": "Get the company-wide payroll rates a payslip starts from.",
        "description": "Returns the single record holding the employee contribution rates (`ahvRate`, `alvRate`,\n`ktgRate`, `nbuRate`, all percentages of the gross), the `vacationSupplementRate` — which is\ncalculated on the LESSON pay only, never on the fixed part — and the flat\n`travelAllowancePerLesson`.\n\nThese are DEFAULTS, not applied values. A payroll run writes them onto a new payslip once, and\nfrom then on that payslip carries its own figures, so correcting a rate here never rewrites a\nmonth that has already been paid. The run resolves each value in a fixed order: the teacher's\nPREVIOUS payslip wins (a figure corrected by hand is a decision), then the teacher's own record\nfor what it holds (the BVG amount, whether withholding tax applies), then these rates.\n\n`ratesConfirmedAt` and the derived `isConfigured` exist because ZERO is a legitimate rate — a\ncontribution that does not apply is entered as zero. Without the stamp there would be no way to\ntell \"all zero on purpose\" from \"nobody has filled this in\", and the payroll run must refuse the\nsecond while accepting the first.\n\nNever `404`: the record always exists.",
        "operationId": "GetOfficePayrollSettings",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PayrollSettingsGet"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Office Billing Settings"
        ],
        "summary": "Partially update the payroll rates.",
        "description": "Partially updates the rates from the `PayrollSettingsPatch` body: only the fields present are\nchanged. **Every save stamps `ratesConfirmedAt`** — saving this mask IS the confirmation, and it\nis what makes the record usable by a payroll run.\n\nThere are no remove flags: a rate has no absent state, and zero is how \"this contribution does\nnot apply\" is expressed.\n\n`400` when a percentage is outside 0–100 or the travel allowance outside 0–1000. Nothing checks a\nrate against the current federal figure — those change by decision, and a validator carrying last\nyear's number would refuse this year's truth.",
        "operationId": "UpdateOfficePayrollSettings",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PayrollSettingsPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PayrollSettingsGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/monthly-closes/current": {
      "get": {
        "tags": [
          "Office Monthly Close"
        ],
        "summary": "Which period is next, whether it may be closed, and any run in flight.",
        "description": "What the month-end closing page and the dashboard tile poll. Answered from the runs table\nALONE — no ledger, no lessons — so it is instant and can be asked every couple of seconds.\n\n* `year`/`month`/`periodDisplay` — the period a close would settle **now: the PREVIOUS month,\n  always**. Press on the 1st of October and September is closed. There is no way to choose a\n  period: one whose month is typed in is one you can run twice for the same month and never for\n  the one in between.\n* `canRun` — whether the button may be pressed.\n* `reason` — why not, and the two cases are deliberately different sentences. \"Already closed\n  on …\" is permanent; \"a close is running\" means wait. A single message would make a running\n  close look like a settled month.\n* `closedBy` — the successful run that settled this period, with its date and counters.\n* `inFlight` — the run currently working, for ANY period, with its `phase`, `done` and `total`.\n* `latest` — the newest run of any kind, so the page can show the last outcome without a\n  second call.\n\nThe expensive verdict — unrecorded attendance, unconfirmed payroll rates — is deliberately NOT\nhere: computing it is the same work as the run itself, so a page that showed it on opening\nwould be exactly as slow as closing the month. Ask for a `checkOnly` run instead.",
        "operationId": "GetOfficeMonthlyCloseStatus",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MonthlyCloseStatusGet"
                }
              }
            }
          }
        }
      }
    },
    "/office/monthly-closes": {
      "get": {
        "tags": [
          "Office Monthly Close"
        ],
        "summary": "The history of month-end closes, newest first.",
        "description": "Every run: the closes, the checks and the ones that failed. `state` is DERIVED from the run's\nstamps (`queued` / `running` / `finished` / `failed`), and `stateDisplay` says what the outcome\nactually was — a FINISHED run that found a blocker reads **Refused**, because \"finished\" over\nan unsettled month is the most misleading thing this list could say.\n\n`limit` defaults to 50 and is capped at 200.",
        "operationId": "ListOfficeMonthlyCloses",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 50
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MonthlyCloseGet2"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MonthlyCloseGet2"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "Office Monthly Close"
        ],
        "summary": "Close the previous month — queued, and answered at once.",
        "description": "Writes the request and hands it to the background worker. Answers **`202 Accepted`** with the\nrun, which the caller then polls (`GET /office/monthly-closes/{id}` or `/current`). **The body\ncarries no period**: the server takes the previous month.\n\n**What the run writes, in ONE save:**\n\n* an **`InvoiceItem` per lesson position of the period** — package-covered ones included — with\n  the amount and the coverage that was decided. That is the freeze: from then on the month\n  recalculates identically (calculation spec §9.12), and the row's existence is what stops a\n  second close from charging the same lesson again.\n* one **DRAFT invoice per recipient**. A budget-funded position goes to the budget's SPONSOR (a\n  budget is a company-funded grant, invoiced for what was actually used), an uncovered one to\n  the student's own customer, and a package-covered one to nobody.\n* one **DRAFT payslip per teacher** with payable lessons, with its rate groups and lesson\n  links, plus every `Approved`/`Adjusted` expense claim that had no period yet. A draft payslip\n  is invisible to its teacher and unpayable by the bank export until\n  `POST /office/teacher-payments/release-period`.\n* the per-position **markers** that stop a second close from charging the same thing again.\n\nThen, in a SECOND phase with its own saves, it draws each invoice's **PDF**. That phase is\nallowed to fail: a missing document heals itself (the office download renders live, the send\nfreezes a fresh one), a missing `InvoiceItem` does not — which is why the order cannot be\nreversed. `documentsProduced` says how many were drawn.\n\n**It never sends anything.** Every invoice is a Draft; `POST /office/invoices/{id}/send` and\n`POST /office/invoices/send-period` remain the only ways to the customer.\n\n**What stops it** is reported on the run as `blockers`, not as an error — a refused run finished\nperfectly well and wrote nothing, so the period stays open on purpose:\n\n* **any lesson OF THE PERIOD whose class list is incomplete.** Such a lesson bills nothing at\n  all — neither the student nor the teacher — so closing around it would leave it unbilled for\n  good. `missingAttendance` names up to 200 of them and `missingAttendanceTotal` says how many\n  there are; the lesson list filtered by `attendanceComplete=false` is exactly this set. Gaps\n  BEFORE the period are counted in `missingAttendanceEarlier` and never block: one forgotten\n  lesson anywhere in history would otherwise make the school unable to close any month again.\n* **unconfirmed company payroll rates.** Zero is a valid rate, so SAVING that mask is what says\n  the rates are deliberate — and since one act settles the month, this stops the invoices too.\n\n`checkOnly: true` runs the same gather and reports the same blockers WITHOUT writing anything\nand without locking the period. It is the same job rather than a synchronous preview because the\ngather is the expensive half.\n\n`409` when the period is already closed (a period is settled once; anything that came to light\nafterwards is maintained by hand), and while any close is still in flight — two runs would read\nthe same ledger twice.",
        "operationId": "RequestOfficeMonthlyClose",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MonthlyClosePost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MonthlyCloseGet2"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/monthly-closes/{id}": {
      "get": {
        "tags": [
          "Office Monthly Close"
        ],
        "summary": "One run of the month-end close.",
        "description": "The full record of a run: its period, its state, its progress, its counters, and — when it was\nrefused — the `blockers` plus the lessons named in `missingAttendance`.\n\n`404` when there is no such run.",
        "operationId": "GetOfficeMonthlyClose",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MonthlyCloseGet2"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/monthly-closes/{id}/rerun-impact": {
      "get": {
        "tags": [
          "Office Monthly Close"
        ],
        "summary": "What re-running that closed period would discard, and what it would keep.",
        "description": "Read-only: no ledger, three counting queries, cheap enough for a confirmation dialog to open on.\nRequires the office portal AND the `office-monthly-close-rerun` realm role; anyone else gets\n`403`.\n\nIt exists because the discard is PERMANENT. `discardedInvoices` and `discardedPayslips` name\nwhat would be deleted — number, customer, amount — rather than counting it, because \"3 invoices\nwill be deleted\" is a figure nobody can check.\n\n`keptInvoices` and `keptPayslips` are the other half, each with the REASON it survives, and\nthat half is the evidence that \"everything beyond draft stays as it is\" actually holds. An\ninvoice is kept when it has been sent, paid or cancelled, when it was sent and only put back to\ndraft to be corrected (the customer holds a copy), when it carries free positions somebody\nentered by hand, or when a package's residual credit was given back on it. A payslip is kept\nwhen it has been released or paid out, or carries hand-entered free positions.\n\n`frozenPositionsKept` is the frozen `InvoiceItem` rows of the period. They are NEVER touched: a\nre-run recomputes from them, and deleting one would let a package be consumed a second time.\n\n`404` when there is no such run.",
        "operationId": "GetOfficeMonthlyCloseRerunImpact",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MonthlyCloseRerunImpactGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    },
    "/office/monthly-closes/{id}/rerun": {
      "post": {
        "tags": [
          "Office Monthly Close"
        ],
        "summary": "Discard that run's still-draft work and close the period again. Answers 202.",
        "description": "Requires the office portal AND the `office-monthly-close-rerun` realm role; anyone else gets\n`403`. A period is otherwise settled ONCE — correct for the business, and unusable for\nclick-testing, which is what this exists for.\n\n**The run to replace is named in the route, not a period.** That is unambiguous, it yields the\ndiscard set for free, it cannot address a month nobody closed, and it survives the month\nrollover — a period-less route would be dead from the 1st, because the period is always \"the\nprevious month\".\n\n**`discardDrafts` must be `true`.** Without it the answer is `409` listing the invoice numbers\nthat would go: the delete cannot be undone, so the question is the refusal. Use\n`GET /office/monthly-closes/{id}/rerun-impact` to see what it covers first.\n\n**What it discards** is what the run it replaces CREATED, and only while that is still a draft.\nNever \"the drafts of the period\": a material Sofortrechnung, a package-purchase invoice and a\nhand-typed invoice all sit in the same period and a re-run recreates none of them.\n\n**What it keeps** is everything beyond draft — see the impact route for the full list of reasons.\nTheir positions stay settled, so nothing is billed twice.\n\n**The frozen positions stay**, and a re-run therefore RECOMPUTES rather than reproduces: an\nadopted position gets its amount and coverage from a fresh gather, so a corrected price or a\nbudget released since is picked up. \"A closed month recalculates identically\" holds for as long\nas nobody closes it again.\n\nThen it walks the ordinary close: same queue, same worker, same two phases, same blockers. If a\nblocker fires NOTHING is discarded — the checks run before the discard, so a forgotten\nattendance cannot leave the month with its invoices deleted and nothing to show.\n\n`409` while any close is in flight, when the period is not closed (\"nothing to re-run\"), and\nwhen the named run is no longer the one holding the period — somebody re-closed it and the\ncaller is looking at a stale screen. `404` when there is no such run.",
        "operationId": "RerunOfficeMonthlyClose",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MonthlyCloseRerunPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MonthlyCloseGet2"
                }
              }
            }
          },
          "404": {
            "description": "Not Found"
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/invoices/{id}/invoice.pdf": {
      "get": {
        "tags": [
          "Office Invoices"
        ],
        "summary": "The invoice as a print-ready PDF.",
        "description": "Returns the invoice as an A4 PDF with the QR-bill payment part at the foot of its last page — one\ndocument to print, fold and post, rather than an invoice and a payment slip to keep together.\n\n**Before the invoice is sent this is rendered LIVE from the current data**, and nothing is stored:\nthe point of looking at an invoice before sending it is to see what it says now. **Once it has\nbeen sent the frozen snapshot is returned instead** — from that moment \"the invoice\" means the\npaper the customer is holding, and a re-render would show a document that has quietly changed\nsince (a corrected address, a new logo, an edited position).\n\nThe layout is the office's own: the logo top left, the invoice date / due date / number top\nright, the sender and recipient addresses below that, then the heading naming the period, then the\npositions. A sponsor's invoice is grouped by BUDGET, and every lesson line carries the day it was\nheld so the payer can check it against their own calendar.\n\nWording is English throughout; the three free paragraphs from `/office/billing-settings` carry the\nrecipient's own language.\n\nTwo things are LEFT OUT rather than refused, because an invoice that cannot be produced is worse\nthan one missing a decoration: a logo the PDF cannot draw (an SVG) is replaced by the sender's\nname set in type, and an invoice that is not a legal QR-bill (no IBAN configured) is produced\nwithout the payment part. `GET /office/invoices/{id}/qr-bill` says what to fix.\n\n`404` when the invoice does not exist.",
        "operationId": "GetOfficeInvoicePdf",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/budgets": {
      "get": {
        "tags": [
          "Budget (Office)"
        ],
        "summary": "List budgets (office).",
        "description": "Returns all budgets in the system (office view — not row-restricted), each as a `BudgetGet`.\nA budget is a sponsorship allocation a sponsor customer (typically a company) sets up for a\nbeneficiary customer; it tracks the total allocated amount together with the consumed, invoiced\nand remaining amounts and the consumption line items (lessons and materials) booked against it.\nSoft-deleted budgets are excluded unless you pass `showRemoved=true`. Filter with `searchTerm`\n(matches the description and the sponsor/beneficiary names), by `sponsorCustomerId` or\n`beneficiaryCustomerId`, by total/consumed amount ranges, and by `hasAverageLessonPrice`.\n\nEach row may carry an **average lesson price** (`averageLessonPrice`) — a plain amount the office\ntypes in, with no price list behind it — which turns the budget into `estimatedTotalLessons` /\n`estimatedRemainingLessons`. Those are an ESTIMATE and nothing is billed from them: a lesson is\ncharged at its own course's price on the day it was held. A budget without it reports `null` for\nboth (not zero: \"we cannot say\" is not \"none left\"); `hasAverageLessonPrice=false` lists exactly\nthose.\n\nThe **sponsor is always a COMPANY** (\"Budgets are company-funded grants\"); a private customer is\nrefused with `400`. Sponsor and beneficiary MAY be the same customer — a company funding its own\ntuition.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (any returned\nproperty name, case-insensitive) and `desc` query parameters. The response body is a plain JSON\narray and the total count is returned in the `Content-Range` header (`items {start}-{end}/{total}`);\nthe status is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetBudgetsOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "SponsorCustomerId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "BeneficiaryCustomerId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "MinTotalAmount",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "MaxTotalAmount",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "HasAverageLessonPrice",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "IsOverdrawn",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/BudgetGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/BudgetGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "Budget (Office)"
        ],
        "summary": "Create a budget.",
        "description": "Creates a new budget from the `BudgetPost` body (sponsor and beneficiary customers, total amount\nand any initial line items) and returns the created `BudgetGet` with its generated id.\n\nTwo optional fields are worth knowing about. **`averageLessonPrice`** is what the lesson estimate is\nworked out from (see the list endpoint); a negative amount is `400`. **`activatedAt`** is the day the\nbudget was RELEASED to the beneficiary —\nthe start of its day-precise coverage window — and may be back-dated, because a budget is agreed on\none day and entered on another; a date in the future is `400`. Unstated, it is today when the budget\nis created active.\n\nReturns `400` with validation details if the body is invalid.",
        "operationId": "CreateBudgetOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BudgetPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BudgetGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/budgets/{id}": {
      "get": {
        "tags": [
          "Budget (Office)"
        ],
        "summary": "Get a single budget by id.",
        "description": "Returns a single budget by its id as a `BudgetGet`, including its consumption line items and the\ncomputed remaining amount. Returns `404` if no budget with that id exists.",
        "operationId": "GetBudgetOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BudgetGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Budget (Office)"
        ],
        "summary": "Partially update a budget.",
        "description": "Partially updates an existing budget: only the fields present in the `BudgetPatch` body are changed,\nthe rest stay as they are.\n\nThe **average lesson price** follows the Remove-flag convention: a null `averageLessonPrice` means\n\"not part of this patch\", so clearing it needs `removeAverageLessonPrice: true`. A negative amount\nis `400`. **`activatedAt`** corrects the day the budget was released — it is the start of the\nday-precise coverage window, so moving it changes which lessons the budget paid for; a date in the\nfuture is `400`.\n\nReturns the updated `BudgetGet`. Returns `404` if the budget does not exist and `400` on validation\nerrors.",
        "operationId": "UpdateBudgetOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BudgetPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BudgetGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Budget (Office)"
        ],
        "summary": "Delete a budget (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a budget. By default this is a soft delete — the budget is hidden from normal listings but\ncan be restored; pass `?hard=true` to remove it permanently. Returns `204`, or `404` if the budget\ndoes not exist.",
        "operationId": "DeleteBudgetOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/budgets/{id}/restore": {
      "post": {
        "tags": [
          "Budget (Office)"
        ],
        "summary": "Restore a soft-deleted budget.",
        "description": "Restores a previously soft-deleted budget so it appears in normal listings again. Returns `204`,\nor `404` if no such budget exists.",
        "operationId": "RestoreBudgetOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/student-invites": {
      "get": {
        "tags": [
          "StudentInvite (Office)"
        ],
        "summary": "List student invites (office).",
        "description": "Returns all student invitations to join a course (office view — not row-restricted), each as a\n`StudentInviteGet` with the invited student, the target course and schedule, current status, when it\nwas sent and responded to, the accepted/declined timestamps, and the material-choice workflow\n(per-material confirmation, chosen delivery method and delivery address).\nSoft-deleted invites are excluded unless you pass `showRemoved=true`. Filter with `searchTerm`\n(matches student name and course name), `courseId`, `studentId` and `statusId`.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (a property name\nof the returned object, case-insensitive) and `desc` query parameters. The body is a plain JSON\narray; the total count is returned in the `Content-Range` response header (`items {start}-{end}/{total}`).\nStatus is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetStudentInvitesOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "CourseId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "StudentId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "StatusId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/StudentInviteGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/StudentInviteGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "StudentInvite (Office)"
        ],
        "summary": "Create a student invite.",
        "description": "Creates a new student invite from the `StudentInvitePost` body (the student and the course to invite\nthem to) and returns the created `StudentInviteGet` with its generated id. Creating it also seeds\nthe course's material list onto the invitation and notifies the student and their customer. Returns\n`400` with validation details if the body is invalid, and `409` when the student is currently\nINACTIVE (paused) — a paused student is not invited to anything.\n\n`409` as well when the course is **FULL**. An open invitation TAKES a place, so the count is\neverybody enrolled plus everybody holding an unanswered invitation: without that, ten invitations\ncould be issued for five places and somebody who accepted would have to lose theirs again. The\nlimit applies to group courses that state a `maxStudents`; the office can still overbook by\nenrolling a student directly on the course (a decision made with the numbers in view).",
        "operationId": "CreateStudentInviteOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StudentInvitePost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StudentInviteGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/student-invites/{id}": {
      "get": {
        "tags": [
          "StudentInvite (Office)"
        ],
        "summary": "Get a single student invite by id.",
        "description": "Returns a single student invite by its id as a `StudentInviteGet`, including its material choices and\ndelivery details. Returns `404` if no student invite with that id exists.",
        "operationId": "GetStudentInviteOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StudentInviteGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "StudentInvite (Office)"
        ],
        "summary": "Partially update a student invite.",
        "description": "Partially updates an existing student invite: only the fields present in the `StudentInvitePatch` body\nare changed, the rest stay as they are (for example to change the status or the material/delivery\nchoices). Returns the updated `StudentInviteGet`. `404` if it does not exist; `400` on validation errors.\n\nThe office answers on the confirmer's behalf — a customer who phones in — so this produces the same\ndata as the portals do: setting the status to accepted **enrols** the student, the articles answered\nas needed are **ordered**, and declining declines the whole material list. Unlike the portals it is\nnot blocked by an incomplete list (setting the status is an administrative act). **How material is\ndelivered may be stated either way**: per article in `materialChoices[].deliveryMethodId`, or once for\nthe whole confirmation in the top-level `deliveryMethodId` — per article wins, then the one for the\nwhole confirmation, then whatever the invitation already holds. An article that ends up with no\ndelivery method at all is a `400` naming it; anything going by mail needs an address.",
        "operationId": "UpdateStudentInviteOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StudentInvitePatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StudentInviteGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "StudentInvite (Office)"
        ],
        "summary": "Delete a student invite (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a student invite. By default this is a soft delete — the invite is hidden from normal listings\nbut can be restored; pass `?hard=true` to remove it permanently. Returns `204`, or `404` if it does\nnot exist.",
        "operationId": "DeleteStudentInviteOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/student-invites/{id}/restore": {
      "post": {
        "tags": [
          "StudentInvite (Office)"
        ],
        "summary": "Restore a soft-deleted student invite.",
        "description": "Restores a previously soft-deleted student invite so it appears in normal listings again. Returns\n`204`, or `404` if no such student invite exists.",
        "operationId": "RestoreStudentInviteOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/material-change-requests": {
      "get": {
        "tags": [
          "MaterialChangeRequest (Office)"
        ],
        "summary": "List material change requests (office).",
        "description": "Returns every material change request — one per student per round — each with the course whose\nlist changed, the student it was put to and their customer, when it was raised and answered, the\narticles it is about with their DERIVED fulfilment status, and the delivery method and address\nconfirmed for the round.\n\nFilter with `isAnswered` (the working question — what is still waiting), `courseId`, `studentId`,\n`customerId`, `materialId`, `requestedFrom`/`requestedTo`, and `searchTerm` (student and course\nname). Soft-deleted rows are excluded unless you pass `showRemoved=true`.\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50, `-50` = last\n50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (a property name of the returned\nobject, case-insensitive) and `desc` query parameters. The body is a plain JSON array; the total count is\nreturned in the `Content-Range` response header (`items {start}-{end}/{total}`). Status is `206` when a\n`Range` is sent, `416` if the range is invalid.",
        "operationId": "GetMaterialChangeRequestsOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "IsAnswered",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "CourseId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "StudentId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "CustomerId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "MaterialId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "RequestedFrom",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "RequestedTo",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MaterialChangeRequestGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MaterialChangeRequestGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      }
    },
    "/office/material-change-requests/{id}": {
      "get": {
        "tags": [
          "MaterialChangeRequest (Office)"
        ],
        "summary": "Get a single material change request by id.",
        "description": "Returns one request with its articles. `404` if no request with that id exists.",
        "operationId": "GetMaterialChangeRequestOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MaterialChangeRequestGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "MaterialChangeRequest (Office)"
        ],
        "summary": "Answer a material change request on the confirmer's behalf.",
        "description": "Answers the request, or corrects an answer already given.\n\nSend one entry per article in `answers`; articles left out keep what they had, so a round may legitimately\nbe answered in two sittings. `alreadyInPossession` is REQUIRED on every entry — omitting it would arrive\nas `false`, which reads as \"please order it\", and a book must never be bought by saying nothing.\n\nThe delivery method may be stated per article or once for the whole round (`deliveryMethodId`); the\nper-article value wins. An article that is NOT already owned needs an effective method, and anything going\nby mail needs a `deliveryAddress` — both are `400` with the article named, because neither can be decided\nfrom one article alone.\n\nAnswering the LAST open article closes the round and produces the material order, in the same transaction:\nan answered round can never exist without its order. It is billed on the next monthly invoice, not on a\nmaterial invoice of its own — this is Fall 2 by definition.\nThe OFFICE path exists for the customer who phones in — the same act, recorded by somebody else.",
        "operationId": "UpdateMaterialChangeRequestOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MaterialChangeRequestPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MaterialChangeRequestGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teacher-invites": {
      "get": {
        "tags": [
          "TeacherInvite (Office)"
        ],
        "summary": "List teacher invites (office).",
        "description": "Returns all teacher invitations to teach a course (office view — not row-restricted), each as a\n`TeacherInviteGet` with the invited teacher, the target course and schedule, current status, and when\nit was sent and responded to.\nBy default only pending invites are returned; set `showPendingOnly=false` to include all statuses, or\npass a specific `statusId` (which takes precedence). Soft-deleted invites are excluded unless you pass\n`showRemoved=true`. Filter also with `searchTerm` (matches teacher name and course name), `courseId`\nand `teacherId`.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (a property name\nof the returned object, case-insensitive) and `desc` query parameters. The body is a plain JSON\narray; the total count is returned in the `Content-Range` response header (`items {start}-{end}/{total}`).\nStatus is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetTeacherInvitesOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "CourseId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "TeacherId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "StatusId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "ShowPendingOnly",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TeacherInviteGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TeacherInviteGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "TeacherInvite (Office)"
        ],
        "summary": "Create a teacher invite.",
        "description": "Creates a new teacher invite from the `TeacherInvitePost` body (the teacher and the course to invite\nthem to) and returns the created `TeacherInviteGet` with its generated id. Returns `400` with\nvalidation details if the body is invalid.",
        "operationId": "CreateTeacherInviteOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TeacherInvitePost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeacherInviteGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teacher-invites/{id}": {
      "get": {
        "tags": [
          "TeacherInvite (Office)"
        ],
        "summary": "Get a single teacher invite by id.",
        "description": "Returns a single teacher invite by its id as a `TeacherInviteGet`. Returns `404` if no teacher invite\nwith that id exists.",
        "operationId": "GetTeacherInviteOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeacherInviteGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "TeacherInvite (Office)"
        ],
        "summary": "Partially update a teacher invite.",
        "description": "Partially updates an existing teacher invite: only the fields present in the `TeacherInvitePatch` body\nare changed, the rest stay as they are (for example to change the status). Returns the updated\n`TeacherInviteGet`. `404` if it does not exist; `400` on validation errors.",
        "operationId": "UpdateTeacherInviteOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TeacherInvitePatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeacherInviteGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "TeacherInvite (Office)"
        ],
        "summary": "Delete a teacher invite (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a teacher invite. By default this is a soft delete — the invite is hidden from normal listings\nbut can be restored; pass `?hard=true` to remove it permanently. Returns `204`, or `404` if it does\nnot exist.",
        "operationId": "DeleteTeacherInviteOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teacher-invites/{id}/restore": {
      "post": {
        "tags": [
          "TeacherInvite (Office)"
        ],
        "summary": "Restore a soft-deleted teacher invite.",
        "description": "Restores a previously soft-deleted teacher invite so it appears in normal listings again. Returns\n`204`, or `404` if no such teacher invite exists.",
        "operationId": "RestoreTeacherInviteOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teacher-salaries": {
      "get": {
        "tags": [
          "TeacherSalary (Office)"
        ],
        "summary": "List teacher salaries (office).",
        "description": "Returns the teacher salary-rate configuration (office view — not row-restricted), each row as a\n`TeacherSalaryGet`: the hourly rate that applies for a given teaching level and course type, whether\nit is for children courses, an optional description, and — for user-defined rows — a custom product\nname. These rates drive how teacher payments are calculated.\nSoft-deleted rates are excluded unless you pass `showRemoved=true`. Filter with `searchTerm`\n(matches the description), `levelId` and `courseTypeId` (resolvable via the corresponding lookups),\n`isForChildren`, and the `hourlyRateMin`/`hourlyRateMax` range.\n\nRates are VERSIONED, and a lesson's salary is computed with the rate that was valid on the DAY of that\nlesson — so re-reading a past month reports the same figures however often the matrix has changed\nsince. Each row reports the rate valid today plus `effectiveFrom` and, for an announced change,\n`pendingChangeFrom`/`pendingChangeAmount`. Pass `asOf=YYYY-MM-DD` to see the matrix as of another day\n(soft-deleted cells included, and the rate range then applies to that day's rate).\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (a property name\nof the returned object, case-insensitive) and `desc` query parameters. The body is a plain JSON\narray; the total count is returned in the `Content-Range` response header (`items {start}-{end}/{total}`).\nStatus is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetTeacherSalariesOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "LevelId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "CourseTypeId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "IsForChildren",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "HourlyRateMin",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "HourlyRateMax",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "AsOf",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TeacherSalaryGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TeacherSalaryGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "TeacherSalary (Office)"
        ],
        "summary": "Create a teacher salary.",
        "description": "Creates a new teacher salary rate from the `TeacherSalaryPost` body (teaching level, course type,\nchildren flag and hourly rate) and returns the created `TeacherSalaryGet` with its generated id.\nReturns `400` with validation details if the body is invalid.",
        "operationId": "CreateTeacherSalaryOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TeacherSalaryPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeacherSalaryGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teacher-salaries/{id}": {
      "get": {
        "tags": [
          "TeacherSalary (Office)"
        ],
        "summary": "Get a single teacher salary by id.",
        "description": "Returns a single teacher salary rate by its id as a `TeacherSalaryGet`. Returns `404` if no teacher\nsalary with that id exists.",
        "operationId": "GetTeacherSalaryOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeacherSalaryGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "TeacherSalary (Office)"
        ],
        "summary": "Partially update a teacher salary (a rate change is VERSIONED — see effectiveFrom).",
        "description": "Partially updates an existing teacher salary rate: only the fields present in the `TeacherSalaryPatch`\nbody are changed, the rest stay as they are.\n\n**A change to `hourlyRate` does not overwrite the rate — it opens a new validity period.**\n`effectiveFrom` says from which day it applies and defaults to **TOMORROW**, so lessons already taught\nkeep the salary they were computed with. The same two rules apply as for course prices, both `400`\nwhen broken: no stichtag in a past month, and a stichtag of today or earlier needs\n`confirmRetroactive: true`. `changeReason` is stored on the period; `GET {id}/history` returns it.\n\nReturns the updated `TeacherSalaryGet`. `404` if it does not exist; `400` on validation errors.",
        "operationId": "UpdateTeacherSalaryOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TeacherSalaryPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeacherSalaryGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "TeacherSalary (Office)"
        ],
        "summary": "Delete a teacher salary (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a teacher salary rate. By default this is a soft delete — the rate is hidden from normal\nlistings but can be restored; pass `?hard=true` to remove it permanently. Returns `204`, or `404`\nif it does not exist.",
        "operationId": "DeleteTeacherSalaryOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teacher-salaries/{id}/history": {
      "get": {
        "tags": [
          "TeacherSalary (Office)"
        ],
        "summary": "Ansatz-Verlauf: the validity periods of one salary-matrix cell.",
        "description": "Returns the rate history of a single salary matrix cell as `PriceVersionGet` entries, newest first:\nwhat the rate was, from which day (`validFrom`, inclusive) until which day (`validUntil`, EXCLUSIVE —\nnull means still in force), who changed it and why.\n\nThis is the audit trail behind every lesson salary and therefore behind the payroll: a rate change\nopens a new period instead of overwriting, so recomputing a past month yields the same figures.\nExactly one entry has `isCurrent=true`; `isScheduled=true` marks an announced change. Filter with\n`onDay` (at most one entry) and `validFromMin`; `searchTerm`/`showRemoved` are ignored.\n\nSupports the usual `Range`/`orderBy`/`desc` pagination (`206` + `Content-Range`, `416` on a bad range).\nReturns `404` if no cell with that id exists.",
        "operationId": "GetTeacherSalaryHistoryOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "ValidFromMin",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "OnDay",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PriceVersionGet"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PriceVersionGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      }
    },
    "/office/teacher-salaries/{id}/restore": {
      "post": {
        "tags": [
          "TeacherSalary (Office)"
        ],
        "summary": "Restore a soft-deleted teacher salary.",
        "description": "Restores a previously soft-deleted teacher salary rate so it appears in normal listings again. Returns\n`204`, or `404` if no such teacher salary exists.",
        "operationId": "RestoreTeacherSalaryOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teacher-payments": {
      "get": {
        "tags": [
          "TeacherPayment (Office)"
        ],
        "summary": "List teacher payments (office).",
        "description": "Returns the monthly teacher compensation records (office view — not row-restricted), each as a\n`TeacherPaymentGet`: the teacher, the payment month, the number of lessons and their line items, base\npay, supplements (vacation), the social-insurance deductions (AHV, ALV, KTG, NBU) with rates and\namounts, gross/net/actual pay, the child entitlement (with its config baseline and any override), and\nthe system-raised warning flags (additional travel cost, pension, Quellensteuer).\nSoft-deleted payments are excluded unless you pass `showRemoved=true`. Filter with `teacherId`, a\n`paymentMonthFrom`/`paymentMonthTo` range, and the `minTotalLessons`/`maxTotalLessons` and\n`minActualPay`/`maxActualPay` ranges. `searchTerm` matches the teacher's name.\n\nA payment belongs to its teacher through a plain foreign key (`teacherId`), so the list can also be\nORDERED by teacher: `orderBy=teacherDisplayName` sorts by their last name. Values computed per row —\nthe child-entitlement baseline, for instance — cannot be ordered and are answered with `400`.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (a property name\nof the returned object, case-insensitive) and `desc` query parameters. The body is a plain JSON\narray; the total count is returned in the `Content-Range` response header (`items {start}-{end}/{total}`).\nStatus is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetTeacherPaymentsOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "TeacherId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "PaymentMonthFrom",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "PaymentMonthTo",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "MinTotalLessons",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "MaxTotalLessons",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "MinActualPay",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "MaxActualPay",
            "in": "query",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "IsPaidOut",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "IsReleased",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TeacherPaymentGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TeacherPaymentGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "TeacherPayment (Office)"
        ],
        "summary": "Create a teacher payment.",
        "description": "Creates a new teacher payment from the `TeacherPaymentPost` body (the owning teacher, the payment\nmonth and its line items) and returns the created `TeacherPaymentGet` with its generated id and the\ncomputed compensation figures. Returns `400` with validation details if the body is invalid.",
        "operationId": "CreateTeacherPaymentOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TeacherPaymentPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeacherPaymentGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teacher-payments/{id}": {
      "get": {
        "tags": [
          "TeacherPayment (Office)"
        ],
        "summary": "Get a single teacher payment by id.",
        "description": "Returns a single teacher payment by its id as a `TeacherPaymentGet`, including its line items,\ndeduction breakdown and warning flags. Returns `404` if no teacher payment with that id exists.",
        "operationId": "GetTeacherPaymentOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeacherPaymentGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "TeacherPayment (Office)"
        ],
        "summary": "Partially update a teacher payment.",
        "description": "Partially updates an existing teacher payment: only the fields present in the `TeacherPaymentPatch`\nbody are changed, the rest stay as they are (for example to override the child entitlement or adjust\nline items). Returns the updated `TeacherPaymentGet`. `404` if it does not exist; `400` on validation\nerrors.",
        "operationId": "UpdateTeacherPaymentOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TeacherPaymentPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeacherPaymentGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "TeacherPayment (Office)"
        ],
        "summary": "Delete a teacher payment (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a teacher payment. By default this is a soft delete — the payment is hidden from normal\nlistings but can be restored; pass `?hard=true` to remove it permanently. Returns `204`, or `404`\nif it does not exist.",
        "operationId": "DeleteTeacherPaymentOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teacher-payments/{id}/restore": {
      "post": {
        "tags": [
          "TeacherPayment (Office)"
        ],
        "summary": "Restore a soft-deleted teacher payment.",
        "description": "Restores a previously soft-deleted teacher payment so it appears in normal listings again. Returns\n`204`, or `404` if no such teacher payment exists.",
        "operationId": "RestoreTeacherPaymentOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teacher-payments/{id}/acknowledge-warning": {
      "post": {
        "tags": [
          "TeacherPayment (Office)"
        ],
        "summary": "Acknowledge a specific warning on a teacher payment (?type=AdditionalTravelCost|Pension|Quellensteuer).",
        "description": "Marks one of the system-raised warnings on a teacher payment as acknowledged so staff can confirm it\nhas been reviewed. Select which warning with the required `type` query parameter — one of\n`AdditionalTravelCost`, `Pension` or `Quellensteuer`. Returns `204` once the acknowledgment is recorded.",
        "operationId": "AcknowledgeTeacherPaymentWarningOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/EPaymentWarningType"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          }
        }
      }
    },
    "/office/teacher-payments/{id}/mark-paid-out": {
      "post": {
        "tags": [
          "TeacherPayment (Office)"
        ],
        "summary": "Record that this payslip has been paid out.",
        "description": "Stamps the day the money left, from the optional `paidOutAt` in the body (null = today), and\nreturns the updated `TeacherPaymentGet` with `paidOutAt` and `isPaidOut` set.\n\nAn ACTION rather than a field on the patch, because it reaches beyond its own row: from this\nmoment every EXPENSE CLAIM of the same teacher settled in this payment's period\nis locked — a payslip is composed live out of those claims, so re-deciding one afterwards would\nrewrite a document the teacher has already been paid on. Use `/reopen` to take it back.\n\n`404` if no such payment exists. `409` if it is already paid out — the stamp is never silently\nmoved, since it is the date the teacher is told. `400` for a date in the future.",
        "operationId": "MarkTeacherPaymentPaidOutOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TeacherPaymentMarkPaidOutPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeacherPaymentGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teacher-payments/{id}/reopen": {
      "post": {
        "tags": [
          "TeacherPayment (Office)"
        ],
        "summary": "Take back the payout mark on a payslip.",
        "description": "Clears `paidOutAt` and returns the updated `TeacherPaymentGet`. No figure changes — what it does\nis make the expense claims of that teacher and month answerable again, which is why it exists at\nall: a lock nobody can lift is a trap, and marking the wrong month paid is one keystroke away.\n\n`404` if no such payment exists. `409` if it was not marked as paid out, so a caller acting on a\nstale view hears about it instead of getting a silent no-op.",
        "operationId": "ReopenTeacherPaymentOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeacherPaymentGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teacher-payments/release-period": {
      "post": {
        "tags": [
          "TeacherPayment (Office)"
        ],
        "summary": "Release every draft payslip of one period to its teacher.",
        "description": "Stamps `releasedAt` on every payslip of `{ year, month }` that does not have one yet, and returns\na `PayslipReleaseResultGet` naming each teacher whose statement just became readable.\n\n**A payslip is created as a DRAFT** by the month-end close, and a draft does not exist for its\nteacher: it is excluded from `/teacher/payslips` (list AND by-id, so a deep link answers `404`)\nand the salary export leaves it lying. Releasing is what makes it their statement.\n\n**The PERIOD is the unit, not the row.** A close produces one payslip per teacher with billable\nwork, so twenty is an ordinary number — pressing a per-row button twenty times is not a decision.\nThe per-row `/{id}/release` stays for the exception.\n\n**Selected by STATE**, never by a list the caller brings: only payslips without a release stamp\nare touched, so one that has moved on is never dragged back and a second run moves nothing.\n\n`409` when the period holds no unreleased payslip — the office only presses this while there IS\nsomething, so an empty run means they acted on a stale view. `400` for a month outside 1-12.",
        "operationId": "ReleaseTeacherPayslipPeriodOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PayslipReleasePeriodPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PayslipReleaseResultGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teacher-payments/{id}/release": {
      "post": {
        "tags": [
          "TeacherPayment (Office)"
        ],
        "summary": "Release ONE payslip to its teacher.",
        "description": "Stamps `releasedAt` with today and returns the updated `TeacherPaymentGet`. The exception rather\nthan the rule — `POST /release-period` is the ordinary path.\n\n`404` if no such payment exists. `409` if it is already released: the stamp is never silently\nmoved, because it is the day the teacher could first read the document.",
        "operationId": "ReleaseTeacherPayslipOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeacherPaymentGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teacher-payments/{id}/unrelease": {
      "post": {
        "tags": [
          "TeacherPayment (Office)"
        ],
        "summary": "Take a payslip back to draft.",
        "description": "Clears `releasedAt`, so the payslip is a draft again and disappears from its teacher's portal.\nIt exists for the same reason `/reopen` does: a state somebody can set and nobody can lift is a\ntrap, and releasing the wrong month is one keystroke away.\n\n**Refused once the money has gone out** (`409`, naming the payout date): the teacher has been\nPAID on this document, so withdrawing it would take away the only statement explaining a\ntransfer they already received. Reopen the payout first.\n\n`404` if no such payment exists. `409` if it was never released.",
        "operationId": "UnreleaseTeacherPayslipOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeacherPaymentGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teacher-students": {
      "get": {
        "tags": [
          "TeacherStudent (Office)"
        ],
        "summary": "List teacher students (office).",
        "description": "Returns students as they appear to teachers (office view — not row-restricted), each as a\n`TeacherStudentGet`: a reduced student record (name, age/child flag, native language, contact and\nlearning subjects) enriched with the student's customer contact and address details, plus their\nenrolled courses, attended lessons, received materials, teacher notes and exam records.\nSoft-deleted rows are excluded unless you pass `showRemoved=true`. Filter with `searchTerm` (matches\nfirst name, last name, customer name and email), `customerId`, `nativeLanguageId`, `customerTypeId`,\n`isChild`, and `status`. The optional `teacherId` narrows the list to students taught by that teacher.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (a property name\nof the returned object, case-insensitive) and `desc` query parameters. The body is a plain JSON\narray; the total count is returned in the `Content-Range` response header (`items {start}-{end}/{total}`).\nStatus is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetTeacherStudentsOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "NativeLanguageId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "CustomerTypeId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "IsChild",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "CustomerId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "CourseId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/NullableOfEStudentStatus"
            }
          },
          {
            "name": "TeacherId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TeacherStudentGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TeacherStudentGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "TeacherStudent (Office)"
        ],
        "summary": "Create a teacher student.",
        "description": "Creates a new teacher-student record from the `TeacherStudentPost` body and returns the created\n`TeacherStudentGet` with its generated id. Returns `400` with validation details if the body is invalid.",
        "operationId": "CreateTeacherStudentOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TeacherStudentPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeacherStudentGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teacher-students/{id}": {
      "get": {
        "tags": [
          "TeacherStudent (Office)"
        ],
        "summary": "Get a single teacher student by id.",
        "description": "Returns a single teacher-student record by its id as a `TeacherStudentGet`, including its customer\ncontact details, courses, notes and exam records. Returns `404` if no such record exists.",
        "operationId": "GetTeacherStudentOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeacherStudentGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "TeacherStudent (Office)"
        ],
        "summary": "Partially update a teacher student.",
        "description": "Partially updates an existing teacher-student record: only the fields present in the\n`TeacherStudentPatch` body are changed, the rest stay as they are. Returns the updated\n`TeacherStudentGet`. `404` if it does not exist; `400` on validation errors.",
        "operationId": "UpdateTeacherStudentOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TeacherStudentPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeacherStudentGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "TeacherStudent (Office)"
        ],
        "summary": "Delete a teacher student (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a teacher-student record. By default this is a soft delete — the record is hidden from normal\nlistings but can be restored; pass `?hard=true` to remove it permanently. Returns `204`, or `404`\nif it does not exist.",
        "operationId": "DeleteTeacherStudentOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teacher-students/{id}/restore": {
      "post": {
        "tags": [
          "TeacherStudent (Office)"
        ],
        "summary": "Restore a soft-deleted teacher student.",
        "description": "Restores a previously soft-deleted teacher-student record so it appears in normal listings again.\nReturns `204`, or `404` if no such record exists.",
        "operationId": "RestoreTeacherStudentOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/homeworks": {
      "get": {
        "tags": [
          "Homework (Office)"
        ],
        "summary": "List homeworks (office). Filter by lessonId / due range.",
        "description": "Returns all homework assignments in the system (office view — not row-restricted), each as a\n`HomeworkGet` with its task description and due date, plus the owning lesson's start date and its\ncourse for context. Results are ordered by due date. Soft-deleted homeworks are excluded unless you\npass `showRemoved=true`. Filter with `searchTerm` (matches the description), by `lessonId` for a\nsingle lesson's homeworks, and by a `dueFrom`/`dueTo` due-date range.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (any returned\nproperty name, case-insensitive) and `desc` query parameters. The response body is a plain JSON\narray and the total count is returned in the `Content-Range` header (`items {start}-{end}/{total}`);\nthe status is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetHomeworksOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "LessonId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "DueFrom",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "DueTo",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/HomeworkGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/HomeworkGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "Homework (Office)"
        ],
        "summary": "Create a homework.",
        "description": "Creates a new homework assignment from the `HomeworkPost` body (the owning lesson, task description\nand due date) and returns the created `HomeworkGet` with its generated id. Returns `400` with\nvalidation details if the body is invalid.",
        "operationId": "CreateHomeworkOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HomeworkPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HomeworkGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/homeworks/{id}": {
      "get": {
        "tags": [
          "Homework (Office)"
        ],
        "summary": "Get a single homework by id.",
        "description": "Returns a single homework assignment by its id as a `HomeworkGet`, including its lesson and course\ncontext. Returns `404` if no homework with that id exists.",
        "operationId": "GetHomeworkOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HomeworkGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Homework (Office)"
        ],
        "summary": "Partially update a homework.",
        "description": "Partially updates an existing homework assignment: only the fields present in the `HomeworkPatch`\nbody are changed, the rest stay as they are. Returns the updated `HomeworkGet`. Returns `404` if the\nhomework does not exist and `400` on validation errors.",
        "operationId": "UpdateHomeworkOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HomeworkPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HomeworkGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Homework (Office)"
        ],
        "summary": "Delete a homework (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a homework assignment. By default this is a soft delete — the homework is hidden from normal\nlistings but can be restored; pass `?hard=true` to remove it permanently. Returns `204`, or `404`\nif the homework does not exist.",
        "operationId": "DeleteHomeworkOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/homeworks/{id}/restore": {
      "post": {
        "tags": [
          "Homework (Office)"
        ],
        "summary": "Restore a soft-deleted homework.",
        "description": "Restores a previously soft-deleted homework assignment so it appears in normal listings again.\nReturns `204`, or `404` if no such homework exists.",
        "operationId": "RestoreHomeworkOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/notifications": {
      "get": {
        "tags": [
          "Notification (Office)"
        ],
        "summary": "List notifications (office).",
        "description": "Returns all notifications in the system (office view — not row-restricted), each as a\n`NotificationGet`. A notification is an inbox message addressed to EXACTLY ONE recipient, a\ncustomer or a student, with a message, issue date, category type, read flag and an optional\ndeep-link to the record it is about. Soft-deleted notifications are excluded unless you pass\n`showRemoved=true`.\n\nThe message and the deep-link URL are COMPOSED per request from the notification's key and the\nrecords it references (`targetId`, `relatedStudentId`, `relatedCustomerId`, `eventDate`), not\nstored — so a renamed person or course, or a changed portal route, is reflected in notifications\nthat already exist.\n\nFilter by `typeId`, `keyId`, `recipientCustomerId`, `recipientStudentId`, `relatedStudentId` and\n`isRead`; the lookup ids come from the matching `/lookups/...` endpoints. **`searchTerm` is not\nsupported here** and is ignored: since the message is composed rather than stored, there is no\ntext column to search, and filtering after the fact would break the pagination contract below.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (any returned\nproperty name, case-insensitive) and `desc` query parameters. The response body is a plain JSON\narray and the total count is returned in the `Content-Range` header (`items {start}-{end}/{total}`);\nthe status is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetNotificationsOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "TypeId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "KeyId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "RecipientCustomerId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "RecipientStudentId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "RelatedStudentId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "IsRead",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/NotificationGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/NotificationGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      },
      "post": {
        "tags": [
          "Notification (Office)"
        ],
        "summary": "Create a notification.",
        "description": "Creates a new notification from the `NotificationPost` body and returns the created\n`NotificationGet` with its generated id. Supply EXACTLY ONE recipient (customer or student), the\ncategory `typeId`, and the `keyId` of the event whose wording should be used — there is no message\ntext to send, because the wording follows from the key and is composed on read. Where that wording\nnames a person, a course or a date, supply the matching `relatedStudentId` /\n`relatedCustomerId` / `targetId` / `eventDate`. An omitted `keyId` stores the neutral `Other` key,\nwhich has no specified wording. Returns `400` with validation details if the body is invalid.",
        "operationId": "CreateNotificationOffice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NotificationPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/notifications/{id}": {
      "get": {
        "tags": [
          "Notification (Office)"
        ],
        "summary": "Get a single notification by id.",
        "description": "Returns a single notification by its id as a `NotificationGet`, including its recipient(s) and read\nstatus. Returns `404` if no notification with that id exists.",
        "operationId": "GetNotificationOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Notification (Office)"
        ],
        "summary": "Partially update a notification.",
        "description": "Partially updates an existing notification: only the fields present in the `NotificationPatch` body\nare changed, the rest stay as they are (e.g. marking it read). Returns the updated\n`NotificationGet`. Returns `404` if the notification does not exist and `400` on validation errors.",
        "operationId": "UpdateNotificationOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NotificationPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Notification (Office)"
        ],
        "summary": "Delete a notification (soft by default; ?hard=true to hard-delete).",
        "description": "Deletes a notification. By default this is a soft delete — the notification is hidden from normal\nlistings but can be restored; pass `?hard=true` to remove it permanently. Returns `204`, or `404`\nif the notification does not exist.",
        "operationId": "DeleteNotificationOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/notifications/{id}/restore": {
      "post": {
        "tags": [
          "Notification (Office)"
        ],
        "summary": "Restore a soft-deleted notification.",
        "description": "Restores a previously soft-deleted notification so it appears in normal listings again. Returns\n`204`, or `404` if no such notification exists.",
        "operationId": "RestoreNotificationOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/notification-triggers": {
      "get": {
        "tags": [
          "Notification triggers (Office)"
        ],
        "summary": "List the notification checks that can be run on demand.",
        "description": "Returns every available check as a `NotificationTriggerGet` (`key` + `description`). These are the\nnotifications that cannot be raised as a side effect of an action, because nothing happens when\nthey become true — a package falls below 20% because a lesson has taken place, i.e. because the\ndate changed. They run nightly on their own; this endpoint is the manual lever.\n\nRequires the office portal AND the `office-trigger-notifications` realm role; anyone else gets `403`.",
        "operationId": "GetNotificationTriggersOffice",
        "parameters": [
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/NotificationTriggerGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/NotificationTriggerGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      }
    },
    "/office/notification-triggers/{key}": {
      "post": {
        "tags": [
          "Notification triggers (Office)"
        ],
        "summary": "Run one notification check now.",
        "description": "Runs the check named by `key` (see the list endpoint) and returns what it changed as a\n`NotificationTriggerRunGet`: how many rows it touched — notifications raised, notifications\nwithdrawn again, and the markers that keep a check from repeating itself.\n\n**Safe to run repeatedly.** Every check decides from the CURRENT state plus a marker, so a second\nrun right after the first finds nothing new and changes nothing. It never produces a duplicate\nmessage for a situation that was already reported.\n\n`404` if no check by that key exists. Requires the office portal AND the `office-trigger-notifications`\nrealm role; anyone else gets `403`.",
        "operationId": "RunNotificationTriggerOffice",
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationTriggerRunGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/mail-templates": {
      "get": {
        "tags": [
          "Mail templates (Office)"
        ],
        "summary": "List mail templates (office).",
        "description": "Returns the mail templates as `MailTemplateGet` — the immutable technical `key`, the readable\n`title`, the `language` this version is written in, the `subject` and the HTML `bodyHtml`, plus\n`placeholders` (the tokens THIS wording contains, derived from the stored text),\n`suppliedPlaceholders` (the tokens the trigger point for this key can actually fill),\n`recipientsDescription` (who receives it — recipients follow from the event and are never stored on\na template) and `missingLanguages` (what a translation may still be added for).\n\nA template exists once per (key, language). A mail is sent in the recipient's correspondence\nlanguage and falls back to ENGLISH when that version does not exist, so the English row is the one\nevery key must keep. Soft-deleted templates are excluded unless you pass `showRemoved=true`. Filter\nwith `searchTerm` (matches title, key, subject and body), `languageId` and `isCustomized` (true =\nedited here, so the seeder no longer touches it).\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50, `-50` =\nlast 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (a `MailTemplateGet`\nproperty name) and `desc` query parameters. The body is a plain JSON array; the total count is\nreturned in the `Content-Range` response header (`items {start}-{end}/{total}`). Status is `206`\nwhen a `Range` is sent, `416` if the range is invalid.\n\nRequires the office portal AND the `office-mail-template` realm role; anyone else gets `403`.",
        "operationId": "GetMailTemplatesOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "LanguageId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "IsCustomized",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "IncludeParked",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MailTemplateGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MailTemplateGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      }
    },
    "/office/mail-templates/{id}": {
      "get": {
        "tags": [
          "Mail templates (Office)"
        ],
        "summary": "Get one mail template (office).",
        "description": "Returns the mail template with the given id as a `MailTemplateGet`, including its full `bodyHtml`.\n\nReturns `404` when no such template exists.\n\nRequires the office portal AND the `office-mail-template` realm role; anyone else gets `403`.",
        "operationId": "GetMailTemplateByIdOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailTemplateGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Mail templates (Office)"
        ],
        "summary": "Change a mail template's wording (office).",
        "description": "Updates the `subject` and/or the `bodyHtml` of one template. A null property means \"no change\".\n\n**The key cannot be changed and is not part of the payload** — neither can the title, the language\nor the recipients. The key is what a trigger point looks the template up by; the recipients follow\nfrom the event that raises the mail.\n\nPlaceholders are written as `[Square brackets]`. A token no trigger point supplies is NOT rejected\nhere: the mail is then stored as `failed` and says which placeholder could not be filled, which is\nvisible in the mail history. `suppliedPlaceholders` on the Get says what this key can fill.\n\nSaving a real change marks the template as customized, after which the seeder never overwrites it —\nuse `POST /office/mail-templates/{id}/reset` to hand it back.\n\nReturns `400` for an empty subject or body, `404` when no such template exists.\n\nRequires the office portal AND the `office-mail-template` realm role; anyone else gets `403`.",
        "operationId": "UpdateMailTemplateOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MailTemplatePatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailTemplateGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Mail templates (Office)"
        ],
        "summary": "Delete a language version of a mail template (office).",
        "description": "Removes ONE language version. Its purpose is to undo an \"add translation\" that is no longer wanted:\nonce the row is gone, recipients of that language fall back to the English wording, which is a\ndefined state rather than a gap.\n\nThe ENGLISH version of a template the code knows cannot be deleted and answers `409` — every mail of\nthat kind falls back to it, so losing it would turn each of them into a failed row. Edit its wording\nor park the template instead.\n\nSoft-deletes by default; `?hard=true` removes the row permanently and additionally requires the\n`hard-delete` realm role.\n\nRequires the office portal AND the `office-mail-template` realm role; anyone else gets `403`.",
        "operationId": "DeleteMailTemplateOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hard",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/mail-templates/{id}/reset": {
      "post": {
        "tags": [
          "Mail templates (Office)"
        ],
        "summary": "Restore a mail template's shipped wording (office).",
        "description": "Replaces the template's title, subject and body with the wording shipped in the application, and\nclears `isCustomized` so the seeder looks after it again.\n\nOnly the ENGLISH version can be reset — the shipped wording exists in English only. To undo a\ntranslation, delete it; sending then falls back to English.\n\nReturns `404` when no such template exists, `409` when the template is a translation or has no\nshipped wording to fall back to.\n\nRequires the office portal AND the `office-mail-template` realm role; anyone else gets `403`.",
        "operationId": "ResetMailTemplateOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailTemplateGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/mail-templates/{id}/translations": {
      "post": {
        "tags": [
          "Mail templates (Office)"
        ],
        "summary": "Add a language version of a mail template (office).",
        "description": "Creates the given language version of this template, cloned from the ENGLISH wording so it can be\ntranslated rather than written from nothing. The new row counts as customized immediately, so the\nseeder never overwrites it.\n\nThis is the only way a template row is created: the shipped keys come from the seeder, and a\ntemplate the code never looks up would be a mail nobody sends.\n\nReturns `400` when the language does not exist, `404` when no such template exists, `409` when this\nkey already has that language.\n\nRequires the office portal AND the `office-mail-template` realm role; anyone else gets `403`.",
        "operationId": "AddMailTemplateTranslationOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MailTemplateTranslationPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailTemplateGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/mail-history": {
      "get": {
        "tags": [
          "Mail history (Office)"
        ],
        "summary": "List sent and unsent mails (office).",
        "description": "Returns the outgoing mails as `MailHistoryGet` — the `templateKey` they were rendered from, the\nrecipients (`to`/`cc`/`bcc` plus a short `recipientsDisplay`), the rendered `subject` and\n`bodyHtml`, the delivery `status`, `queuedAt`/`sentAt`/`resendAt`, `attemptCount`, and\n`errorMessage` when something went wrong.\n\n`bodyHtml` is the FINISHED message: it is rendered when the event happens and sent exactly as it\nstands. In the normal case it contains no placeholders at all — one that is still in it is the\nrecord of a value that could not be filled, and the mail is then `failed`.\n\n**Status values**: `pending` (staged, waiting), `sent` (a provider accepted it), `failed` (refused\nbefore sending, or delivery failed after the retries), `notSent` (picked up, but no mail provider is\nconfigured — nothing left the building). While no provider is configured, mails end as `notSent`\nrather than `sent`, so the history never claims a delivery that did not happen.\n\n**Corrections are excluded by default.** A hand-made correction is content belonging to a mail, not\na mail of its own, so it is not a row of its own here: the original carries `hasChange` and the\n`change` object. Pass `includeChanges=true` to see the correction rows themselves.\n\nSoft-deleted rows are excluded unless you pass `showRemoved=true`. Filter with `searchTerm` (matches\nsubject, template key and the recipient addresses), `statusId`, `templateKey`, `recipient` (address\nsubstring), `queuedFrom`/`queuedTo`, `sentFrom`/`sentTo`, `hasError`, and `customerId`/`studentId`/\n`courseId`/`invoiceId` (which record the mail was about).\n\n`attachments` on a row says what the letter CARRIES, and `originalAttachments` what it was set up\nwith — the reference point for restoring an enclosure the office removed. The invoice letter and\nits reminder both promise the invoice in their own wording, so \"did that go out with the\ndocument?\" is a question the office asks, and a re-send hands over exactly that document rather\nthan a fresh rendering.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50, `-50` =\nlast 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (a `MailHistoryGet`\nproperty name) and `desc` query parameters. The body is a plain JSON array; the total count is\nreturned in the `Content-Range` response header (`items {start}-{end}/{total}`). Status is `206`\nwhen a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetMailHistoryOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "StatusId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "TemplateKey",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Recipient",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "QueuedFrom",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "QueuedTo",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "SentFrom",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "SentTo",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "HasError",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "CustomerId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "StudentId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "CourseId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "InvoiceId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "IncludeChanges",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MailHistoryGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MailHistoryGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      }
    },
    "/office/mail-history/{id}": {
      "get": {
        "tags": [
          "Mail history (Office)"
        ],
        "summary": "Get one mail (office).",
        "description": "Returns the mail with the given id as a `MailHistoryGet`, including the full `bodyHtml`, the\n`change` object when the mail has been corrected by hand, and `sources` — the records the mail was\nbuilt from (customer, student, teacher, course, invoice …), resolved to names where there is one.\n\n`sources` is how a broken mail is repaired: the mail stores the finished text and no placeholder\nvalues, so the way back to the data is the record it names. A source whose record has since been\ndeleted is still listed, with no display name.\n\nReturns `404` when no such mail exists.",
        "operationId": "GetMailHistoryByIdOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailHistoryGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/mail-history/{id}/resend": {
      "post": {
        "tags": [
          "Mail history (Office)"
        ],
        "summary": "Send a mail again, optionally with corrected content (office).",
        "description": "Puts the mail back into the queue and stamps `resendAt`. Every property of the payload is optional:\nan empty body re-sends exactly what is in force.\n\n**Corrections.** A payload that changes the content is stored as ONE correction linked to this mail\n— repeated edits update that same correction, never a second one. A correction whose content ends up\nidentical to the original DELETES it, so \"revert to original, then send\" leaves no trace of an edit\nthat no longer exists. The original row always keeps the delivery status, so the list stays one row\nper mail.\n\nA corrected message has to clear the same bar as a rendered one: at least one recipient, valid\naddresses, a subject and a body, and **no `[placeholder]` left in the subject or body** — a message\nnobody could fill is not made sendable by being typed.\n\n**Attachments.** `attachmentDocumentIds` is the COMPLETE list of enclosures the mail should carry:\nomitting the property changes nothing, an EMPTY array removes every attachment, and adding an id\nencloses that document. Upload a new file through `POST /office/mail-attachments` first — it\nanswers with the id to put here. The attachments the mail was originally staged with are always\nreported unchanged as `originalAttachments`, so **restoring one that was removed is putting its id\nback into this list**. An id that names neither an uploaded document nor one of the originals is a\n`400`.\n\nWith no mail provider configured the result is `notSent`, not `sent`.\n\nReturns `400` for an unusable message (no recipient, a malformed address, an empty subject or body,\na leftover placeholder), `404` when no such mail exists, and `409` when the id belongs to a\ncorrection rather than to a mail — re-send the original entry instead.",
        "operationId": "ResendMailOffice",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MailResendPost"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailHistoryGet"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/office/teacher-profiles": {
      "get": {
        "tags": [
          "TeacherProfile (Office)"
        ],
        "summary": "List teacher profiles (office).",
        "description": "Returns the read-only teacher profiles (office view — not row-restricted), each as a\n`TeacherProfileGet`. A profile is the non-sensitive subset of a teacher: personal info, contact,\naddress, teaching capabilities, teaching and spoken languages, children and availability — it\ndeliberately excludes all employment, insurance, salary and other HR data (use the teacher endpoints\nfor those).\nSoft-deleted profiles are excluded unless you pass `showRemoved=true`. Filter with `searchTerm`\n(matches first name, last name, email and city), `genderId`, `countryCodeId`, `city`,\n`lessonsToChildren` and `challengingStudents`.\n\nSupports pagination and sorting: send an optional `Range` request header (`0-49` = first 50,\n`-50` = last 50, `50` = from index 50; 0-based, end inclusive) plus the `orderBy` (a property name\nof the returned object, case-insensitive) and `desc` query parameters. The body is a plain JSON\narray; the total count is returned in the `Content-Range` response header (`items {start}-{end}/{total}`).\nStatus is `206` when a `Range` is sent, `416` if the range is invalid.",
        "operationId": "GetTeacherProfilesOffice",
        "parameters": [
          {
            "name": "SearchTerm",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ShowRemoved",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "GenderId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "CountryCodeId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "City",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "LessonsToChildren",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "ChallengingStudents",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "Pagination window (zero-based, end inclusive): `{start}-{end}` e.g. `0-49`; `-{n}` = last n; `{n}` = first n. When set, the response is 206 with a `Content-Range` header.",
            "schema": {
              "type": "string",
              "example": "0-49"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Sort by this result property name (case-insensitive). Unknown property → 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "desc",
            "in": "query",
            "description": "Sort descending when true (default false). Applied before pagination.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TeacherProfileGet"
                  }
                }
              }
            }
          },
          "206": {
            "description": "Partial Content — returned when a `Range` header is present. Body is the requested window.",
            "headers": {
              "Content-Range": {
                "description": "`items {start}-{end}/{total}` (empty page → `items */{total}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TeacherProfileGet"
                  }
                }
              }
            }
          },
          "416": {
            "description": "Range Not Satisfiable — the `Range` header is malformed or starts beyond the last item."
          }
        }
      }
    },
    "/office/teacher-profiles/{id}": {
      "get": {
        "tags": [
          "TeacherProfile (Office)"
        ],
        "summary": "Get a single teacher profile by id.",
        "description": "Returns a single teacher profile by its id as a `TeacherProfileGet` (the non-sensitive teacher subset,\nwithout any HR/insurance/salary data). Returns `404` if no teacher profile with that id exists.",
        "operationId": "GetTeacherProfileOfficeById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeacherProfileGet"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "AbsencePeriodGet": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "startDate": {
            "type": "string",
            "format": "date-time"
          },
          "endDate": {
            "type": "string",
            "format": "date-time"
          },
          "reason": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "AbsencePeriodPatch": {
        "type": "object",
        "properties": {
          "startDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "endDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "reason": {
            "type": "string",
            "nullable": true
          },
          "removeReason": {
            "type": "boolean"
          }
        }
      },
      "AbsencePeriodPatchWithId": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "patch": {
            "$ref": "#/components/schemas/AbsencePeriodPatch"
          }
        }
      },
      "AbsencePeriodPost": {
        "type": "object",
        "properties": {
          "startDate": {
            "type": "string",
            "format": "date-time"
          },
          "endDate": {
            "type": "string",
            "format": "date-time"
          },
          "reason": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "AddressInfoDto": {
        "type": "object",
        "properties": {
          "street": {
            "type": "string"
          },
          "houseNumber": {
            "type": "string",
            "nullable": true
          },
          "additionalLine": {
            "type": "string",
            "nullable": true
          },
          "postalCode": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "poBox": {
            "type": "string",
            "nullable": true
          }
        },
        "nullable": true
      },
      "AddressInfoDto2": {
        "type": "object",
        "properties": {
          "street": {
            "type": "string"
          },
          "houseNumber": {
            "type": "string",
            "nullable": true
          },
          "additionalLine": {
            "type": "string",
            "nullable": true
          },
          "postalCode": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "poBox": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "AttendanceRecordGet": {
        "type": "object",
        "properties": {
          "ownerRef": {
            "type": "string",
            "format": "uuid"
          },
          "ownerTypeName": {
            "type": "string"
          },
          "studentId": {
            "type": "string",
            "format": "uuid"
          },
          "student": {
            "$ref": "#/components/schemas/StudentRef"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfEAttendanceStatus"
          },
          "absenceDocumentPath": {
            "type": "string",
            "nullable": true
          },
          "absenceDocumentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "absenceDocumentSizeBytes": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "AttendanceRecordPatch": {
        "type": "object",
        "properties": {
          "ownerRef": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "ownerTypeName": {
            "type": "string",
            "nullable": true
          },
          "studentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "statusId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "absenceDocumentPath": {
            "type": "string",
            "nullable": true
          },
          "absenceDocumentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "removeAbsenceDocument": {
            "type": "boolean"
          },
          "removeAbsenceDocumentPath": {
            "type": "boolean"
          }
        }
      },
      "AttendanceRecordPatchWithId": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "patch": {
            "$ref": "#/components/schemas/AttendanceRecordPatch"
          }
        }
      },
      "AttendanceRecordPost": {
        "type": "object",
        "properties": {
          "ownerRef": {
            "type": "string",
            "format": "uuid"
          },
          "ownerTypeName": {
            "type": "string"
          },
          "studentId": {
            "type": "string",
            "format": "uuid"
          },
          "statusId": {
            "type": "string",
            "format": "uuid"
          },
          "absenceDocumentPath": {
            "type": "string",
            "nullable": true
          },
          "absenceDocumentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "BankPaymentGet": {
        "type": "object",
        "properties": {
          "importId": {
            "type": "string",
            "format": "uuid"
          },
          "importFileName": {
            "type": "string"
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "currency": {
            "type": "string"
          },
          "valueDate": {
            "type": "string",
            "format": "date"
          },
          "bookingDate": {
            "type": "string",
            "format": "date"
          },
          "reference": {
            "type": "string",
            "nullable": true
          },
          "debtorName": {
            "type": "string",
            "nullable": true
          },
          "debtorIban": {
            "type": "string",
            "nullable": true
          },
          "outcome": {
            "$ref": "#/components/schemas/EnumDtoOfEBankPaymentOutcome"
          },
          "invoiceId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "invoiceNumber": {
            "type": "string",
            "nullable": true
          },
          "openAfter": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "isBooked": {
            "type": "boolean"
          },
          "needsReview": {
            "type": "boolean"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "BillingSettingsGet": {
        "type": "object",
        "properties": {
          "paymentTermDays": {
            "type": "integer",
            "format": "int32"
          },
          "phone": {
            "type": "string",
            "nullable": true
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "website": {
            "type": "string",
            "nullable": true
          },
          "logoDocumentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "logoFileName": {
            "type": "string",
            "nullable": true
          },
          "logoContentType": {
            "type": "string",
            "nullable": true
          },
          "texts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InvoiceTextGet"
            }
          },
          "isConfigured": {
            "type": "boolean"
          },
          "isIssuerConfigured": {
            "type": "boolean"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "BillingSettingsPatch": {
        "type": "object",
        "properties": {
          "paymentTermDays": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "phone": {
            "type": "string",
            "nullable": true
          },
          "removePhone": {
            "type": "boolean"
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "removeEmail": {
            "type": "boolean"
          },
          "website": {
            "type": "string",
            "nullable": true
          },
          "removeWebsite": {
            "type": "boolean"
          },
          "removeLogoDocumentId": {
            "type": "boolean"
          },
          "texts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InvoiceTextPatch"
            },
            "nullable": true
          },
          "textLanguageIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          }
        }
      },
      "BudgetGet": {
        "type": "object",
        "properties": {
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfEBudgetStatus"
          },
          "totalAmount": {
            "type": "number",
            "format": "double"
          },
          "consumedAmount": {
            "type": "number",
            "format": "double"
          },
          "activatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "revokedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "invoicedAmount": {
            "type": "number",
            "format": "double"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "sponsorCustomerId": {
            "type": "string",
            "format": "uuid"
          },
          "sponsorCustomer": {
            "$ref": "#/components/schemas/CustomerRef"
          },
          "beneficiaryCustomerId": {
            "type": "string",
            "format": "uuid"
          },
          "beneficiaryCustomer": {
            "$ref": "#/components/schemas/CustomerRef"
          },
          "averageLessonPrice": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "estimatedTotalLessons": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "estimatedRemainingLessons": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "lineItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BudgetLineItemGet"
            }
          },
          "remainingAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "overdrawnAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "itemCount": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "sponsorCustomerDisplay": {
            "type": "string",
            "nullable": true
          },
          "beneficiaryCustomerDisplay": {
            "type": "string",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "BudgetLineItemGet": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string"
          },
          "type": {
            "$ref": "#/components/schemas/EnumDtoOfEBudgetLineItemType"
          },
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "lessonId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "lesson": {
            "$ref": "#/components/schemas/LessonRef2"
          },
          "courseId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "course": {
            "$ref": "#/components/schemas/CourseRef"
          },
          "studentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "student": {
            "$ref": "#/components/schemas/StudentRef"
          },
          "materialId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "amount": {
            "type": "number",
            "format": "double"
          }
        }
      },
      "BudgetPatch": {
        "type": "object",
        "properties": {
          "totalAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "activatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "removeDescription": {
            "type": "boolean"
          },
          "sponsorCustomerId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "beneficiaryCustomerId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "averageLessonPrice": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "removeAverageLessonPrice": {
            "type": "boolean"
          },
          "revokedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "removeRevokedAt": {
            "type": "boolean"
          }
        }
      },
      "BudgetPost": {
        "type": "object",
        "properties": {
          "totalAmount": {
            "type": "number",
            "format": "double"
          },
          "activatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "sponsorCustomerId": {
            "type": "string",
            "format": "uuid"
          },
          "beneficiaryCustomerId": {
            "type": "string",
            "format": "uuid"
          },
          "averageLessonPrice": {
            "type": "number",
            "format": "double",
            "nullable": true
          }
        }
      },
      "ChildInfoGet": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "childDob": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "childEntitledFrom": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "childEntitledTo": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "childName": {
            "type": "string",
            "nullable": true
          },
          "childAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          }
        }
      },
      "ChildInfoPatch": {
        "type": "object",
        "properties": {
          "childDob": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "removeChildDob": {
            "type": "boolean"
          },
          "childEntitledFrom": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "removeChildEntitledFrom": {
            "type": "boolean"
          },
          "childEntitledTo": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "removeChildEntitledTo": {
            "type": "boolean"
          },
          "childName": {
            "type": "string",
            "nullable": true
          },
          "removeChildName": {
            "type": "boolean"
          },
          "childAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "removeChildAmount": {
            "type": "boolean"
          }
        }
      },
      "ChildInfoPatchWithId": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "patch": {
            "$ref": "#/components/schemas/ChildInfoPatch"
          }
        }
      },
      "ChildInfoPost": {
        "type": "object",
        "properties": {
          "childDob": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "childEntitledFrom": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "childEntitledTo": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "childName": {
            "type": "string",
            "nullable": true
          },
          "childAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          }
        }
      },
      "CompletedTrainingGet": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "trainingCourseId": {
            "type": "string",
            "format": "uuid"
          },
          "trainingCourse": {
            "$ref": "#/components/schemas/TrainingCourseRef"
          },
          "completionDate": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CompletedTrainingPatch": {
        "type": "object",
        "properties": {
          "trainingCourseId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "completionDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "CompletedTrainingPatchWithId": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "patch": {
            "$ref": "#/components/schemas/CompletedTrainingPatch"
          }
        }
      },
      "CompletedTrainingPost": {
        "type": "object",
        "properties": {
          "trainingCourseId": {
            "type": "string",
            "format": "uuid"
          },
          "completionDate": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CourseBillingImpactGet": {
        "required": [
          "openInvoices",
          "sentInvoices"
        ],
        "type": "object",
        "properties": {
          "openInvoices": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CourseChargeInvoiceInfoDto"
            }
          },
          "sentInvoices": {
            "type": "array",
            "items": { }
          }
        }
      },
      "CourseChargeInvoiceInfoDto": {
        "required": [
          "invoiceId",
          "invoiceNumber",
          "totalAmount",
          "studentIds"
        ],
        "type": "object",
        "properties": {
          "invoiceId": {
            "type": "string",
            "format": "uuid"
          },
          "invoiceNumber": {
            "type": "string"
          },
          "totalAmount": {
            "type": "number",
            "format": "double"
          },
          "studentIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          }
        }
      },
      "CourseColorEnumDto": {
        "type": "object",
        "properties": {
          "propertyName": {
            "type": "string"
          },
          "value": {
            "type": "string"
          },
          "enumValue": {
            "$ref": "#/components/schemas/ECourseColor"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "CourseEnrollmentRequestGet": {
        "type": "object",
        "properties": {
          "courseId": {
            "type": "string",
            "format": "uuid"
          },
          "course": {
            "$ref": "#/components/schemas/CourseGet2"
          },
          "requestingCustomerId": {
            "type": "string",
            "format": "uuid"
          },
          "requestingCustomer": {
            "$ref": "#/components/schemas/CustomerRef"
          },
          "requestedStudentIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "requestedStudents": {
            "type": "array",
            "items": { }
          },
          "requestDate": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfECourseEnrollmentStatus2"
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "declineReason": {
            "type": "string",
            "nullable": true
          },
          "invitations": {
            "type": "array",
            "items": { }
          },
          "requesterDisplay": {
            "type": "string",
            "nullable": true
          },
          "studentsDisplay": {
            "type": "string",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "CourseEnrollmentRequestGet2": {
        "type": "object",
        "properties": {
          "courseId": {
            "type": "string",
            "format": "uuid"
          },
          "course": {
            "$ref": "#/components/schemas/CourseGet3"
          },
          "requestingCustomerId": {
            "type": "string",
            "format": "uuid"
          },
          "requestingCustomer": {
            "$ref": "#/components/schemas/CustomerRef"
          },
          "requestedStudentIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "requestedStudents": {
            "type": "array",
            "items": { }
          },
          "requestDate": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfECourseEnrollmentStatus2"
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "declineReason": {
            "type": "string",
            "nullable": true
          },
          "invitations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StudentInviteRef"
            }
          },
          "requesterDisplay": {
            "type": "string",
            "nullable": true
          },
          "studentsDisplay": {
            "type": "string",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "CourseEnrollmentRequestPatch": {
        "type": "object",
        "properties": {
          "requestedStudentIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "statusId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "declineReason": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "CourseGet": {
        "type": "object",
        "properties": {
          "courseType": {
            "$ref": "#/components/schemas/EnumDtoOfECourseType"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfECourseStatus"
          },
          "subject": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage"
          },
          "level": {
            "$ref": "#/components/schemas/EnumDtoOfETeachingLevel"
          },
          "color": {
            "$ref": "#/components/schemas/CourseColorEnumDto"
          },
          "focus": {
            "$ref": "#/components/schemas/EnumDtoOfECourseFocus"
          },
          "isForChildren": {
            "type": "boolean"
          },
          "minStudents": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "maxStudents": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "capacity": {
            "$ref": "#/components/schemas/EnumDtoOfECourseCapacity"
          },
          "price": {
            "type": "number",
            "format": "double"
          },
          "isPriceLocked": {
            "type": "boolean"
          },
          "displayName": {
            "type": "string",
            "nullable": true
          },
          "totalMaterialCost": {
            "type": "number",
            "format": "double"
          },
          "hasTeacher": {
            "type": "boolean"
          },
          "studentCount": {
            "type": "integer",
            "format": "int32"
          },
          "teacherId": {
            "type": "string",
            "format": "uuid"
          },
          "teacher": {
            "$ref": "#/components/schemas/TeacherRef"
          },
          "teacherIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "teachers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TeacherRef2"
            },
            "nullable": true
          },
          "additionalTeachers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CourseTeacherRef"
            },
            "nullable": true
          },
          "deactivatedTeacherIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "studentIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "students": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StudentRef2"
            },
            "nullable": true
          },
          "interestIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "interests": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CourseInterestRef"
            },
            "nullable": true
          },
          "startDate": {
            "type": "string",
            "format": "date"
          },
          "endDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "weeklySchedule": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WeeklyScheduleSlotDto"
            }
          },
          "roomId": {
            "type": "string",
            "format": "uuid"
          },
          "room": {
            "$ref": "#/components/schemas/RoomRef"
          },
          "pendingStudentInviteCount": {
            "type": "integer",
            "format": "int32"
          },
          "openInvitations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StudentInviteRef"
            }
          },
          "pendingInviteStudentIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "enrollmentRequests": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CourseEnrollmentRequestGet"
            },
            "nullable": true
          },
          "pendingTeacherInviteCount": {
            "type": "integer",
            "format": "int32"
          },
          "requiredMaterialIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "requiredMaterials": {
            "type": "array",
            "items": { },
            "nullable": true
          },
          "lessons": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LessonRef"
            },
            "nullable": true
          },
          "lessonCount": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "CourseGet2": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "courseType": {
            "$ref": "#/components/schemas/EnumDtoOfECourseType"
          },
          "subject": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage"
          },
          "level": {
            "$ref": "#/components/schemas/EnumDtoOfETeachingLevel"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfECourseStatus"
          },
          "capacity": {
            "$ref": "#/components/schemas/EnumDtoOfECourseCapacity2"
          },
          "color": {
            "$ref": "#/components/schemas/CourseColorEnumDto"
          },
          "startDate": {
            "type": "string",
            "format": "date"
          },
          "endDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "weeklySchedule": {
            "type": "array",
            "items": { }
          },
          "room": {
            "$ref": "#/components/schemas/RoomRef"
          },
          "teacher": {
            "$ref": "#/components/schemas/TeacherRef"
          },
          "requiredMaterials": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MaterialRef"
            },
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "isAvailable": {
            "type": "boolean"
          },
          "isEnrolled": {
            "type": "boolean"
          },
          "isRequested": {
            "type": "boolean"
          },
          "enrollmentStatus": {
            "$ref": "#/components/schemas/EnumDtoOfECourseEnrollmentStatus"
          },
          "enrolledStudents": {
            "type": "array",
            "items": { },
            "nullable": true
          },
          "enrollmentRequests": {
            "type": "array",
            "items": { },
            "nullable": true
          },
          "lessons": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CourseLessonGet"
            },
            "nullable": true
          },
          "displayName": {
            "type": "string",
            "nullable": true
          },
          "studentCount": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "lessonCount": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        },
        "nullable": true
      },
      "CourseGet3": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "courseType": {
            "$ref": "#/components/schemas/EnumDtoOfECourseType"
          },
          "subject": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage"
          },
          "level": {
            "$ref": "#/components/schemas/EnumDtoOfETeachingLevel"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfECourseStatus"
          },
          "capacity": {
            "$ref": "#/components/schemas/EnumDtoOfECourseCapacity2"
          },
          "color": {
            "$ref": "#/components/schemas/CourseColorEnumDto"
          },
          "startDate": {
            "type": "string",
            "format": "date"
          },
          "endDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "weeklySchedule": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WeeklyScheduleSlotDto"
            }
          },
          "room": {
            "$ref": "#/components/schemas/RoomRef"
          },
          "teacher": {
            "$ref": "#/components/schemas/TeacherRef"
          },
          "requiredMaterials": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MaterialRef"
            },
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "isAvailable": {
            "type": "boolean"
          },
          "isEnrolled": {
            "type": "boolean"
          },
          "isRequested": {
            "type": "boolean"
          },
          "enrollmentStatus": {
            "$ref": "#/components/schemas/EnumDtoOfECourseEnrollmentStatus"
          },
          "enrolledStudents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StudentRef2"
            },
            "nullable": true
          },
          "enrollmentRequests": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CourseEnrollmentRequestGet"
            },
            "nullable": true
          },
          "lessons": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CourseLessonGet"
            },
            "nullable": true
          },
          "displayName": {
            "type": "string",
            "nullable": true
          },
          "studentCount": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "lessonCount": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        },
        "nullable": true
      },
      "CourseInterestRef": {
        "type": "object",
        "properties": {
          "interest": {
            "$ref": "#/components/schemas/InterestRef"
          },
          "interestId": {
            "type": "string",
            "format": "uuid"
          },
          "invitedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "invited": {
            "type": "boolean"
          },
          "students": {
            "type": "array",
            "items": { }
          }
        }
      },
      "CourseLessonDto": {
        "type": "object",
        "properties": {
          "tempId": {
            "type": "string",
            "format": "uuid"
          },
          "startDate": {
            "type": "string",
            "format": "date"
          },
          "startTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          },
          "endTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          },
          "durationMinutes": {
            "type": "integer",
            "format": "int32"
          },
          "dayOfWeek": {
            "$ref": "#/components/schemas/DayOfWeek"
          },
          "display": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "CourseLessonGet": {
        "type": "object",
        "properties": {
          "courseId": {
            "type": "string",
            "format": "uuid"
          },
          "startDate": {
            "type": "string",
            "format": "date"
          },
          "endDate": {
            "type": "string",
            "format": "date"
          },
          "startTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          },
          "endTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          },
          "teacher": {
            "$ref": "#/components/schemas/TeacherRef"
          },
          "room": {
            "$ref": "#/components/schemas/RoomRef"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfELessonStatus"
          },
          "isCancelled": {
            "type": "boolean"
          },
          "homeworks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HomeworkGet"
            },
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "CoursePatch": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "removeDescription": {
            "type": "boolean"
          },
          "statusId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "subjectId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "levelId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "courseColorId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "focusId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "isForChildren": {
            "type": "boolean",
            "nullable": true
          },
          "minStudents": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "removeMinStudents": {
            "type": "boolean"
          },
          "maxStudents": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "removeMaxStudents": {
            "type": "boolean"
          },
          "price": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "effectiveFrom": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "confirmRetroactive": {
            "type": "boolean"
          },
          "changeReason": {
            "type": "string",
            "nullable": true
          },
          "startDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "endDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "removeEndDate": {
            "type": "boolean"
          },
          "interestIdsToAdd": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "interestIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "studentIdsToInvite": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "studentIdsToInviteRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "weeklyScheduleToAdd": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WeeklyScheduleSlotDto"
            },
            "nullable": true
          },
          "weeklyScheduleToRemove": {
            "type": "array",
            "items": { },
            "nullable": true
          },
          "roomId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "teacherId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "additionalTeacherIdsToAdd": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "additionalTeacherIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "additionalTeacherIdsToDeactivate": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "additionalTeacherIdsToReactivate": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "studentIdsToAdd": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "studentIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "requiredMaterialIdsToAdd": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "requiredMaterialIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "deleteOpenChargeInvoices": {
            "type": "boolean"
          }
        }
      },
      "CoursePost": {
        "type": "object",
        "properties": {
          "courseTypeId": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "statusId": {
            "type": "string",
            "format": "uuid"
          },
          "subjectId": {
            "type": "string",
            "format": "uuid"
          },
          "levelId": {
            "type": "string",
            "format": "uuid"
          },
          "courseColorId": {
            "type": "string",
            "format": "uuid"
          },
          "focusId": {
            "type": "string",
            "format": "uuid"
          },
          "isForChildren": {
            "type": "boolean"
          },
          "minStudents": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "maxStudents": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "price": {
            "type": "number",
            "format": "double"
          },
          "startDate": {
            "type": "string",
            "format": "date"
          },
          "endDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "weeklySchedule": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WeeklyScheduleSlotDto"
            },
            "nullable": true
          },
          "lessons": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CourseLessonDto"
            },
            "nullable": true
          },
          "roomId": {
            "type": "string",
            "format": "uuid"
          },
          "teacherId": {
            "type": "string",
            "format": "uuid"
          },
          "additionalTeacherIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "studentIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "interestIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "studentIdsToInvite": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "requiredMaterialIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          }
        }
      },
      "CoursePriceGet": {
        "type": "object",
        "properties": {
          "level": {
            "$ref": "#/components/schemas/EnumDtoOfETeachingLevel"
          },
          "courseType": {
            "$ref": "#/components/schemas/EnumDtoOfECourseType"
          },
          "isForChildren": {
            "type": "boolean"
          },
          "price": {
            "type": "number",
            "format": "double"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "productName": {
            "type": "string",
            "nullable": true
          },
          "isCustomProduct": {
            "type": "boolean"
          },
          "effectiveFrom": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "pendingChangeFrom": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "pendingChangeAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "CoursePricePatch": {
        "type": "object",
        "properties": {
          "levelId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "courseTypeId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "isForChildren": {
            "type": "boolean",
            "nullable": true
          },
          "price": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "removeDescription": {
            "type": "boolean"
          },
          "productName": {
            "type": "string",
            "nullable": true
          },
          "removeProductName": {
            "type": "boolean"
          },
          "effectiveFrom": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "confirmRetroactive": {
            "type": "boolean"
          },
          "changeReason": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "CoursePricePost": {
        "type": "object",
        "properties": {
          "levelId": {
            "type": "string",
            "format": "uuid"
          },
          "courseTypeId": {
            "type": "string",
            "format": "uuid"
          },
          "isForChildren": {
            "type": "boolean"
          },
          "price": {
            "type": "number",
            "format": "double"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "productName": {
            "type": "string",
            "nullable": true
          },
          "effectiveFrom": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "confirmRetroactive": {
            "type": "boolean"
          },
          "changeReason": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "CourseRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "courseType": {
            "$ref": "#/components/schemas/EnumDtoOfECourseType"
          },
          "name": {
            "type": "string"
          },
          "displayName": {
            "type": "string",
            "nullable": true
          },
          "subject": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage"
          },
          "level": {
            "$ref": "#/components/schemas/EnumDtoOfETeachingLevel"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfECourseStatus"
          },
          "color": {
            "$ref": "#/components/schemas/CourseColorEnumDto"
          },
          "teacher": {
            "$ref": "#/components/schemas/TeacherRef"
          },
          "price": {
            "type": "number",
            "format": "double"
          },
          "roomId": {
            "type": "string",
            "format": "uuid"
          },
          "startDate": {
            "type": "string",
            "format": "date-time"
          },
          "endDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "weeklySchedule": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WeeklyScheduleSlotDto"
            }
          }
        },
        "nullable": true
      },
      "CourseRef2": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "courseType": {
            "$ref": "#/components/schemas/EnumDtoOfECourseType"
          },
          "name": {
            "type": "string"
          },
          "displayName": {
            "type": "string",
            "nullable": true
          },
          "subject": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage"
          },
          "level": {
            "$ref": "#/components/schemas/EnumDtoOfETeachingLevel"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfECourseStatus"
          },
          "color": {
            "$ref": "#/components/schemas/CourseColorEnumDto"
          },
          "teacher": {
            "$ref": "#/components/schemas/TeacherRef"
          },
          "price": {
            "type": "number",
            "format": "double"
          },
          "roomId": {
            "type": "string",
            "format": "uuid"
          },
          "startDate": {
            "type": "string",
            "format": "date-time"
          },
          "endDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "weeklySchedule": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WeeklyScheduleSlotDto"
            }
          }
        }
      },
      "CourseTeacherRef": {
        "type": "object",
        "properties": {
          "teacher": {
            "$ref": "#/components/schemas/TeacherRef2"
          },
          "teacherId": {
            "type": "string",
            "format": "uuid"
          },
          "inactive": {
            "type": "boolean"
          },
          "deactivatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "CreditPackageOrderRequest": {
        "required": [
          "packageOrderId"
        ],
        "type": "object",
        "properties": {
          "packageOrderId": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "CustomerContactInfoGet": {
        "type": "object",
        "properties": {
          "birthdate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "salutation": {
            "type": "string",
            "nullable": true
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "gender": {
            "$ref": "#/components/schemas/EnumDtoOfEGender2"
          },
          "email": {
            "type": "string"
          },
          "mobilePhone": {
            "type": "string"
          },
          "phone": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "CustomerContactInfoPatch": {
        "type": "object",
        "properties": {
          "birthdate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "firstName": {
            "type": "string",
            "nullable": true
          },
          "lastName": {
            "type": "string",
            "nullable": true
          },
          "genderId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "removeGenderId": {
            "type": "boolean"
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "mobilePhone": {
            "type": "string",
            "nullable": true
          },
          "phone": {
            "type": "string",
            "nullable": true
          }
        },
        "nullable": true
      },
      "CustomerContactInfoPost": {
        "type": "object",
        "properties": {
          "birthdate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "genderId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "email": {
            "type": "string"
          },
          "mobilePhone": {
            "type": "string"
          },
          "phone": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "CustomerGet": {
        "type": "object",
        "properties": {
          "customerType": {
            "$ref": "#/components/schemas/EnumDtoOfECustomerType"
          },
          "isActive": {
            "type": "boolean"
          },
          "correspondenceLanguage": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage"
          },
          "profileConfirmed": {
            "type": "boolean"
          },
          "accountStatus": {
            "$ref": "#/components/schemas/EnumDtoOfEAccountStatus"
          },
          "loginNumber": {
            "type": "string",
            "nullable": true
          },
          "displayName": {
            "type": "string",
            "nullable": true
          },
          "contactDisplay": {
            "type": "string",
            "nullable": true
          },
          "addressDisplay": {
            "type": "string",
            "nullable": true
          },
          "companyName": {
            "type": "string",
            "nullable": true
          },
          "vatNumber": {
            "type": "string",
            "nullable": true
          },
          "contactInfo": {
            "$ref": "#/components/schemas/CustomerContactInfoGet"
          },
          "billingAddress": {
            "$ref": "#/components/schemas/AddressInfoDto2"
          },
          "students": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StudentRef2"
            }
          },
          "studentCount": {
            "type": "integer",
            "format": "int32"
          },
          "openInvoiceNoticeCount": {
            "type": "integer",
            "format": "int32"
          },
          "interest": {
            "type": "string",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "CustomerInactivityItem": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "format": "uuid"
          },
          "customerName": {
            "type": "string"
          },
          "studentCount": {
            "type": "integer",
            "format": "int32"
          },
          "lastLessonDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "monthsInactive": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "CustomerInvitePost": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string"
          },
          "customerTypeId": {
            "type": "string",
            "format": "uuid"
          },
          "correspondenceLanguageId": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "CustomerPatch": {
        "type": "object",
        "properties": {
          "customerTypeId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "isActive": {
            "type": "boolean",
            "nullable": true
          },
          "correspondenceLanguageId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "companyName": {
            "type": "string",
            "nullable": true
          },
          "removeCompanyName": {
            "type": "boolean"
          },
          "vatNumber": {
            "type": "string",
            "nullable": true
          },
          "removeVatNumber": {
            "type": "boolean"
          },
          "contactInfo": {
            "$ref": "#/components/schemas/CustomerContactInfoPatch"
          },
          "billingAddress": {
            "$ref": "#/components/schemas/AddressInfoDto"
          },
          "interest": {
            "type": "string",
            "nullable": true
          },
          "removeInterest": {
            "type": "boolean"
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "removeNotes": {
            "type": "boolean"
          }
        }
      },
      "CustomerPost": {
        "type": "object",
        "properties": {
          "customerTypeId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "isActive": {
            "type": "boolean"
          },
          "correspondenceLanguageId": {
            "type": "string",
            "format": "uuid"
          },
          "companyName": {
            "type": "string",
            "nullable": true
          },
          "vatNumber": {
            "type": "string",
            "nullable": true
          },
          "contactInfo": {
            "$ref": "#/components/schemas/CustomerContactInfoPost"
          },
          "billingAddress": {
            "$ref": "#/components/schemas/AddressInfoDto2"
          },
          "interest": {
            "type": "string",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "CustomerRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "displayName": {
            "type": "string"
          },
          "customerType": {
            "$ref": "#/components/schemas/EnumDtoOfECustomerType"
          },
          "isActive": {
            "type": "boolean"
          },
          "address": {
            "$ref": "#/components/schemas/AddressInfoDto"
          }
        },
        "nullable": true
      },
      "DayOfWeek": {
        "type": "integer"
      },
      "EAccountStatus": {
        "type": "integer"
      },
      "EAttendanceStatus": {
        "type": "integer"
      },
      "EBankPaymentOutcome": {
        "type": "integer"
      },
      "EBudgetLineItemType": {
        "type": "integer"
      },
      "EBudgetStatus": {
        "type": "integer"
      },
      "EBvgMode": {
        "type": "integer"
      },
      "ECivilStatus": {
        "type": "integer"
      },
      "ECountryCode": {
        "type": "integer"
      },
      "ECourseCapacity": {
        "type": "integer"
      },
      "ECourseColor": {
        "type": "integer"
      },
      "ECourseEnrollmentStatus": {
        "type": "integer"
      },
      "ECourseFocus": {
        "type": "integer"
      },
      "ECourseStatus": {
        "type": "integer"
      },
      "ECourseType": {
        "type": "integer"
      },
      "ECustomerType": {
        "type": "integer"
      },
      "EDiscountType": {
        "type": "integer"
      },
      "EExpenseClaimStatus": {
        "type": "integer"
      },
      "EGender": {
        "type": "integer"
      },
      "EInterestStatus": {
        "type": "integer"
      },
      "EInterestType": {
        "type": "integer"
      },
      "EInviteMaterialStatus": {
        "type": "integer"
      },
      "EInviteStatus": {
        "type": "integer"
      },
      "EInvoiceStatus": {
        "type": "integer"
      },
      "ELanguage": {
        "type": "integer"
      },
      "ELessonCancellationReason": {
        "type": "integer"
      },
      "ELessonStatus": {
        "type": "integer"
      },
      "EMailStatus": {
        "type": "integer"
      },
      "EMaterialDeliveryMethod": {
        "type": "integer"
      },
      "EMaterialLevel": {
        "type": "integer"
      },
      "EMaterialType": {
        "type": "integer"
      },
      "EMonthlyCloseState": {
        "type": "integer"
      },
      "ENotificationKey": {
        "type": "integer"
      },
      "ENotificationType": {
        "type": "integer"
      },
      "EnumDtoOfEAccountStatus": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EAccountStatus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEAttendanceStatus": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EAttendanceStatus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEBankPaymentOutcome": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EBankPaymentOutcome"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEBudgetLineItemType": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EBudgetLineItemType"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEBudgetStatus": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EBudgetStatus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEBvgMode": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EBvgMode"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        },
        "nullable": true
      },
      "EnumDtoOfECivilStatus": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ECivilStatus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfECountryCode": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ECountryCode"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfECourseCapacity": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ECourseCapacity"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfECourseCapacity2": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ECourseCapacity"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        },
        "nullable": true
      },
      "EnumDtoOfECourseEnrollmentStatus": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ECourseEnrollmentStatus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        },
        "nullable": true
      },
      "EnumDtoOfECourseEnrollmentStatus2": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ECourseEnrollmentStatus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfECourseFocus": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ECourseFocus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfECourseStatus": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ECourseStatus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfECourseType": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ECourseType"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfECustomerType": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ECustomerType"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfECustomerType2": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ECustomerType"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        },
        "nullable": true
      },
      "EnumDtoOfEDiscountType": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EDiscountType"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEExpenseClaimStatus": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EExpenseClaimStatus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEGender": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EGender"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEGender2": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EGender"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        },
        "nullable": true
      },
      "EnumDtoOfEInterestStatus": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EInterestStatus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEInterestType": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EInterestType"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEInviteMaterialStatus": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EInviteMaterialStatus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        },
        "nullable": true
      },
      "EnumDtoOfEInviteMaterialStatus2": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EInviteMaterialStatus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEInviteStatus": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EInviteStatus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEInvoiceStatus": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EInvoiceStatus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfELanguage": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ELanguage"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfELanguage2": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ELanguage"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        },
        "nullable": true
      },
      "EnumDtoOfELessonCancellationReason": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ELessonCancellationReason"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        },
        "nullable": true
      },
      "EnumDtoOfELessonStatus": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ELessonStatus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEMailStatus": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EMailStatus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEMaterialDeliveryMethod": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EMaterialDeliveryMethod"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        },
        "nullable": true
      },
      "EnumDtoOfEMaterialLevel": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EMaterialLevel"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEMaterialType": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EMaterialType"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfENotificationKey": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ENotificationKey"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfENotificationType": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ENotificationType"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEOrderStatus": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EOrderStatus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEPackageAssignment": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EPackageAssignment"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEPackageOrderStatus": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EPackageOrderStatus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEPackageStatus": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EPackageStatus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEPreferredLocation": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EPreferredLocation"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        },
        "nullable": true
      },
      "EnumDtoOfEPreferredTeachingFormat": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EPreferredTeachingFormat"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        },
        "nullable": true
      },
      "EnumDtoOfERoomStatus": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ERoomStatus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfEStudentStatus": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/EStudentStatus"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfESwissCanton": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ESwissCanton"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        },
        "nullable": true
      },
      "EnumDtoOfETeachingLevel": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ETeachingLevel"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EnumDtoOfETest": {
        "type": "object",
        "properties": {
          "enumValue": {
            "$ref": "#/components/schemas/ETest"
          },
          "displayName": {
            "type": "string"
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "EOrderStatus": {
        "type": "integer"
      },
      "EPackageAssignment": {
        "type": "integer"
      },
      "EPackageOrderStatus": {
        "type": "integer"
      },
      "EPackageStatus": {
        "type": "integer"
      },
      "EPaymentWarningType": {
        "type": "integer"
      },
      "EPreferredLocation": {
        "type": "integer"
      },
      "EPreferredTeachingFormat": {
        "type": "integer"
      },
      "EQrBillReferenceType": {
        "type": "integer"
      },
      "ERoomStatus": {
        "type": "integer"
      },
      "EStudentStatus": {
        "type": "integer"
      },
      "ESwissCanton": {
        "type": "integer"
      },
      "ETeachingLevel": {
        "type": "integer"
      },
      "ETest": {
        "type": "integer"
      },
      "ExamRecordGet": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "description": {
            "type": "string"
          },
          "examDate": {
            "type": "string",
            "format": "date-time"
          },
          "result": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "ExamRecordPatch": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string",
            "nullable": true
          },
          "examDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "result": {
            "type": "string",
            "nullable": true
          },
          "removeResult": {
            "type": "boolean"
          }
        }
      },
      "ExamRecordPatchWithId": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "patch": {
            "$ref": "#/components/schemas/ExamRecordPatch"
          }
        }
      },
      "ExamRecordPost": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string"
          },
          "examDate": {
            "type": "string",
            "format": "date-time"
          },
          "result": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "ExpenseClaimGet": {
        "type": "object",
        "properties": {
          "teacherId": {
            "type": "string",
            "format": "uuid"
          },
          "teacher": {
            "$ref": "#/components/schemas/TeacherRef"
          },
          "teacherDisplayName": {
            "type": "string"
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "settledPeriodYear": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "settledPeriodMonth": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "settledPeriodDisplay": {
            "type": "string",
            "nullable": true
          },
          "isPaidOut": {
            "type": "boolean"
          },
          "claimedAmount": {
            "type": "number",
            "format": "double"
          },
          "reason": {
            "type": "string"
          },
          "receiptDocumentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "receiptDocumentSizeBytes": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "hasReceipt": {
            "type": "boolean"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfEExpenseClaimStatus"
          },
          "approvedAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "officeNote": {
            "type": "string",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "ExpenseClaimPatch": {
        "type": "object",
        "properties": {
          "statusId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "approvedAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "removeApprovedAmount": {
            "type": "boolean"
          },
          "officeNote": {
            "type": "string",
            "nullable": true
          },
          "removeOfficeNote": {
            "type": "boolean"
          },
          "settledPeriodYear": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "settledPeriodMonth": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "removeSettledPeriod": {
            "type": "boolean"
          }
        }
      },
      "HomeworkDocumentRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "fileName": {
            "type": "string"
          },
          "contentType": {
            "type": "string"
          },
          "sizeBytes": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "HomeworkGet": {
        "type": "object",
        "properties": {
          "lessonId": {
            "type": "string",
            "format": "uuid"
          },
          "description": {
            "type": "string"
          },
          "dueDate": {
            "type": "string",
            "format": "date"
          },
          "lessonStartDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "course": {
            "$ref": "#/components/schemas/CourseRef"
          },
          "documents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HomeworkDocumentRef"
            }
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "HomeworkPatch": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string",
            "nullable": true
          },
          "dueDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "documentIdsToAdd": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "documentIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          }
        }
      },
      "HomeworkPost": {
        "type": "object",
        "properties": {
          "lessonId": {
            "type": "string",
            "format": "uuid"
          },
          "description": {
            "type": "string"
          },
          "dueDate": {
            "type": "string",
            "format": "date"
          },
          "documentIdsToAdd": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          }
        }
      },
      "HomeworkRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "description": {
            "type": "string"
          },
          "dueDate": {
            "type": "string",
            "format": "date"
          },
          "documents": {
            "type": "array",
            "items": { }
          }
        }
      },
      "HttpValidationProblemDetails": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "nullable": true
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "detail": {
            "type": "string",
            "nullable": true
          },
          "instance": {
            "type": "string",
            "nullable": true
          },
          "errors": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        }
      },
      "IFormFile": {
        "type": "string",
        "format": "binary"
      },
      "InterestGet": {
        "type": "object",
        "properties": {
          "studentId": {
            "type": "string",
            "format": "uuid"
          },
          "studentRef": {
            "$ref": "#/components/schemas/StudentRef"
          },
          "requestingCustomerId": {
            "type": "string",
            "format": "uuid"
          },
          "requestingCustomer": {
            "$ref": "#/components/schemas/CustomerRef"
          },
          "requestedStudentIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "requestedStudents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StudentRef2"
            },
            "nullable": true
          },
          "language": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage"
          },
          "level": {
            "$ref": "#/components/schemas/EnumDtoOfETeachingLevel"
          },
          "interestType": {
            "$ref": "#/components/schemas/EnumDtoOfEInterestType"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfEInterestStatus"
          },
          "declinedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "declineReason": {
            "type": "string",
            "nullable": true
          },
          "startDate": {
            "type": "string",
            "format": "date-time"
          },
          "endDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "lessonsPerWeek": {
            "type": "integer",
            "format": "int32"
          },
          "lessonDurationMinutes": {
            "type": "integer",
            "format": "int32"
          },
          "numberOfLessons": {
            "type": "integer",
            "format": "int32"
          },
          "preferredLocation": {
            "$ref": "#/components/schemas/EnumDtoOfEPreferredLocation"
          },
          "preferredAddress": {
            "type": "string",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "estimatedPrice": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "timeSlots": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TimeSlotDto"
            }
          },
          "teacherApplications": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TeacherApplicationGet"
            }
          },
          "teacherApplicationCount": {
            "type": "integer",
            "format": "int32"
          },
          "hasTeacherApplications": {
            "type": "boolean"
          },
          "studentName": {
            "type": "string",
            "nullable": true
          },
          "studentAge": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "displayName": {
            "type": "string",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "InterestPatch": {
        "type": "object",
        "properties": {
          "requestingCustomerId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "requestedStudentIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "languageId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "levelId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "interestTypeId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "statusId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "declineReason": {
            "type": "string",
            "nullable": true
          },
          "startDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "endDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "removeEndDate": {
            "type": "boolean"
          },
          "lessonsPerWeek": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "lessonDurationMinutes": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "numberOfLessons": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "preferredLocationId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "preferredAddress": {
            "type": "string",
            "nullable": true
          },
          "removePreferredAddress": {
            "type": "boolean"
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "removeNotes": {
            "type": "boolean"
          },
          "timeSlotsToAdd": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TimeSlotDto"
            },
            "nullable": true
          },
          "timeSlotsToUpdate": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TimeSlotPatchWithId"
            },
            "nullable": true
          },
          "timeSlotIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "teacherApplicationsToAdd": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TeacherApplicationPost"
            },
            "nullable": true
          },
          "teacherApplicationIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          }
        }
      },
      "InterestPost": {
        "type": "object",
        "properties": {
          "studentId": {
            "type": "string",
            "format": "uuid"
          },
          "requestingCustomerId": {
            "type": "string",
            "format": "uuid"
          },
          "requestedStudentIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "languageId": {
            "type": "string",
            "format": "uuid"
          },
          "levelId": {
            "type": "string",
            "format": "uuid"
          },
          "interestTypeId": {
            "type": "string",
            "format": "uuid"
          },
          "statusId": {
            "type": "string",
            "format": "uuid"
          },
          "startDate": {
            "type": "string",
            "format": "date-time"
          },
          "endDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "lessonsPerWeek": {
            "type": "integer",
            "format": "int32"
          },
          "lessonDurationMinutes": {
            "type": "integer",
            "format": "int32"
          },
          "numberOfLessons": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "preferredLocationId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "preferredAddress": {
            "type": "string",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "timeSlots": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TimeSlotDto"
            },
            "nullable": true
          }
        }
      },
      "InterestRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "studentId": {
            "type": "string",
            "format": "uuid"
          },
          "language": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage"
          },
          "level": {
            "$ref": "#/components/schemas/EnumDtoOfETeachingLevel"
          },
          "interestType": {
            "$ref": "#/components/schemas/EnumDtoOfEInterestType"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfEInterestStatus"
          },
          "displayName": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "InviteMaterialChoiceGet": {
        "type": "object",
        "properties": {
          "materialId": {
            "type": "string",
            "format": "uuid"
          },
          "material": {
            "$ref": "#/components/schemas/MaterialRef2"
          },
          "alreadyInPossession": {
            "type": "boolean"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfEInviteMaterialStatus2"
          },
          "deliveryMethod": {
            "$ref": "#/components/schemas/EnumDtoOfEMaterialDeliveryMethod"
          },
          "answeredAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "InviteMaterialChoicePost": {
        "type": "object",
        "properties": {
          "materialId": {
            "type": "string",
            "format": "uuid"
          },
          "alreadyInPossession": {
            "type": "boolean",
            "nullable": true
          },
          "deliveryMethodId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "InvoiceExtraItemGet": {
        "type": "object",
        "properties": {
          "invoiceId": {
            "type": "string",
            "format": "uuid"
          },
          "description": {
            "type": "string"
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "InvoiceExtraItemPatch": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string",
            "nullable": true
          },
          "amount": {
            "type": "number",
            "format": "double",
            "nullable": true
          }
        }
      },
      "InvoiceExtraItemPatchWithId": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "patch": {
            "$ref": "#/components/schemas/InvoiceExtraItemPatch"
          }
        }
      },
      "InvoiceExtraItemPost": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string"
          },
          "amount": {
            "type": "number",
            "format": "double"
          }
        }
      },
      "InvoiceGet": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "invoiceDate": {
            "type": "string",
            "format": "date-time"
          },
          "periodYear": {
            "type": "integer",
            "format": "int32"
          },
          "periodMonth": {
            "type": "integer",
            "format": "int32"
          },
          "dueDate": {
            "type": "string",
            "format": "date-time"
          },
          "invoiceNumber": {
            "type": "string"
          },
          "periodDisplay": {
            "type": "string",
            "nullable": true
          },
          "purchaseOrderNumber": {
            "type": "string",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfEInvoiceStatus"
          },
          "isOverdue": {
            "type": "boolean"
          },
          "customerId": {
            "type": "string",
            "format": "uuid"
          },
          "customer": {
            "$ref": "#/components/schemas/CustomerRef"
          },
          "lineItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InvoiceLineItemGet"
            }
          },
          "extraItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InvoiceExtraItemGet"
            }
          },
          "extraItemsTotal": {
            "type": "number",
            "format": "double"
          },
          "lastEmailSentDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "isEditable": {
            "type": "boolean"
          },
          "editRefusalReason": {
            "type": "string",
            "nullable": true
          },
          "hasBeenSent": {
            "type": "boolean"
          },
          "totalAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "itemCount": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "customerDisplay": {
            "type": "string",
            "nullable": true
          },
          "customerType": {
            "$ref": "#/components/schemas/EnumDtoOfECustomerType2"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "InvoiceLineItemGet": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string"
          },
          "courseId": {
            "type": "string",
            "format": "uuid"
          },
          "course": {
            "$ref": "#/components/schemas/CourseRef"
          },
          "studentId": {
            "type": "string",
            "format": "uuid"
          },
          "student": {
            "$ref": "#/components/schemas/StudentRef"
          },
          "lessonIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "lessons": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LessonRef"
            },
            "nullable": true
          },
          "units": {
            "type": "integer",
            "format": "int32"
          },
          "pricePerUnit": {
            "type": "number",
            "format": "double"
          },
          "discountType": {
            "$ref": "#/components/schemas/EnumDtoOfEDiscountType"
          },
          "discountValue": {
            "type": "number",
            "format": "double"
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "serviceDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "budgetId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "InvoiceLineItemPost": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string"
          },
          "courseId": {
            "type": "string",
            "format": "uuid"
          },
          "course": {
            "$ref": "#/components/schemas/CourseRef"
          },
          "studentId": {
            "type": "string",
            "format": "uuid"
          },
          "student": {
            "$ref": "#/components/schemas/StudentRef"
          },
          "lessonIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "lessons": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LessonRef"
            },
            "nullable": true
          },
          "units": {
            "type": "integer",
            "format": "int32"
          },
          "pricePerUnit": {
            "type": "number",
            "format": "double"
          },
          "discountTypeId": {
            "type": "string",
            "format": "uuid"
          },
          "discountValue": {
            "type": "number",
            "format": "double"
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "serviceDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "budgetId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "sortOrder": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "InvoiceNoticeGet": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "format": "uuid"
          },
          "customer": {
            "$ref": "#/components/schemas/CustomerRef"
          },
          "text": {
            "type": "string"
          },
          "settledInvoiceId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "settledInvoice": {
            "$ref": "#/components/schemas/InvoiceRef"
          },
          "settledAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "isSettled": {
            "type": "boolean"
          },
          "customerDisplay": {
            "type": "string"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "InvoiceNoticePatch": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string",
            "nullable": true
          },
          "settledInvoiceId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "removeSettledInvoice": {
            "type": "boolean"
          }
        }
      },
      "InvoiceNoticePost": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "format": "uuid"
          },
          "text": {
            "type": "string"
          }
        }
      },
      "InvoicePatch": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "nullable": true
          },
          "invoiceDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "periodYear": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "periodMonth": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "dueDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "invoiceNumber": {
            "type": "string",
            "nullable": true
          },
          "removeInvoiceNumber": {
            "type": "boolean"
          },
          "purchaseOrderNumber": {
            "type": "string",
            "nullable": true
          },
          "removePurchaseOrderNumber": {
            "type": "boolean"
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "removeNotes": {
            "type": "boolean"
          },
          "statusId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "customerId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "lineItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InvoiceLineItemPost"
            },
            "nullable": true
          },
          "extraItemsToAdd": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InvoiceExtraItemPost"
            },
            "nullable": true
          },
          "extraItemsToUpdate": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InvoiceExtraItemPatchWithId"
            },
            "nullable": true
          },
          "extraItemIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "settleNoticeIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "reopenNoticeIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          }
        }
      },
      "InvoicePost": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "invoiceDate": {
            "type": "string",
            "format": "date-time"
          },
          "periodYear": {
            "type": "integer",
            "format": "int32"
          },
          "periodMonth": {
            "type": "integer",
            "format": "int32"
          },
          "dueDate": {
            "type": "string",
            "format": "date-time"
          },
          "invoiceNumber": {
            "type": "string"
          },
          "purchaseOrderNumber": {
            "type": "string",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "statusId": {
            "type": "string",
            "format": "uuid"
          },
          "customerId": {
            "type": "string",
            "format": "uuid"
          },
          "lineItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InvoiceLineItemPost"
            },
            "nullable": true
          },
          "extraItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InvoiceExtraItemPost"
            },
            "nullable": true
          },
          "settleNoticeIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          }
        }
      },
      "InvoiceRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "invoiceNumber": {
            "type": "string"
          },
          "invoiceDate": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfEInvoiceStatus"
          },
          "totalAmount": {
            "type": "number",
            "format": "double"
          },
          "customerDisplay": {
            "type": "string"
          }
        },
        "nullable": true
      },
      "InvoiceReminderSentResponse": {
        "required": [
          "sentAt"
        ],
        "type": "object",
        "properties": {
          "sentAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "InvoiceSendOutcomeGet": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "invoiceNumber": {
            "type": "string"
          },
          "customerName": {
            "type": "string"
          },
          "totalAmount": {
            "type": "number",
            "format": "double"
          },
          "sent": {
            "type": "boolean"
          },
          "reason": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "InvoiceSendPeriodPost": {
        "required": [
          "year",
          "month"
        ],
        "type": "object",
        "properties": {
          "year": {
            "type": "integer",
            "format": "int32"
          },
          "month": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "InvoiceSendPeriodResultGet": {
        "type": "object",
        "properties": {
          "periodDisplay": {
            "type": "string"
          },
          "year": {
            "type": "integer",
            "format": "int32"
          },
          "month": {
            "type": "integer",
            "format": "int32"
          },
          "sentCount": {
            "type": "integer",
            "format": "int32"
          },
          "failedCount": {
            "type": "integer",
            "format": "int32"
          },
          "sentTotal": {
            "type": "number",
            "format": "double"
          },
          "invoices": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InvoiceSendOutcomeGet"
            }
          }
        }
      },
      "InvoiceSentResponse": {
        "required": [
          "sentAt"
        ],
        "type": "object",
        "properties": {
          "sentAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "InvoiceTextGet": {
        "type": "object",
        "properties": {
          "language": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage"
          },
          "introText": {
            "type": "string",
            "nullable": true
          },
          "closingText": {
            "type": "string",
            "nullable": true
          },
          "footerText": {
            "type": "string",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "InvoiceTextPatch": {
        "type": "object",
        "properties": {
          "languageId": {
            "type": "string",
            "format": "uuid"
          },
          "introText": {
            "type": "string",
            "nullable": true
          },
          "closingText": {
            "type": "string",
            "nullable": true
          },
          "footerText": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "LessonGet": {
        "type": "object",
        "properties": {
          "courseId": {
            "type": "string",
            "format": "uuid"
          },
          "course": {
            "$ref": "#/components/schemas/CourseRef"
          },
          "startDate": {
            "type": "string",
            "format": "date"
          },
          "endDate": {
            "type": "string",
            "format": "date"
          },
          "startTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          },
          "endTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          },
          "roomId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "room": {
            "$ref": "#/components/schemas/RoomRef"
          },
          "hasOwnRoom": {
            "type": "boolean"
          },
          "durationMinutes": {
            "type": "integer",
            "format": "int32"
          },
          "courseDisplay": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfELessonStatus"
          },
          "teachingSalary": {
            "type": "number",
            "format": "double"
          },
          "travelAllowance": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "isCancelled": {
            "type": "boolean"
          },
          "cancellationReason": {
            "$ref": "#/components/schemas/EnumDtoOfELessonCancellationReason"
          },
          "homeworks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HomeworkGet"
            },
            "nullable": true
          },
          "nextLessonStartDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "isAttendanceComplete": {
            "type": "boolean",
            "nullable": true
          },
          "grammar": {
            "type": "string",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "teacherId": {
            "type": "string",
            "format": "uuid"
          },
          "teacher": {
            "$ref": "#/components/schemas/TeacherRef"
          },
          "attendanceRecords": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AttendanceRecordGet"
            },
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "LessonPatch": {
        "type": "object",
        "properties": {
          "courseId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "startDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "startTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string",
            "nullable": true
          },
          "endTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string",
            "nullable": true
          },
          "roomId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "removeRoom": {
            "type": "boolean"
          },
          "isCancelled": {
            "type": "boolean",
            "nullable": true
          },
          "cancellationReasonId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "grammar": {
            "type": "string",
            "nullable": true
          },
          "removeGrammar": {
            "type": "boolean"
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "removeNotes": {
            "type": "boolean"
          },
          "travelAllowance": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "removeTravelAllowance": {
            "type": "boolean"
          },
          "teacherId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "attendanceRecordsToAdd": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AttendanceRecordPost"
            },
            "nullable": true
          },
          "attendanceRecordsToUpdate": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AttendanceRecordPatchWithId"
            },
            "nullable": true
          },
          "attendanceRecordIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          }
        }
      },
      "LessonPost": {
        "type": "object",
        "properties": {
          "courseId": {
            "type": "string",
            "format": "uuid"
          },
          "startDate": {
            "type": "string",
            "format": "date"
          },
          "startTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          },
          "endTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          },
          "roomId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "isCancelled": {
            "type": "boolean"
          },
          "cancellationReasonId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "grammar": {
            "type": "string",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "travelAllowance": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "teacherId": {
            "type": "string",
            "format": "uuid"
          },
          "attendanceRecords": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AttendanceRecordPost"
            },
            "nullable": true
          }
        }
      },
      "LessonRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "displayName": {
            "type": "string"
          },
          "startDate": {
            "type": "string",
            "format": "date"
          },
          "startTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          },
          "endTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          },
          "isCancelled": {
            "type": "boolean"
          },
          "travelAllowance": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "courseDisplay": {
            "type": "string"
          },
          "lessonPrice": {
            "type": "number",
            "format": "double"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfELessonStatus"
          },
          "course": {
            "$ref": "#/components/schemas/CourseRef"
          },
          "teacher": {
            "$ref": "#/components/schemas/TeacherRef"
          },
          "homeworks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HomeworkRef"
            },
            "nullable": true
          }
        }
      },
      "LessonRef2": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "displayName": {
            "type": "string"
          },
          "startDate": {
            "type": "string",
            "format": "date"
          },
          "startTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          },
          "endTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          },
          "isCancelled": {
            "type": "boolean"
          },
          "travelAllowance": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "courseDisplay": {
            "type": "string"
          },
          "lessonPrice": {
            "type": "number",
            "format": "double"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfELessonStatus"
          },
          "course": {
            "$ref": "#/components/schemas/CourseRef"
          },
          "teacher": {
            "$ref": "#/components/schemas/TeacherRef"
          },
          "homeworks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HomeworkRef"
            },
            "nullable": true
          }
        },
        "nullable": true
      },
      "LocationAddressGet": {
        "type": "object",
        "properties": {
          "street": {
            "type": "string"
          },
          "houseNumber": {
            "type": "string",
            "nullable": true
          },
          "postalCode": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "canton": {
            "$ref": "#/components/schemas/EnumDtoOfESwissCanton"
          }
        }
      },
      "LocationAddressPatch": {
        "type": "object",
        "properties": {
          "street": {
            "type": "string",
            "nullable": true
          },
          "houseNumber": {
            "type": "string",
            "nullable": true
          },
          "removeHouseNumber": {
            "type": "boolean"
          },
          "postalCode": {
            "type": "string",
            "nullable": true
          },
          "city": {
            "type": "string",
            "nullable": true
          },
          "cantonId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "removeCantonId": {
            "type": "boolean"
          }
        },
        "nullable": true
      },
      "LocationAddressPost": {
        "type": "object",
        "properties": {
          "street": {
            "type": "string"
          },
          "houseNumber": {
            "type": "string",
            "nullable": true
          },
          "postalCode": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "cantonId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "LocationGet": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "address": {
            "$ref": "#/components/schemas/LocationAddressGet"
          },
          "addressDisplay": {
            "type": "string",
            "nullable": true
          },
          "arrivalDescription": {
            "type": "string",
            "nullable": true
          },
          "parkingDescription": {
            "type": "string",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "LocationGet2": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "address": {
            "$ref": "#/components/schemas/LocationAddressGet"
          },
          "addressDisplay": {
            "type": "string",
            "nullable": true
          },
          "arrivalDescription": {
            "type": "string",
            "nullable": true
          },
          "parkingDescription": {
            "type": "string",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        },
        "nullable": true
      },
      "LocationPatch": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "removeDescription": {
            "type": "boolean"
          },
          "address": {
            "$ref": "#/components/schemas/LocationAddressPatch"
          },
          "arrivalDescription": {
            "type": "string",
            "nullable": true
          },
          "removeArrivalDescription": {
            "type": "boolean"
          },
          "parkingDescription": {
            "type": "string",
            "nullable": true
          },
          "removeParkingDescription": {
            "type": "boolean"
          }
        }
      },
      "LocationPost": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "address": {
            "$ref": "#/components/schemas/LocationAddressPost"
          },
          "arrivalDescription": {
            "type": "string",
            "nullable": true
          },
          "parkingDescription": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "MailAttachmentGet": {
        "type": "object",
        "properties": {
          "documentId": {
            "type": "string",
            "format": "uuid"
          },
          "fileName": {
            "type": "string"
          },
          "contentType": {
            "type": "string"
          },
          "sizeBytes": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "MailHistoryChangeGet": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "to": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "cc": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "bcc": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "subject": {
            "type": "string"
          },
          "bodyHtml": {
            "type": "string"
          },
          "changedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "nullable": true
      },
      "MailHistoryGet": {
        "type": "object",
        "properties": {
          "templateKey": {
            "type": "string"
          },
          "templateTitle": {
            "type": "string",
            "nullable": true
          },
          "to": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "cc": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "bcc": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "recipientsDisplay": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "bodyHtml": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfEMailStatus"
          },
          "queuedAt": {
            "type": "string",
            "format": "date-time"
          },
          "sentAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "resendAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "eventDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "attemptCount": {
            "type": "integer",
            "format": "int32"
          },
          "errorMessage": {
            "type": "string",
            "nullable": true
          },
          "originalMailHistoryId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "isChangeEntry": {
            "type": "boolean"
          },
          "hasChange": {
            "type": "boolean"
          },
          "change": {
            "$ref": "#/components/schemas/MailHistoryChangeGet"
          },
          "sources": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MailSourceRecordGet"
            }
          },
          "attachments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MailAttachmentGet"
            }
          },
          "originalAttachments": {
            "type": "array",
            "items": { }
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "MailResendPost": {
        "type": "object",
        "properties": {
          "to": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          },
          "cc": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          },
          "bcc": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          },
          "subject": {
            "type": "string",
            "nullable": true
          },
          "bodyHtml": {
            "type": "string",
            "nullable": true
          },
          "attachmentDocumentIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          }
        }
      },
      "MailSourceRecordGet": {
        "required": [
          "kind",
          "id",
          "display"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "type": "string"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "display": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "MailTemplateGet": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "language": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage"
          },
          "isFallbackLanguage": {
            "type": "boolean"
          },
          "subject": {
            "type": "string"
          },
          "bodyHtml": {
            "type": "string"
          },
          "isCustomized": {
            "type": "boolean"
          },
          "parked": {
            "type": "boolean"
          },
          "recipientsDescription": {
            "type": "string"
          },
          "placeholders": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "suppliedPlaceholders": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "missingLanguages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EnumDtoOfELanguage"
            }
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "MailTemplatePatch": {
        "type": "object",
        "properties": {
          "subject": {
            "type": "string",
            "nullable": true
          },
          "bodyHtml": {
            "type": "string",
            "nullable": true
          },
          "parked": {
            "type": "boolean",
            "nullable": true
          }
        }
      },
      "MailTemplateTranslationPost": {
        "type": "object",
        "properties": {
          "languageId": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "MaterialChangeRequestAnswer": {
        "type": "object",
        "properties": {
          "materialId": {
            "type": "string",
            "format": "uuid"
          },
          "alreadyInPossession": {
            "type": "boolean",
            "nullable": true
          },
          "deliveryMethodId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "MaterialChangeRequestGet": {
        "type": "object",
        "properties": {
          "courseId": {
            "type": "string",
            "format": "uuid"
          },
          "course": {
            "$ref": "#/components/schemas/CourseRef"
          },
          "studentId": {
            "type": "string",
            "format": "uuid"
          },
          "student": {
            "$ref": "#/components/schemas/StudentRef"
          },
          "customer": {
            "$ref": "#/components/schemas/CustomerRef"
          },
          "requestedAt": {
            "type": "string",
            "format": "date-time"
          },
          "answeredAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "isAnswered": {
            "type": "boolean"
          },
          "deliveryMethod": {
            "$ref": "#/components/schemas/EnumDtoOfEMaterialDeliveryMethod"
          },
          "deliveryAddress": {
            "$ref": "#/components/schemas/AddressInfoDto"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MaterialChangeRequestItemGet"
            }
          },
          "itemCount": {
            "type": "integer",
            "format": "int32"
          },
          "itemsDisplay": {
            "type": "string",
            "nullable": true
          },
          "studentDisplay": {
            "type": "string",
            "nullable": true
          },
          "courseDisplay": {
            "type": "string",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "MaterialChangeRequestItemGet": {
        "type": "object",
        "properties": {
          "materialId": {
            "type": "string",
            "format": "uuid"
          },
          "material": {
            "$ref": "#/components/schemas/MaterialRef2"
          },
          "alreadyInPossession": {
            "type": "boolean"
          },
          "answeredAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "deliveryMethod": {
            "$ref": "#/components/schemas/EnumDtoOfEMaterialDeliveryMethod"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfEInviteMaterialStatus"
          }
        }
      },
      "MaterialChangeRequestPatch": {
        "type": "object",
        "properties": {
          "answers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MaterialChangeRequestAnswer"
            },
            "nullable": true
          },
          "deliveryMethodId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "deliveryAddress": {
            "$ref": "#/components/schemas/AddressInfoDto"
          }
        }
      },
      "MaterialGet": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "isbn": {
            "type": "string"
          },
          "language": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage"
          },
          "level": {
            "$ref": "#/components/schemas/EnumDtoOfEMaterialLevel"
          },
          "price": {
            "type": "number",
            "format": "double"
          },
          "type": {
            "$ref": "#/components/schemas/EnumDtoOfEMaterialType"
          },
          "citizenship": {
            "type": "boolean"
          },
          "storageAmount": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "MaterialPatch": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "removeDescription": {
            "type": "boolean"
          },
          "isbn": {
            "type": "string",
            "nullable": true
          },
          "languageId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "levelId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "price": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "typeId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "citizenship": {
            "type": "boolean",
            "nullable": true
          },
          "storageAmount": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        }
      },
      "MaterialPost": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "isbn": {
            "type": "string"
          },
          "languageId": {
            "type": "string",
            "format": "uuid"
          },
          "levelId": {
            "type": "string",
            "format": "uuid"
          },
          "price": {
            "type": "number",
            "format": "double"
          },
          "typeId": {
            "type": "string",
            "format": "uuid"
          },
          "citizenship": {
            "type": "boolean"
          },
          "storageAmount": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "MaterialRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "isbn": {
            "type": "string"
          },
          "language": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage"
          },
          "level": {
            "$ref": "#/components/schemas/EnumDtoOfEMaterialLevel"
          },
          "price": {
            "type": "number",
            "format": "double"
          },
          "type": {
            "$ref": "#/components/schemas/EnumDtoOfEMaterialType"
          }
        }
      },
      "MaterialRef2": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "isbn": {
            "type": "string"
          },
          "language": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage"
          },
          "level": {
            "$ref": "#/components/schemas/EnumDtoOfEMaterialLevel"
          },
          "price": {
            "type": "number",
            "format": "double"
          },
          "type": {
            "$ref": "#/components/schemas/EnumDtoOfEMaterialType"
          }
        },
        "nullable": true
      },
      "MiniListDataOfCustomerInactivityItem": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CustomerInactivityItem"
            }
          },
          "viewAllUrl": {
            "type": "string",
            "nullable": true
          },
          "maxItems": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "MiniListDataOfTeacherAnniversaryItem": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TeacherAnniversaryItem"
            }
          },
          "viewAllUrl": {
            "type": "string",
            "nullable": true
          },
          "maxItems": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "MiniListDataOfTeacherBirthdayItem": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TeacherBirthdayItem"
            }
          },
          "viewAllUrl": {
            "type": "string",
            "nullable": true
          },
          "maxItems": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "MiniListDataOfUpcomingExamItem": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UpcomingExamItem"
            }
          },
          "viewAllUrl": {
            "type": "string",
            "nullable": true
          },
          "maxItems": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "MonthlyCloseFindingGet": {
        "required": [
          "lessonId",
          "date",
          "courseName",
          "recordedCount",
          "enrolledCount"
        ],
        "type": "object",
        "properties": {
          "lessonId": {
            "type": "string",
            "format": "uuid"
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "courseName": {
            "type": "string"
          },
          "recordedCount": {
            "type": "integer",
            "format": "int32"
          },
          "enrolledCount": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "MonthlyCloseGet": {
        "type": "object",
        "properties": {
          "year": {
            "type": "integer",
            "format": "int32"
          },
          "month": {
            "type": "integer",
            "format": "int32"
          },
          "periodDisplay": {
            "type": "string"
          },
          "checkOnly": {
            "type": "boolean"
          },
          "rerun": {
            "type": "boolean"
          },
          "supersededAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "isSuperseded": {
            "type": "boolean"
          },
          "requestedBy": {
            "type": "string",
            "nullable": true
          },
          "startedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "finishedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "state": {
            "$ref": "#/components/schemas/EMonthlyCloseState"
          },
          "stateDisplay": {
            "type": "string"
          },
          "isInFlight": {
            "type": "boolean"
          },
          "phase": {
            "type": "string",
            "nullable": true
          },
          "done": {
            "type": "integer",
            "format": "int32"
          },
          "total": {
            "type": "integer",
            "format": "int32"
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "blockers": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "missingAttendance": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MonthlyCloseFindingGet"
            }
          },
          "missingAttendanceTotal": {
            "type": "integer",
            "format": "int32"
          },
          "missingAttendanceEarlier": {
            "type": "integer",
            "format": "int32"
          },
          "invoicesCreated": {
            "type": "integer",
            "format": "int32"
          },
          "invoiceIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "payslipIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "positionsFrozen": {
            "type": "integer",
            "format": "int32"
          },
          "payslipsCreated": {
            "type": "integer",
            "format": "int32"
          },
          "expenseClaimsAssigned": {
            "type": "integer",
            "format": "int32"
          },
          "documentsProduced": {
            "type": "integer",
            "format": "int32"
          },
          "totalAmount": {
            "type": "number",
            "format": "double"
          },
          "discardedInvoices": {
            "type": "integer",
            "format": "int32"
          },
          "discardedPayslips": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        },
        "nullable": true
      },
      "MonthlyCloseGet2": {
        "type": "object",
        "properties": {
          "year": {
            "type": "integer",
            "format": "int32"
          },
          "month": {
            "type": "integer",
            "format": "int32"
          },
          "periodDisplay": {
            "type": "string"
          },
          "checkOnly": {
            "type": "boolean"
          },
          "rerun": {
            "type": "boolean"
          },
          "supersededAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "isSuperseded": {
            "type": "boolean"
          },
          "requestedBy": {
            "type": "string",
            "nullable": true
          },
          "startedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "finishedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "state": {
            "$ref": "#/components/schemas/EMonthlyCloseState"
          },
          "stateDisplay": {
            "type": "string"
          },
          "isInFlight": {
            "type": "boolean"
          },
          "phase": {
            "type": "string",
            "nullable": true
          },
          "done": {
            "type": "integer",
            "format": "int32"
          },
          "total": {
            "type": "integer",
            "format": "int32"
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "blockers": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "missingAttendance": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MonthlyCloseFindingGet"
            }
          },
          "missingAttendanceTotal": {
            "type": "integer",
            "format": "int32"
          },
          "missingAttendanceEarlier": {
            "type": "integer",
            "format": "int32"
          },
          "invoicesCreated": {
            "type": "integer",
            "format": "int32"
          },
          "invoiceIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "payslipIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "positionsFrozen": {
            "type": "integer",
            "format": "int32"
          },
          "payslipsCreated": {
            "type": "integer",
            "format": "int32"
          },
          "expenseClaimsAssigned": {
            "type": "integer",
            "format": "int32"
          },
          "documentsProduced": {
            "type": "integer",
            "format": "int32"
          },
          "totalAmount": {
            "type": "number",
            "format": "double"
          },
          "discardedInvoices": {
            "type": "integer",
            "format": "int32"
          },
          "discardedPayslips": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "MonthlyClosePost": {
        "type": "object",
        "properties": {
          "checkOnly": {
            "type": "boolean"
          }
        }
      },
      "MonthlyCloseRerunImpactGet": {
        "type": "object",
        "properties": {
          "supersededRunId": {
            "type": "string",
            "format": "uuid"
          },
          "periodDisplay": {
            "type": "string"
          },
          "discardedInvoices": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MonthlyCloseRerunInvoiceGet"
            }
          },
          "keptInvoices": {
            "type": "array",
            "items": { }
          },
          "discardedPayslips": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MonthlyCloseRerunPayslipGet"
            }
          },
          "keptPayslips": {
            "type": "array",
            "items": { }
          },
          "frozenPositionsKept": {
            "type": "integer",
            "format": "int32"
          },
          "hasAnythingToDiscard": {
            "type": "boolean"
          }
        }
      },
      "MonthlyCloseRerunInvoiceGet": {
        "required": [
          "id",
          "invoiceNumber",
          "customerDisplay",
          "totalAmount",
          "statusDisplay",
          "keptReason"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "invoiceNumber": {
            "type": "string"
          },
          "customerDisplay": {
            "type": "string"
          },
          "totalAmount": {
            "type": "number",
            "format": "double"
          },
          "statusDisplay": {
            "type": "string"
          },
          "keptReason": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "MonthlyCloseRerunPayslipGet": {
        "required": [
          "id",
          "teacherDisplay",
          "payout",
          "keptReason"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "teacherDisplay": {
            "type": "string"
          },
          "payout": {
            "type": "number",
            "format": "double"
          },
          "keptReason": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "MonthlyCloseRerunPost": {
        "type": "object",
        "properties": {
          "discardDrafts": {
            "type": "boolean"
          }
        }
      },
      "MonthlyCloseStatusGet": {
        "type": "object",
        "properties": {
          "year": {
            "type": "integer",
            "format": "int32"
          },
          "month": {
            "type": "integer",
            "format": "int32"
          },
          "periodDisplay": {
            "type": "string"
          },
          "canRun": {
            "type": "boolean"
          },
          "canRerun": {
            "type": "boolean"
          },
          "reason": {
            "type": "string",
            "nullable": true
          },
          "closedBy": {
            "$ref": "#/components/schemas/MonthlyCloseGet"
          },
          "inFlight": {
            "$ref": "#/components/schemas/MonthlyCloseGet"
          },
          "latest": {
            "$ref": "#/components/schemas/MonthlyCloseGet"
          }
        }
      },
      "MonthlyHoursOverview": {
        "type": "object",
        "properties": {
          "month": {
            "type": "string",
            "format": "date-time"
          },
          "lessonCount": {
            "type": "integer",
            "format": "int32"
          },
          "billableHours": {
            "type": "number",
            "format": "double"
          },
          "billableAmount": {
            "type": "number",
            "format": "double"
          }
        }
      },
      "NotificationGet": {
        "type": "object",
        "properties": {
          "recipientCustomerId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "recipientCustomer": {
            "$ref": "#/components/schemas/CustomerRef"
          },
          "recipientStudentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "recipientStudent": {
            "$ref": "#/components/schemas/StudentRef"
          },
          "relatedStudentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "relatedStudent": {
            "$ref": "#/components/schemas/StudentRef"
          },
          "relatedCustomerId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "relatedCustomer": {
            "$ref": "#/components/schemas/CustomerRef"
          },
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "message": {
            "type": "string"
          },
          "eventDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "isRead": {
            "type": "boolean"
          },
          "targetId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "targetUrl": {
            "type": "string",
            "nullable": true
          },
          "type": {
            "$ref": "#/components/schemas/EnumDtoOfENotificationType"
          },
          "key": {
            "$ref": "#/components/schemas/EnumDtoOfENotificationKey"
          },
          "recipientDisplay": {
            "type": "string",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "NotificationPatch": {
        "type": "object",
        "properties": {
          "recipientCustomerId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "recipientStudentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "relatedStudentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "relatedCustomerId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "date": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "eventDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "isRead": {
            "type": "boolean",
            "nullable": true
          },
          "targetId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "typeId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "keyId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "NotificationPost": {
        "type": "object",
        "properties": {
          "recipientCustomerId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "recipientStudentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "relatedStudentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "relatedCustomerId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "isRead": {
            "type": "boolean"
          },
          "targetId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "eventDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "typeId": {
            "type": "string",
            "format": "uuid"
          },
          "keyId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "NotificationTriggerGet": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string"
          },
          "description": {
            "type": "string"
          }
        }
      },
      "NotificationTriggerRunGet": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string"
          },
          "changes": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "NullableOfDayOfWeek": {
        "type": "integer",
        "nullable": true
      },
      "NullableOfEAccountStatus": {
        "type": "integer"
      },
      "NullableOfEExpenseClaimStatus": {
        "type": "integer"
      },
      "NullableOfEStudentStatus": {
        "type": "integer"
      },
      "NullableOfETeachingLevel": {
        "type": "integer"
      },
      "OfficeDashboardGet": {
        "type": "object",
        "properties": {
          "upcomingBirthdays": {
            "$ref": "#/components/schemas/MiniListDataOfTeacherBirthdayItem"
          },
          "upcomingAnniversaries": {
            "$ref": "#/components/schemas/MiniListDataOfTeacherAnniversaryItem"
          },
          "upcomingExams": {
            "$ref": "#/components/schemas/MiniListDataOfUpcomingExamItem"
          },
          "currentMonthHours": {
            "$ref": "#/components/schemas/MonthlyHoursOverview"
          },
          "inactiveCustomers": {
            "$ref": "#/components/schemas/MiniListDataOfCustomerInactivityItem"
          }
        }
      },
      "OrderGet": {
        "type": "object",
        "properties": {
          "createdDate": {
            "type": "string",
            "format": "date-time"
          },
          "studentId": {
            "type": "string",
            "format": "uuid"
          },
          "student": {
            "$ref": "#/components/schemas/StudentRef"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfEOrderStatus"
          },
          "isPaid": {
            "type": "boolean"
          },
          "comment": {
            "type": "string",
            "nullable": true
          },
          "courseId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "course": {
            "$ref": "#/components/schemas/CourseRef"
          },
          "deliveryAddress": {
            "$ref": "#/components/schemas/AddressInfoDto"
          },
          "billedSeparately": {
            "type": "boolean"
          },
          "invoiceId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "invoiceNumber": {
            "type": "string",
            "nullable": true
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderItemDto"
            }
          },
          "studentDisplay": {
            "type": "string",
            "nullable": true
          },
          "customerDisplay": {
            "type": "string",
            "nullable": true
          },
          "itemCount": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "totalCost": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "courseDisplay": {
            "type": "string",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "OrderItemDto": {
        "type": "object",
        "properties": {
          "materialId": {
            "type": "string",
            "format": "uuid"
          },
          "material": {
            "$ref": "#/components/schemas/MaterialRef2"
          },
          "unitPrice": {
            "type": "number",
            "format": "double"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfEInviteMaterialStatus"
          },
          "statusId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "deliveryMethod": {
            "$ref": "#/components/schemas/EnumDtoOfEMaterialDeliveryMethod"
          },
          "deliveryMethodId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "OrderPatch": {
        "type": "object",
        "properties": {
          "createdDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "studentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "statusId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "courseId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "deliveryAddress": {
            "$ref": "#/components/schemas/AddressInfoDto"
          },
          "billedSeparately": {
            "type": "boolean",
            "nullable": true
          },
          "comment": {
            "type": "string",
            "nullable": true
          },
          "removeComment": {
            "type": "boolean"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderItemDto"
            },
            "nullable": true
          }
        }
      },
      "OrderPost": {
        "type": "object",
        "properties": {
          "createdDate": {
            "type": "string",
            "format": "date-time"
          },
          "studentId": {
            "type": "string",
            "format": "uuid"
          },
          "statusId": {
            "type": "string",
            "format": "uuid"
          },
          "courseId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "deliveryAddress": {
            "$ref": "#/components/schemas/AddressInfoDto"
          },
          "billedSeparately": {
            "type": "boolean"
          },
          "comment": {
            "type": "string",
            "nullable": true
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderItemDto"
            },
            "nullable": true
          }
        }
      },
      "PackageCreditGivenResponse": {
        "required": [
          "amount"
        ],
        "type": "object",
        "properties": {
          "amount": {
            "type": "number",
            "format": "double"
          }
        }
      },
      "PackageGet": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "billingTitle": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "price": {
            "type": "number",
            "format": "double"
          },
          "credit": {
            "type": "number",
            "format": "double"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfEPackageStatus"
          },
          "isPrivate": {
            "type": "boolean"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "PackageGet2": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "billingTitle": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "price": {
            "type": "number",
            "format": "double"
          },
          "credit": {
            "type": "number",
            "format": "double"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfEPackageStatus"
          },
          "isPrivate": {
            "type": "boolean"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        },
        "nullable": true
      },
      "PackageOrderGet": {
        "type": "object",
        "properties": {
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfEPackageOrderStatus"
          },
          "activeFrom": {
            "type": "string",
            "format": "date-time"
          },
          "paidAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "refundRequestedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "refundedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "settledAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "refundReason": {
            "type": "string",
            "nullable": true
          },
          "packageId": {
            "type": "string",
            "format": "uuid"
          },
          "packageRef": {
            "$ref": "#/components/schemas/PackageGet2"
          },
          "customerId": {
            "type": "string",
            "format": "uuid"
          },
          "customerRef": {
            "$ref": "#/components/schemas/CustomerRef"
          },
          "assignedToCustomerId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "assignedToCustomer": {
            "$ref": "#/components/schemas/CustomerRef"
          },
          "assignedToCustomerEmail": {
            "type": "string",
            "nullable": true
          },
          "passedOnType": {
            "$ref": "#/components/schemas/EnumDtoOfEPackageAssignment"
          },
          "usageByStudent": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PackageStudentUsageDto"
            }
          },
          "displayName": {
            "type": "string",
            "nullable": true
          },
          "customerName": {
            "type": "string",
            "nullable": true
          },
          "usedCredit": {
            "type": "number",
            "format": "double"
          },
          "purchasedPrice": {
            "type": "number",
            "format": "double"
          },
          "purchasedCredit": {
            "type": "number",
            "format": "double"
          },
          "remainingCredit": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "residualCredit": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "creditedInvoiceId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "creditedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "cancelledAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "creditedInvoiceNumber": {
            "type": "string",
            "nullable": true
          },
          "isCreditOpen": {
            "type": "boolean"
          },
          "overdrawnAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "PackageOrderPatch": {
        "type": "object",
        "properties": {
          "statusId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "refundReason": {
            "type": "string",
            "nullable": true
          },
          "activeFrom": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "paidAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "settledAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "passedOnTypeId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "assignedToCustomerId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "removeAssignedToCustomerId": {
            "type": "boolean"
          },
          "assignedToCustomerEmail": {
            "type": "string",
            "nullable": true
          },
          "removeAssignedToCustomerEmail": {
            "type": "boolean"
          }
        }
      },
      "PackagePatch": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "nullable": true
          },
          "billingTitle": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "price": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "credit": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "isPrivate": {
            "type": "boolean",
            "nullable": true
          },
          "statusId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "PackagePost": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "billingTitle": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "price": {
            "type": "number",
            "format": "double"
          },
          "credit": {
            "type": "number",
            "format": "double"
          },
          "isPrivate": {
            "type": "boolean"
          },
          "statusId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "PackagePurchaseGet": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "format": "uuid"
          },
          "customerRef": {
            "$ref": "#/components/schemas/CustomerRef"
          },
          "purchasedAt": {
            "type": "string",
            "format": "date-time"
          },
          "invoiceId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "invoiceNumber": {
            "type": "string",
            "nullable": true
          },
          "orders": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PackageOrderGet"
            }
          },
          "customerName": {
            "type": "string",
            "nullable": true
          },
          "itemCount": {
            "type": "integer",
            "format": "int32"
          },
          "isCompleted": {
            "type": "boolean"
          },
          "totalPrice": {
            "type": "number",
            "format": "double"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "PackagePurchaseItemPost": {
        "type": "object",
        "properties": {
          "packageId": {
            "type": "string",
            "format": "uuid"
          },
          "beneficiaryCustomerEmail": {
            "type": "string",
            "nullable": true
          },
          "passedOnTypeId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "PackagePurchaseMarkPaidPost": {
        "type": "object",
        "properties": {
          "paidAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "PackagePurchasePost": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "format": "uuid"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PackagePurchaseItemPost"
            }
          }
        }
      },
      "PackageStudentUsageDto": {
        "type": "object",
        "properties": {
          "studentId": {
            "type": "string",
            "format": "uuid"
          },
          "student": {
            "$ref": "#/components/schemas/StudentRef"
          },
          "usedCredit": {
            "type": "number",
            "format": "double"
          },
          "lessonCount": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "PaymentExtraItemGet": {
        "type": "object",
        "properties": {
          "teacherPaymentId": {
            "type": "string",
            "format": "uuid"
          },
          "description": {
            "type": "string"
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "documentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "documentFileName": {
            "type": "string",
            "nullable": true
          },
          "documentSizeBytes": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "PaymentExtraItemPatch": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string",
            "nullable": true
          },
          "amount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "documentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "removeDocument": {
            "type": "boolean"
          }
        }
      },
      "PaymentExtraItemPatchWithId": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "patch": {
            "$ref": "#/components/schemas/PaymentExtraItemPatch"
          }
        }
      },
      "PaymentExtraItemPost": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string"
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "documentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "PaymentImportGet": {
        "type": "object",
        "properties": {
          "fileName": {
            "type": "string"
          },
          "messageId": {
            "type": "string"
          },
          "accountIban": {
            "type": "string",
            "nullable": true
          },
          "statementCreatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "importedAt": {
            "type": "string",
            "format": "date-time"
          },
          "payments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BankPaymentGet"
            }
          },
          "paymentCount": {
            "type": "integer",
            "format": "int32"
          },
          "bookedCount": {
            "type": "integer",
            "format": "int32"
          },
          "alreadyImportedCount": {
            "type": "integer",
            "format": "int32"
          },
          "conflictCount": {
            "type": "integer",
            "format": "int32"
          },
          "bookedAmount": {
            "type": "number",
            "format": "double"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "PaymentLineItemGet": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "lessonIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "lessons": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LessonRef"
            },
            "nullable": true
          },
          "rateGroupName": {
            "type": "string"
          },
          "lessonCount": {
            "type": "integer",
            "format": "int32"
          },
          "rate": {
            "type": "number",
            "format": "double"
          },
          "totalMinutes": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "hourlyRate": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "lineTotal": {
            "type": "number",
            "format": "double"
          }
        }
      },
      "PaymentLineItemPatch": {
        "type": "object",
        "properties": {
          "lessonIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "lessonCount": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "rate": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "lineTotal": {
            "type": "number",
            "format": "double",
            "nullable": true
          }
        }
      },
      "PaymentLineItemPatchWithId": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "patch": {
            "$ref": "#/components/schemas/PaymentLineItemPatch"
          }
        }
      },
      "PaymentLineItemPost": {
        "type": "object",
        "properties": {
          "lessonIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "rateGroupName": {
            "type": "string"
          },
          "lessonCount": {
            "type": "integer",
            "format": "int32"
          },
          "rate": {
            "type": "number",
            "format": "double"
          },
          "lineTotal": {
            "type": "number",
            "format": "double"
          }
        }
      },
      "PayrollAccountGet": {
        "type": "object",
        "properties": {
          "iban": {
            "type": "string"
          },
          "ibanFormatted": {
            "type": "string"
          },
          "bic": {
            "type": "string",
            "nullable": true
          },
          "name": {
            "type": "string"
          },
          "street": {
            "type": "string",
            "nullable": true
          },
          "buildingNumber": {
            "type": "string",
            "nullable": true
          },
          "postalCode": {
            "type": "string"
          },
          "town": {
            "type": "string"
          },
          "countryCode": {
            "type": "string"
          },
          "payoutDayOffset": {
            "type": "integer",
            "format": "int32"
          },
          "confidential": {
            "type": "boolean"
          },
          "isConfigured": {
            "type": "boolean"
          },
          "isIbanValid": {
            "type": "boolean"
          },
          "isQrIban": {
            "type": "boolean"
          },
          "institutionNumber": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "PayrollAccountPatch": {
        "type": "object",
        "properties": {
          "iban": {
            "type": "string",
            "nullable": true
          },
          "bic": {
            "type": "string",
            "nullable": true
          },
          "removeBic": {
            "type": "boolean"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "street": {
            "type": "string",
            "nullable": true
          },
          "removeStreet": {
            "type": "boolean"
          },
          "buildingNumber": {
            "type": "string",
            "nullable": true
          },
          "removeBuildingNumber": {
            "type": "boolean"
          },
          "postalCode": {
            "type": "string",
            "nullable": true
          },
          "town": {
            "type": "string",
            "nullable": true
          },
          "countryCode": {
            "type": "string",
            "nullable": true
          },
          "payoutDayOffset": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "confidential": {
            "type": "boolean",
            "nullable": true
          }
        }
      },
      "PayrollSettingsGet": {
        "type": "object",
        "properties": {
          "ahvRate": {
            "type": "number",
            "format": "double"
          },
          "alvRate": {
            "type": "number",
            "format": "double"
          },
          "ktgRate": {
            "type": "number",
            "format": "double"
          },
          "nbuRate": {
            "type": "number",
            "format": "double"
          },
          "quellensteuerRate": {
            "type": "number",
            "format": "double"
          },
          "vacationSupplementRate": {
            "type": "number",
            "format": "double"
          },
          "travelAllowancePerLesson": {
            "type": "number",
            "format": "double"
          },
          "ratesConfirmedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "isConfigured": {
            "type": "boolean"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "PayrollSettingsPatch": {
        "type": "object",
        "properties": {
          "ahvRate": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "alvRate": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "ktgRate": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "nbuRate": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "quellensteuerRate": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "vacationSupplementRate": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "travelAllowancePerLesson": {
            "type": "number",
            "format": "double",
            "nullable": true
          }
        }
      },
      "PayslipReleasePeriodPost": {
        "required": [
          "year",
          "month"
        ],
        "type": "object",
        "properties": {
          "year": {
            "type": "integer",
            "format": "int32"
          },
          "month": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "PayslipReleaseResultGet": {
        "type": "object",
        "properties": {
          "periodDisplay": {
            "type": "string"
          },
          "year": {
            "type": "integer",
            "format": "int32"
          },
          "month": {
            "type": "integer",
            "format": "int32"
          },
          "releasedAt": {
            "type": "string",
            "format": "date"
          },
          "releasedCount": {
            "type": "integer",
            "format": "int32"
          },
          "released": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReleasedPayslipGet"
            }
          }
        }
      },
      "PriceActivationResult": {
        "required": [
          "coursePrices",
          "teacherSalaries",
          "courses",
          "normalised"
        ],
        "type": "object",
        "properties": {
          "coursePrices": {
            "type": "integer",
            "format": "int32"
          },
          "teacherSalaries": {
            "type": "integer",
            "format": "int32"
          },
          "courses": {
            "type": "integer",
            "format": "int32"
          },
          "normalised": {
            "type": "integer",
            "format": "int32"
          },
          "total": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "PriceVersionGet": {
        "type": "object",
        "properties": {
          "ownerId": {
            "type": "string",
            "format": "uuid"
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "validFrom": {
            "type": "string",
            "format": "date-time"
          },
          "validUntil": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "isCurrent": {
            "type": "boolean"
          },
          "isScheduled": {
            "type": "boolean"
          },
          "changedByName": {
            "type": "string",
            "nullable": true
          },
          "reason": {
            "type": "string",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "ProblemDetails": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "nullable": true
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "detail": {
            "type": "string",
            "nullable": true
          },
          "instance": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "QrBillCreditorGet": {
        "type": "object",
        "properties": {
          "iban": {
            "type": "string"
          },
          "ibanFormatted": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "street": {
            "type": "string",
            "nullable": true
          },
          "buildingNumber": {
            "type": "string",
            "nullable": true
          },
          "postalCode": {
            "type": "string"
          },
          "town": {
            "type": "string"
          },
          "countryCode": {
            "type": "string"
          },
          "vatNumber": {
            "type": "string",
            "nullable": true
          },
          "isConfigured": {
            "type": "boolean"
          },
          "isIbanValid": {
            "type": "boolean"
          },
          "isQrIban": {
            "type": "boolean"
          },
          "referenceType": {
            "$ref": "#/components/schemas/EQrBillReferenceType"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "QrBillCreditorPatch": {
        "type": "object",
        "properties": {
          "iban": {
            "type": "string",
            "nullable": true
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "street": {
            "type": "string",
            "nullable": true
          },
          "removeStreet": {
            "type": "boolean"
          },
          "buildingNumber": {
            "type": "string",
            "nullable": true
          },
          "removeBuildingNumber": {
            "type": "boolean"
          },
          "postalCode": {
            "type": "string",
            "nullable": true
          },
          "town": {
            "type": "string",
            "nullable": true
          },
          "countryCode": {
            "type": "string",
            "nullable": true
          },
          "vatNumber": {
            "type": "string",
            "nullable": true
          },
          "removeVatNumber": {
            "type": "boolean"
          }
        }
      },
      "QrBillPreviewGet": {
        "type": "object",
        "properties": {
          "invoiceId": {
            "type": "string",
            "format": "uuid"
          },
          "invoiceNumber": {
            "type": "string"
          },
          "payload": {
            "type": "string"
          },
          "payloadLength": {
            "type": "integer",
            "format": "int32"
          },
          "referenceType": {
            "$ref": "#/components/schemas/EQrBillReferenceType"
          },
          "reference": {
            "type": "string",
            "nullable": true
          },
          "referenceFormatted": {
            "type": "string",
            "nullable": true
          },
          "ibanFormatted": {
            "type": "string"
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "currency": {
            "type": "string"
          },
          "debtorName": {
            "type": "string",
            "nullable": true
          },
          "isValid": {
            "type": "boolean"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ReleasedPayslipGet": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "teacherId": {
            "type": "string",
            "format": "uuid"
          },
          "teacherName": {
            "type": "string"
          },
          "actualPay": {
            "type": "number",
            "format": "double"
          }
        }
      },
      "RoomGet": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "sizeInSquareMeters": {
            "type": "number",
            "format": "double"
          },
          "maxStudents": {
            "type": "integer",
            "format": "int32"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfERoomStatus"
          },
          "isFixed": {
            "type": "boolean"
          },
          "locationId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "location": {
            "$ref": "#/components/schemas/LocationGet2"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "RoomOccupancySlotGet": {
        "required": [
          "lessonId",
          "courseId",
          "courseName",
          "date",
          "startTime",
          "endTime",
          "hasOwnRoom"
        ],
        "type": "object",
        "properties": {
          "lessonId": {
            "type": "string",
            "format": "uuid"
          },
          "courseId": {
            "type": "string",
            "format": "uuid"
          },
          "courseName": {
            "type": "string"
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "startTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          },
          "endTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          },
          "hasOwnRoom": {
            "type": "boolean"
          }
        }
      },
      "RoomPatch": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true
          },
          "sizeInSquareMeters": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "maxStudents": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "statusId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "locationId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "RoomPost": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "sizeInSquareMeters": {
            "type": "number",
            "format": "double"
          },
          "maxStudents": {
            "type": "integer",
            "format": "int32"
          },
          "statusId": {
            "type": "string",
            "format": "uuid"
          },
          "locationId": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "RoomRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "displayName": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "maxStudents": {
            "type": "integer",
            "format": "int32"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfERoomStatus"
          }
        },
        "nullable": true
      },
      "SalaryExportGet": {
        "type": "object",
        "properties": {
          "fileName": {
            "type": "string"
          },
          "messageId": {
            "type": "string"
          },
          "paymentInformationId": {
            "type": "string"
          },
          "periodStart": {
            "type": "string",
            "format": "date"
          },
          "periodDisplay": {
            "type": "string",
            "nullable": true
          },
          "requestedExecutionDate": {
            "type": "string",
            "format": "date"
          },
          "confidential": {
            "type": "boolean"
          },
          "debtorIban": {
            "type": "string"
          },
          "debtorName": {
            "type": "string"
          },
          "exportedAt": {
            "type": "string",
            "format": "date-time"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SalaryExportItemGet"
            }
          },
          "paymentCount": {
            "type": "integer",
            "format": "int32"
          },
          "totalAmount": {
            "type": "number",
            "format": "double"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "SalaryExportItemGet": {
        "type": "object",
        "properties": {
          "teacherPaymentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "teacherId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "creditorName": {
            "type": "string"
          },
          "creditorIban": {
            "type": "string"
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "currency": {
            "type": "string"
          },
          "endToEndId": {
            "type": "string"
          },
          "uetr": {
            "type": "string",
            "format": "uuid"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "SalaryExportPost": {
        "type": "object",
        "properties": {
          "periodStart": {
            "type": "string",
            "format": "date"
          },
          "requestedExecutionDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "teacherPaymentIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          }
        }
      },
      "SalaryExportPreviewGet": {
        "type": "object",
        "properties": {
          "periodStart": {
            "type": "string",
            "format": "date"
          },
          "requestedExecutionDate": {
            "type": "string",
            "format": "date"
          },
          "confidential": {
            "type": "boolean"
          },
          "payrollAccountConfigured": {
            "type": "boolean"
          },
          "payrollAccountProblems": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "payments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SalaryPaymentPreviewGet"
            }
          },
          "payableCount": {
            "type": "integer",
            "format": "int32"
          },
          "blockedCount": {
            "type": "integer",
            "format": "int32"
          },
          "alreadyExportedCount": {
            "type": "integer",
            "format": "int32"
          },
          "totalAmount": {
            "type": "number",
            "format": "double"
          },
          "canExport": {
            "type": "boolean"
          }
        }
      },
      "SalaryPaymentPreviewGet": {
        "type": "object",
        "properties": {
          "teacherPaymentId": {
            "type": "string",
            "format": "uuid"
          },
          "teacherId": {
            "type": "string",
            "format": "uuid"
          },
          "teacherName": {
            "type": "string"
          },
          "creditorName": {
            "type": "string"
          },
          "creditorIban": {
            "type": "string"
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "isPayable": {
            "type": "boolean"
          },
          "problems": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "previouslyExportedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "previousExportId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "SetPasswordPost": {
        "type": "object",
        "properties": {
          "password": {
            "type": "string"
          }
        }
      },
      "SpokenLanguageGet": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "language": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage"
          },
          "level": {
            "$ref": "#/components/schemas/EnumDtoOfETeachingLevel"
          }
        }
      },
      "SpokenLanguagePatch": {
        "type": "object",
        "properties": {
          "languageId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "levelId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "SpokenLanguagePatchWithId": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "patch": {
            "$ref": "#/components/schemas/SpokenLanguagePatch"
          }
        }
      },
      "SpokenLanguagePost": {
        "type": "object",
        "properties": {
          "languageId": {
            "type": "string",
            "format": "uuid"
          },
          "levelId": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "StudentGet": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "format": "uuid"
          },
          "customer": {
            "$ref": "#/components/schemas/CustomerRef"
          },
          "lastName": {
            "type": "string"
          },
          "firstName": {
            "type": "string"
          },
          "birthdate": {
            "type": "string",
            "format": "date-time"
          },
          "joinDate": {
            "type": "string",
            "format": "date-time"
          },
          "nativeLanguage": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage2"
          },
          "gender": {
            "$ref": "#/components/schemas/EnumDtoOfEGender2"
          },
          "correspondenceLanguage": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage2"
          },
          "preferredTeachingFormat": {
            "$ref": "#/components/schemas/EnumDtoOfEPreferredTeachingFormat"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfEStudentStatus"
          },
          "inactive": {
            "type": "boolean"
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "accountStatus": {
            "$ref": "#/components/schemas/EnumDtoOfEAccountStatus"
          },
          "loginNumber": {
            "type": "string",
            "nullable": true
          },
          "phone": {
            "type": "string",
            "nullable": true
          },
          "useCustomerAddress": {
            "type": "boolean"
          },
          "address": {
            "$ref": "#/components/schemas/AddressInfoDto"
          },
          "subjects": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SubjectGet"
            }
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "courseIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "courses": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CourseRef2"
            },
            "nullable": true
          },
          "lessonIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "lessons": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LessonRef"
            },
            "nullable": true
          },
          "materialIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "materials": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MaterialRef"
            },
            "nullable": true
          },
          "studentNotes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StudentNoteGet"
            },
            "nullable": true
          },
          "examRecords": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ExamRecordGet"
            },
            "nullable": true
          },
          "age": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "isChild": {
            "type": "boolean",
            "nullable": true
          },
          "displayName": {
            "type": "string",
            "nullable": true
          },
          "customerDisplay": {
            "type": "string",
            "nullable": true
          },
          "contactDisplay": {
            "type": "string",
            "nullable": true
          },
          "subjectsDisplay": {
            "type": "string",
            "nullable": true
          },
          "absenceCountThisYear": {
            "type": "integer",
            "format": "int32"
          },
          "teachers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StudentTeacherSummary"
            }
          },
          "courseCount": {
            "type": "integer",
            "format": "int32"
          },
          "lessonCount": {
            "type": "integer",
            "format": "int32"
          },
          "materialCount": {
            "type": "integer",
            "format": "int32"
          },
          "noteCount": {
            "type": "integer",
            "format": "int32"
          },
          "examRecordCount": {
            "type": "integer",
            "format": "int32"
          },
          "currentLevelsDisplay": {
            "type": "string",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "StudentInviteGet": {
        "type": "object",
        "properties": {
          "courseId": {
            "type": "string",
            "format": "uuid"
          },
          "course": {
            "$ref": "#/components/schemas/CourseRef"
          },
          "studentId": {
            "type": "string",
            "format": "uuid"
          },
          "student": {
            "$ref": "#/components/schemas/StudentRef"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfEInviteStatus"
          },
          "invitedAt": {
            "type": "string",
            "format": "date-time"
          },
          "respondedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "materialChoices": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InviteMaterialChoiceGet"
            }
          },
          "deliveryMethod": {
            "$ref": "#/components/schemas/EnumDtoOfEMaterialDeliveryMethod"
          },
          "deliveryAddress": {
            "$ref": "#/components/schemas/AddressInfoDto"
          },
          "acceptedDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "declinedDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "displayName": {
            "type": "string",
            "nullable": true
          },
          "isPending": {
            "type": "boolean"
          },
          "materialListComplete": {
            "type": "boolean"
          },
          "courseInfo": {
            "type": "string",
            "nullable": true
          },
          "scheduleInfo": {
            "type": "string",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "StudentInvitePatch": {
        "type": "object",
        "properties": {
          "statusId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "respondedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "materialChoices": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InviteMaterialChoicePost"
            },
            "nullable": true
          },
          "deliveryMethodId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "deliveryAddress": {
            "$ref": "#/components/schemas/AddressInfoDto"
          }
        }
      },
      "StudentInvitePost": {
        "type": "object",
        "properties": {
          "courseId": {
            "type": "string",
            "format": "uuid"
          },
          "studentId": {
            "type": "string",
            "format": "uuid"
          },
          "statusId": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "StudentInviteRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "studentId": {
            "type": "string",
            "format": "uuid"
          },
          "student": {
            "$ref": "#/components/schemas/StudentRef"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfEInviteStatus"
          },
          "invitedAt": {
            "type": "string",
            "format": "date-time"
          },
          "respondedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "isPending": {
            "type": "boolean"
          },
          "displayName": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "StudentNoteGet": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "teacherId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "authorName": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "text": {
            "type": "string"
          }
        }
      },
      "StudentNotePost": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string"
          }
        }
      },
      "StudentPatch": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "lastName": {
            "type": "string",
            "nullable": true
          },
          "firstName": {
            "type": "string",
            "nullable": true
          },
          "birthdate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "joinDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "nativeLanguageId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "genderId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "removeGender": {
            "type": "boolean"
          },
          "correspondenceLanguageId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "removeCorrespondenceLanguage": {
            "type": "boolean"
          },
          "preferredTeachingFormatId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "removePreferredTeachingFormat": {
            "type": "boolean"
          },
          "inactive": {
            "type": "boolean",
            "nullable": true
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "removeEmail": {
            "type": "boolean"
          },
          "phone": {
            "type": "string",
            "nullable": true
          },
          "removePhone": {
            "type": "boolean"
          },
          "useCustomerAddress": {
            "type": "boolean",
            "nullable": true
          },
          "address": {
            "$ref": "#/components/schemas/AddressInfoDto"
          },
          "removeAddress": {
            "type": "boolean"
          },
          "subjectsToAdd": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SubjectPost"
            },
            "nullable": true
          },
          "subjectsToUpdate": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SubjectPatchWithId"
            },
            "nullable": true
          },
          "subjectIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "removeNotes": {
            "type": "boolean"
          },
          "studentNotesToAdd": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StudentNotePost"
            },
            "nullable": true
          },
          "studentNoteIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "examRecordsToAdd": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ExamRecordPost"
            },
            "nullable": true
          },
          "examRecordsToUpdate": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ExamRecordPatchWithId"
            },
            "nullable": true
          },
          "examRecordIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          }
        }
      },
      "StudentPost": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "format": "uuid"
          },
          "lastName": {
            "type": "string"
          },
          "firstName": {
            "type": "string"
          },
          "birthdate": {
            "type": "string",
            "format": "date-time"
          },
          "joinDate": {
            "type": "string",
            "format": "date-time"
          },
          "nativeLanguageId": {
            "type": "string",
            "format": "uuid"
          },
          "genderId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "correspondenceLanguageId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "preferredTeachingFormatId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "inactive": {
            "type": "boolean"
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "phone": {
            "type": "string",
            "nullable": true
          },
          "useCustomerAddress": {
            "type": "boolean"
          },
          "address": {
            "$ref": "#/components/schemas/AddressInfoDto"
          },
          "subjects": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SubjectPost"
            },
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "StudentRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "nativeLanguage": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage2"
          },
          "isChild": {
            "type": "boolean",
            "nullable": true
          },
          "age": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        },
        "nullable": true
      },
      "StudentRef2": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "nativeLanguage": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage2"
          },
          "isChild": {
            "type": "boolean",
            "nullable": true
          },
          "age": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        }
      },
      "StudentTeacherSummary": {
        "type": "object",
        "properties": {
          "teacherId": {
            "type": "string",
            "format": "uuid"
          },
          "displayName": {
            "type": "string"
          },
          "lessonCount": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "SubjectGet": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "language": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage"
          },
          "level": {
            "$ref": "#/components/schemas/EnumDtoOfETeachingLevel"
          }
        }
      },
      "SubjectPatch": {
        "type": "object",
        "properties": {
          "languageId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "levelId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "SubjectPatchWithId": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "patch": {
            "$ref": "#/components/schemas/SubjectPatch"
          }
        }
      },
      "SubjectPost": {
        "type": "object",
        "properties": {
          "languageId": {
            "type": "string",
            "format": "uuid"
          },
          "levelId": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "TeacherAnniversaryItem": {
        "type": "object",
        "properties": {
          "teacherId": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "joinDate": {
            "type": "string",
            "format": "date-time"
          },
          "years": {
            "type": "integer",
            "format": "int32"
          },
          "daysUntil": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "TeacherApplicationGet": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "teacherId": {
            "type": "string",
            "format": "uuid"
          },
          "teacherRef": {
            "$ref": "#/components/schemas/TeacherRef"
          },
          "proposedTimeSlots": {
            "type": "array",
            "items": { }
          },
          "appliedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TeacherApplicationPost": {
        "type": "object",
        "properties": {
          "teacherId": {
            "type": "string",
            "format": "uuid"
          },
          "proposedTimeSlots": {
            "type": "array",
            "items": { }
          }
        }
      },
      "TeacherBirthdayItem": {
        "type": "object",
        "properties": {
          "teacherId": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "birthDate": {
            "type": "string",
            "format": "date-time"
          },
          "daysUntil": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "TeacherGet": {
        "type": "object",
        "properties": {
          "gender": {
            "$ref": "#/components/schemas/EnumDtoOfEGender"
          },
          "birthDate": {
            "type": "string",
            "format": "date-time"
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "accountStatus": {
            "$ref": "#/components/schemas/EnumDtoOfEAccountStatus"
          },
          "loginNumber": {
            "type": "string",
            "nullable": true
          },
          "privateEmail": {
            "type": "string",
            "nullable": true
          },
          "mobilePhone": {
            "type": "string"
          },
          "landlinePhone": {
            "type": "string",
            "nullable": true
          },
          "address": {
            "type": "string"
          },
          "postalCode": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "countryCode": {
            "$ref": "#/components/schemas/EnumDtoOfECountryCode"
          },
          "joinDate": {
            "type": "string",
            "format": "date-time"
          },
          "leftSoftLandingDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "inactive": {
            "type": "boolean"
          },
          "nationality": {
            "type": "string",
            "nullable": true
          },
          "civilStatus": {
            "$ref": "#/components/schemas/EnumDtoOfECivilStatus"
          },
          "ahvNumber": {
            "type": "string"
          },
          "quellensteuer": {
            "type": "boolean"
          },
          "quellensteuerFreibetrag": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "ahv": {
            "type": "boolean"
          },
          "alv": {
            "type": "boolean"
          },
          "nbu": {
            "type": "boolean"
          },
          "ktg": {
            "type": "boolean"
          },
          "bvg": {
            "type": "boolean"
          },
          "bvgDeductionAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "flatSalary": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "iban": {
            "type": "string"
          },
          "accountHolder": {
            "type": "string",
            "nullable": true
          },
          "lessonsToChildren": {
            "type": "boolean"
          },
          "challengingStudents": {
            "type": "boolean"
          },
          "einbuergerungsprogramm": {
            "type": "boolean"
          },
          "testPreparation": {
            "type": "boolean"
          },
          "teachingLanguages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TeachingLanguageGet"
            }
          },
          "teachingLanguagesDisplay": {
            "type": "string",
            "nullable": true
          },
          "spokenLanguages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SpokenLanguageGet"
            }
          },
          "spokenLanguagesDisplay": {
            "type": "string",
            "nullable": true
          },
          "hasChildren": {
            "type": "boolean"
          },
          "children": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChildInfoGet"
            }
          },
          "weeklyTimeSlots": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WeeklyTimeSlotGet"
            }
          },
          "absencePeriods": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AbsencePeriodGet"
            }
          },
          "completedTrainings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CompletedTrainingGet"
            }
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "TeacherInviteGet": {
        "type": "object",
        "properties": {
          "courseId": {
            "type": "string",
            "format": "uuid"
          },
          "course": {
            "$ref": "#/components/schemas/CourseRef"
          },
          "teacherId": {
            "type": "string",
            "format": "uuid"
          },
          "teacher": {
            "$ref": "#/components/schemas/TeacherRef"
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfEInviteStatus"
          },
          "invitedAt": {
            "type": "string",
            "format": "date-time"
          },
          "respondedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "denyReason": {
            "type": "string",
            "nullable": true
          },
          "displayName": {
            "type": "string",
            "nullable": true
          },
          "isPending": {
            "type": "boolean"
          },
          "courseInfo": {
            "type": "string",
            "nullable": true
          },
          "scheduleInfo": {
            "type": "string",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "TeacherInvitePatch": {
        "type": "object",
        "properties": {
          "statusId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "respondedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "denyReason": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "TeacherInvitePost": {
        "type": "object",
        "properties": {
          "courseId": {
            "type": "string",
            "format": "uuid"
          },
          "teacherId": {
            "type": "string",
            "format": "uuid"
          },
          "statusId": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "TeacherPatch": {
        "type": "object",
        "properties": {
          "genderId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "birthDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "firstName": {
            "type": "string",
            "nullable": true
          },
          "lastName": {
            "type": "string",
            "nullable": true
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "privateEmail": {
            "type": "string",
            "nullable": true
          },
          "removePrivateEmail": {
            "type": "boolean"
          },
          "mobilePhone": {
            "type": "string",
            "nullable": true
          },
          "landlinePhone": {
            "type": "string",
            "nullable": true
          },
          "removeLandlinePhone": {
            "type": "boolean"
          },
          "address": {
            "type": "string",
            "nullable": true
          },
          "postalCode": {
            "type": "string",
            "nullable": true
          },
          "city": {
            "type": "string",
            "nullable": true
          },
          "countryCodeId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "joinDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "leftSoftLandingDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "removeLeftSoftLandingDate": {
            "type": "boolean"
          },
          "inactive": {
            "type": "boolean",
            "nullable": true
          },
          "nationality": {
            "type": "string",
            "nullable": true
          },
          "removeNationality": {
            "type": "boolean"
          },
          "civilStatusId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "ahvNumber": {
            "type": "string",
            "nullable": true
          },
          "quellensteuer": {
            "type": "boolean",
            "nullable": true
          },
          "quellensteuerFreibetrag": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "removeQuellensteuerFreibetrag": {
            "type": "boolean"
          },
          "ahv": {
            "type": "boolean",
            "nullable": true
          },
          "alv": {
            "type": "boolean",
            "nullable": true
          },
          "nbu": {
            "type": "boolean",
            "nullable": true
          },
          "ktg": {
            "type": "boolean",
            "nullable": true
          },
          "bvg": {
            "type": "boolean",
            "nullable": true
          },
          "bvgDeductionAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "removeBvgDeductionAmount": {
            "type": "boolean"
          },
          "flatSalary": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "removeFlatSalary": {
            "type": "boolean"
          },
          "iban": {
            "type": "string",
            "nullable": true
          },
          "accountHolder": {
            "type": "string",
            "nullable": true
          },
          "removeAccountHolder": {
            "type": "boolean"
          },
          "lessonsToChildren": {
            "type": "boolean",
            "nullable": true
          },
          "challengingStudents": {
            "type": "boolean",
            "nullable": true
          },
          "einbuergerungsprogramm": {
            "type": "boolean",
            "nullable": true
          },
          "testPreparation": {
            "type": "boolean",
            "nullable": true
          },
          "teachingLanguagesToAdd": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TeachingLanguagePost"
            },
            "nullable": true
          },
          "teachingLanguagesToUpdate": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TeachingLanguagePatchWithId"
            },
            "nullable": true
          },
          "teachingLanguageIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "spokenLanguagesToAdd": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SpokenLanguagePost"
            },
            "nullable": true
          },
          "spokenLanguagesToUpdate": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SpokenLanguagePatchWithId"
            },
            "nullable": true
          },
          "spokenLanguageIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "childrenToAdd": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChildInfoPost"
            },
            "nullable": true
          },
          "childrenToUpdate": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChildInfoPatchWithId"
            },
            "nullable": true
          },
          "childIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "weeklyTimeSlotsToAdd": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WeeklyTimeSlotPost"
            },
            "nullable": true
          },
          "weeklyTimeSlotsToUpdate": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WeeklyTimeSlotPatchWithId"
            },
            "nullable": true
          },
          "weeklyTimeSlotIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "absencePeriodsToAdd": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AbsencePeriodPost"
            },
            "nullable": true
          },
          "absencePeriodsToUpdate": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AbsencePeriodPatchWithId"
            },
            "nullable": true
          },
          "absencePeriodIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "completedTrainingsToAdd": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CompletedTrainingPost"
            },
            "nullable": true
          },
          "completedTrainingsToUpdate": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CompletedTrainingPatchWithId"
            },
            "nullable": true
          },
          "completedTrainingIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          }
        }
      },
      "TeacherPaymentGet": {
        "type": "object",
        "properties": {
          "teacherId": {
            "type": "string",
            "format": "uuid"
          },
          "teacher": {
            "$ref": "#/components/schemas/TeacherRef"
          },
          "teacherDisplayName": {
            "type": "string",
            "nullable": true
          },
          "paymentMonth": {
            "type": "string",
            "format": "date-time"
          },
          "totalLessons": {
            "type": "integer",
            "format": "int32"
          },
          "lineItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentLineItemGet"
            }
          },
          "extraItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentExtraItemGet"
            }
          },
          "extraItemsTotal": {
            "type": "number",
            "format": "double"
          },
          "totalPayout": {
            "type": "number",
            "format": "double"
          },
          "basePay": {
            "type": "number",
            "format": "double"
          },
          "basePayFixed": {
            "type": "number",
            "format": "double"
          },
          "bvgAmount": {
            "type": "number",
            "format": "double"
          },
          "bvgMode": {
            "$ref": "#/components/schemas/EnumDtoOfEBvgMode"
          },
          "bvgMonth": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "quellensteuerRate": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "quellensteuerAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "travelAllowancePerLesson": {
            "type": "number",
            "format": "double"
          },
          "paidOutAt": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "isPaidOut": {
            "type": "boolean"
          },
          "releasedAt": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "isReleased": {
            "type": "boolean"
          },
          "payslipDocumentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "vacationSupplementRate": {
            "type": "number",
            "format": "double"
          },
          "vacationSupplementAmount": {
            "type": "number",
            "format": "double"
          },
          "grossPay": {
            "type": "number",
            "format": "double"
          },
          "ahvRate": {
            "type": "number",
            "format": "double"
          },
          "ahvAmount": {
            "type": "number",
            "format": "double"
          },
          "alvRate": {
            "type": "number",
            "format": "double"
          },
          "alvAmount": {
            "type": "number",
            "format": "double"
          },
          "ktgRate": {
            "type": "number",
            "format": "double"
          },
          "ktgAmount": {
            "type": "number",
            "format": "double"
          },
          "nbuRate": {
            "type": "number",
            "format": "double"
          },
          "nbuAmount": {
            "type": "number",
            "format": "double"
          },
          "netPay": {
            "type": "number",
            "format": "double"
          },
          "travelExpenses": {
            "type": "number",
            "format": "double"
          },
          "actualPay": {
            "type": "number",
            "format": "double"
          },
          "childEntitlement": {
            "type": "number",
            "format": "double"
          },
          "childEntitlementFromConfig": {
            "type": "number",
            "format": "double"
          },
          "childEntitlementComment": {
            "type": "string",
            "nullable": true
          },
          "isChildEntitlementOverridden": {
            "type": "boolean"
          },
          "hasAdditionalTravelCostWarning": {
            "type": "boolean"
          },
          "hasPensionWarning": {
            "type": "boolean"
          },
          "hasQuellensteuerWarning": {
            "type": "boolean"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "TeacherPaymentMarkPaidOutPost": {
        "type": "object",
        "properties": {
          "paidOutAt": {
            "type": "string",
            "format": "date",
            "nullable": true
          }
        }
      },
      "TeacherPaymentPatch": {
        "type": "object",
        "properties": {
          "teacherId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "paymentMonth": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "totalLessons": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "lineItemsToAdd": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentLineItemPost"
            },
            "nullable": true
          },
          "lineItemsToUpdate": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentLineItemPatchWithId"
            },
            "nullable": true
          },
          "lineItemIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "extraItemsToAdd": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentExtraItemPost"
            },
            "nullable": true
          },
          "extraItemsToUpdate": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentExtraItemPatchWithId"
            },
            "nullable": true
          },
          "extraItemIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "basePay": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "basePayFixed": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "bvgAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "bvgModeId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "bvgMonth": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "removeBvgMonth": {
            "type": "boolean"
          },
          "quellensteuerRate": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "quellensteuerAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "removeQuellensteuer": {
            "type": "boolean"
          },
          "travelAllowancePerLesson": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "payslipDocumentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "removePayslipDocument": {
            "type": "boolean"
          },
          "vacationSupplementRate": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "vacationSupplementAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "grossPay": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "ahvRate": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "ahvAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "alvRate": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "alvAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "ktgRate": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "ktgAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "nbuRate": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "nbuAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "netPay": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "travelExpenses": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "actualPay": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "childEntitlement": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "childEntitlementComment": {
            "type": "string",
            "nullable": true
          },
          "removeChildEntitlementComment": {
            "type": "boolean"
          },
          "resetChildEntitlementToConfig": {
            "type": "boolean"
          }
        }
      },
      "TeacherPaymentPost": {
        "type": "object",
        "properties": {
          "teacherId": {
            "type": "string",
            "format": "uuid"
          },
          "paymentMonth": {
            "type": "string",
            "format": "date-time"
          },
          "totalLessons": {
            "type": "integer",
            "format": "int32"
          },
          "lineItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentLineItemPost"
            },
            "nullable": true
          },
          "extraItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentExtraItemPost"
            },
            "nullable": true
          },
          "basePay": {
            "type": "number",
            "format": "double"
          },
          "basePayFixed": {
            "type": "number",
            "format": "double"
          },
          "bvgAmount": {
            "type": "number",
            "format": "double"
          },
          "bvgModeId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "bvgMonth": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "quellensteuerRate": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "quellensteuerAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "travelAllowancePerLesson": {
            "type": "number",
            "format": "double"
          },
          "payslipDocumentId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "vacationSupplementRate": {
            "type": "number",
            "format": "double"
          },
          "vacationSupplementAmount": {
            "type": "number",
            "format": "double"
          },
          "grossPay": {
            "type": "number",
            "format": "double"
          },
          "ahvRate": {
            "type": "number",
            "format": "double"
          },
          "ahvAmount": {
            "type": "number",
            "format": "double"
          },
          "alvRate": {
            "type": "number",
            "format": "double"
          },
          "alvAmount": {
            "type": "number",
            "format": "double"
          },
          "ktgRate": {
            "type": "number",
            "format": "double"
          },
          "ktgAmount": {
            "type": "number",
            "format": "double"
          },
          "nbuRate": {
            "type": "number",
            "format": "double"
          },
          "nbuAmount": {
            "type": "number",
            "format": "double"
          },
          "netPay": {
            "type": "number",
            "format": "double"
          },
          "travelExpenses": {
            "type": "number",
            "format": "double"
          },
          "actualPay": {
            "type": "number",
            "format": "double"
          },
          "childEntitlement": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "childEntitlementComment": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "TeacherPost": {
        "type": "object",
        "properties": {
          "genderId": {
            "type": "string",
            "format": "uuid"
          },
          "birthDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "privateEmail": {
            "type": "string",
            "nullable": true
          },
          "mobilePhone": {
            "type": "string"
          },
          "landlinePhone": {
            "type": "string",
            "nullable": true
          },
          "address": {
            "type": "string"
          },
          "postalCode": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "countryCodeId": {
            "type": "string",
            "format": "uuid"
          },
          "joinDate": {
            "type": "string",
            "format": "date-time"
          },
          "nationality": {
            "type": "string",
            "nullable": true
          },
          "civilStatusId": {
            "type": "string",
            "format": "uuid"
          },
          "ahvNumber": {
            "type": "string"
          },
          "quellensteuer": {
            "type": "boolean"
          },
          "quellensteuerFreibetrag": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "ahv": {
            "type": "boolean"
          },
          "alv": {
            "type": "boolean"
          },
          "nbu": {
            "type": "boolean"
          },
          "ktg": {
            "type": "boolean"
          },
          "bvg": {
            "type": "boolean"
          },
          "bvgDeductionAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "flatSalary": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "iban": {
            "type": "string"
          },
          "accountHolder": {
            "type": "string",
            "nullable": true
          },
          "lessonsToChildren": {
            "type": "boolean"
          },
          "challengingStudents": {
            "type": "boolean"
          },
          "einbuergerungsprogramm": {
            "type": "boolean"
          },
          "testPreparation": {
            "type": "boolean"
          },
          "teachingLanguages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TeachingLanguagePost"
            }
          },
          "spokenLanguages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SpokenLanguagePost"
            }
          },
          "children": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChildInfoPost"
            }
          },
          "weeklyTimeSlots": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WeeklyTimeSlotPost"
            }
          },
          "absencePeriods": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AbsencePeriodPost"
            }
          },
          "completedTrainings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CompletedTrainingPost"
            }
          }
        }
      },
      "TeacherProfileGet": {
        "type": "object",
        "properties": {
          "gender": {
            "$ref": "#/components/schemas/EnumDtoOfEGender"
          },
          "birthDate": {
            "type": "string",
            "format": "date-time"
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "privateEmail": {
            "type": "string",
            "nullable": true
          },
          "mobilePhone": {
            "type": "string"
          },
          "landlinePhone": {
            "type": "string",
            "nullable": true
          },
          "address": {
            "type": "string"
          },
          "postalCode": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "countryCode": {
            "$ref": "#/components/schemas/EnumDtoOfECountryCode"
          },
          "lessonsToChildren": {
            "type": "boolean"
          },
          "challengingStudents": {
            "type": "boolean"
          },
          "teachingLanguages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TeachingLanguageGet"
            }
          },
          "spokenLanguages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SpokenLanguageGet"
            }
          },
          "children": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChildInfoGet"
            }
          },
          "weeklyTimeSlots": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WeeklyTimeSlotGet"
            }
          },
          "absencePeriods": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AbsencePeriodGet"
            }
          },
          "displayName": {
            "type": "string",
            "nullable": true
          },
          "hasChildren": {
            "type": "boolean"
          },
          "teachingLanguagesDisplay": {
            "type": "string",
            "nullable": true
          },
          "spokenLanguagesDisplay": {
            "type": "string",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "TeacherRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "displayName": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "mobilePhone": {
            "type": "string"
          },
          "lessonsToChildren": {
            "type": "boolean"
          },
          "inactive": {
            "type": "boolean"
          },
          "teachingLanguages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EnumDtoOfELanguage"
            }
          }
        },
        "nullable": true
      },
      "TeacherRef2": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "displayName": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "mobilePhone": {
            "type": "string"
          },
          "lessonsToChildren": {
            "type": "boolean"
          },
          "inactive": {
            "type": "boolean"
          },
          "teachingLanguages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EnumDtoOfELanguage"
            }
          }
        }
      },
      "TeacherSalaryGet": {
        "type": "object",
        "properties": {
          "level": {
            "$ref": "#/components/schemas/EnumDtoOfETeachingLevel"
          },
          "courseType": {
            "$ref": "#/components/schemas/EnumDtoOfECourseType"
          },
          "isForChildren": {
            "type": "boolean"
          },
          "hourlyRate": {
            "type": "number",
            "format": "double"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "productName": {
            "type": "string",
            "nullable": true
          },
          "isCustomProduct": {
            "type": "boolean"
          },
          "effectiveFrom": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "pendingChangeFrom": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "pendingChangeAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "TeacherSalaryPatch": {
        "type": "object",
        "properties": {
          "levelId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "courseTypeId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "isForChildren": {
            "type": "boolean",
            "nullable": true
          },
          "hourlyRate": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "removeDescription": {
            "type": "boolean"
          },
          "productName": {
            "type": "string",
            "nullable": true
          },
          "removeProductName": {
            "type": "boolean"
          },
          "effectiveFrom": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "confirmRetroactive": {
            "type": "boolean"
          },
          "changeReason": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "TeacherSalaryPost": {
        "type": "object",
        "properties": {
          "levelId": {
            "type": "string",
            "format": "uuid"
          },
          "courseTypeId": {
            "type": "string",
            "format": "uuid"
          },
          "isForChildren": {
            "type": "boolean"
          },
          "hourlyRate": {
            "type": "number",
            "format": "double"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "productName": {
            "type": "string",
            "nullable": true
          },
          "effectiveFrom": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "confirmRetroactive": {
            "type": "boolean"
          },
          "changeReason": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "TeacherStudentGet": {
        "type": "object",
        "properties": {
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "age": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "isChild": {
            "type": "boolean",
            "nullable": true
          },
          "nativeLanguage": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage2"
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "phone": {
            "type": "string",
            "nullable": true
          },
          "subjects": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SubjectGet"
            }
          },
          "customerId": {
            "type": "string",
            "format": "uuid"
          },
          "customerDisplayName": {
            "type": "string"
          },
          "customerType": {
            "$ref": "#/components/schemas/EnumDtoOfECustomerType"
          },
          "customerEmail": {
            "type": "string",
            "nullable": true
          },
          "customerPhone": {
            "type": "string",
            "nullable": true
          },
          "customerStreet": {
            "type": "string",
            "nullable": true
          },
          "customerPostalCode": {
            "type": "string",
            "nullable": true
          },
          "customerCity": {
            "type": "string",
            "nullable": true
          },
          "customerCountry": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "$ref": "#/components/schemas/EnumDtoOfEStudentStatus"
          },
          "inactive": {
            "type": "boolean"
          },
          "courses": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CourseRef2"
            },
            "nullable": true
          },
          "lessons": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LessonRef"
            },
            "nullable": true
          },
          "materials": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MaterialRef"
            },
            "nullable": true
          },
          "studentNotes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StudentNoteGet"
            },
            "nullable": true
          },
          "examRecords": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ExamRecordGet"
            },
            "nullable": true
          },
          "displayName": {
            "type": "string",
            "nullable": true
          },
          "subjectsDisplay": {
            "type": "string",
            "nullable": true
          },
          "contactDisplay": {
            "type": "string",
            "nullable": true
          },
          "customerAddressDisplay": {
            "type": "string",
            "nullable": true
          },
          "courseCount": {
            "type": "integer",
            "format": "int32"
          },
          "lessonCount": {
            "type": "integer",
            "format": "int32"
          },
          "materialCount": {
            "type": "integer",
            "format": "int32"
          },
          "noteCount": {
            "type": "integer",
            "format": "int32"
          },
          "examRecordCount": {
            "type": "integer",
            "format": "int32"
          },
          "currentLevelsDisplay": {
            "type": "string",
            "nullable": true
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "TeacherStudentPatch": {
        "type": "object",
        "properties": {
          "studentNotesToAdd": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StudentNotePost"
            },
            "nullable": true
          },
          "studentNoteIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "examRecordsToAdd": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ExamRecordPost"
            },
            "nullable": true
          },
          "examRecordsToUpdate": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ExamRecordPatchWithId"
            },
            "nullable": true
          },
          "examRecordIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          }
        }
      },
      "TeacherStudentPost": {
        "type": "object"
      },
      "TeachingLanguageGet": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "language": {
            "$ref": "#/components/schemas/EnumDtoOfELanguage"
          },
          "level": {
            "$ref": "#/components/schemas/EnumDtoOfETeachingLevel"
          },
          "testPreparations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EnumDtoOfETest"
            }
          },
          "testExaminers": {
            "type": "array",
            "items": { }
          }
        }
      },
      "TeachingLanguagePatch": {
        "type": "object",
        "properties": {
          "languageId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "levelId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "testPreparationIdsToAdd": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "testPreparationIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "testExaminerIdsToAdd": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          },
          "testExaminerIdsToRemove": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "nullable": true
          }
        }
      },
      "TeachingLanguagePatchWithId": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "patch": {
            "$ref": "#/components/schemas/TeachingLanguagePatch"
          }
        }
      },
      "TeachingLanguagePost": {
        "type": "object",
        "properties": {
          "languageId": {
            "type": "string",
            "format": "uuid"
          },
          "levelId": {
            "type": "string",
            "format": "uuid"
          },
          "testPreparationIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "testExaminerIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          }
        }
      },
      "TimeSlotDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "day": {
            "$ref": "#/components/schemas/DayOfWeek"
          },
          "startTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          },
          "endTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          }
        }
      },
      "TimeSlotPatch": {
        "type": "object",
        "properties": {
          "day": {
            "$ref": "#/components/schemas/NullableOfDayOfWeek"
          },
          "startTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string",
            "nullable": true
          },
          "endTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string",
            "nullable": true
          }
        }
      },
      "TimeSlotPatchWithId": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "patch": {
            "$ref": "#/components/schemas/TimeSlotPatch"
          }
        }
      },
      "TrainingCourseGet": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "points": {
            "type": "integer",
            "format": "int32"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "modifiedAt": {
            "type": "string",
            "format": "date-time"
          },
          "removed": {
            "type": "boolean"
          }
        }
      },
      "TrainingCoursePatch": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true
          },
          "points": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        }
      },
      "TrainingCoursePost": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "points": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "TrainingCourseRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "points": {
            "type": "integer",
            "format": "int32"
          }
        },
        "nullable": true
      },
      "UpcomingExamItem": {
        "type": "object",
        "properties": {
          "studentId": {
            "type": "string",
            "format": "uuid"
          },
          "studentName": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "examDate": {
            "type": "string",
            "format": "date-time"
          },
          "daysUntil": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "UploadedDocument": {
        "required": [
          "id",
          "fileName",
          "sizeBytes"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "fileName": {
            "type": "string"
          },
          "sizeBytes": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "UploadedHomeworkDocument": {
        "required": [
          "id",
          "fileName",
          "contentType",
          "sizeBytes"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "fileName": {
            "type": "string"
          },
          "contentType": {
            "type": "string"
          },
          "sizeBytes": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "UploadedLogo": {
        "required": [
          "id",
          "fileName",
          "contentType",
          "sizeBytes"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "fileName": {
            "type": "string"
          },
          "contentType": {
            "type": "string"
          },
          "sizeBytes": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "UploadedMailAttachment": {
        "required": [
          "id",
          "fileName",
          "contentType",
          "sizeBytes"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "fileName": {
            "type": "string"
          },
          "contentType": {
            "type": "string"
          },
          "sizeBytes": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "UploadedPaymentDocument": {
        "required": [
          "id",
          "fileName",
          "sizeBytes"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "fileName": {
            "type": "string"
          },
          "sizeBytes": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "WeeklyScheduleSlotDto": {
        "type": "object",
        "properties": {
          "day": {
            "$ref": "#/components/schemas/DayOfWeek"
          },
          "startTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          },
          "endTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          }
        }
      },
      "WeeklyTimeSlotGet": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "dayOfWeek": {
            "$ref": "#/components/schemas/DayOfWeek"
          },
          "startTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          },
          "endTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          }
        }
      },
      "WeeklyTimeSlotPatch": {
        "type": "object",
        "properties": {
          "dayOfWeek": {
            "$ref": "#/components/schemas/NullableOfDayOfWeek"
          },
          "startTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string",
            "nullable": true
          },
          "endTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string",
            "nullable": true
          }
        }
      },
      "WeeklyTimeSlotPatchWithId": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "patch": {
            "$ref": "#/components/schemas/WeeklyTimeSlotPatch"
          }
        }
      },
      "WeeklyTimeSlotPost": {
        "type": "object",
        "properties": {
          "dayOfWeek": {
            "$ref": "#/components/schemas/DayOfWeek"
          },
          "startTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          },
          "endTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string"
          }
        }
      }
    },
    "securitySchemes": {
      "keycloak": {
        "type": "oauth2",
        "description": "Log in via Keycloak (Authorization Code + PKCE). Uses the public 'swagger-ui' client.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://auth.triskela.com/realms/softlanding/protocol/openid-connect/auth",
            "tokenUrl": "https://auth.triskela.com/realms/softlanding/protocol/openid-connect/token",
            "scopes": {
              "openid": "OpenID Connect",
              "profile": "User profile",
              "email": "User email"
            }
          }
        }
      }
    }
  },
  "security": [
    {
      "keycloak": [
        "openid",
        "profile",
        "email"
      ]
    }
  ],
  "tags": [
    {
      "name": "Teacher (Office)"
    },
    {
      "name": "Location (Office)"
    },
    {
      "name": "Room (Office)"
    },
    {
      "name": "Material (Office)"
    },
    {
      "name": "TrainingCourse (Office)"
    },
    {
      "name": "Lesson (Office)"
    },
    {
      "name": "AttendanceDocument (Office)"
    },
    {
      "name": "PaymentDocument (Office)"
    },
    {
      "name": "HomeworkDocument (Office)"
    },
    {
      "name": "MailAttachment (Office)"
    },
    {
      "name": "PaymentImport (Office)"
    },
    {
      "name": "BankPayment (Office)"
    },
    {
      "name": "PayrollAccount (Office)"
    },
    {
      "name": "SalaryExport (Office)"
    },
    {
      "name": "Office Expense Receipts"
    },
    {
      "name": "Office Expense Claims"
    },
    {
      "name": "Dashboard (Office)"
    },
    {
      "name": "Course (Office)"
    },
    {
      "name": "CoursePrice (Office)"
    },
    {
      "name": "PriceKeys (Office)"
    },
    {
      "name": "Order (Office)"
    },
    {
      "name": "Package (Office)"
    },
    {
      "name": "PackageOrder (Office)"
    },
    {
      "name": "PackagePurchase (Office)"
    },
    {
      "name": "Student (Office)"
    },
    {
      "name": "Interest (Office)"
    },
    {
      "name": "MatchingInterest (Office)"
    },
    {
      "name": "EnrollmentRequest (Office)"
    },
    {
      "name": "Customer (Office)"
    },
    {
      "name": "Invoice (Office)"
    },
    {
      "name": "Invoice Notice (Office)"
    },
    {
      "name": "Office QR-Bill"
    },
    {
      "name": "Office Billing Settings"
    },
    {
      "name": "Office Monthly Close"
    },
    {
      "name": "Office Invoices"
    },
    {
      "name": "Budget (Office)"
    },
    {
      "name": "StudentInvite (Office)"
    },
    {
      "name": "MaterialChangeRequest (Office)"
    },
    {
      "name": "TeacherInvite (Office)"
    },
    {
      "name": "TeacherSalary (Office)"
    },
    {
      "name": "TeacherPayment (Office)"
    },
    {
      "name": "TeacherStudent (Office)"
    },
    {
      "name": "Homework (Office)"
    },
    {
      "name": "Notification (Office)"
    },
    {
      "name": "Notification triggers (Office)"
    },
    {
      "name": "Mail templates (Office)"
    },
    {
      "name": "Mail history (Office)"
    },
    {
      "name": "TeacherProfile (Office)"
    }
  ]
}