diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..7dc2354 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +**/node_modules +**/dist +**/bin +**/obj +omsorgCore/**/App_Data +.git +.gitea +*.log diff --git a/.gitea/workflows/docker-build.yml b/.gitea/workflows/docker-build.yml new file mode 100644 index 0000000..3e827e8 --- /dev/null +++ b/.gitea/workflows/docker-build.yml @@ -0,0 +1,55 @@ +name: Docker-Images bauen und veröffentlichen + +on: + push: + branches: [main] + tags: ["v*"] + +env: + REGISTRY: git.omsorg-pflegedienste.de + IMAGE_NAMESPACE: admin/omsorg + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + include: + - image: omsorgcore + dockerfile: omsorgCore/Dockerfile + build_args: "" + - image: omsorgapp + dockerfile: omsorgapp/Dockerfile + build_args: "VITE_OMSORG_CORE_URL=${{ vars.OMSORG_CORE_PUBLIC_URL }}" + - image: omsorgweb + dockerfile: omsorgWeb/Dockerfile + build_args: "" + steps: + - name: Code auschecken + uses: actions/checkout@v4 + + - name: Bei Registry anmelden + run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login ${{ env.REGISTRY }} -u "${{ gitea.actor }}" --password-stdin + + - name: Image bauen und pushen + run: | + BUILD_ARGS="" + if [ -n "${{ matrix.build_args }}" ]; then + BUILD_ARGS="--build-arg ${{ matrix.build_args }}" + fi + + IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/${{ matrix.image }}" + TAGS="-t $IMAGE:latest -t $IMAGE:${{ gitea.sha }}" + + # Bei einem Tag-Push (z.B. v0.1.0) zusätzlich mit dem Tag-Namen selbst versionieren, + # damit ein bestimmter Release pinnbar bleibt statt nur :latest/:. + if [ "${{ gitea.ref_type }}" = "tag" ]; then + TAGS="$TAGS -t $IMAGE:${{ gitea.ref_name }}" + fi + + docker buildx build \ + --push \ + -f "${{ matrix.dockerfile }}" \ + $TAGS \ + $BUILD_ARGS \ + . diff --git a/.gitignore b/.gitignore index f64c69e..89ac81e 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,13 @@ Thumbs.db # .NET (omsorgCore) omsorgCore/**/bin/ omsorgCore/**/obj/ +omsorgCore/**/App_Data/ +# Lokal hochgeladene Dokumente (Storage:DocumentsRootPath, appsettings.Development.json) - Laufzeitdaten, kein Repo-Inhalt +omsorgCore/src/OmsorgCore.Api/data/ + +# Zufällig im Repo-Root abgelegte lokale Dateien, kein Projektinhalt +/Arbeitsstunden.ods +/.~lock.*# + +# VS-Code-Extension-Report (Codezeilen-Statistik), kein Projektinhalt +/.VSCodeCounter/ diff --git a/CLAUDE.md b/CLAUDE.md index fc49895..488bcca 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,7 +9,7 @@ Ein Monorepo für die OMSORG-Plattform, einer Software für ein Pflege-Zeitarbei | Verzeichnis | Rolle | Stack | Ist-Stand | |---|---|---|---| | `omsorgWeb/` | Öffentliche Website + **OMSORG Connect** (Mitarbeiter-App für Außendienst) | PHP, MySQL, vanilla JS, PWA | ✅ produktiv im Einsatz | -| `omsorgapp/` | **OMSORG Desktop** — Software für Büromitarbeiter (Sabina, Malik, Sabrina, Sascha) | Electron + React + Vite | 🔶 frühes Grundgerüst (Release 0.1.1), lokale JSON-Datenhaltung | +| `omsorgapp/` | **OMSORG Desktop** — Software für Büromitarbeiter (Sabina, Malik, Sabrina, Sascha), als Browser-Tab genutzt (kein Electron) | React + Vite | 🔶 frühes Grundgerüst (Release 0.1.1), Daten kommen bereits über `omsorgCore` (kein lokaler Datenspeicher mehr) | | `omsorgCore/` | **OMSORG Backend** — gemeinsame Datenbasis + Automatisierung (Core + Engine als eine Komponente) | C# / .NET 8, ASP.NET Core (Controller), PostgreSQL/EF Core, JWT-Auth | 🔶 Grundgerüst steht (Domain/Application/Infrastructure/Engine/Api, Rechtesystem, Auth, Employee-Endpoint end-to-end verifiziert); noch keine Anbindung von `omsorgWeb`/`omsorgapp`, DB-Migration noch nicht gegen echte Postgres getestet | Leitdokumente: diff --git a/REQUIREMENTS.md b/REQUIREMENTS.md index eabf680..5acd463 100644 --- a/REQUIREMENTS.md +++ b/REQUIREMENTS.md @@ -41,7 +41,7 @@ Umfasst alle drei Plattform-Ebenen: OMSORG Desktop, OMSORG Connect, OMSORG Backe |---|---|---| | **OMSORG Desktop** | Vollständige Unternehmensmodule für Büromitarbeiter | 🔶 `omsorgapp/` — Electron/React, Release 0.1.1. Vorhanden: `HomePage`, `HomeStats`, `ContractWidget`, `EmployeesPage` mit Tabs/Detailpanel. Lokale JSON-DB (`~/Documents/Omsorg Business Controls Pro/database/omsorg-local-db.json`), noch keine SQLite/Server-Anbindung. | | **OMSORG Connect** | Mobile/Web-App für Außendienst | ✅ `omsorgWeb/mitarbeiter-app/` — PHP/MySQL, produktiv als PWA (Manifest + Service Worker). Deckt bereits Zeiterfassung/Stundennachweis, Urlaub, Abwesenheit, Fortbildung, Dokumente, News, Benefits, Werben, Einsatzanweisung, Dienstplan, Bewertungen ab. | -| **OMSORG Backend (Core + Engine)** | Eine Backend-Komponente, zwei interne Schichten: **Datenschicht (Core)** — gemeinsame Datenbasis, 6 Objekte; **Event-Schicht (Engine)** — Ereignis→Aktion-Automatisierung ohne eigene UI, arbeitet auf denselben Objekten der Datenschicht | 🔶 `omsorgCore/` — Grundgerüst steht (C#/.NET 8, ASP.NET Core Controller, EF Core/PostgreSQL, JWT-Auth, Rollen+Permission-Override-Rechtesystem, In-Process-Event-Dispatcher). `Employee`, `Facility` (+ `FacilityContact`), `Contract` und `Order` haben inzwischen volles Repository/Service/Controller; `TimeEntry`/`Invoice` existieren weiterhin nur als Domain-Entitäten, aber ohne Endpunkte. `omsorgapp` ist für Mitarbeiter jetzt **echt angebunden** (Login/Refresh sowie Employees-CRUD laufen über `omsorgCore`, keine lokale JSON-Datenhaltung mehr für dieses Modul), ebenso Kunden/Einrichtungen (`FacilitiesPage`). **Noch keine Anbindung** von `omsorgWeb` (MySQL) an dieses Backend, und die übrigen `omsorgapp`-Fachmodule (Disposition, ...) nutzen weiterhin die lokale JSON-DB — die Insellösungen bestehen dort technisch weiter, bis diese Migration erfolgt. DB-Migration wurde gegen eine echte PostgreSQL-Instanz verifiziert (siehe `omsorgCore/CLAUDE.md`, "Verifiziert"); die neueste Migration (`Order`/Statuspipeline) noch nicht. Details: `omsorgCore/CLAUDE.md`. | +| **OMSORG Backend (Core + Engine)** | Eine Backend-Komponente, zwei interne Schichten: **Datenschicht (Core)** — gemeinsame Datenbasis, 6 Objekte; **Event-Schicht (Engine)** — Ereignis→Aktion-Automatisierung ohne eigene UI, arbeitet auf denselben Objekten der Datenschicht | 🔶 `omsorgCore/` — Grundgerüst steht (C#/.NET 8, ASP.NET Core Controller, EF Core/PostgreSQL, JWT-Auth, Rollen+Permission-Override-Rechtesystem, In-Process-Event-Dispatcher). `Employee`, `Facility` (+ `FacilityContact`), `Contract`, `Order`, `Absence` und `TimeEntry` haben inzwischen volles Repository/Service/Controller; `Invoice` existiert weiterhin nur als Domain-Entität, aber ohne Endpunkte. `omsorgapp` ist für Mitarbeiter jetzt **echt angebunden** (Login/Refresh sowie Employees-CRUD laufen über `omsorgCore`, keine lokale JSON-Datenhaltung mehr für dieses Modul), ebenso Kunden/Einrichtungen (`FacilitiesPage`) und Aufträge/Disposition (`OrdersPage`). **Noch keine Anbindung** von `omsorgWeb` (MySQL) an dieses Backend, und die übrigen `omsorgapp`-Fachmodule (Kalkulation, Fahrzeuge, Rechnungen, Controlling) sind weiterhin reine `PlaceholderPage`-Stubs ohne Datenhaltung. DB-Migration wurde gegen eine echte PostgreSQL-Instanz verifiziert (siehe `omsorgCore/CLAUDE.md`, "Verifiziert"); die neueste Migration (`Order`/Statuspipeline) noch nicht. Details: `omsorgCore/CLAUDE.md`. | **Kernrisiko für die Roadmap:** Solange das Backend (Core + Engine) nicht existiert, sind Connect (MySQL) und Desktop (JSON) zwei Insellösungen — genau das, was Blueprint Kap. 19.9 ausschließt. Phase 1 der Roadmap muss dies zuerst auflösen (siehe Abschnitt 10). @@ -75,29 +75,29 @@ Umfasst alle drei Plattform-Ebenen: OMSORG Desktop, OMSORG Connect, OMSORG Backe | ID | Anforderung | Akteur | Akzeptanzkriterium | Status | |---|---|---|---|---| | FR-MA-1 | Stammdaten erfassen (Name, Geburtsdatum, Adresse, Kontakt, Notfallkontakt, Beschäftigungsart, Qualifikation, Ein-/Austritt, Status) [Blueprint 19.1] | Sabina, Malik, Sabrina | Datensatz anlegen/bearbeiten mit Pflichtfeldern; Validierung verhindert unvollständige Sätze | ✅ (alle Felder in `Employee`-Entity + `EmployeeForm` in `omsorgapp` vorhanden; Validierung serverseitig in `EmployeesController` (Pflichtfelder, Längen, Ein-/Austrittslogik, Beschäftigungsart-Allowlist) und clientseitig verdrahtet; `omsorgapp` spricht für Mitarbeiter jetzt über `employeesClient.cjs` echt gegen `omsorgCore`, keine JSON-lokale Persistenz mehr für dieses Modul — Abgleich mit Connect/`omsorgWeb` weiterhin offen, siehe FR-CORE-1) | -| FR-MA-2 | Arbeitsvertragsdaten (Beginn/Ende, Arbeitszeit, Stundenlohn, Zuschläge, Überstunden, Urlaubsanspruch, Probezeit) [Blueprint 19.1] | Sabina, Malik, Sabrina | Vertragsfelder je Mitarbeiter editierbar, Historie bei Änderung nachvollziehbar | 🔶 (`Contract`-Entity in `omsorgCore` um alle geforderten Felder erweitert, volles Repository/Service/Controller (`ContractsController`, `GET/POST/PUT /api/contracts`) nach dem Facility-Muster, gegated über `[RequirePermission(ModuleType.Contracts, ...)]`; Änderungshistorie automatisch über den generischen `AuditSaveChangesInterceptor` abgedeckt — noch kein `omsorgapp`-UI-Modul dafür) | -| FR-MA-3 | Dokumente, Qualifikationen, Fortbildungen, Führerschein, Gesundheitsnachweise, Notizen, Historie je Mitarbeiter | Sabina, Malik, Sabrina | Upload/Anzeige je Kategorie, Zugriffsprotokoll | 🔶 (Dokumentenarchiv existiert bereits in Connect: `pages/dokumentenarchiv.php`, `actions/upload-dokument.php`; im Desktop-Modul fehlt es) | +| FR-MA-2 | Arbeitsvertragsdaten (Beginn/Ende, Arbeitszeit, Stundenlohn, Zuschläge, Überstunden, Urlaubsanspruch, Probezeit) [Blueprint 19.1] | Sabina, Malik, Sabrina | Vertragsfelder je Mitarbeiter editierbar, Historie bei Änderung nachvollziehbar | ✅ (`Contract`-Entity in `omsorgCore` um alle geforderten Felder erweitert, volles Repository/Service/Controller (`ContractsController`, `GET/POST/PUT/DELETE /api/contracts`) nach dem Facility-Muster, gegated über `[RequirePermission(ModuleType.Contracts, ...)]`; Änderungshistorie automatisch über den generischen `AuditSaveChangesInterceptor` abgedeckt; `omsorgapp`-UI jetzt vorhanden — "Verträge"-Tab in `EmployeeDetailPanel` (`ContractsList`/`ContractForm`/`Create-`/`EditContractDialog.jsx`), Anlegen/Bearbeiten/Löschen funktionsfähig gegen `omsorgCore`, neue Verträge starten als "Entwurf", Statuswechsel nur im Bearbeiten-Formular) | +| FR-MA-3 | Dokumente, Qualifikationen, Fortbildungen, Führerschein, Gesundheitsnachweise, Notizen, Historie je Mitarbeiter | Sabina, Malik, Sabrina | Upload/Anzeige je Kategorie, Zugriffsprotokoll | ✅ (Backend: `omsorgCore` `DocumentsController`/`Document`-Entität, Dateien auf Disk + Metadaten in Postgres, Zugriffsprotokoll über `AuditEvent "DocumentDownloaded"`; Frontend: "Dokumente"-Tab in der Personalakte (`omsorgapp`, `DocumentsList`/`UploadDocumentDialog`/`EditDocumentDialog`/`DocumentViewerDialog`) mit Upload, Bearbeiten, In-App-Vorschau (PDF/Bild) und Download je Kategorie, rechtegegated — siehe `omsorgCore/CLAUDE.md`/`omsorgapp/CLAUDE.md` "Dokumentenarchiv"/"Dokumente". Offen: `omsorgWeb`-Insellösung (`pages/dokumentenarchiv.php`) ist noch nicht abgelöst/migriert, siehe FR-CORE-1) | | FR-MA-4 | Eintrittsdatum löst Dashboard-Hinweis auf bevorstehenden Mitarbeiterstart aus [Blueprint 7] | alle Büro-Rollen | X Tage vor Eintritt erscheint Engine-Hinweis im Dashboard | ⬜ (abhängig von Engine, siehe 4.10) | -| FR-MA-5 | Bei Büromitarbeitern kann der Mitarbeiterdatensatz mit einem Benutzerkonto + individuellen Rechten verknüpft werden [Blueprint 19.1] | Sabina, Malik | Rechteliste pro Modul (sehen/anlegen/bearbeiten/löschen/exportieren/freigeben) editierbar | 🔶 (`users`-Tabelle mit Rolle in `omsorgWeb/mitarbeiter-app` vorhanden, aber nur grobe Rolle, keine granularen Einzelrechte) | +| FR-MA-5 | Bei Büromitarbeitern kann der Mitarbeiterdatensatz mit einem Benutzerkonto + individuellen Rechten verknüpft werden [Blueprint 19.1] | Sabina, Malik | Rechteliste pro Modul (sehen/anlegen/bearbeiten/löschen/exportieren/freigeben) editierbar | ✅ (in `omsorgCore`, nicht mehr `omsorgWeb`: `User.EmployeeId` verknüpft ein Benutzerkonto mit dem `Employee`-Datensatz, `UsersController.Create`/`UserService.CreateForEmployeeAsync` erzwingt genau 1 Konto je Mitarbeiter; Rechte sind granular pro Modul × Aktion — Rolle als Vorlage (`RolePermission`) plus individuelle Grant/Revoke-Ausnahmen je Nutzer (`UserPermissionOverride`), inkl. `Recover` seit dem Soft-Delete-Schritt; Admin-UI in `omsorgapp`: `RolesPanel`/`RolePermissionMatrix`/`UserOverridesPanel` unter "Einstellungen") | | FR-MA-6 | Außendienstmitarbeiter sehen ausschließlich eigene Daten [Blueprint 4.2] | Außendienst | Query/Route liefert nur Datensätze mit `user_id = aktueller Nutzer` | ✅ (Session-basierter Zugriff in `omsorgWeb/mitarbeiter-app`, z. B. `pages/urlaubsantrag.php`) | ### 4.3 Einrichtungen & CRM | ID | Anforderung | Akteur | Akzeptanzkriterium | Status | |---|---|---|---|---| -| FR-EIN-1 | Stammdaten (Name, Art, Adresse, Rechnungsadresse, Telefon, E-Mail, Website, Status) [Blueprint 19.2] | Sabina, Malik, Sabrina, Sascha (Leads) | Datensatz anlegen/bearbeiten | 🔶 (`Facility`-Entity + volles Repository/Service/Controller (`FacilitiesController`, `GET/POST/PUT /api/facilities`) in `omsorgCore` vorhanden, serverseitige Rechteprüfung über `[RequirePermission(ModuleType.Facilities, ...)]`; Felder Name/Art(`FacilityType`)/Adresse/Rechnungsadresse/**Website**/Status(`CrmStatus`) abgedeckt — Adresse und Rechnungsadresse sind je strukturierte Felder (Straße/PLZ/Ort/Land, analog Mitarbeiter-Adresse), kein Freitext; UI-Modul in `omsorgapp` vorhanden (`FacilitiesPage`, Sidebar-Tab "Kunden", analog `EmployeesPage`) — Anlegen/Bearbeiten funktionsfähig gegen `omsorgCore`; **Telefon/E-Mail sind entgegen dieser Zeile bisher nicht als eigene `Facility`-Felder modelliert** — nur `FacilityContact` (FR-EIN-2) trägt Telefon/E-Mail je Ansprechpartner, es gibt kein allgemeines Einrichtungs-Telefon/-E-Mail; das war schon vor dieser Änderung so dokumentiert, aber sachlich falsch — noch zu klären/nachzuziehen) | -| FR-EIN-2 | Mehrere Ansprechpartner je Einrichtung mit Funktion, Abteilung, Kontaktwegen, Notizen | Sabina, Malik, Sabrina, Sascha | Liste von Ansprechpartnern editierbar, mind. 1:n-Beziehung | 🔶 (neue Entität `FacilityContact` (1:n zu `Facility`) in `omsorgCore`, `FacilityContactsController` unter `GET/POST /api/facilities/{facilityId}/contacts`, `PUT .../contacts/{id}`, gegated über dieselben `ModuleType.Facilities`-Rechte wie die Einrichtung selbst; Felder Name/Funktion(`Role`)/Abteilung(`Department`)/Telefon/E-Mail/Notizen abgedeckt; UI-Liste im `FacilityDetailPanel` (`omsorgapp`) anlegen/bearbeiten funktionsfähig; **kein Löschen** — konsistent mit dem noch fehlenden Soft-Delete für die übrigen Core-Objekte, siehe `omsorgCore/CLAUDE.md` "Offene Punkte") | -| FR-EIN-3 | CRM-Status-Pipeline: Lead → kontaktiert → kein Bedarf → Wiedervorlage → Interesse → Angebot → Kunde → Bestandskunde [Blueprint, omsorg.md] | Sascha, Sabrina | Statuswechsel wird protokolliert; bei „kein Bedarf" wird automatisch Wiedervorlage in 14 Tagen erzeugt | ⬜ | -| FR-EIN-4 | Konditionen (Verrechnungssatz, Zuschläge, Fahrtkosten, Mindeststunden, Zahlungsziel etc.) je Einrichtung [Blueprint 19.2] | Sabina, Malik, Sabrina | Konditionssatz ist Grundlage für Rechnungserstellung (siehe FR-RE-1) | ⬜ | +| FR-EIN-1 | Stammdaten (Name, Art, Adresse, Rechnungsadresse, Telefon, E-Mail, Website, Status) [Blueprint 19.2] | Sabina, Malik, Sabrina, Sascha (Leads) | Datensatz anlegen/bearbeiten | ✅ (`Facility`-Entity + volles Repository/Service/Controller (`FacilitiesController`, `GET/POST/PUT /api/facilities`) in `omsorgCore`, serverseitige Rechteprüfung über `[RequirePermission(ModuleType.Facilities, ...)]`; Felder Name/Art(`FacilityType`)/Adresse/Rechnungsadresse/Website/Status(`CrmStatus`) abgedeckt — Adresse und Rechnungsadresse sind je strukturierte Felder (Straße/PLZ/Ort/Land, analog Mitarbeiter-Adresse), kein Freitext; UI-Modul in `omsorgapp` vorhanden (`FacilitiesPage`, Sidebar-Tab "Kunden", analog `EmployeesPage`) — Anlegen/Bearbeiten funktionsfähig gegen `omsorgCore`. **Bewusste Abweichung von der Zeile:** kein allgemeines Telefon/E-Mail direkt auf `Facility` — Kontaktwege leben ausschließlich pro Ansprechpartner auf `FacilityContact` (FR-EIN-2), da eine Einrichtung i. d. R. mehrere Ansprechpartner mit je eigenen Kontaktdaten hat und ein zusätzliches, nicht klar zuordenbares "allgemeines" Einrichtungstelefon eine zweite, redundante Kontakt-Datenquelle wäre (widerspricht dem Grundsatz "jede Information wird nur einmal gespeichert", siehe Root-`CLAUDE.md`). Diese Entscheidung ist absichtlich, kein offener Punkt.) | +| FR-EIN-2 | Mehrere Ansprechpartner je Einrichtung mit Funktion, Abteilung, Kontaktwegen, Notizen | Sabina, Malik, Sabrina, Sascha | Liste von Ansprechpartnern editierbar, mind. 1:n-Beziehung | ✅ (neue Entität `FacilityContact` (1:n zu `Facility`) in `omsorgCore`, `FacilityContactsController` unter `GET/POST/PUT/DELETE /api/facilities/{facilityId}/contacts[/...]`, gegated über dieselben `ModuleType.Facilities`-Rechte wie die Einrichtung selbst; Felder Name/Funktion(`Role`)/Abteilung(`Department`)/Telefon/E-Mail/Notizen abgedeckt; UI-Liste im `FacilityDetailPanel` (`omsorgapp`) anlegen/bearbeiten/löschen funktionsfähig; Löschen ist Soft-Delete (`IsDeleted`/`DeletedAt`) und über die neue "Papierkorb"-Seite (`ModuleType.Facilities`+`PermissionAction.Recover`) wiederherstellbar) | +| FR-EIN-3 | CRM-Status-Pipeline: Lead → kontaktiert → kein Bedarf → Wiedervorlage → Interesse → Angebot → Kunde → Bestandskunde [Blueprint, omsorg.md] | Sascha, Sabrina | Statuswechsel wird protokolliert; bei „kein Bedarf" wird automatisch Wiedervorlage in 14 Tagen erzeugt | ✅ (Statuswechsel-Protokollierung läuft automatisch über den bestehenden `AuditSaveChangesInterceptor`, kein Zusatzcode nötig; neues Feld `Facility.FollowUpDueDate` (nullable), gesetzt über `FacilitiesController.Update`, wenn der neu gewählte `CrmStatus`-Wert das Flag `TriggersFollowUp` trägt (aktuell nur "Kein Bedarf" — generisch statt hartcodiert, damit künftig weitere Status dieselbe Mechanik nutzen können) — serverseitig muss dann ein `FollowUpDays` aus der admin-editierbaren `ValueList` `"FollowUpPeriods"` (7/14/21/31 Tage, Default 14) mitgegeben werden; `omsorgapp` zeigt dafür beim Statuswechsel auf einen solchen Status einen Auswahldialog (`FollowUpDaysDialog.jsx` in `EditFacilityDialog.jsx`, 14 Tage vorausgewählt) und das Fälligkeitsdatum im `FacilityDetailPanel`; Dashboard-Widget "Fällige Wiedervorlagen" (`FollowUpWidget.jsx`, in `HomePage.jsx` eingebunden) zeigt fällige Einrichtungen an; die Statuspipeline erzwingt wie bei `OrderStatus` erlaubte Übergänge (`ValueListItemTransition` für `"CrmStatus"`, geseedet mit der Vorwärtskette der Pipeline plus jederzeitigem Rückfall auf "Kein Bedarf"/"Wiedervorlage", admin-editierbar über dieselbe "Erlaubte Übergänge"-Matrix wie beim Auftragsstatus, `FacilitiesController.Update` prüft serverseitig via `CanTransitionAsync`, das CRM-Status-Dropdown in `FacilityForm.jsx` (nur im `EditFacilityDialog`) zeigt dem Nutzer dabei ebenfalls nur den aktuellen Status plus die laut `GET /api/value-lists/CrmStatus/transitions` erlaubten Zielstatus an — vorher listete das Dropdown ungefiltert alle CRM-Status auf, ein unzulässiger Wechsel wurde erst nach "Speichern" per 400-Fehler vom Server abgelehnt). Die wählbare Frist statt einer starren 14-Tage-Automatik ist eine bewusste, finale Designentscheidung — kein offener Punkt.) | +| FR-EIN-4 | Konditionen (Verrechnungssatz, Zuschläge, Fahrtkosten, Mindeststunden, Zahlungsziel etc.) je Einrichtung [Blueprint 19.2] | Sabina, Malik, Sabrina | Konditionssatz ist Grundlage für Rechnungserstellung (siehe FR-RE-1) | ✅ (Backend: elf der zwölf Blueprint-19.2-Felder als flache, nullable Spalten auf `Facility` — Verrechnungssatz, vier Zuschläge (als Prozent), Fahrtkosten (Pauschale je Einsatz), Mindeststunden, Pausenregelung, Abrechnungsintervall (validiert gegen neue `ValueList` `"BillingInterval"`), Zahlungsziel, individuelle Vereinbarungen —, nur über `PUT /api/facilities/{id}` pflegbar; qualifikationsabhängige Preise als neue 1:n-Unterressource `FacilityQualificationRate` (`GET/POST/PUT/DELETE /api/facilities/{facilityId}/qualification-rates[/...]`, Qualifikation gegen die bestehende `ValueList` `"Qualification"` validiert) nach dem `FacilityContact`-Muster, inkl. Soft-Delete/Papierkorb; Migration `AddFacilityConditionsAndQualificationRates` erfolgreich gegen die echte Postgres-Instanz angewendet (automatisch beim API-Start); `omsorgapp`-UI (Konditionen-Fieldset in `FacilityForm.jsx`, `FacilityQualificationRatesList` im `FacilityDetailPanel`) vorhanden, `omsorgapp/api-client-ts` regeneriert und gebaut (Methodennamen/Feldnamen gegen den generierten Client verifiziert). Akzeptanzkriterium "Grundlage für Rechnungserstellung" im engeren Sinn bewusst weiterhin nicht vollständig erfüllt, da FR-RE-1/`Invoice` diese Daten noch nicht konsumiert — dieser Schritt legt nur die Datenbasis.) | | FR-EIN-5 | Verknüpfte Historie: Verträge, Aufträge, zugewiesene Mitarbeiter, Nachweise, Rechnungen, Zahlungen, Mahnungen, Kommunikation | alle Büro-Rollen (rechteabhängig) | Einrichtungsakte zeigt konsolidierte Historie ohne Datenduplizierung | ⬜ | ### 4.4 Einsatzmanagement (Auftrag → Einsatz → Zuweisung) | ID | Anforderung | Akteur | Akzeptanzkriterium | Status | |---|---|---|---|---| -| FR-EM-1 | Auftrag erfassen (Einrichtung, Ansprechpartner, Qualifikation, Zeitraum, Schichtart, Anzahl Mitarbeiter, Konditionen, Priorität) [Blueprint 19.4] | Sabrina | Auftragsdatensatz mit Pflichtfeldern anlegbar | 🔶 (`Order`-Entity in `omsorgCore` um alle geforderten Felder erweitert, volles Repository/Service/Controller (`OrdersController`, `GET/POST/PUT /api/orders`) nach dem Facility/Contract-Muster, gegated über `[RequirePermission(ModuleType.Orders, ...)]`; Ansprechpartner (`FacilityContactId`) wird gegen die angegebene Einrichtung cross-validiert — noch kein `omsorgapp`-UI-Modul dafür) | -| FR-EM-2 | Auftragsstatus-Pipeline: Anfrage → Prüfung → offen → teilweise besetzt → vollständig besetzt → aktiv → abgeschlossen/storniert [Blueprint 19.4] | Sabrina | Statuswechsel nur in zulässiger Reihenfolge, sichtbar im Dashboard | 🔶 (Pipeline serverseitig erzwungen: `OrderStatusDefinition`/`OrderStatusTransition` in `omsorgCore` bilden die Status und erlaubten Übergänge **DB-konfigurierbar** statt hartcodiert ab, Standard-Pipeline per `DbSeeder.SeedOrderStatusesAsync` geseedet, `OrderService.UpdateAsync` lehnt unzulässige Übergänge mit `400` ab; Dashboard-Sichtbarkeit fehlt noch — kein `omsorgapp`-UI) | -| FR-EM-3 | Mitarbeiterzuweisung prüft Qualifikation, Verfügbarkeit, Arbeitszeit, Abwesenheiten, Überschneidungen, Vertragsbedingungen [Blueprint 19.4] | Sabrina | System verhindert/warnt bei Konflikten vor Zuweisung | ⬜ | +| FR-EM-1 | Auftrag erfassen (Einrichtung, Ansprechpartner, Qualifikation, Zeitraum, Schichtart, Anzahl Mitarbeiter, Konditionen, Priorität) [Blueprint 19.4] | Sabrina | Auftragsdatensatz mit Pflichtfeldern anlegbar | ✅ (`Order`-Entity in `omsorgCore` um alle geforderten Felder erweitert, volles Repository/Service/Controller (`OrdersController`, `GET/POST/PUT/DELETE /api/orders`) nach dem Facility/Contract-Muster, gegated über `[RequirePermission(ModuleType.Orders, ...)]`; Ansprechpartner (`FacilityContactId`) wird gegen die angegebene Einrichtung cross-validiert; `omsorgapp`-UI-Modul vorhanden (Sidebar-Tab "Disposition" → `OrdersPage`/`OrderForm`/`OrderDetailPanel`/`Create-`/`EditOrderDialog`, über `src/api/ordersApi.js` echt gegen `omsorgCore`), alle Pflichtfelder inkl. abhängigem Ansprechpartner-Dropdown (lädt Kontakte der gewählten Einrichtung nach) und Qualifikation/Schichtart/Priorität als admin-editierbare Auswahllisten abgedeckt) | +| FR-EM-2 | Auftragsstatus-Pipeline: Anfrage → Prüfung → offen → teilweise besetzt → vollständig besetzt → aktiv → abgeschlossen/storniert [Blueprint 19.4] | Sabrina | Statuswechsel nur in zulässiger Reihenfolge, sichtbar im Dashboard | ✅ (Pipeline serverseitig erzwungen: Status und erlaubte Übergänge sind **DB-konfigurierbar** über das generische `ValueList`/`ValueListItem`/`ValueListItemTransition`-Modell (Liste `"OrderStatus"`, gleiches Muster wie CRM-Status bei Facilities, siehe `omsorgCore/CLAUDE.md` "Konfigurierbare Auswahllisten") statt eines eigenen `OrderStatusDefinition`/`OrderStatusTransition`-Modells, Standard-Pipeline per `DbSeeder` geseedet, `OrderService.UpdateAsync` lehnt unzulässige Übergänge über `ValueListRepository.CanTransitionAsync` mit `400` ab; `omsorgapp`-`OrderForm.jsx` filtert das Status-Dropdown im Bearbeiten-Formular serverseitig identisch auf die laut `/api/value-lists/OrderStatus/transitions` erlaubten Zielstatus (kein Client/Server-Auseinanderlaufen); Dashboard-Sichtbarkeit jetzt über `OrderStatusWidget.jsx` in `HomePage.jsx` (Auftragsanzahl je Status, rechtegegated über `hasPermission("Orders","View")`, analog `FollowUpWidget.jsx`)) | +| FR-EM-3 | Mitarbeiterzuweisung prüft Qualifikation, Verfügbarkeit, Arbeitszeit, Abwesenheiten, Überschneidungen, Vertragsbedingungen [Blueprint 19.4] | Sabrina | System verhindert/warnt bei Konflikten vor Zuweisung | ⬜ (weiterhin offen — es gibt noch keine Mitarbeiterzuweisung/Einsatz-Entität, nur den Auftrag selbst. Die Abwesenheitsdatenbasis für den "Abwesenheiten"-Teil der Konfliktprüfung existiert jetzt aber bereits in `omsorgCore` (`Absence`, siehe FR-CON-1/`omsorgCore/CLAUDE.md`) — von der eigentlichen Zuweisungs-Konfliktprüfung wird sie noch nicht konsumiert) | | FR-EM-4 | Nach Zuweisung erhält Mitarbeiter automatisch Einsatzanweisung über OMSORG Connect [Blueprint 19.4] | System → Außendienst | Einsatzanweisung erscheint in Connect ohne manuellen Zusatzschritt | 🔶 (Connect hat bereits `pages/einsatzanweisung.php` inkl. Admin-Upload `actions/einsatzanweisung-action.php`; automatische Erzeugung aus Zuweisung fehlt, da Aufträge/Zuweisung noch nicht existieren) | | FR-EM-5 | Krankmeldung löst Ersatzbesetzungs-Workflow aus [omsorg.md, Blueprint 20.2] | System, Sabrina | Bei Krankmeldung erscheint Einsatz als "muss neu besetzt werden" inkl. Vorschlägen | ⬜ | @@ -105,8 +105,8 @@ Umfasst alle drei Plattform-Ebenen: OMSORG Desktop, OMSORG Connect, OMSORG Backe | ID | Anforderung | Akteur | Akzeptanzkriterium | Status | |---|---|---|---|---| -| FR-ZE-1 | Erfassung von Beginn, Pause, Ende, Einrichtung, Auftrag, Nacht-/Wochenend-/Feiertagsstunden [Blueprint 19.5] | Außendienst | Eintrag pro Schicht mit Pflichtfeldern | 🔶 (aktuell nur monatlicher Upload via `pages/stundennachweis.php`/`actions/submit-stundennachweis.php`, keine strukturierte Beginn/Pause/Ende-Erfassung pro Schicht) | -| FR-ZE-2 | Statuspipeline: Entwurf → eingereicht → Prüfung → Rückfrage → freigegeben → abgerechnet [Blueprint 19.5] | Außendienst, Sabrina | Statuswechsel sichtbar für beide Seiten, Rückfrage möglich | 🔶 (aktueller Status ist binär offen/geprüft über Admin-Anträge, kein granularer Workflow) | +| FR-ZE-1 | Erfassung von Beginn, Pause, Ende, Einrichtung, Auftrag, Nacht-/Wochenend-/Feiertagsstunden [Blueprint 19.5] | Außendienst | Eintrag pro Schicht mit Pflichtfeldern | ✅ (`TimeEntry` in `omsorgCore`, volles CRUD über `TimeEntriesController`; Einrichtung wird über `Order.FacilityId` aufgelöst statt redundant gespeichert; Erfassung über `omsorgWeb/mitarbeiter-app` `pages/stundenerfassung.php`, Prüfung/Freigabe über `omsorgapp` "Zeiterfassung") | +| FR-ZE-2 | Statuspipeline: Entwurf → eingereicht → Prüfung → Rückfrage → freigegeben → abgerechnet [Blueprint 19.5] | Außendienst, Sabrina | Statuswechsel sichtbar für beide Seiten, Rückfrage möglich | ✅ (ValueList `"TimeEntryStatus"` mit `ValueListItemTransition`-Graph wie `OrderStatus`, zusätzlich `RequiresApproval` je Kante — Außendienst löst nur die Selbst-Einreichungs-Kanten über `POST /api/time-entries/{id}/submit` aus, Sabrina/Büro entscheidet über `POST /api/time-entries/{id}/decision`) | | FR-ZE-3 | Nur freigegebene Zeiten dürfen in Rechnungsstellung einfließen [Blueprint 19.5, 19.8] | System | Rechnungslauf ignoriert nicht-freigegebene Datensätze; Verstoß ist technisch unmöglich, nicht nur UI-Regel | ⬜ | | FR-ZE-4 | Optionale Unterschrift/Bestätigung der Einrichtung, Upload als Foto/PDF | Außendienst | Upload-Feld vorhanden, an Zeiterfassungssatz gekoppelt | 🔶 (Upload existiert für Monatsnachweis, nicht pro Einzel-Einsatz) | @@ -140,7 +140,7 @@ Umfasst alle drei Plattform-Ebenen: OMSORG Desktop, OMSORG Connect, OMSORG Backe | ID | Anforderung | Akteur | Akzeptanzkriterium | Status | |---|---|---|---|---| -| FR-CON-1 | Zeiterfassung, digitaler Tätigkeitsnachweis, Fahrtenbuch, Einsatzanweisung, Krankmeldung, Urlaub, Dokumente, News, Benefits, Ansprechpartner [omsorg.md, Blueprint 4.2] | Außendienst | Jede Funktion als eigener Menüpunkt erreichbar | 🔶 (vorhanden: Stundennachweis-Upload, Urlaubs-/Abwesenheitsantrag, Dokumentenarchiv, News, Benefits, Einsatzanweisung, Fortbildung, Dienstplan, Werben, Bewertung — **Fahrtenbuch fehlt vollständig**, Zeiterfassung ist Upload statt Live-Erfassung) | +| FR-CON-1 | Zeiterfassung, digitaler Tätigkeitsnachweis, Fahrtenbuch, Einsatzanweisung, Krankmeldung, Urlaub, Dokumente, News, Benefits, Ansprechpartner [omsorg.md, Blueprint 4.2] | Außendienst | Jede Funktion als eigener Menüpunkt erreichbar | 🔶 (produktiv über `omsorgWeb/mitarbeiter-app-legacy` (eigene MySQL): Stundennachweis-Upload, Urlaubs-/Abwesenheitsantrag, Dokumentenarchiv, News, Benefits, Einsatzanweisung, Fortbildung, Dienstplan, Werben, Bewertung — **Fahrtenbuch fehlt vollständig**, Zeiterfassung ist Upload statt Live-Erfassung. Der aktiv weiterentwickelte Neuaufbau `omsorgWeb/mitarbeiter-app` (gegen `omsorgCore`, keine eigene Datenhaltung mehr) deckt bisher Login/Passwort sowie Urlaubs-/Abwesenheits-/Krankmeldungsanträge ab (`pages/urlaubsantrag.php` gegen den neuen `AbsencesController`, Datenbasis zugleich für FR-EM-3 — siehe `omsorgCore/CLAUDE.md` "Abwesenheits-/Urlaubs-/Krankmeldungsanträge"); alle anderen Menüpunkte müssen im Neuaufbau noch nachgezogen werden, bis die Legacy-App abgelöst werden kann) | | FR-CON-2 | Automatische Synchronisation mit OMSORG Desktop [omsorg.md] | System | Änderung in Connect ist ohne manuellen Export in Desktop sichtbar | ⬜ (blockiert durch fehlendes OMSORG Backend, s. Abschnitt 2) | | FR-CON-3 | PWA-Installierbarkeit ("Add to Home Screen") | Außendienst | Manifest + Service Worker vorhanden und funktionsfähig | ✅ (`manifest.webmanifest`, `service-worker.js`) | | FR-CON-4 | Login-Rate-Limiting: 5 Fehlversuche → 10 Min. Sperre | Außendienst/alle | Nach 5 Fehlversuchen wird Login für 10 Minuten blockiert | ✅ (`index.php`, `login_attempts`-Tabelle, Migration 004) | @@ -180,7 +180,7 @@ Die Event-Schicht ist Teil des Backends (siehe Abschnitt 2) und läuft im selben | NFR-5 | Ausgabe-Escaping gegen XSS | omsorgWeb CLAUDE.md | ✅ (`e()`-Helper) | | NFR-6 | Automatische Backups + regelmäßige Wiederherstellungsprüfung | Blueprint 13 | ⬜ | | NFR-7 | Nachvollziehbare Änderungshistorie / Protokollierung sicherheitsrelevanter Vorgänge | Blueprint 13, 19.8 | 🔶 (Login-Versuche protokolliert; generisches Audit-Log für Datensatzänderungen fehlt) | -| NFR-8 | Aufbewahrungs- und Löschkonzept, Schutz vor unbefugtem Export | Blueprint 13 | ⬜ | +| NFR-8 | Aufbewahrungs- und Löschkonzept, Schutz vor unbefugtem Export | Blueprint 13 | 🔶 (Löschkonzept-Teil umgesetzt: `Employee`/`Facility`/`Contract`/`Order`/`FacilityContact` werden ausschließlich soft-gelöscht (`IsDeleted`/`DeletedAt`, kein Hard-Delete), gegated über `PermissionAction.Delete` je Modul, und über eine neue "Papierkorb"-Seite (`PermissionAction.Recover`) wiederherstellbar — nichts geht verloren, nichts wird unkontrolliert entfernt; **noch offen:** Aufbewahrungsfristen/automatisches endgültiges Löschen nach Frist sowie expliziter Schutz vor unbefugtem Export) | | NFR-9 | Berechtigungsprüfung auf Oberflächen- **und** Datenebene | Blueprint 13, 19.8 | 🔶 (in Connect vorhanden für Außendienst-Eigendaten; granulare Rechtematrix für Büromitarbeiter fehlt) | | NFR-10 | White-Label-Fähigkeit: Branding (Name, Logo, Farben, Schrift, Vorlagen) von Geschäftslogik getrennt | Blueprint 14 | ⬜ (aktuell Branding fest im Code/CSS von `omsorgWeb`/`omsorgapp`) | | NFR-11 | Vollständige Mandantentrennung für spätere Multi-Tenant-Nutzung | Blueprint 14 | ⬜ (nicht Teil der aktuellen Datenmodelle; explizit erst „bei Bedarf") | @@ -198,7 +198,7 @@ Verbindliche Regeln [Blueprint 19.8]: 2. Beziehungen werden über IDs hergestellt, nicht durch doppelte Texteingaben. 3. Stammdaten werden nur an einer zentralen Stelle gepflegt. 4. Änderungen werden nachvollziehbar protokolliert. -5. Löschungen sensibler/geschäftsrelevanter Daten erfolgen nicht unkontrolliert (Soft-Delete/Archivierung statt Hard-Delete). +5. Löschungen sensibler/geschäftsrelevanter Daten erfolgen nicht unkontrolliert (Soft-Delete/Archivierung statt Hard-Delete). ✅ umgesetzt für alle Core-Objekte mit vollem CRUD (`Employee`, `Facility`, `Contract`, `Order`, `FacilityContact`, `Absence`, `TimeEntry`) — `IsDeleted`/`DeletedAt` statt Hard-Delete, wiederherstellbar über die "Papierkorb"-Seite; `Invoice` hat noch kein CRUD, daher hier noch nicht relevant. 6. Berechtigungen werden auf Daten- und Funktionsebene geprüft. 7. Automatisierungen (Event-Schicht) greifen ausschließlich auf verlässliche, freigegebene Daten zu — dadurch, dass Datenschicht und Event-Schicht im selben Backend-Prozess laufen, ist dies technisch einfacher garantierbar als bei getrennten Services (kein Risiko von veralteten/inkonsistenten Zwischenständen durch asynchrone Synchronisation). 8. Rechnungen entstehen ausschließlich aus freigegebenen Arbeitszeiten. @@ -219,7 +219,11 @@ Verbindliche Regeln [Blueprint 19.8]: | Rechnungen/Zahlungen | voll | erstellen/freigeben | **kein Zugriff** | – | | Recruiting/CRM | voll | nutzen | voll | – | | Controlling | voll (inkl. Gewinn/Margen) | eingeschränkt (kein Gewinn) | **kein Zugriff** | – | -| Benutzerverwaltung | voll | – | – | – | +| Benutzerkonten (anlegen, (de)aktivieren, Passwort zurücksetzen) | voll | – | – | – | +| Rollen & Rechte (Rechte-Matrix, individuelle Ausnahmen) | voll | – | – | – | +| Konfiguration (Status-Verwaltung/Auswahllisten) | voll | – | – | – | + +Die drei letzten Zeilen waren bis 2026-08-09 ein einziger Punkt "Benutzerverwaltung" — technisch jetzt als drei getrennte Rechte (`ModuleType.Users`/`UserManagement`/`Configuration`, siehe `omsorgCore/CLAUDE.md`, Abschnitt "Rechtesystem") umgesetzt, damit z. B. künftig Status-Verwaltung an Sabrina vergeben werden kann, ohne ihr auch Zugriff auf die Rechte-Matrix zu geben. Aktuell hat davon nur Sabina/Malik überhaupt Zugriff (Basis-Rollen-Seed unverändert). Für zukünftige Büromitarbeiter: Rolle als Vorlage, zusätzlich granular je Modul einstellbar: sehen / lesen / anlegen / bearbeiten / löschen / exportieren / freigeben [Blueprint 6.5]. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..585fe90 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,101 @@ +name: omsorg + +# WICHTIG (der fehleranfälligste Punkt in diesem Setup): +# - omsorgapp.build.args.VITE_OMSORG_CORE_URL muss die vom BROWSER erreichbare Adresse von +# omsorgcore sein (hier: der auf dem Host published Port), NICHT der interne Compose-DNS-Name - +# Vite bäckt diese URL zur Build-Zeit in den JS-Bundle ein (siehe omsorgapp/src/api/config.js). +# - omsorgweb.environment.OMSORG_CORE_URL ist dagegen der interne Servicename (http://omsorgcore:8080), +# da PHP dort serverseitig per cURL aufruft (kein Browser-Kontext), siehe +# omsorgWeb/docker/bootstrap-config.php. + +services: + postgres: + image: postgres:16 + environment: + POSTGRES_DB: omsorg_core + POSTGRES_USER: omsorg_core + POSTGRES_PASSWORD: omsorg_core_dev_password + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U omsorg_core"] + interval: 5s + timeout: 5s + retries: 10 + + mysql: + image: mysql:8.0 + environment: + MYSQL_DATABASE: omsorg_web + MYSQL_USER: omsorg_web + MYSQL_PASSWORD: omsorg_web_dev_password + MYSQL_ROOT_PASSWORD: omsorg_web_root_dev_password + volumes: + - mysql_data:/var/lib/mysql + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + interval: 5s + timeout: 5s + retries: 10 + + omsorgcore: + build: + context: . + dockerfile: omsorgCore/Dockerfile + depends_on: + postgres: + condition: service_healthy + environment: + ASPNETCORE_ENVIRONMENT: Development + ConnectionStrings__OmsorgCore: "Host=postgres;Port=5432;Database=omsorg_core;Username=omsorg_core;Password=omsorg_core_dev_password" + Jwt__Secret: "CHANGE_ME_LOCAL_DEV_SECRET_MIN_32_CHARS_LONG" + Cors__AllowedOrigins__0: "http://localhost:5173" + ports: + - "8080:8080" + volumes: + - omsorgcore_documents:/app/App_Data/documents + + omsorgapp: + build: + context: . + dockerfile: omsorgapp/Dockerfile + args: + VITE_OMSORG_CORE_URL: "http://localhost:8080" + ports: + - "5173:80" + depends_on: + - omsorgcore + + omsorgweb: + build: + context: . + dockerfile: omsorgWeb/Dockerfile + depends_on: + mysql: + condition: service_healthy + omsorgcore: + condition: service_started + environment: + DB_HOST: mysql + DB_NAME: omsorg_web + DB_USER: omsorg_web + DB_PASSWORD: omsorg_web_dev_password + OMSORG_CORE_URL: "http://omsorgcore:8080" + ports: + - "8081:80" + volumes: + - web_uploads:/var/www/html/mitarbeiter-app-legacy/uploads + - web_downloads:/var/www/html/mitarbeiter-app-legacy/downloads + - web_fortbildung:/var/www/html/mitarbeiter-app-legacy/fortbildung-materials + - web_avatars:/var/www/html/mitarbeiter-app-legacy/assets/avatars + - web_data:/var/www/html/mitarbeiter-app-legacy/data + +volumes: + postgres_data: + mysql_data: + omsorgcore_documents: + web_uploads: + web_downloads: + web_fortbildung: + web_avatars: + web_data: diff --git a/omsorgCore/CLAUDE.md b/omsorgCore/CLAUDE.md index fe084ca..0ff6791 100644 --- a/omsorgCore/CLAUDE.md +++ b/omsorgCore/CLAUDE.md @@ -28,8 +28,8 @@ omsorgCore/ src/ OmsorgCore.Domain/ # Entitäten, Enums. Keine Abhängigkeit auf andere Projekte. Common/ # Entity, AuditableEntity, AuditRedactedAttribute (Basisklassen) - Enums/ # ModuleType, PermissionAction, PermissionEffect, AuditEventCategory - Entities/ # Employee, Facility, Contract, Order, TimeEntry, Invoice, + Enums/ # ModuleType, PermissionAction, PermissionEffect, AuditEventCategory, DocumentEntityType + Entities/ # Employee, Facility, Contract, Order, TimeEntry, Invoice, Document, # User, Role, RolePermission, UserPermissionOverride, AuditLogEntry OmsorgCore.Application/ # Business-Logik. Abhängig von Domain. Abstractions/ # Interfaces: IEmployeeRepository, IUserRepository, @@ -42,7 +42,8 @@ omsorgCore/ OmsorgCoreDbContext.cs Configurations/ # ein IEntityTypeConfiguration pro Entität Migrations/ # EF-Core-Migrationen (InitialCreate bereits erzeugt) - Repositories/ # EmployeeRepository, FacilityRepository, FacilityContactRepository, UserRepository, RefreshTokenRepository, AuditLogRepository (implementieren Application-Interfaces) + Repositories/ # EmployeeRepository, FacilityRepository, FacilityContactRepository, DocumentRepository, UserRepository, RefreshTokenRepository, AuditLogRepository (implementieren Application-Interfaces) + Storage/ # StorageOptions, DocumentUploadPolicy, FileSystemDocumentStorage (Dokument-Bytes auf Disk, siehe "Dokumentenarchiv" unten) Security/ # PasswordHasher, JwtOptions, JwtTokenGenerator, RefreshTokenOptions, RefreshTokenGenerator DependencyInjection.cs # AddInfrastructure(configuration) OmsorgCore.Engine/ # Event-Schicht. Abhängig von Domain + Application. @@ -51,7 +52,7 @@ omsorgCore/ Handlers/ # Beispiel-Handler (EmployeeCreatedHandler), AuditEventHandler DependencyInjection.cs # AddEngine() OmsorgCore.Api/ # ASP.NET Core Web API. Abhängig von Application+Infrastructure+Engine. - Controllers/ # AuthController, EmployeesController, FacilitiesController, FacilityContactsController, HealthController, AdminSessionsController, AuditLogController + Controllers/ # AuthController, EmployeesController, FacilitiesController, FacilityContactsController, DocumentsController, HealthController, AdminSessionsController, AuditLogController Contracts/ # Request-/Response-DTOs (LoginRequest, EmployeeResponse, ...) Security/ # CurrentUserService, RequirePermissionAttribute Program.cs # einziger Ort, an dem alle Schichten verdrahtet werden @@ -67,7 +68,7 @@ omsorgCore/ Rolle liefert Standard-Rechte (`RolePermission`: Modul × Aktion), ein individueller `UserPermissionOverride` (Grant/Revoke) gewinnt immer gegen den Rollen-Default — siehe `PermissionService.HasPermissionAsync` (`src/OmsorgCore.Application/Services/PermissionService.cs`). Deckt Blueprint 6.5 ("Rolle als Vorlage + individuelle Rechte") ab. -Die aufgelösten Rechte eines Users (nicht nur eine einzelne Prüfung) liefert `PermissionService.GetGrantedPermissionsAsync` als Liste von `PermissionGrant(Module, Action)`. Exponiert über `GET /api/auth/me` (`AuthController.Me`, `[Authorize]`) als `MeResponse { username, role, permissions: [{ module, action }, ...] }` — der einzige Weg, wie granulare Rechte den Client erreichen (das JWT trägt nur den Rollennamen). `omsorgapp` ruft diesen Endpunkt nach Login/Refresh auf (siehe `omsorgapp/CLAUDE.md`, "Rechtesystem im Client") und trifft UI-Entscheidungen darüber statt über einen Rollennamen-Vergleich. +Die aufgelösten Rechte eines Users (nicht nur eine einzelne Prüfung) liefert `PermissionService.GetGrantedPermissionsAsync` als Liste von `PermissionGrant(Module, Action, Scope)`. Exponiert über `GET /api/auth/me` (`AuthController.Me`, `[Authorize]`) als `MeResponse { username, role, permissions: [{ module, action, scope }, ...] }` — der einzige Weg, wie granulare Rechte den Client erreichen (das JWT trägt nur den Rollennamen). `omsorgapp` ruft diesen Endpunkt nach Login/Refresh auf (siehe `omsorgapp/CLAUDE.md`, "Rechtesystem im Client") und trifft UI-Entscheidungen darüber statt über einen Rollennamen-Vergleich. Rechteprüfung auf Controller-Actions: ```csharp @@ -75,7 +76,18 @@ Rechteprüfung auf Controller-Actions: ``` Das Attribut (`src/OmsorgCore.Api/Security/RequirePermissionAttribute.cs`) prüft serverseitig über `IPermissionService` — nicht nur im Client (REQUIREMENTS.md NFR-9). Jeder Controller außer `AuthController` trägt zusätzlich `[Authorize]`. -**Rollen-Rechte-Matrix und User-Overrides verwalten (Admin-Flow):** Eine neu angelegte Rolle (`RoleService.CreateAsync`) hat zunächst keine `RolePermission`-Einträge — die Rechte-Matrix wird separat gesetzt über `GET /api/roles/{id}` (Rolle inkl. ihrer aktuellen `RolePermission`-Liste, `RoleService.GetByIdWithPermissionsAsync`) und `PUT /api/roles/{id}/permissions` (`RoleService.UpdatePermissionsAsync` — ersetzt die komplette `RolePermission`-Menge der Rolle durch die übergebene Menge, kein inkrementelles Patchen). Individuelle `UserPermissionOverride`-Ausnahmen eines Users werden über `GET/POST/DELETE /api/users/{id}/permission-overrides[...]` verwaltet (`UserService.GetPermissionOverridesAsync`/`AddPermissionOverrideAsync`/`RemovePermissionOverrideAsync` — `AddPermissionOverrideAsync` ist ein Upsert: existiert bereits ein Override für dasselbe Modul+Aktion bei diesem User, wird dessen `Effect` aktualisiert statt dupliziert). Alle diese Endpoints liegen auf `RolesController`/`UsersController`, gegated über `[RequirePermission(ModuleType.UserManagement, View|Edit)]` wie der Rest der Nutzerverwaltung. Admin-UI dazu: `omsorgapp/src/modules/settings/` (`SettingsPage`, `RolesPanel`, `RolePermissionMatrix`, `UserOverridesPanel`). +**Drei getrennte Admin-Rechte statt einer Sammelkategorie (seit 2026-08-09):** `UserManagement` deckte ursprünglich die komplette "Einstellungen"-Seite ab (Benutzerkonten, Rollen-Rechte-Matrix, User-Overrides, Status-Verwaltung, Debug/Sessions) — wer irgendeinen dieser Bereiche brauchte, bekam automatisch Zugriff auf alle, inklusive der Möglichkeit, sich selbst beliebige Rechte zu geben. Jetzt drei fachlich getrennte `ModuleType`-Werte: +- **`Users`** — Benutzerkonten sehen/anlegen/(de)aktivieren/Passwort zurücksetzen (`UsersController`: `GetAll`/`Create`/`ResetPassword`/`Update`, alle `[RequirePermission(ModuleType.Users, ...)]`). Admin-UI: `omsorgapp/src/modules/settings/UsersPanel.jsx` (Tab "Benutzer" in `SettingsPage`). +- **`UserManagement`** (enger als zuvor) — nur noch die rechte-eskalierenden Aktionen: Rollen-Rechte-Matrix + individuelle User-Permission-Overrides + Session-Killswitch (`AdminSessionsController`) + Test-Mail (`AdminEmailController`). Bewusst weiterhin ein eigenes, sensibleres Recht, weil hierüber Rechte selbst verändert werden. +- **`Configuration`** — Status-Verwaltung/Auswahllisten (`ValueListsController`, alle schreibenden Endpoints `[RequirePermission(ModuleType.Configuration, PermissionAction.Edit)]`), fachlich unabhängig von Nutzerverwaltung. + +Die Sidebar-Sichtbarkeit von "Einstellungen" in `omsorgapp` ist deshalb kein einzelnes Modul mehr, sondern ein OR über alle drei (`navPermissions.js`, `SETTINGS_MODULES`, analog zum bestehenden `TRASH_MODULES`-Muster für den Papierkorb) — `SettingsPage.jsx` filtert die vier Tabs (Benutzer/Rollen/Benutzerrechte/Status-Verwaltung) zusätzlich einzeln nach ihrem jeweiligen Modul, ein Nutzer sieht also nur die Tabs, für die er tatsächlich `View` hat. **Wichtig für neue Rollen:** `GET /api/users` (Liste aller Benutzerkonten) prüft `Users`/`View` — wer individuelle `UserPermissionOverride`-Ausnahmen verwalten will (`UserManagement`/Edit), braucht zusätzlich `Users`/`View`, um überhaupt einen Nutzer zur Auswahl zu bekommen (`UserOverridesPanel.jsx`); ebenso nutzen `EmployeesPage.jsx` (Account-anlegen-Button in der Personalakte) und `AuditLogPage.jsx` (Akteur-Filter) `GET /api/users` und prüfen daher `Users`/View bzw. `Users`/Create, nicht mehr `UserManagement`. + +Der Basis-Rollen-Seed (`DbSeeder.SeedBaseRolesAsync`, siehe unten) wurde bei diesem Split **nicht** angepasst — nur `Geschäftsführung` iteriert ohnehin generisch über `Enum.GetValues()` und bekommt damit automatisch alle drei neuen Rechte, die anderen Basis-Rollen hatten vorher kein `UserManagement` und haben jetzt entsprechend auch keins der drei neuen Rechte. Eine Vergabe (z. B. `Configuration` an Disposition/Buchhaltung) ist eine bewusste, spätere Entscheidung über die Rollen-UI, kein Teil dieses technischen Splits. + +**Rollen-Rechte-Matrix und User-Overrides verwalten (Admin-Flow):** Eine neu angelegte Rolle (`RoleService.CreateAsync`) hat zunächst keine `RolePermission`-Einträge — die Rechte-Matrix wird separat gesetzt über `GET /api/roles/{id}` (Rolle inkl. ihrer aktuellen `RolePermission`-Liste, `RoleService.GetByIdWithPermissionsAsync`) und `PUT /api/roles/{id}/permissions` (`RoleService.UpdatePermissionsAsync` — ersetzt die komplette `RolePermission`-Menge der Rolle durch die übergebene Menge, kein inkrementelles Patchen). Individuelle `UserPermissionOverride`-Ausnahmen eines Users werden über `GET/POST/DELETE /api/users/{id}/permission-overrides[...]` verwaltet (`UserService.GetPermissionOverridesAsync`/`AddPermissionOverrideAsync`/`RemovePermissionOverrideAsync` — `AddPermissionOverrideAsync` ist ein Upsert: existiert bereits ein Override für dasselbe Modul+Aktion bei diesem User, werden dessen `Effect` **und** `Scope` aktualisiert statt dupliziert). Diese Endpoints liegen auf `RolesController`/`UsersController` (die `.../permission-overrides`-Routen), gegated über `[RequirePermission(ModuleType.UserManagement, View|Edit)]`. Admin-UI dazu: `omsorgapp/src/modules/settings/` (`SettingsPage`, `RolesPanel`, `RolePermissionMatrix`, `UserOverridesPanel`). + +**Datenebenen-Scope (`PermissionScope`, "nur eigene Daten"):** Dritte Dimension neben Modul×Aktion — jede `RolePermission`/`UserPermissionOverride`-Zeile trägt zusätzlich `Scope` (`All` oder `Own`, `src/OmsorgCore.Domain/Enums/PermissionScope.cs`). `PermissionService.GetScopeAsync(userId, module, action)` löst das auf und liefert `PermissionScope?` (`null` = gar nicht gewährt) — ein Override ersetzt dabei die komplette Zelle (Grant **und** Scope) der Rolle, es wird nicht gemergt, analog zur bestehenden Effect-Semantik. `HasPermissionAsync`/`RequirePermissionAttribute` bleiben bewusst scope-unabhängig (ein Own-User muss den Endpunkt-Gate trotzdem passieren) — die eigentliche Einschränkung passiert in den Application-Services: `EmployeeService`/`ContractService` konsultieren `GetScopeAsync` vor `GetPagedAsync`/`GetByIdAsync` und filtern bei `Own` auf `ICurrentUserService.EmployeeId` (neuer JWT-Claim `"employeeId"`, aus `User.EmployeeId`, nur eingebettet wenn gesetzt — wirkt daher erst mit dem nächsten Token-Refresh, wenn die Verknüpfung sich ändert). Own ohne verknüpfte `EmployeeId` liefert bewusst keine Datensätze (nicht "alle", nicht 500). **`Employees`, `Contracts`, `Absences` und `TimeEntries` werten den Scope aktuell aus** (alle vier haben einen Ownership-Anker: `User.EmployeeId`, `Contract.EmployeeId`, `Absence.EmployeeId`, `TimeEntry.EmployeeId` — siehe "Abwesenheits-/Urlaubs-/Krankmeldungsanträge" und "Zeiterfassung" unten) — alle anderen Module ignorieren `Scope` faktisch, weil ihnen kein Ownership-Anker zugrunde liegt (`Order` z. B. hat noch keinen Mitarbeiter-Bezug). **Wichtig:** Wird ein neuer `ModuleType` oder `PermissionAction`-Wert hinzugefügt, oder ändert sich sonst das Rollen-/Rechtesystem, muss dieser Abschnitt (Rechtesystem) im selben Change aktualisiert werden — diese Dokumentation ist keine Momentaufnahme, sondern muss mit der Software mitwachsen. @@ -92,11 +104,11 @@ Aktor-Informationen (`Username`/`RoleName`/`IpAddress`) kommen über `ICurrentUs ## Auth-Flow -1. `POST /api/auth/login` (`AuthController`) → `AuthService.LoginAsync` prüft Username/Passwort-Hash, widerruft **alle bisherigen aktiven Refresh-Tokens dieses Users und würfelt seinen `SecurityStamp` neu** (`EndOtherSessionsAsync` — ein User hat immer nur eine aktive Session; ältere Sessions werden per Killswitch sofort ungültig, siehe "Session-Killswitch" unten), dann erzeugt `JwtTokenGenerator` ein Access-Token (Claims `sub`/`name`/`role`) + `RefreshTokenGenerator` einen langlebigen Refresh-Token. Response (`LoginResponse`): `accessToken`, `refreshToken`, `expiresAt` (camelCase, Default-JSON-Serialisierung von ASP.NET Core). +1. `POST /api/auth/login` (`AuthController`) → `AuthService.LoginAsync` prüft Username/Passwort-Hash, widerruft **alle bisherigen aktiven Refresh-Tokens dieses Users und würfelt seinen `SecurityStamp` neu** (`EndOtherSessionsAsync` — ein User hat immer nur eine aktive Session; ältere Sessions werden per Killswitch sofort ungültig, siehe "Session-Killswitch" unten), dann erzeugt `JwtTokenGenerator` ein Access-Token (Claims `sub`/`name`/`role`) + `RefreshTokenGenerator` einen langlebigen Refresh-Token. Response (`LoginResponse`): nur noch `accessToken`, `expiresAt`, `mustChangePassword` (camelCase, Default-JSON-Serialisierung von ASP.NET Core) — der Refresh-Token selbst geht **nicht** im Body raus, sondern als **HttpOnly-Secure-Cookie** (`refreshToken`, `Path=/api/auth`, `AuthController.SetRefreshTokenCookie`, `Secure` nur außerhalb von Development, da der lokale Dev-Server standardmäßig nur über `http://` läuft), seit `omsorgapp` als Browser-SPA (nicht mehr Electron) läuft und ein Browser-Frontend den Token sonst nie sicher clientseitig halten könnte (kein `safeStorage`-Äquivalent im Browser). Das setzt CORS mit `AllowCredentials()` voraus (`Program.cs`, `Cors:AllowedOrigins`-Config), sonst schickt der Browser die Cookie nicht mit. 2. Client sendet Access-Token als `Authorization: Bearer `. 3. `Program.cs` validiert das Token gegen `Jwt:Issuer`/`Jwt:Audience`/`Jwt:Secret` aus der Konfiguration. -4. `POST /api/auth/refresh` (kein `[Authorize]` — der Refresh-Token selbst ist das Credential): `AuthService.RefreshAsync` prüft den Refresh-Token per Hash-Lookup, **rotiert** ihn (alten Token widerrufen, neuen ausstellen, per `ReplacedByTokenId` verkettet) und liefert ein neues Token-Paar. Erlaubt langlebige Sessions ohne täglichen Passwort-Login (siehe `omsorgapp/CLAUDE.md`). -5. `POST /api/auth/logout` widerruft den vorgelegten Refresh-Token (`AuthService.RevokeAsync`, idempotent). +4. `POST /api/auth/refresh` (kein `[Authorize]`, kein Body — der Refresh-Token kommt aus der Cookie, die selbst das Credential ist): `AuthService.RefreshAsync` prüft den Refresh-Token per Hash-Lookup, **rotiert** ihn (alten Token widerrufen, neuen ausstellen, per `ReplacedByTokenId` verkettet, neue Cookie gesetzt) und liefert ein neues Token-Paar. Erlaubt langlebige Sessions ohne täglichen Passwort-Login (siehe `omsorgapp/CLAUDE.md`). +5. `POST /api/auth/logout` (kein Body) widerruft den Refresh-Token aus der Cookie (`AuthService.RevokeAsync`, idempotent) und löscht die Cookie (`Response.Cookies.Delete`). **Refresh-Token:** kein Rohtoken wird gespeichert, nur sein SHA-256-Hash (`RefreshToken`-Entity, `IRefreshTokenGenerator`). Gültigkeit über `RefreshToken:ExpiryDays` in `appsettings.json` (Default 60 Tage, sliding — jede Nutzung verlängert effektiv die Session), überschreibbar per `RefreshToken__ExpiryDays`. @@ -104,7 +116,7 @@ Aktor-Informationen (`Username`/`RoleName`/`IpAddress`) kommen über `ICurrentUs **Secret-Handling:** `appsettings.json` enthält nur Issuer/Audience/ExpiryMinutes/RefreshToken:ExpiryDays. `appsettings.Development.json` enthält einen **lokalen Platzhalter** für `Jwt:Secret` und den Connection-String (`CHANGE_ME_...`) — für echte Umgebungen über Umgebungsvariable (`Jwt__Secret`) oder `dotnet user-secrets` überschreiben, nie ein echtes Secret einchecken. -**Basis-Rollen-Seed (alle Umgebungen):** `DbSeeder.SeedBaseRolesAsync` (`src/OmsorgCore.Infrastructure/Persistence/DbSeeder.cs`) legt bei jedem Start die vier in `REQUIREMENTS.md` Abschnitt 3 ("Akteure & Rollen") und Abschnitt 7 ("Rechtematrix") beschriebenen Basis-Rollen an — `Geschäftsführung` (Sabina/Malik, voller Zugriff auf alle Module), `Disposition/Buchhaltung` (Sabrina), `Recruiting` (Sascha), `Außendienst` (ohne Modul-Rechte, da OMSORG Connect noch nicht gegen dieses Backend spricht und "nur eigene Daten" ohnehin Datenebene statt Modul-Recht ist). Läuft in Program.cs direkt nach den Migrationen, **nicht** auf `IsDevelopment()` beschränkt (im Gegensatz zum Admin-Seed unten) — enthält keine Zugangsdaten, nur Rollen-Stammdaten. Idempotent pro Rollenname: existiert eine Rolle schon (z. B. weil sie über die Rechte-Matrix-UI unter "Einstellungen" angepasst wurde), fasst der Seed sie nicht an. +**Basis-Rollen-Seed (alle Umgebungen):** `DbSeeder.SeedBaseRolesAsync` (`src/OmsorgCore.Infrastructure/Persistence/DbSeeder.cs`) legt bei jedem Start die vier in `REQUIREMENTS.md` Abschnitt 3 ("Akteure & Rollen") und Abschnitt 7 ("Rechtematrix") beschriebenen Basis-Rollen an — `Geschäftsführung` (Sabina/Malik, voller Zugriff auf alle Module), `Disposition/Buchhaltung` (Sabrina, u. a. `Absences` mit allen Aktionen), `Recruiting` (Sascha), `Außendienst` (`Employees.View` + `Contracts.View` + `Absences.{Create,View}`, alle drei mit `PermissionScope.Own` — siehe "Datenebenen-Scope" oben; `Absences` ist dabei bereits an `omsorgWeb/mitarbeiter-app` angebunden, siehe "Abwesenheits-/Urlaubs-/Krankmeldungsanträge" unten, während `Employees`/`Contracts` dort noch nicht konsumiert werden). Läuft in Program.cs direkt nach den Migrationen, **nicht** auf `IsDevelopment()` beschränkt (im Gegensatz zum Admin-Seed unten) — enthält keine Zugangsdaten, nur Rollen-Stammdaten. Idempotent pro Rollenname: existiert eine Rolle schon (z. B. weil sie über die Rechte-Matrix-UI unter "Einstellungen" angepasst wurde), fasst der Seed sie nicht an. **Wichtig:** Die Zuordnung in `SeedBaseRolesAsync` ist eine Übersetzung der Rechtematrix aus `REQUIREMENTS.md` auf die aktuellen `ModuleType`/`PermissionAction`-Werte. Kommt ein neuer `ModuleType`/eine neue `PermissionAction` dazu, oder ändert sich die Rechtematrix in `REQUIREMENTS.md`, muss dieser Seed im selben Change mit aktualisiert werden — er ist keine Momentaufnahme, sondern muss mit der Software mitwachsen (siehe auch den allgemeinen Pflegehinweis am Ende dieses Abschnitts). @@ -139,12 +151,16 @@ Vollständig implementiert: `PasswordResetCode`-Entity + `PasswordResetService` **Testversand ohne Zugangsdaten-Leak:** `AdminEmailController` (`POST /api/admin/email/test-send`, `[RequirePermission(ModuleType.UserManagement, PermissionAction.Edit)]`) verschickt eine feste Testmail über den aktuell konfigurierten `IEmailSender` — Host/Username/Passwort verlassen dabei nie den Server, nur Erfolg/Fehlschlag geht an den Client. Im Frontend: `omsorgapp/src/modules/debug/DebugSessionsPage.jsx`, gleicher Rechteschutz wie die Sessions-Verwaltung dort. +**Fehlerbehandlung bei SMTP-Fehlschlag (seit 2026-08-10):** `SmtpEmailSender.SendAsync` selbst hat bewusst **kein** try/catch (MailKit-Exceptions sollen bis zum Aufrufer durchschlagen, nicht dort verschluckt werden) — die Behandlung passiert an den beiden Stellen, wo der Versand tatsächlich ausgelöst wird: +- `AuthController.ForgotPasswordRequest` fängt eine fehlgeschlagene `IDomainEventDispatcher.DispatchAsync(PasswordResetRequestedEvent)` ab (loggt via `ILogger`) und antwortet mit `ForgotPasswordRequestResponse("email_unavailable")` statt `"sent"` — der Reset-Code selbst wurde da schon in der DB angelegt (`PasswordResetService.RequestResetAsync`), nur die Mail kam nicht raus. **Kein zusätzliches Enumeration-Risiko** ggü. dem Status quo: `"sent"` vs. `"cannot_reset"` unterscheidet bereits heute, ob der Username existiert — `"email_unavailable"` ist nur ein dritter, ebenso ehrlicher Zustand für "Username existiert, aber die Mail-Infrastruktur ist gerade kaputt". Beide Frontends müssen `"email_unavailable"` separat von `"sent"` behandeln (nicht zum PIN-Eingabe-Schritt weitergehen) — umgesetzt in `omsorgapp/src/modules/auth/ForgotPasswordUsernamePage.jsx` und `omsorgWeb/mitarbeiter-app/pages/forgot-password.php`. +- `AdminEmailController.TestSend` fängt den Fehler ebenfalls (loggt via `ILogger`) und antwortet `502` mit `{ error: "send_failed", message: }` statt eines rohen, unbehandelten `500` — die Exception-Message darf hier raus (nur `UserManagement`/`Edit`-Admins erreichen den Endpoint), sie enthält keine Zugangsdaten, nur Diagnoseinfos wie "Authentication failed"/"Connection refused". `DebugSessionsPage.jsx` zeigt `result.data.message` direkt an. + ## Datenbank - Connection-String-Key: `ConnectionStrings:OmsorgCore` (Format `Host=...;Port=5432;Database=omsorg_core;Username=...;Password=...`). - `dotnet-ef` ist als lokales Tool eingerichtet (`.config/dotnet-tools.json`) — vor erster Nutzung `dotnet tool restore`. Wird nur noch zum **Erzeugen** neuer Migrationen gebraucht (`dotnet ef migrations add ...`), nicht mehr zum Anwenden. - **`Program.cs` ruft bei jedem Start `db.Database.MigrateAsync()` auf, in jeder Umgebung** (nicht nur Development) — ausstehende Migrationen werden automatisch angewendet, bevor der Server Requests annimmt. Ein manuelles `dotnet ef database update` ist dadurch nur noch zum gezielten Vorab-Prüfen/Debuggen einer Migration nötig, nicht mehr für den normalen Start/Deploy. Schlägt die Migration fehl, crasht der Start bewusst fatal (fail-fast) statt mit einem veralteten Schema weiterzulaufen. -- Migrationen `InitialCreate`, `AddRefreshTokens`, `AddUserSecurityStamp`, `AddEmployeeContactFieldConstraints` und `AddAuditableSoftDelete` existieren (`src/OmsorgCore.Infrastructure/Persistence/Migrations/`) und wurden erfolgreich gegen eine echte PostgreSQL-Instanz angewendet. `AddContractDetailsAndQueryFilter` ist erzeugt, aber noch nicht gegen eine echte Instanz verifiziert (wird beim nächsten API-Start automatisch angewendet). +- Migrationen `InitialCreate`, `AddRefreshTokens`, `AddUserSecurityStamp`, `AddEmployeeContactFieldConstraints` und `AddAuditableSoftDelete` existieren (`src/OmsorgCore.Infrastructure/Persistence/Migrations/`) und wurden erfolgreich gegen eine echte PostgreSQL-Instanz angewendet. `AddContractDetailsAndQueryFilter` ist erzeugt, aber noch nicht gegen eine echte Instanz verifiziert (wird beim nächsten API-Start automatisch angewendet). `AddPermissionScope` (fügt `Scope` auf `role_permissions`/`user_permission_overrides` hinzu, siehe "Rechtesystem") wurde per `dotnet ef database update` erfolgreich gegen die echte Instanz angewendet. `AddFacilityConditionsAndQualificationRates` (Konditionen-Felder auf `Facility` + Tabelle `facility_qualification_rates`, FR-EIN-4), `AddAbsences` (Tabelle `absences`, FR-CON-1/FR-EM-3) und `AddTimeEntryStatusAndSurchargeHours` (`TimeEntry.StatusId`+Zuschlagsstunden, `ValueListItem.IsEditableByOwner`, `ValueListItemTransition.RequiresApproval`, FR-ZE-1/FR-ZE-2) wurden beim automatischen API-Start erfolgreich gegen die echte Instanz angewendet. ## Build- und Run-Befehle @@ -174,31 +190,93 @@ ASPNETCORE_ENVIRONMENT=Development dotnet run --project src/OmsorgCore.Api - `omsorgapp`s `authClient.cjs` erfolgreich gegen den laufenden Server getestet (Login/Refresh/Logout-Fehlerfälle). - **Kompletter Login-Flow end-to-end mit echtem Postgres verifiziert:** Login mit `admin`/`abersicher` (Seed) → gültiges Token-Paar; `refresh` rotiert korrekt (neues Paar, alter Refresh-Token danach 401 bei Wiederverwendung); `GET /api/employees` mit frischem Access-Token → 200 (Administrator-Rolle hat volle Rechte über den Seed). - **Session-Killswitch end-to-end verifiziert:** `GET /api/admin/sessions` liefert aktive Sessions; `POST /api/admin/sessions/revoke-all` → 204, danach liefert **derselbe, zuvor gültige Access-Token sofort 401** (nicht erst nach Ablauf) und der zugehörige Refresh-Token liefert bei `POST /api/auth/refresh` ebenfalls 401. Erneuter Login mit `admin`/`abersicher` funktioniert danach wieder normal. -- `DbSeeder.SeedBaseRolesAsync` gegen echte PostgreSQL-Instanz verifiziert: legt `Geschäftsführung`/`Disposition/Buchhaltung`/`Recruiting`/`Außendienst` mit der erwarteten Rechteanzahl an (54/33/10/0 Permissions), zweiter Lauf verändert nichts (idempotent pro Rollenname). +- `DbSeeder.SeedBaseRolesAsync` gegen echte PostgreSQL-Instanz verifiziert: legt `Geschäftsführung`/`Disposition/Buchhaltung`/`Recruiting`/`Außendienst` mit der erwarteten Rechteanzahl an (54/33/10/0 Permissions), zweiter Lauf verändert nichts (idempotent pro Rollenname). **Hinweis:** Seit `AddPermissionScope` bekommt `Außendienst` neu `Employees.View`+`Contracts.View` (Scope `Own`) — die Zahl "0" für Außendienst ist damit veraltet (jetzt 2 erwartet), aber noch nicht erneut per echtem Seed-Lauf verifiziert (der Seed läuft nur bei leerer `Roles`-Tabelle bzw. pro fehlendem Rollennamen, nicht erneut gegen eine bereits befüllte Instanz). ## Konfigurierbare Auswahllisten Dropdown-Werte, die früher als hartcodierte Arrays im `omsorgapp`-Frontend lebten (Mitarbeiterstatus, Beschäftigungsart, CRM-Status, Einrichtungstyp) plus die entsprechenden, bisher nur als freier String modellierten Felder auf `Contract` (Vertragstyp/-status) und der Auftragsstatus (FR-EM-2) sind jetzt eine gemeinsame, admin-editierbare Stammdaten-Struktur statt Enum/hartcodiertes Array — Ziel: Löschen/Umbenennen/Hinzufügen ohne Code-Deploy, über die "Status-Verwaltung" unter "Einstellungen" in `omsorgapp`. -**Datenmodell** (`src/OmsorgCore.Domain/Entities/`): `ValueList` (Stammdaten einer Liste — `Key`, eindeutig, z. B. `"EmployeeStatus"`, `"EmploymentType"`, `"CrmStatus"`, `"FacilityType"`, `"ContractType"`, `"ContractStatus"`, `"OrderStatus"`; `DisplayName` für die Admin-UI) + `ValueListItem` (`Value`, `SortOrder`, `IsDefault`, `IsInitial`/`IsTerminal` — die letzten beiden nur für `"OrderStatus"` relevant) + `ValueListItemTransition` (erlaubte Übergänge zwischen zwei Items derselben Liste, wird nur für `"OrderStatus"` befüllt). Ersetzt das frühere `OrderStatusDefinition`/`OrderStatusTransition`-Sondermodell — Migration `ReplaceOrderStatusWithValueLists` übernimmt bestehende Auftragsstatus-Zeilen 1:1 mit identischen Ids in die neuen Tabellen, damit `Order.StatusId` unverändert gültig bleibt. +**Datenmodell** (`src/OmsorgCore.Domain/Entities/`): `ValueList` (Stammdaten einer Liste — `Key`, eindeutig, z. B. `"EmployeeStatus"`, `"EmploymentType"`, `"CrmStatus"`, `"FacilityType"`, `"ContractType"`, `"ContractStatus"`, `"OrderStatus"`, `"DocumentCategory"`, `"Qualification"`, `"ShiftType"`, `"Priority"`, `"BillingInterval"`, `"AbsenceType"`, `"AbsenceStatus"`; `DisplayName` für die Admin-UI) + `ValueListItem` (`Value`, `SortOrder`, `IsDefault`, `IsInitial`/`IsTerminal` — `IsTerminal` nur für `"OrderStatus"` relevant, `IsInitial` zusätzlich für `"AbsenceStatus"` (markiert dort den "noch nicht entschieden"-Zustand, siehe "Abwesenheits-/Urlaubs-/Krankmeldungsanträge" unten)) + `ValueListItemTransition` (erlaubte Übergänge zwischen zwei Items derselben Liste, wird nur für `"OrderStatus"` befüllt). Ersetzt das frühere `OrderStatusDefinition`/`OrderStatusTransition`-Sondermodell — Migration `ReplaceOrderStatusWithValueLists` übernimmt bestehende Auftragsstatus-Zeilen 1:1 mit identischen Ids in die neuen Tabellen, damit `Order.StatusId` unverändert gültig bleibt. **Wo welches Feld referenziert wird:** - `Order.StatusId` (FK, echte Fremdschlüsselbeziehung auf `ValueListItem.Id`) — einzige Liste mit Übergangsregeln. `OrderService.CreateAsync`/`UpdateAsync` nutzen `IValueListRepository.GetInitialItemAsync("OrderStatus", ...)`/`CanTransitionAsync(...)` genau wie zuvor `IOrderStatusRepository`. - `Employee.Status`/`EmploymentType`, `Facility.CrmStatus`/`FacilityType`, `Contract.ContractType`/`Status` bleiben bewusst einfache `string`-Spalten (kein FK, keine Schema-Migration auf diesen Tabellen nötig) — stattdessen prüfen `EmployeesController`/`FacilitiesController`/`ContractsController` beim Schreiben serverseitig über `IValueListRepository.GetActiveValuesAsync(key, ...)`, dass der übergebene Wert unter den aktuell konfigurierten Werten der zugehörigen Liste ist (`400` sonst) — analog zur bereits bestehenden Passwort-Policy-Validierung. +- `Employee.Qualification`, `Order.RequiredQualification` **und** `FacilityQualificationRate.Qualification` (siehe "Konditionen einer Einrichtung" unten) referenzieren alle drei dieselbe Liste `"Qualification"` (ebenfalls einfache `string`-Spalten, gegen `GetActiveValuesAsync`/`GetItemsAsync` validiert wie oben — `EmployeesController`/`OrdersController`/`FacilityQualificationRatesController`). Bewusst eine gemeinsame Liste statt getrennter, damit "benötigte Qualifikation" auf einem Auftrag, "Qualifikation" eines Mitarbeiters und der qualifikationsabhängige Preis einer Einrichtung aus derselben, gleich sortierten Werteliste kommen — Voraussetzung für einen künftigen automatisierten Abgleich (FR-EM-3, Mitarbeiterzuweisung prüft Qualifikation, noch nicht umgesetzt). `SortOrder` bildet dabei die Rangfolge der Qualifikationsniveaus ab (aufsteigend, siehe Seed unten) — es gibt bewusst kein zusätzliches "Level"-Feld, `SortOrder` übernimmt diese Rolle bereits für jede Liste. Da der Wert von mehreren Entitäten referenziert wird, prüft **eine eigene** `IValueListUsageChecker`-Implementierung (`QualificationValueListUsageChecker`, nicht der generische `StringFieldValueListUsageChecker`) alle drei Tabellen — `ValueListService.FindUsagesAsync` befragt pro Key nur den ersten registrierten Checker, separate Registrierungen mit demselben Key hätten weitere Verwendungsstellen beim Löschschutz stillschweigend ignoriert. +- `Order.ShiftType` referenziert die Liste `"ShiftType"` (nur eine Entität betroffen, daher regulärer `StringFieldValueListUsageChecker` wie bei den übrigen einfachen String-Listen). +- `Order.Priority` (Pflichtfeld, kein `?`) referenziert die Liste `"Priority"` (Niedrig/Normal/Hoch/Dringend, Default "Normal" passend zum Entity-Default). `GET /api/orders` unterstützt zusätzlich `?priority=`/`?requiredQualification=`/`?shiftType=` als exakte Gleichheitsfilter (neben den bereits bestehenden `?statusId=`/`?facilityId=`, `OrderRepository.GetPagedAsync`) — **Achtung:** ein neuer Query-Parameter auf einem bestehenden Endpoint ändert die OpenAPI-Spec genauso wie ein geändertes Contract-DTO, `omsorgapp/api-client-ts` muss danach ebenfalls neu generiert werden (siehe "Generierte API-Clients" unten), sonst kennt der generierte `OrdersApi.apiOrdersGetRaw` den Parameter nicht und die Filterung wirkt sich nicht aus, obwohl Backend und Frontend-Code beide "richtig" aussehen. +- `Facility.BillingInterval` referenziert die Liste `"BillingInterval"` (Wöchentlich/Monatlich/Quartalsweise, nur eine Entität betroffen, regulärer `StringFieldValueListUsageChecker`), Teil der Konditionen einer Einrichtung — siehe eigener Abschnitt "Konditionen einer Einrichtung" unten. +- `Absence.Type`/`Status` referenzieren `"AbsenceType"` (Urlaub/Krankmeldung/Sonstige) bzw. `"AbsenceStatus"` (Eingereicht/Genehmigt/Abgelehnt) — beide einfache `string`-Spalten wie `Contract.Status`, **ohne** `ValueListItemTransition` (kein Übergangsgraph nötig), aber mit `IsInitial` auf dem "Eingereicht"-Item, damit "noch nicht entschieden" nicht als Anzeigetext-Vergleich hartkodiert werden muss (siehe eigener Abschnitt "Abwesenheits-/Urlaubs-/Krankmeldungsanträge" unten). -**Verwaltungs-API** (`ValueListsController`, Route `api/value-lists`): `GET /api/value-lists` (alle Listen), `GET /api/value-lists/{key}/items` (nur `[Authorize]`, kein Modul-Recht — die aufrufenden Formulare gehören zu unterschiedlichen Modulen), `POST`/`PUT/DELETE .../items[/...]` sowie `GET/PUT .../transitions` (nur für `"OrderStatus"`) gegated über `[RequirePermission(ModuleType.UserManagement, PermissionAction.Edit)]` — dieselbe Admin-Berechtigung wie die übrige "Einstellungen"-Seite. +**Verwaltungs-API** (`ValueListsController`, Route `api/value-lists`): `GET /api/value-lists` (alle Listen), `GET /api/value-lists/{key}/items` (nur `[Authorize]`, kein Modul-Recht — die aufrufenden Formulare gehören zu unterschiedlichen Modulen), `POST`/`PUT/DELETE .../items[/...]` sowie `GET/PUT .../transitions` (nur für `"OrderStatus"`) gegated über `[RequirePermission(ModuleType.Configuration, PermissionAction.Edit)]` — ein eigenes Admin-Recht, getrennt von Benutzer-/Rechteverwaltung (siehe "Rechtesystem" oben, Abschnitt "Drei getrennte Admin-Rechte"). **Löschschutz ("erst überall entfernen"):** `ValueListService.DeleteItemAsync` löscht ein `ValueListItem` nur, wenn keine Verwendung mehr existiert. Eine `IValueListUsageChecker`-Implementierung je Liste (`Infrastructure/Repositories/StringFieldValueListUsageChecker.cs` — eine generische Klasse für alle String-Feld-Listen, mehrfach mit unterschiedlicher Query registriert in `Infrastructure/DependencyInjection.cs`; `OrderStatusValueListUsageChecker.cs` für die FK-basierte `"OrderStatus"`-Liste inkl. Übergangsregeln) prüft, ob der Wert noch irgendwo gesetzt ist. Bei Treffern liefert `DELETE .../items/{id}` `409` mit den Fundstellen (`EntityType`/`EntityId`/`DisplayLabel`) im Body, statt zu löschen. `GET .../items/{id}/usages` liefert dieselbe Prüfung jederzeit (nicht nur beim Löschversuch) — für den "wo wird das noch verwendet"-Info-Button in der UI. -**Seed:** `DbSeeder.SeedValueListsAsync` (jede Umgebung, idempotent — läuft nur, solange `ValueLists` leer ist) legt alle sieben Listen mit Startwerten an, inkl. der Auftragsstatus-Pipeline (Anfrage → Prüfung → offen → teilweise besetzt → vollständig besetzt → aktiv → abgeschlossen, plus Storno aus jedem nicht-terminalen Status) samt Übergangsregeln. **Wichtig:** Kommt eine neue admin-editierbare Auswahlliste hinzu, gehört sie hier als weiterer `SeedSimpleListAsync`-Aufruf rein plus eine `IValueListUsageChecker`-Registrierung in `Infrastructure/DependencyInjection.cs` — dieser Abschnitt und der Seed müssen mit der Software mitwachsen. +**Seed:** `DbSeeder.SeedValueListsAsync` (jede Umgebung, idempotent — läuft nur, solange `ValueLists` leer ist) legt alle zwölf Listen mit Startwerten an, inkl. der Auftragsstatus-Pipeline (Anfrage → Prüfung → offen → teilweise besetzt → vollständig besetzt → aktiv → abgeschlossen, plus Storno aus jedem nicht-terminalen Status) samt Übergangsregeln, sowie `"Qualification"` mit sieben Startwerten in aufsteigender Rangfolge (Ungelernte Kraft → Betreuungskraft → Pflegehelfer/in → Pflegefachassistent/in → Altenpfleger/in → Gesundheits- und Krankenpfleger/in → Pflegefachkraft mit Leitungsfunktion), `"ShiftType"` mit sechs Startwerten (Frühdienst, Spätdienst, Nachtdienst, Tagdienst, Bereitschaftsdienst, Sonstige), `"Priority"` mit vier Startwerten (Niedrig, Normal, Hoch, Dringend) und `"BillingInterval"` mit drei Startwerten (Wöchentlich, Monatlich (Default), Quartalsweise) — reine Startbefüllung, über "Status-Verwaltung" admin-editierbar. **Wichtig:** Kommt eine neue admin-editierbare Auswahlliste hinzu, gehört sie hier als weiterer `SeedSimpleListAsync`-Aufruf rein plus eine `IValueListUsageChecker`-Registrierung in `Infrastructure/DependencyInjection.cs` — dieser Abschnitt und der Seed müssen mit der Software mitwachsen. -**Frontend (`omsorgapp`):** `electron/backend/valueListsClient.cjs` kapselt `/api/value-lists`, `src/app/useValueListItems.js` (Hook) lädt die Items einer Liste für Dropdowns (ersetzt die früheren hartcodierten Arrays in `EmployeeForm.jsx`/`FacilityForm.jsx`/`EmployeesPage.jsx`/`FacilitiesPage.jsx`). Verwaltungs-UI: `src/modules/settings/StatusManagementPanel.jsx`, dritter Tab ("Status-Verwaltung") in `SettingsPage.jsx`, gegated wie Rollen/Benutzerrechte über `hasPermission("UserManagement", ...)`. +**Frontend (`omsorgapp`):** `src/api/valueListsApi.js` kapselt `/api/value-lists`, `src/app/useValueListItems.js` (Hook) lädt die Items einer Liste für Dropdowns (ersetzt die früheren hartcodierten Arrays in `EmployeeForm.jsx`/`FacilityForm.jsx`/`EmployeesPage.jsx`/`FacilitiesPage.jsx`). Verwaltungs-UI: `src/modules/settings/StatusManagementPanel.jsx`, Tab "Status-Verwaltung" in `SettingsPage.jsx`, gegated über `hasPermission("Configuration", ...)`. + +## Dokumentenarchiv (FR-MA-3) + +`Document` (`src/OmsorgCore.Domain/Entities/Document.cs`, `AuditableEntity`) referenziert eine beliebige Kern-Entität polymorph über `EntityType` (String-Wert aus `DocumentEntityType`, aktuell nur `Employee` mit echtem Upload-/Validierungspfad — `Facility`/`Contract`/`Order` sind als Enum-Werte für ein künftiges Ausrollen vorgesehen, ohne Migration nachzuziehen) + `EntityId` (kein FK, da mehrere Zieltabellen). `Category` wird wie `Employee.Status`/`Contract.ContractType` gegen die admin-editierbare ValueList `"DocumentCategory"` validiert (siehe "Konfigurierbare Auswahllisten"). + +**Bytes liegen auf dem Dateisystem, nicht als Blob in Postgres:** `IDocumentStorage`/`FileSystemDocumentStorage` (`Infrastructure/Storage/`) legt Uploads unter `Storage:DocumentsRootPath/{EntityType}/{EntityId}/{DocumentId}{Extension}` ab (Guids im Pfad, kein Client-String — kein Path-Traversal-Vektor); die DB speichert nur den relativen Pfad (`Document.StorageKey`) + Metadaten. Größen-/Dateityp-Grenzen (`Storage:MaxDocumentSizeBytes`/`Storage:AllowedDocumentContentTypes`, siehe `CONFIGURATION.md`) prüft `IDocumentUploadPolicy`/`DocumentUploadPolicy`, injiziert in `DocumentService.UploadAsync` — analog zu `IPasswordPolicy`. + +**API** (`DocumentsController`, Route `api/documents`, gegated über `[RequirePermission(ModuleType.Documents, ...)]`): `GET ?entityType=&entityId=` (Liste), `POST` (multipart/form-data, `UploadDocumentRequest` mit `IFormFile`; bei `EntityType=Employee` prüft der Controller zusätzlich per `IEmployeeRepository`, dass der Mitarbeiter existiert — analog zur `FacilityContactId`-Cross-Validierung in `OrdersController`), `PUT /{id}` (nur Metadaten — `Category`/`Description`/`FileName`, JSON-Body, kein erneuter Datei-Upload — `PermissionAction.Edit`), `GET /{id}/download` (streamt die Datei), `DELETE /{id}` (Soft-Delete). + +**Zugriffsprotokoll (FR-MA-3-Anforderung):** Der automatische `AuditSaveChangesInterceptor` erfasst nur Create/Update/(Soft-)Delete an `Document`, nicht das lesende Herunterladen. `DocumentsController.Download` dispatcht deshalb zusätzlich ein `AuditEvent(..., "DocumentDownloaded", ...)` über `IDomainEventDispatcher` — derselbe Mechanismus wie Login/Logout/Session-Kill (siehe "Audit-Log" oben). Jeder Download ist damit über `GET /api/audit-log` nachvollziehbar. + +**Rechtematrix:** `ModuleType.Documents` ist Teil der Basis-Rollen-Seed (`DbSeeder.SeedBaseRolesAsync`) — `Geschäftsführung` und `Disposition/Buchhaltung` (Sabrina) haben vollen Zugriff (FR-MA-3: Sabina/Malik/Sabrina), `Recruiting`/`Außendienst` bewusst nicht. Ein Außendienst-Selbstzugriff ("nur eigene Dokumente") ist noch nicht abgebildet, weil OMSORG Connect noch nicht gegen dieses Backend spricht (siehe "Offene Punkte"). + +## Konditionen einer Einrichtung (FR-EIN-4) + +Elf der zwölf Blueprint-19.2-Konditionsfelder (Verrechnungssatz, vier Zuschläge, Fahrtkosten, Mindeststunden, Pausenregelung, Abrechnungsintervall, Zahlungsziel, individuelle Vereinbarungen) sind flache, nullable Felder direkt auf `Facility` — kein `OwnsOne`/keine eigene Tabelle, analog zu Adresse/Rechnungsadresse auf `Facility` selbst und den Finanzfeldern auf `Contract`: + +- `BillingRate` (Verrechnungssatz, EUR/Std.), `NightSurchargePercent`/`SaturdaySurchargePercent`/`SundaySurchargePercent`/`HolidaySurchargePercent` (Zuschläge als **Prozent** auf den Verrechnungssatz, nicht als EUR-Betrag), `TravelCostRate` (Fahrtkosten als **Pauschale je Einsatz**, kein km-Modell), `MinimumHours` (Mindeststunden je Einsatz), `BreakPolicy` (Pausenregelung, Freitext), `PaymentTermDays` (Zahlungsziel in Tagen), `IndividualAgreements` (Freitext). +- `BillingInterval` wird wie `FacilityType`/`ContractType` gegen die admin-editierbare `ValueList` `"BillingInterval"` (Wöchentlich/Monatlich/Quartalsweise) validiert — siehe "Konfigurierbare Auswahllisten". +- Alle elf Felder sind nur über `PUT /api/facilities/{id}` (`UpdateFacilityRequest`) setzbar, nicht beim Anlegen (`CreateFacilityRequest`) — analog zu `CrmStatus`, der ebenfalls erst nach dem Anlegen über "Bearbeiten" gepflegt wird. + +**Ausnahme "Qualifikationsabhängige Preise":** eine variable Liste (ein Satz je Qualifikationsstufe) lässt sich nicht als feste Spaltengruppe abbilden — dafür die neue Entität `FacilityQualificationRate` (1:n zu `Facility`, `Qualification` gegen die ValueList `"Qualification"` validiert — dieselbe Liste wie `Employee.Qualification`/`Order.RequiredQualification`, siehe "Wo welches Feld referenziert wird" oben) als 1:n-Unterressource unter `GET/POST/PUT/DELETE /api/facilities/{facilityId}/qualification-rates[/...]` (`FacilityQualificationRatesController`) — exakt nach dem Muster von `FacilityContact`, kein eigener `ModuleType`, gegated über dieselben `Facilities`-Rechte. Löschen ist Soft-Delete, über `TrashController` (`api/trash/facility-qualification-rates/...`) wiederherstellbar. + +FR-EIN-4s Akzeptanzkriterium ("Konditionssatz ist Grundlage für Rechnungserstellung") ist damit nur zur Hälfte erfüllt — dieser Schritt legt ausschließlich die Datenbasis, `FR-RE-1`/`Invoice` (weiterhin ⬜) konsumiert die Konditionen noch nicht. + +## Abwesenheits-/Urlaubs-/Krankmeldungsanträge (FR-CON-1, Datenbasis für FR-EM-3) + +`Absence` (`src/OmsorgCore.Domain/Entities/Absence.cs`, `AuditableEntity`, first-class Core-Objekt wie `Contract`/`Order`, keine Unterressource) ersetzt die frühere Insellösung aus `omsorgWeb/mitarbeiter-app-legacy` (dort zwei getrennte MySQL-Tabellen `requests_urlaubsantrag`/`requests_abwesenheitsantrag`, verknüpft an lokale `user_id` statt an `omsorgCore`-IDs). Ein gemeinsames Objekt statt zwei getrennter Stacks, weil bis auf die Art alle Felder identisch sind (Root-`CLAUDE.md`: "jede Information wird nur einmal gespeichert"): + +- `EmployeeId` (Pflicht, FK auf `Employee`), `Type` (gegen `ValueList "AbsenceType"`: Urlaub/Krankmeldung/Sonstige), `StartDate`/`EndDate` (`DateOnly`, `EndDate >= StartDate`), `Reason`/`Substitute`/`Note` (alle optionaler Freitext), `Status` (gegen `ValueList "AbsenceStatus"`: Eingereicht/Genehmigt/Abgelehnt), `AdminNote` (optionaler Freitext der entscheidenden Büro-Rolle). `Absence.Status` selbst hat **keinen** hartkodierten C#-Default (anders als z. B. `Contract.Status = "Entwurf"`) — der initiale Wert wird in `AbsenceService.CreateAsync` zur Laufzeit aus `GetInitialItemAsync("AbsenceStatus", ...)` gelesen (`DbSeeder` markiert "Eingereicht" dafür mit `IsInitial = true`, idempotent nachgezogen über `SeedAbsenceStatusInitialFlagIfMissingAsync` auch für bereits existierende Umgebungen). Grund: würde man `"Eingereicht"` als String an mehreren Stellen (Entity-Default, `UpdateAsync`-Prüfung, Decision-Endpoint, beide Frontends) fest verdrahten, würde ein Umbenennen dieses Werts über die Status-Verwaltung die Logik lautlos brechen, ohne dass Backend oder Frontend einen Fehler zeigen — exakt das Muster, das schon bei `Order.RequiredQualification` (ValueList-Referenz statt Enum) und `TriggersFollowUp` bei `CrmStatus` vermieden wird. +- **Kein `ValueListItemTransition`** für `AbsenceStatus` (anders als `OrderStatus`) — wer wohin darf, ergibt sich vollständig aus Rechten: Außendienst hat nur `Create`/`View`/`Edit` (kann also nur im initialen Status anlegen/bearbeiten, keinen bereits entschiedenen Antrag ändern), Büro-Rollen haben zusätzlich `Approve` für den eigenen Entscheidungs-Endpoint (siehe unten) und `Delete`/`Recover`. `IAbsenceService.GetInitialStatusValueAsync(...)` ist der einzige Ort, der den initialen Statuswert auflöst — `AbsenceService.UpdateAsync` und `AbsencesController.Decide` nutzen ihn beide, statt jeweils eigene String-Vergleiche zu pflegen. +- `ModuleType.Absences` (neuer, additiv angehängter Enum-Wert) ist das erste Modul außer `Employees`/`Contracts`, das `PermissionScope.Own` tatsächlich auswertet (`Absence.EmployeeId` als Ownership-Anker, siehe "Datenebenen-Scope") — `AbsenceService` folgt exakt dem `EmployeeService`/`ContractService`-Muster (`ResolveOwnScopeRestrictionAsync`), mit einer Ergänzung: `CreateAsync` überschreibt bei Own-Scope die `EmployeeId` **immer** serverseitig aus `ICurrentUserService.EmployeeId` (JWT-Claim) und ignoriert einen ggf. vom Client mitgeschickten Wert komplett — kein Client-Vertrauen darauf, wer die eigene Mitarbeiter-Id ist. `CreateAbsenceRequest` hat bewusst **kein** `employeeId`-Feld ("im Namen von" anlegen ist nicht Teil dieser ersten UI). + **Own-Scope ist dabei nur eine Sichtbarkeits-/Anlege-Einschränkung, kein Ausschlusskriterium für Selbstanträge (korrigiert 2026-08-10, ursprünglich zu eng):** auch All-Scope-Aufrufer (Büro-Rollen, `Administrator`) haben oft eine eigene verknüpfte `Employee` und wollen für sich selbst einen Antrag stellen können — `CreateAsync` fällt für sie deshalb ebenfalls auf `ICurrentUserService.EmployeeId` zurück (nicht nur bei Own-Scope), und lehnt nur ab (`400`, ohne die Exception bis zur `employees`-FK-Constraint durchzureichen wie beim ursprünglichen Vorfall), wenn wirklich **kein** Mitarbeiter mit dem aufrufenden `User` verknüpft ist. `POST /api/absences` funktioniert damit für jeden angemeldeten Nutzer mit verknüpftem Mitarbeiter, unabhängig von Rolle/Scope — nur die *Sichtbarkeit* anderer Anträge (`GET /api/absences`) bleibt weiterhin durch Own/All eingeschränkt. +- **`AbsencesController`** (Route `api/absences`): `GET`/`GET/{id}` (`View`), `POST` (`Create`, `CreateAbsenceRequest` ohne `EmployeeId`-Feld — wird immer serverseitig gesetzt), `PUT /{id}` (`Edit`, `UpdateAbsenceRequest { type, startDate, endDate, reason, substitute, note }` — **nur solange der Status noch der initiale ist** (`IAbsenceService.GetInitialStatusValueAsync`, nicht der Literal `"Eingereicht"`), sonst `400`; `AbsenceService.UpdateAsync`/`UpdateAbsenceResult` prüft das serverseitig, nicht nur im UI. Own-Scope-Aufrufer dürfen dabei nur eigene Anträge bearbeiten, wie bei `GetByIdAsync` — ein fremder Own-Scope-Antrag liefert `404`, nicht `403`, um dessen Existenz nicht zu verraten), `POST /{id}/decision` (`Approve`, `AbsenceDecisionRequest { status, adminNote }`, `status` muss ein Wert aus `"AbsenceStatus"` sein, der **nicht** der initiale ist — **anders als `Update` gibt es hier bewusst keine Prüfung des aktuellen Status**, eine Entscheidung ist jederzeit erneut änderbar (Korrektur einer versehentlichen Genehmigung/Ablehnung), `AbsenceService.DecideAsync` überschreibt `Status`/`AdminNote` unabhängig vom bisherigen Wert), `DELETE` (`Delete`, Soft-Delete, über `TrashController` (`api/trash/absences/...`) wiederherstellbar). `PermissionAction.Edit` ist Teil des Außendienst-Basis-Rollen-Seeds (`Absences.{Create,View,Edit}`, `PermissionScope.Own`) — jeder darf also nur den eigenen, noch nicht entschiedenen Antrag ändern, nicht fremde. +- **Basis-Rollen-Seed:** Geschäftsführung automatisch (generische Schleife), `Disposition/Buchhaltung` bekommt `Absences` mit allen Aktionen/`Scope.All`, `Außendienst` bekommt `Absences.{Create,View}` mit `Scope.Own` (`DbSeeder.SeedBaseRolesAsync`). +- **Verwendung von `omsorgWeb/mitarbeiter-app`:** der Außendienst stellt Anträge über `pages/urlaubsantrag.php` (neue Seite in der Connect-Neuauflage, nicht in der Legacy-App) gegen genau diesen Endpoint — siehe `omsorgWeb/CLAUDE.md`. `omsorgapp` bekommt die Prüfen/Genehmigen-Seite (`AbsencesPage`/`AbsenceDetailPanel`, Sidebar-Tab "Abwesenheiten") — siehe `omsorgapp/CLAUDE.md`. +- **Bewusst nicht Teil dieses Schritts:** kein Konsum dieser Daten in FR-EM-3 (Verfügbarkeitsprüfung bei Zuweisung) — dieser Schritt legt nur die Datenbasis, analog zu FR-EIN-4/`FR-RE-1` oben. Keine Migration/Portierung der alten Legacy-Anträge aus `mitarbeiter-app-legacy`s MySQL. + +## Zeiterfassung (FR-ZE-1/FR-ZE-2) + +`TimeEntry` (`src/OmsorgCore.Domain/Entities/TimeEntry.cs`, `AuditableEntity`, first-class Core-Objekt wie `Order`/`Absence`) bildet die geleistete Arbeitszeit einer Schicht ab: `EmployeeId`/`OrderId` (Pflicht-FKs), `Date`/`Start`/`End`/`BreakDuration`, vier manuell erfasste Zuschlagsstunden-Felder (`NightHours`/`SaturdayHours`/`SundayHours`/`HolidayHours`, Blueprint 19.5 listet sie als "Erfasste Daten" — keine automatische Berechnung aus Beginn/Ende, das wäre ein Nachtfenster-/Feiertagskalender-Feature, das es aktuell nicht gibt), `StatusId` (FK auf `ValueListItem`, wie `Order.StatusId`), `AdminNote`. **Einrichtung wird nicht redundant gespeichert** — sie ergibt sich über `Order.FacilityId`, `TimeEntryResponse` löst sie nur für die Anzeige mit auf (Root-`CLAUDE.md`-Prinzip "jede Information nur einmal speichern"). + +**Statuspipeline (FR-ZE-2) — Hybrid aus dem Order- und dem Absence-Muster:** Anders als `Absence` (binäre Entscheidung, kein Übergangsgraph) braucht `TimeEntry` eine echte Mehrstufen-Pipeline (Entwurf → Eingereicht → Prüfung → Rückfrage → Freigegeben → Abgerechnet), dafür wird das bestehende `OrderStatus`-Muster (`ValueListItemTransition`-Graph, `CanTransitionAsync`) wiederverwendet. Zwei zusätzliche, generische Flags lösen die Frage "wer darf welche Kante auslösen", ohne pro Endpoint eigene Statuslisten zu pflegen: +- `ValueListItem.IsEditableByOwner` (neues, listenspezifisches Flag analog `IsInitial`/`IsTerminal`) — markiert, in welchen Status-Werten der Ersteller den Datensatz noch inhaltlich bearbeiten darf. Für `"TimeEntryStatus"`: `true` auf Entwurf/Eingereicht/Rückfrage, sonst `false`. Für alle anderen Listen bleibt es `false` (keine Verhaltensänderung). +- `ValueListItemTransition.RequiresApproval` (neues Flag, Default `true`) — unterscheidet Selbst-Einreichungs-Kanten (Entwurf→Eingereicht, Rückfrage→Eingereicht, `false`) von Büro-Entscheidungen (alle übrigen Kanten, `true`). `"OrderStatus"`-Transitionen bleiben beim Default `true`, das Feld wird dort schlicht nicht ausgewertet. + +Drei Endpoints statt zwei (Absence hat Edit+Decide, TimeEntry hat Edit+Submit+Decide): +- `PUT /api/time-entries/{id}` (`Edit`) — nur Inhaltsfelder (`Order`/`Date`/`Start`/`End`/`BreakDuration`/Zuschlagsstunden), **kein** `StatusId` im DTO. `TimeEntryService.UpdateAsync` lehnt ab (`400`, `NotEditable`), sobald der aktuelle Status `IsEditableByOwner == false` ist. +- `POST /api/time-entries/{id}/submit` (`Edit`, kein Body) — `TimeEntryService.SubmitAsync` sucht über `IValueListRepository.GetSelfServiceTransitionAsync(statusId)` die eine ausgehende Kante mit `RequiresApproval == false` und wendet sie an; `400` falls keine existiert. Das ist die einzige Möglichkeit für den Ersteller, den Status selbst zu ändern. +- `POST /api/time-entries/{id}/decision` (`Approve`) — `TimeEntryService.DecideAsync` validiert die Ziel-Transition über `CanTransitionAsync` **und** dass sie `RequiresApproval == true` ist (verhindert, dass dieser Endpoint für die Selbst-Einreichungs-Kante missbraucht wird), keine Prüfung des bisherigen Status (wie bei `Absence.DecideAsync` — Büro kann jederzeit erneut entscheiden). + +`ModuleType.TimeEntries` wertet `PermissionScope.Own` aus (`TimeEntry.EmployeeId` als Ownership-Anker, `TimeEntryService` folgt exakt dem `AbsenceService`-Muster inkl. `CreateAsync`-Fallback auf `ICurrentUserService.EmployeeId` für All-Scope-Aufrufer mit eigenem Mitarbeiterbezug). Basis-Rollen-Seed: Geschäftsführung automatisch, Disposition/Buchhaltung alle Aktionen/`Scope.All`, Außendienst `{Create,View,Edit}`/`Scope.Own`, Recruiting kein Zugriff. + +`ValueListsController`s generische `GET/PUT {key}/transitions`-Endpoints funktionieren unverändert für `"TimeEntryStatus"` (keine Sonderbehandlung im Code nötig, siehe "Konfigurierbare Auswahllisten" oben) — `ValueListTransitionResponse` liefert jetzt zusätzlich `RequiresApproval`, damit Clients (siehe `omsorgapp`/`omsorgWeb` unten) die für ihre Rolle relevanten Kanten selbst herausfiltern können. + +**Verwendung:** `omsorgWeb/mitarbeiter-app` (`pages/stundenerfassung.php`) — Außendienst legt/bearbeitet eigene Einträge und löst `submit` aus. `omsorgapp` (`TimeEntriesPage`/`TimeEntryDetailPanel`, Sidebar-Tab "Zeiterfassung") — Büro prüft/entscheidet über `decision`. **Bewusst offen:** `Order` hat keine Mitarbeiter-Zuweisung (FR-EM-3), das Auftrags-Dropdown in beiden Frontends zeigt deshalb alle aktiven Aufträge statt nur zugewiesene. ## Offene Punkte -- `Facility` hat jetzt volles Repository/Service/Controller (`FacilitiesController`, `GET/POST/PUT /api/facilities`) nach dem Employee-Muster, inkl. `FacilityCreatedEvent`. Zusätzlich `FacilityContact` (FR-EIN-2, Ansprechpartner) als 1:n-Unterressource unter `GET/POST /api/facilities/{facilityId}/contacts`, `PUT .../contacts/{id}` (`FacilityContactsController`) — bewusst kein eigener `ModuleType`, sondern über dieselben `Facilities`-Rechte gegated, da Ansprechpartner kein eigenständiges Core-Objekt sind. Kein Delete-Endpoint für Ansprechpartner (konsistent mit dem noch fehlenden Soft-Delete für die übrigen Core-Objekte). -- `Contract` hat jetzt ebenfalls volles Repository/Service/Controller (`ContractsController`, `GET/POST/PUT /api/contracts`, gegated über `[RequirePermission(ModuleType.Contracts, ...)]`) nach demselben Facility-Muster, inkl. `ContractCreatedEvent`. Deckt FR-MA-2 auf Backend-Seite ab: `WeeklyHours` (Arbeitszeit), `HourlyWage` (Stundenlohn), `AllowancesDescription` (Zuschläge, Freitext), `OvertimeRules` (Überstundenregelung, Freitext), `VacationDaysPerYear` (Urlaubsanspruch), `ProbationPeriodMonths` (Probezeit) — alle nullable, da ein Vertrag entweder einem Mitarbeiter oder einer Einrichtung zugeordnet ist (`EmployeeId`/`FacilityId`, mindestens eins muss gesetzt sein, per Controller-Validierung erzwungen) und nicht jeder Vertragstyp alle Felder braucht. `ContractConfiguration` hat jetzt (wie `Facility`) einen `HasQueryFilter(!IsDeleted)`. Kein `omsorgapp`-UI-Modul dafür in diesem Schritt — nur das Backend-CRUD. -- `Order` hat jetzt ebenfalls volles Repository/Service/Controller (`OrdersController`, `GET/POST/PUT /api/orders`, gegated über `[RequirePermission(ModuleType.Orders, ...)]`) nach demselben Facility/Contract-Muster, inkl. `OrderCreatedEvent`. Deckt FR-EM-1 auf Backend-Seite ab: `FacilityContactId` (optionaler Ansprechpartner, gegen `FacilityId` cross-validiert — der Kontakt muss zur angegebenen Einrichtung gehören, sonst `400`), `ShiftType` (Schichtart, Freitext), `RequiredHeadcount` (Anzahl Mitarbeiter, mindestens 1), `Conditions` (Konditionen, Freitext), `Priority` (Priorität, Freitext). Der Auftragsstatus (FR-EM-2, `Order.StatusId`) ist Teil der generischen Auswahllisten — siehe "Konfigurierbare Auswahllisten" unten. `OrderConfiguration` hat jetzt (wie `Facility`/`Contract`) einen `HasQueryFilter(!IsDeleted)`. Kein `omsorgapp`-UI-Modul für Aufträge selbst in diesem Schritt (nur die Statuspflege über "Status-Verwaltung"). `TimeEntry`/`Invoice` haben weiterhin nur Domain-Entitäten + DB-Konfiguration — nächste Schritte folgen demselben Muster (Repository-Interface in Application, Implementierung in Infrastructure, Service in Application, Controller in Api). +- `Facility` hat jetzt volles Repository/Service/Controller (`FacilitiesController`, `GET/POST/PUT/DELETE /api/facilities`) nach dem Employee-Muster, inkl. `FacilityCreatedEvent`. Zusätzlich `FacilityContact` (FR-EIN-2, Ansprechpartner) als 1:n-Unterressource unter `GET/POST/PUT/DELETE /api/facilities/{facilityId}/contacts[/...]` (`FacilityContactsController`) und `FacilityQualificationRate` (FR-EIN-4, qualifikationsabhängige Preise) als 1:n-Unterressource unter `GET/POST/PUT/DELETE /api/facilities/{facilityId}/qualification-rates[/...]` (`FacilityQualificationRatesController`, siehe "Konditionen einer Einrichtung" oben) — beide bewusst kein eigener `ModuleType`, sondern über dieselben `Facilities`-Rechte gegated, da beides kein eigenständiges Core-Objekt ist. Löschen (`Employee`/`Facility`/`Contract`/`Order`/`FacilityContact`/`FacilityQualificationRate`) ist jetzt durchgängig Soft-Delete (`IsDeleted`/`DeletedAt`, `PermissionAction.Delete` je Modul, `DELETE`-Endpoint pro Controller) und über den `TrashController` (`api/trash/...`, `PermissionAction.Recover` je Modul, "Papierkorb"-Seite in `omsorgapp`) wiederherstellbar. +- `Contract` hat jetzt ebenfalls volles Repository/Service/Controller (`ContractsController`, `GET/POST/PUT /api/contracts`, gegated über `[RequirePermission(ModuleType.Contracts, ...)]`) nach demselben Facility-Muster, inkl. `ContractCreatedEvent`. Deckt FR-MA-2 auf Backend-Seite ab: `WeeklyHours` (Arbeitszeit), `HourlyWage` (Stundenlohn), `AllowancesDescription` (Zuschläge, Freitext), `OvertimeRules` (Überstundenregelung, Freitext), `VacationDaysPerYear` (Urlaubsanspruch), `ProbationPeriodMonths` (Probezeit) — alle nullable, da ein Vertrag entweder einem Mitarbeiter oder einer Einrichtung zugeordnet ist (`EmployeeId`/`FacilityId`, mindestens eins muss gesetzt sein, per Controller-Validierung erzwungen) und nicht jeder Vertragstyp alle Felder braucht. `ContractConfiguration` hat jetzt (wie `Facility`) einen `HasQueryFilter(!IsDeleted)`. FR-MA-2 ist damit inkl. `omsorgapp`-UI abgeschlossen: "Verträge"-Tab in `EmployeeDetailPanel` (`ContractsList`/`ContractForm`/`Create-`/`EditContractDialog.jsx`), neue Verträge starten als "Entwurf", Statuswechsel nur im Bearbeiten-Formular, Löschen als Soft-Delete über den Papierkorb wiederherstellbar (siehe `omsorgapp/CLAUDE.md`). +- `Order` hat jetzt ebenfalls volles Repository/Service/Controller (`OrdersController`, `GET/POST/PUT /api/orders`, gegated über `[RequirePermission(ModuleType.Orders, ...)]`) nach demselben Facility/Contract-Muster, inkl. `OrderCreatedEvent`. Deckt FR-EM-1 auf Backend-Seite ab: `FacilityContactId` (optionaler Ansprechpartner, gegen `FacilityId` cross-validiert — der Kontakt muss zur angegebenen Einrichtung gehören, sonst `400`), `ShiftType` (Schichtart, Freitext), `RequiredHeadcount` (Anzahl Mitarbeiter, mindestens 1), `Conditions` (Konditionen, Freitext), `Priority` (Priorität, Freitext). Der Auftragsstatus (FR-EM-2, `Order.StatusId`) ist Teil der generischen Auswahllisten — siehe "Konfigurierbare Auswahllisten" unten. `OrderConfiguration` hat jetzt (wie `Facility`/`Contract`) einen `HasQueryFilter(!IsDeleted)`. Kein `omsorgapp`-UI-Modul für Aufträge selbst in diesem Schritt (nur die Statuspflege über "Status-Verwaltung"). `TimeEntry` hat jetzt ebenfalls volles Repository/Service/Controller (FR-ZE-1/FR-ZE-2, siehe "Zeiterfassung" oben) — `Invoice` hat weiterhin nur Domain-Entität + DB-Konfiguration, nächster Schritt folgt demselben Muster (Repository-Interface in Application, Implementierung in Infrastructure, Service in Application, Controller in Api) und muss FR-ZE-3 (nur freigegebene Zeit fließt ein) auf Domain-/Application-Ebene erzwingen. +- Dokumentenarchiv (FR-MA-3) hat jetzt volles Repository/Service/Controller (`DocumentsController`, `GET/POST /api/documents`, `PUT /{id}` für Metadaten, `GET /{id}/download`, `DELETE /{id}`) — siehe "Dokumentenarchiv" oben. `omsorgapp`-UI ("Dokumente"-Tab in der Personalakte) existiert jetzt ebenfalls, siehe `omsorgapp/CLAUDE.md`. Kein Außendienst-Selbstzugriff, kein physisches Löschen von Dateien beim Soft-Delete/Papierkorb (keine Hard-Purge-Stelle im System, die man konsistent mitziehen müsste). - Keine E-Mail-Verifizierung bei User-Anlage (Passwort-Reset per E-Mail ist fertig, siehe "Passwort-Reset / E-Mail-Versand" oben). - `omsorgapp` spricht seit Kurzem gegen dieses Backend (Login-Screen + Refresh-Token-Session, siehe `omsorgapp/CLAUDE.md`) — `omsorgWeb` ist noch nicht angebunden. - Kein Docker-/CI-Setup. @@ -207,9 +285,9 @@ Dropdown-Werte, die früher als hartcodierte Arrays im `omsorgapp`-Frontend lebt Aus der Swagger/OpenAPI-JSON dieses Backends (`/swagger/v1/swagger.json`, nur im Development-Modus aktiv) werden mit `openapi-generator-cli` typisierte Clients generiert — `omsorgapp/api-client-ts/` (TypeScript, `typescript-fetch`-Template) und `omsorgWeb/mitarbeiter-app/api-client-php/` (PHP). -**Wichtig — `omsorgapp/api-client-ts` ist kein optionales Extra mehr, sondern im echten Datenpfad:** Alle Wrapper unter `omsorgapp/electron/backend/*Client.cjs` (`employeesClient.cjs`, `facilitiesClient.cjs`, `facilityContactsClient.cjs`, `usersClient.cjs`, `rolesClient.cjs`, `valueListsClient.cjs`, `auditLogClient.cjs`, `authClient.cjs`, ...) importieren die jeweilige `*Api`-Klasse aus dem generierten Paket `omsorgcore-client-ts` (`require('omsorgcore-client-ts')`) und reichen Requests/Responses **ungeprüft typisiert** durch. Nur `omsorgWeb/mitarbeiter-app/lib/omsorgCoreClient.php` bleibt tatsächlich unabhängig vom generierten PHP-Client. +**Wichtig — beide generierten Clients sind im echten Datenpfad, nicht optional:** Alle Wrapper unter `omsorgapp/src/api/*Api.js` (`employeesApi.js`, `facilitiesApi.js`, `facilityContactsApi.js`, `facilityQualificationRatesApi.js`, `usersApi.js`, `rolesApi.js`, `valueListsApi.js`, `auditLogApi.js`, `authApi.js`, `absencesApi.js`, `timeEntriesApi.js`, ...) importieren die jeweilige `*Api`-Klasse aus dem generierten Paket `omsorgcore-client-ts` (`import { ... } from "omsorgcore-client-ts"`) und reichen Requests/Responses **ungeprüft typisiert** durch. `omsorgWeb/mitarbeiter-app/lib/omsorgCoreClient.php` nutzt seit den Absences-/Orders-/TimeEntries-Wrappern ebenfalls den generierten PHP-Client (`../api-client-php/`, `\OmsorgCoreClient\Api\...`) statt rohem cURL — beide Frontends müssen also nach einer Contract-Änderung neu generiert werden, nicht nur `omsorgapp`. -**Verbindliche Regel: nach *jeder* Änderung an einem Controller oder DTO in `omsorgCore.Api/Contracts` muss `omsorgapp/api-client-ts` neu generiert und neu gebaut werden — noch in demselben Change, nicht als Nachgang.** Wird das vergessen, gibt es **keinen Fehler, keine Exception, keine Warnung** — der generierte Client kennt das neue/geänderte Feld schlicht nicht und lässt es beim Serialisieren/Deserialisieren stillschweigend weg. Das Symptom in der UI: Speichern/Anlegen meldet Erfolg, aber das betroffene Feld kommt nie im Backend an bzw. taucht nie in der Antwort auf — schwer zu debuggen, weil weder Backend noch Frontend-Code einen sichtbaren Fehler werfen (siehe FR-EIN-1/Website-Vorfall, 2026-08-08). +**Verbindliche Regel: nach *jeder* Änderung an einem Controller oder DTO in `omsorgCore.Api/Contracts` müssen `omsorgapp/api-client-ts` **und** `omsorgWeb/mitarbeiter-app/api-client-php` neu generiert und neu gebaut werden — noch in demselben Change, nicht als Nachgang.** Wird das vergessen, gibt es **keinen Fehler, keine Exception, keine Warnung** — der generierte Client kennt das neue/geänderte Feld schlicht nicht und lässt es beim Serialisieren/Deserialisieren stillschweigend weg. Das Symptom in der UI: Speichern/Anlegen meldet Erfolg, aber das betroffene Feld kommt nie im Backend an bzw. taucht nie in der Antwort auf — schwer zu debuggen, weil weder Backend noch Frontend-Code einen sichtbaren Fehler werfen (siehe FR-EIN-1/Website-Vorfall, 2026-08-08). ```bash # omsorgCore muss dafür lokal im Development-Modus laufen (Swagger nur dort aktiv): @@ -221,7 +299,7 @@ npm run generate # entspricht ./generate.sh — überschreibt src/, README.md, npm run build # erzeugt dist/, das die *Client.cjs-Wrapper tatsächlich importieren ``` -`npm run generate` überschreibt `package.json` komplett (Standard-Output von openapi-generator) — das dort eingetragene `generate`-Script muss danach jedes Mal erneut ergänzt werden (`git diff package.json` prüfen), sonst verschwindet es beim nächsten Lauf wieder. Details/Voraussetzungen: `omsorgapp/api-client-ts/ANLEITUNG.md` (das jeweilige `README.md` wird vom Generator automatisch überschrieben, `ANLEITUNG.md` bleibt stabil). `omsorgWeb/mitarbeiter-app/api-client-php` ist aktuell nicht im echten Datenpfad (siehe oben), sollte aber aus Konsistenzgründen bei Gelegenheit ebenfalls regeneriert werden. +`npm run generate` überschreibt `package.json` komplett (Standard-Output von openapi-generator) — das dort eingetragene `generate`-Script muss danach jedes Mal erneut ergänzt werden (`git diff package.json` prüfen), sonst verschwindet es beim nächsten Lauf wieder. Details/Voraussetzungen: `omsorgapp/api-client-ts/ANLEITUNG.md` (das jeweilige `README.md` wird vom Generator automatisch überschrieben, `ANLEITUNG.md` bleibt stabil). `omsorgWeb/mitarbeiter-app/api-client-php` ist seit den Absences-/Orders-/TimeEntries-Wrappern ebenfalls im echten Datenpfad (siehe oben) und muss im selben Zug regeneriert werden, nicht nur "bei Gelegenheit". ## Die sechs Core-Objekte (Domain-Entitäten) @@ -232,7 +310,7 @@ Verbindliche Regeln für das Datenmodell: 2. Beziehungen ausschließlich über Foreign Keys/IDs, keine redundante Texteingabe verwandter Daten. 3. Stammdaten nur an einer Stelle — kein Feld, das auch in `omsorgWeb` oder `omsorgapp` unabhängig gepflegt wird, sobald die Migration dorthin begonnen hat. 4. Änderungen an geschäftsrelevanten Daten müssen nachvollziehbar sein — `AuditableEntity` liefert `CreatedAt`/`UpdatedAt`; ein vollständiger Audit-Trail (wer hat was geändert) läuft automatisch über den `AuditSaveChangesInterceptor` (siehe "Audit-Log" oben), keine Handarbeit pro Entität nötig. -5. Kein Hard-Delete für sensible/geschäftsrelevante Daten — Soft-Delete/Archivierung (noch nicht implementiert, bei Bedarf einbauen statt Datensätze zu löschen). +5. Kein Hard-Delete für sensible/geschäftsrelevante Daten — Soft-Delete/Archivierung. Umgesetzt für `Employee`/`Facility`/`Contract`/`Order`/`FacilityContact`/`FacilityQualificationRate`/`Absence`/`TimeEntry` (`IsDeleted`/`DeletedAt`, `DELETE`-Endpoints gegated über `PermissionAction.Delete`, Wiederherstellung über `TrashController`/`PermissionAction.Recover`); `Invoice` hat noch kein CRUD, daher hier noch nicht relevant. 6. Berechtigungsprüfung auf Daten- und Funktionsebene (siehe Rechtesystem oben und Rechtematrix in `REQUIREMENTS.md` Abschnitt 7). 7. Rechnungen entstehen ausschließlich aus freigegebener Zeiterfassung (FR-ZE-3/FR-RE-1) — muss bei Ausbau von `TimeEntry`/`Invoice` auf Domain-/Application-Ebene erzwungen werden, nicht nur als UI-Regel im Client. diff --git a/omsorgCore/CONFIGURATION.md b/omsorgCore/CONFIGURATION.md index 74505f9..eca9a51 100644 --- a/omsorgCore/CONFIGURATION.md +++ b/omsorgCore/CONFIGURATION.md @@ -26,6 +26,14 @@ ASP.NET Core liest Konfiguration in dieser Reihenfolge (später gewinnt): | `Auth:LoginLockoutMinutes` | `10` | `Auth__LoginLockoutMinutes` | Zeitfenster, in dem Fehlversuche gezählt werden, bevor die Sperre wieder abläuft. | | `PasswordPolicy:MinLength` | `8` | `PasswordPolicy__MinLength` | Mindestlänge für jedes neu gesetzte Passwort (Account-Anlage, Admin-Reset, Passwort ändern, Passwort-vergessen-Reset — zentral über `IPasswordPolicy`, siehe `omsorgCore/CLAUDE.md`). Über `GET /api/auth/password-policy` auch unauthentifiziert abrufbar, damit Clients denselben Wert für Hinweistexte/Vorab-Validierung nutzen können. | +## Dokumentenarchiv / Storage + +| Key | Default | Env-Var | Beschreibung | +|---|---|---|---| +| `Storage:DocumentsRootPath` | `"App_Data/documents"` | `Storage__DocumentsRootPath` | Wurzelverzeichnis, unter dem Dokument-Uploads (FR-MA-3) physisch auf dem Dateisystem abgelegt werden — relativ zum Arbeitsverzeichnis der `OmsorgCore.Api`, wenn kein absoluter Pfad angegeben wird. Nur der Pfad/Metadaten landen in Postgres, nicht die Bytes selbst (siehe `omsorgCore/CLAUDE.md`, "Dokumentenarchiv"). Produktiv auf einen echten, persistenten Pfad zeigen (nicht das Deployment-Verzeichnis selbst). | +| `Storage:MaxDocumentSizeBytes` | `20971520` (20 MB) | `Storage__MaxDocumentSizeBytes` | Maximal erlaubte Dateigröße pro Upload, geprüft in `IDocumentUploadPolicy`/`DocumentService.UploadAsync`. | +| `Storage:AllowedDocumentContentTypes` | `"application/pdf,image/jpeg,image/png"` | `Storage__AllowedDocumentContentTypes` | Kommagetrennte Liste erlaubter `Content-Type`-Werte für Uploads. | + ## Passwort-Reset / E-Mail | Key | Default | Env-Var | Beschreibung | diff --git a/omsorgCore/Dockerfile b/omsorgCore/Dockerfile new file mode 100644 index 0000000..ae4d8f2 --- /dev/null +++ b/omsorgCore/Dockerfile @@ -0,0 +1,21 @@ +# Build-Kontext ist der Repo-Root (siehe .gitea/workflows/docker-build.yml und docker-compose.yml) - +# damit bleibt der Kontext für alle drei Dockerfiles im Monorepo einheitlich, auch wenn dieses +# Image allein aus omsorgCore/ besteht. +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +WORKDIR /src +COPY omsorgCore/ . +RUN dotnet restore OmsorgCore.sln +RUN dotnet publish src/OmsorgCore.Api/OmsorgCore.Api.csproj -c Release -o /app/publish --no-restore + +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime +WORKDIR /app +COPY --from=build /app/publish . + +# Migrationen laufen automatisch bei jedem Start (Program.cs: db.Database.MigrateAsync()) - +# kein separater Migrations-Schritt im Image nötig. Konfiguration (ConnectionStrings__OmsorgCore, +# Jwt__Secret, Cors__AllowedOrigins__0, ...) kommt ausschließlich über Env-Vars zur Laufzeit, +# nie ins Image gebacken (siehe omsorgCore/CLAUDE.md, Abschnitt "Secret-Handling"). +ENV ASPNETCORE_URLS=http://+:8080 +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "OmsorgCore.Api.dll"] diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/AbsenceDecisionRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/AbsenceDecisionRequest.cs new file mode 100644 index 0000000..221976c --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/AbsenceDecisionRequest.cs @@ -0,0 +1,3 @@ +namespace OmsorgCore.Api.Contracts; + +public record AbsenceDecisionRequest(string Status, string? AdminNote); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/AbsenceResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/AbsenceResponse.cs new file mode 100644 index 0000000..7eb4b51 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/AbsenceResponse.cs @@ -0,0 +1,15 @@ +namespace OmsorgCore.Api.Contracts; + +public record AbsenceResponse( + Guid Id, + Guid EmployeeId, + string EmployeeName, + string Type, + DateOnly StartDate, + DateOnly EndDate, + string? Reason, + string? Substitute, + string? Note, + string Status, + string? AdminNote, + DateTime CreatedAt); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/AddUserPermissionOverrideRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/AddUserPermissionOverrideRequest.cs index 330ad59..733d590 100644 --- a/omsorgCore/src/OmsorgCore.Api/Contracts/AddUserPermissionOverrideRequest.cs +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/AddUserPermissionOverrideRequest.cs @@ -2,4 +2,4 @@ using OmsorgCore.Domain.Enums; namespace OmsorgCore.Api.Contracts; -public record AddUserPermissionOverrideRequest(ModuleType Module, PermissionAction Action, PermissionEffect Effect); +public record AddUserPermissionOverrideRequest(ModuleType Module, PermissionAction Action, PermissionEffect Effect, PermissionScope Scope); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/CreateAbsenceRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/CreateAbsenceRequest.cs new file mode 100644 index 0000000..be111ae --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/CreateAbsenceRequest.cs @@ -0,0 +1,9 @@ +namespace OmsorgCore.Api.Contracts; + +public record CreateAbsenceRequest( + string Type, + DateOnly StartDate, + DateOnly EndDate, + string? Reason, + string? Substitute, + string? Note); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/CreateFacilityQualificationRateRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/CreateFacilityQualificationRateRequest.cs new file mode 100644 index 0000000..ba78ff0 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/CreateFacilityQualificationRateRequest.cs @@ -0,0 +1,5 @@ +namespace OmsorgCore.Api.Contracts; + +public record CreateFacilityQualificationRateRequest( + string Qualification, + decimal Rate); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/CreateTimeEntryRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/CreateTimeEntryRequest.cs new file mode 100644 index 0000000..26766a7 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/CreateTimeEntryRequest.cs @@ -0,0 +1,12 @@ +namespace OmsorgCore.Api.Contracts; + +public record CreateTimeEntryRequest( + Guid OrderId, + DateOnly Date, + TimeOnly Start, + TimeOnly End, + TimeSpan BreakDuration, + decimal NightHours, + decimal SaturdayHours, + decimal SundayHours, + decimal HolidayHours); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/CreateValueListItemRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/CreateValueListItemRequest.cs index dd79123..6544f71 100644 --- a/omsorgCore/src/OmsorgCore.Api/Contracts/CreateValueListItemRequest.cs +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/CreateValueListItemRequest.cs @@ -5,4 +5,5 @@ public record CreateValueListItemRequest( int SortOrder, bool IsDefault = false, bool IsInitial = false, - bool IsTerminal = false); + bool IsTerminal = false, + bool TriggersFollowUp = false); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/DocumentResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/DocumentResponse.cs new file mode 100644 index 0000000..e00b59f --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/DocumentResponse.cs @@ -0,0 +1,14 @@ +namespace OmsorgCore.Api.Contracts; + +public record DocumentResponse( + Guid Id, + string EntityType, + Guid EntityId, + string Category, + string FileName, + string ContentType, + long SizeBytes, + string? Description, + Guid UploadedByUserId, + string? UploadedByUsername, + DateTime CreatedAt); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/FacilityQualificationRateResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/FacilityQualificationRateResponse.cs new file mode 100644 index 0000000..6220a36 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/FacilityQualificationRateResponse.cs @@ -0,0 +1,7 @@ +namespace OmsorgCore.Api.Contracts; + +public record FacilityQualificationRateResponse( + Guid Id, + Guid FacilityId, + string Qualification, + decimal Rate); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/FacilityResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/FacilityResponse.cs index cfad41e..070c150 100644 --- a/omsorgCore/src/OmsorgCore.Api/Contracts/FacilityResponse.cs +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/FacilityResponse.cs @@ -13,4 +13,16 @@ public record FacilityResponse( string? BillingStreet, string? BillingPostalCode, string? BillingCity, - string? BillingCountry); + string? BillingCountry, + DateTime? FollowUpDueDate, + decimal? BillingRate, + decimal? NightSurchargePercent, + decimal? SaturdaySurchargePercent, + decimal? SundaySurchargePercent, + decimal? HolidaySurchargePercent, + decimal? TravelCostRate, + decimal? MinimumHours, + string? BreakPolicy, + string? BillingInterval, + int? PaymentTermDays, + string? IndividualAgreements); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/ForgotPasswordRequestResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/ForgotPasswordRequestResponse.cs index f74578a..f246279 100644 --- a/omsorgCore/src/OmsorgCore.Api/Contracts/ForgotPasswordRequestResponse.cs +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/ForgotPasswordRequestResponse.cs @@ -1,4 +1,9 @@ namespace OmsorgCore.Api.Contracts; -/// "sent" | "cannot_reset" - siehe AuthController.ForgotPasswordRequest für die Anti-Enumeration-Abwägung. +/// +/// "sent" | "cannot_reset" | "email_unavailable" - siehe AuthController.ForgotPasswordRequest für die +/// Anti-Enumeration-Abwägung. "email_unavailable": Reset-Code wurde angelegt, aber der E-Mail-Versand +/// ist fehlgeschlagen (z.B. SMTP nicht erreichbar) - Client soll das ehrlich anzeigen statt zum +/// PIN-Eingabe-Schritt weiterzuleiten. +/// public record ForgotPasswordRequestResponse(string Status); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/LoginResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/LoginResponse.cs index f817bc7..0916845 100644 --- a/omsorgCore/src/OmsorgCore.Api/Contracts/LoginResponse.cs +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/LoginResponse.cs @@ -1,3 +1,3 @@ namespace OmsorgCore.Api.Contracts; -public record LoginResponse(string AccessToken, string RefreshToken, DateTime ExpiresAt, bool MustChangePassword); +public record LoginResponse(string AccessToken, DateTime ExpiresAt, bool MustChangePassword); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/LogoutRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/LogoutRequest.cs deleted file mode 100644 index 8a3a554..0000000 --- a/omsorgCore/src/OmsorgCore.Api/Contracts/LogoutRequest.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace OmsorgCore.Api.Contracts; - -public record LogoutRequest(string RefreshToken); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/PermissionDto.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/PermissionDto.cs index 34fa004..3f5c70d 100644 --- a/omsorgCore/src/OmsorgCore.Api/Contracts/PermissionDto.cs +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/PermissionDto.cs @@ -2,4 +2,4 @@ using OmsorgCore.Domain.Enums; namespace OmsorgCore.Api.Contracts; -public record PermissionDto(ModuleType Module, PermissionAction Action); +public record PermissionDto(ModuleType Module, PermissionAction Action, PermissionScope Scope); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/RefreshRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/RefreshRequest.cs deleted file mode 100644 index c580648..0000000 --- a/omsorgCore/src/OmsorgCore.Api/Contracts/RefreshRequest.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace OmsorgCore.Api.Contracts; - -public record RefreshRequest(string RefreshToken); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/TimeEntryDecisionRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/TimeEntryDecisionRequest.cs new file mode 100644 index 0000000..7b2b9cf --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/TimeEntryDecisionRequest.cs @@ -0,0 +1,3 @@ +namespace OmsorgCore.Api.Contracts; + +public record TimeEntryDecisionRequest(Guid StatusId, string? AdminNote); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/TimeEntryResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/TimeEntryResponse.cs new file mode 100644 index 0000000..56a0f5c --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/TimeEntryResponse.cs @@ -0,0 +1,22 @@ +namespace OmsorgCore.Api.Contracts; + +public record TimeEntryResponse( + Guid Id, + Guid EmployeeId, + string EmployeeName, + Guid OrderId, + Guid FacilityId, + string FacilityName, + DateOnly Date, + TimeOnly Start, + TimeOnly End, + TimeSpan BreakDuration, + decimal NightHours, + decimal SaturdayHours, + decimal SundayHours, + decimal HolidayHours, + Guid StatusId, + string StatusName, + bool IsEditableByOwner, + string? AdminNote, + DateTime CreatedAt); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/TrashAbsenceResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/TrashAbsenceResponse.cs new file mode 100644 index 0000000..86f6e0c --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/TrashAbsenceResponse.cs @@ -0,0 +1,6 @@ +namespace OmsorgCore.Api.Contracts; + +public record TrashAbsenceResponse( + Guid Id, + string Type, + DateTime? DeletedAt); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/TrashContractResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/TrashContractResponse.cs new file mode 100644 index 0000000..1425219 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/TrashContractResponse.cs @@ -0,0 +1,6 @@ +namespace OmsorgCore.Api.Contracts; + +public record TrashContractResponse( + Guid Id, + string ContractType, + DateTime? DeletedAt); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/TrashEmployeeResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/TrashEmployeeResponse.cs new file mode 100644 index 0000000..6a60d00 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/TrashEmployeeResponse.cs @@ -0,0 +1,7 @@ +namespace OmsorgCore.Api.Contracts; + +public record TrashEmployeeResponse( + Guid Id, + string FirstName, + string LastName, + DateTime? DeletedAt); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/TrashFacilityContactResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/TrashFacilityContactResponse.cs new file mode 100644 index 0000000..7ddc7ab --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/TrashFacilityContactResponse.cs @@ -0,0 +1,7 @@ +namespace OmsorgCore.Api.Contracts; + +public record TrashFacilityContactResponse( + Guid Id, + Guid FacilityId, + string Name, + DateTime? DeletedAt); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/TrashFacilityQualificationRateResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/TrashFacilityQualificationRateResponse.cs new file mode 100644 index 0000000..cc29410 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/TrashFacilityQualificationRateResponse.cs @@ -0,0 +1,7 @@ +namespace OmsorgCore.Api.Contracts; + +public record TrashFacilityQualificationRateResponse( + Guid Id, + Guid FacilityId, + string Qualification, + DateTime? DeletedAt); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/TrashFacilityResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/TrashFacilityResponse.cs new file mode 100644 index 0000000..9c62fcd --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/TrashFacilityResponse.cs @@ -0,0 +1,6 @@ +namespace OmsorgCore.Api.Contracts; + +public record TrashFacilityResponse( + Guid Id, + string Name, + DateTime? DeletedAt); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/TrashOrderResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/TrashOrderResponse.cs new file mode 100644 index 0000000..6cd3644 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/TrashOrderResponse.cs @@ -0,0 +1,6 @@ +namespace OmsorgCore.Api.Contracts; + +public record TrashOrderResponse( + Guid Id, + string? RequiredQualification, + DateTime? DeletedAt); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/TrashTimeEntryResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/TrashTimeEntryResponse.cs new file mode 100644 index 0000000..19a9e6f --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/TrashTimeEntryResponse.cs @@ -0,0 +1,3 @@ +namespace OmsorgCore.Api.Contracts; + +public record TrashTimeEntryResponse(Guid Id, DateOnly Date, DateTime? DeletedAt); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateAbsenceRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateAbsenceRequest.cs new file mode 100644 index 0000000..5632b7e --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateAbsenceRequest.cs @@ -0,0 +1,9 @@ +namespace OmsorgCore.Api.Contracts; + +public record UpdateAbsenceRequest( + string Type, + DateOnly StartDate, + DateOnly EndDate, + string? Reason, + string? Substitute, + string? Note); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateDocumentRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateDocumentRequest.cs new file mode 100644 index 0000000..f00ef7e --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateDocumentRequest.cs @@ -0,0 +1,6 @@ +namespace OmsorgCore.Api.Contracts; + +public record UpdateDocumentRequest( + string Category, + string? Description, + string FileName); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateFacilityQualificationRateRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateFacilityQualificationRateRequest.cs new file mode 100644 index 0000000..20b93ea --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateFacilityQualificationRateRequest.cs @@ -0,0 +1,5 @@ +namespace OmsorgCore.Api.Contracts; + +public record UpdateFacilityQualificationRateRequest( + string Qualification, + decimal Rate); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateFacilityRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateFacilityRequest.cs index d09501f..dfec2a8 100644 --- a/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateFacilityRequest.cs +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateFacilityRequest.cs @@ -12,4 +12,16 @@ public record UpdateFacilityRequest( string? BillingStreet, string? BillingPostalCode, string? BillingCity, - string? BillingCountry); + string? BillingCountry, + int? FollowUpDays, + decimal? BillingRate, + decimal? NightSurchargePercent, + decimal? SaturdaySurchargePercent, + decimal? SundaySurchargePercent, + decimal? HolidaySurchargePercent, + decimal? TravelCostRate, + decimal? MinimumHours, + string? BreakPolicy, + string? BillingInterval, + int? PaymentTermDays, + string? IndividualAgreements); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateTimeEntryRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateTimeEntryRequest.cs new file mode 100644 index 0000000..f92b48e --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateTimeEntryRequest.cs @@ -0,0 +1,12 @@ +namespace OmsorgCore.Api.Contracts; + +public record UpdateTimeEntryRequest( + Guid OrderId, + DateOnly Date, + TimeOnly Start, + TimeOnly End, + TimeSpan BreakDuration, + decimal NightHours, + decimal SaturdayHours, + decimal SundayHours, + decimal HolidayHours); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateValueListItemRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateValueListItemRequest.cs index 4634dc7..426ef99 100644 --- a/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateValueListItemRequest.cs +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateValueListItemRequest.cs @@ -5,4 +5,5 @@ public record UpdateValueListItemRequest( int SortOrder, bool IsDefault = false, bool IsInitial = false, - bool IsTerminal = false); + bool IsTerminal = false, + bool TriggersFollowUp = false); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/UploadDocumentRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/UploadDocumentRequest.cs new file mode 100644 index 0000000..906183c --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/UploadDocumentRequest.cs @@ -0,0 +1,8 @@ +namespace OmsorgCore.Api.Contracts; + +public record UploadDocumentRequest( + string EntityType, + Guid EntityId, + string Category, + string? Description, + IFormFile File); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/UserPermissionOverrideResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/UserPermissionOverrideResponse.cs index 34f8a1a..02c1d44 100644 --- a/omsorgCore/src/OmsorgCore.Api/Contracts/UserPermissionOverrideResponse.cs +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/UserPermissionOverrideResponse.cs @@ -2,4 +2,4 @@ using OmsorgCore.Domain.Enums; namespace OmsorgCore.Api.Contracts; -public record UserPermissionOverrideResponse(Guid Id, ModuleType Module, PermissionAction Action, PermissionEffect Effect); +public record UserPermissionOverrideResponse(Guid Id, ModuleType Module, PermissionAction Action, PermissionEffect Effect, PermissionScope Scope); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/ValueListItemResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/ValueListItemResponse.cs index 57eb227..f834c14 100644 --- a/omsorgCore/src/OmsorgCore.Api/Contracts/ValueListItemResponse.cs +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/ValueListItemResponse.cs @@ -6,4 +6,5 @@ public record ValueListItemResponse( int SortOrder, bool IsDefault, bool IsInitial, - bool IsTerminal); + bool IsTerminal, + bool TriggersFollowUp); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/ValueListTransitionResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/ValueListTransitionResponse.cs index c4ea6c1..66348e9 100644 --- a/omsorgCore/src/OmsorgCore.Api/Contracts/ValueListTransitionResponse.cs +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/ValueListTransitionResponse.cs @@ -1,3 +1,3 @@ namespace OmsorgCore.Api.Contracts; -public record ValueListTransitionResponse(Guid Id, Guid FromItemId, Guid ToItemId); +public record ValueListTransitionResponse(Guid Id, Guid FromItemId, Guid ToItemId, bool RequiresApproval); diff --git a/omsorgCore/src/OmsorgCore.Api/Controllers/AbsencesController.cs b/omsorgCore/src/OmsorgCore.Api/Controllers/AbsencesController.cs new file mode 100644 index 0000000..7b566e8 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Controllers/AbsencesController.cs @@ -0,0 +1,212 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using OmsorgCore.Api.Contracts; +using OmsorgCore.Api.Security; +using OmsorgCore.Application.Abstractions; +using OmsorgCore.Application.Services; +using OmsorgCore.Domain.Entities; +using OmsorgCore.Domain.Enums; + +namespace OmsorgCore.Api.Controllers; + +/// +/// Abwesenheits-/Urlaubs-/Krankmeldungsanträge (FR-CON-1, Datenbasis für FR-EM-3). Außendienst +/// darf nur Create/View mit PermissionScope.Own (eigene Anträge, EmployeeId wird serverseitig aus +/// dem JWT gesetzt, siehe AbsenceService.CreateAsync), Büro-Rollen sehen/entscheiden über alle. +/// +[ApiController] +[Authorize] +[Route("api/absences")] +public class AbsencesController : ControllerBase +{ + private const string AbsenceTypeListKey = "AbsenceType"; + private const string AbsenceStatusListKey = "AbsenceStatus"; + + private readonly IAbsenceService _absenceService; + private readonly IValueListRepository _valueListRepository; + + public AbsencesController(IAbsenceService absenceService, IValueListRepository valueListRepository) + { + _absenceService = absenceService; + _valueListRepository = valueListRepository; + } + + [HttpGet] + [RequirePermission(ModuleType.Absences, PermissionAction.View)] + public async Task>> GetAll( + [FromQuery] string? status, + [FromQuery] string? type, + [FromQuery] Guid? employeeId, + [FromQuery] int page = 1, + [FromQuery] int pageSize = 20, + CancellationToken cancellationToken = default) + { + page = Math.Max(page, 1); + pageSize = Math.Clamp(pageSize, 1, 100); + + var (items, totalCount) = await _absenceService.GetPagedAsync(status, type, employeeId, page, pageSize, cancellationToken); + return Ok(new PagedResponse(items.Select(ToResponse).ToList(), totalCount, page, pageSize)); + } + + [HttpGet("{id:guid}")] + [RequirePermission(ModuleType.Absences, PermissionAction.View)] + public async Task> GetById(Guid id, CancellationToken cancellationToken) + { + var absence = await _absenceService.GetByIdAsync(id, cancellationToken); + return absence is null ? NotFound() : Ok(ToResponse(absence)); + } + + [HttpPost] + [RequirePermission(ModuleType.Absences, PermissionAction.Create)] + public async Task> Create(CreateAbsenceRequest request, CancellationToken cancellationToken) + { + var fieldError = await ValidateFieldsAsync(request.Type, request.StartDate, request.EndDate, request.Reason, request.Substitute, request.Note, cancellationToken); + if (fieldError is not null) + { + return BadRequest(fieldError); + } + + var absence = new Absence + { + Type = request.Type, + StartDate = request.StartDate, + EndDate = request.EndDate, + Reason = request.Reason, + Substitute = request.Substitute, + Note = request.Note + }; + + var created = await _absenceService.CreateAsync(absence, cancellationToken); + if (created is null) + { + return BadRequest("Kein Mitarbeiter verknüpft - Abwesenheitsanträge können nur für einen verknüpften Mitarbeiter angelegt werden."); + } + + var reloaded = await _absenceService.GetByIdAsync(created.Id, cancellationToken); + return CreatedAtAction(nameof(GetById), new { id = created.Id }, ToResponse(reloaded!)); + } + + [HttpPut("{id:guid}")] + [RequirePermission(ModuleType.Absences, PermissionAction.Edit)] + public async Task> Update(Guid id, UpdateAbsenceRequest request, CancellationToken cancellationToken) + { + var fieldError = await ValidateFieldsAsync(request.Type, request.StartDate, request.EndDate, request.Reason, request.Substitute, request.Note, cancellationToken); + if (fieldError is not null) + { + return BadRequest(fieldError); + } + + var updates = new Absence + { + Type = request.Type, + StartDate = request.StartDate, + EndDate = request.EndDate, + Reason = request.Reason, + Substitute = request.Substitute, + Note = request.Note + }; + + var result = await _absenceService.UpdateAsync(id, updates, cancellationToken); + if (!result.Success) + { + return result.FailureReason == UpdateAbsenceFailureReason.AlreadyDecided + ? BadRequest("Der Antrag wurde bereits entschieden und kann nicht mehr bearbeitet werden.") + : NotFound(); + } + + var reloaded = await _absenceService.GetByIdAsync(result.Absence!.Id, cancellationToken); + return Ok(ToResponse(reloaded!)); + } + + [HttpPost("{id:guid}/decision")] + [RequirePermission(ModuleType.Absences, PermissionAction.Approve)] + public async Task> Decide(Guid id, AbsenceDecisionRequest request, CancellationToken cancellationToken) + { + var allowedStatuses = await _valueListRepository.GetActiveValuesAsync(AbsenceStatusListKey, cancellationToken); + var initialStatus = await _absenceService.GetInitialStatusValueAsync(cancellationToken); + var decidableStatuses = allowedStatuses.Where(s => s != initialStatus).ToList(); + if (string.IsNullOrWhiteSpace(request.Status) || !decidableStatuses.Contains(request.Status)) + { + return BadRequest($"Status muss einer der folgenden Werte sein: {string.Join(", ", decidableStatuses)}."); + } + + if (request.AdminNote is { Length: > 500 }) + { + return BadRequest("AdminNote darf maximal 500 Zeichen lang sein."); + } + + var decided = await _absenceService.DecideAsync(id, request.Status, request.AdminNote, cancellationToken); + return decided is null ? NotFound() : Ok(ToResponse(decided)); + } + + [HttpDelete("{id:guid}")] + [RequirePermission(ModuleType.Absences, PermissionAction.Delete)] + public async Task Delete(Guid id, CancellationToken cancellationToken) + { + var deleted = await _absenceService.DeleteAsync(id, cancellationToken); + return deleted ? NoContent() : NotFound(); + } + + private async Task ValidateFieldsAsync( + string type, + DateOnly startDate, + DateOnly endDate, + string? reason, + string? substitute, + string? note, + CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(type)) + { + return "Type ist erforderlich."; + } + + var allowedTypes = await _valueListRepository.GetActiveValuesAsync(AbsenceTypeListKey, cancellationToken); + if (!allowedTypes.Contains(type)) + { + return $"Type muss einer der folgenden Werte sein: {string.Join(", ", allowedTypes)}."; + } + + if (startDate == default) + { + return "StartDate ist erforderlich."; + } + + if (endDate < startDate) + { + return "EndDate darf nicht vor StartDate liegen."; + } + + if (reason is { Length: > 500 }) + { + return "Reason darf maximal 500 Zeichen lang sein."; + } + + if (substitute is { Length: > 200 }) + { + return "Substitute darf maximal 200 Zeichen lang sein."; + } + + if (note is { Length: > 500 }) + { + return "Note darf maximal 500 Zeichen lang sein."; + } + + return null; + } + + private static AbsenceResponse ToResponse(Absence absence) + => new( + absence.Id, + absence.EmployeeId, + absence.Employee is null ? string.Empty : $"{absence.Employee.FirstName} {absence.Employee.LastName}", + absence.Type, + absence.StartDate, + absence.EndDate, + absence.Reason, + absence.Substitute, + absence.Note, + absence.Status, + absence.AdminNote, + absence.CreatedAt); +} diff --git a/omsorgCore/src/OmsorgCore.Api/Controllers/AdminEmailController.cs b/omsorgCore/src/OmsorgCore.Api/Controllers/AdminEmailController.cs index 726d1a5..c13a54b 100644 --- a/omsorgCore/src/OmsorgCore.Api/Controllers/AdminEmailController.cs +++ b/omsorgCore/src/OmsorgCore.Api/Controllers/AdminEmailController.cs @@ -22,11 +22,13 @@ public class AdminEmailController : ControllerBase { private readonly IEmailSender _emailSender; private readonly EmailOptions _emailOptions; + private readonly ILogger _logger; - public AdminEmailController(IEmailSender emailSender, IOptions emailOptions) + public AdminEmailController(IEmailSender emailSender, IOptions emailOptions, ILogger logger) { _emailSender = emailSender; _emailOptions = emailOptions.Value; + _logger = logger; } [HttpGet("password-reset-template")] @@ -60,7 +62,18 @@ public class AdminEmailController : ControllerBase subject = EmailTemplateRenderer.Render(subject, values); body = EmailTemplateRenderer.Render(body, values); - await _emailSender.SendAsync(new EmailMessage(request.ToAddress, subject, body), cancellationToken); + try + { + await _emailSender.SendAsync(new EmailMessage(request.ToAddress, subject, body), cancellationToken); + } + catch (Exception ex) + { + // Admin-only (RequirePermission oben) - die Exception-Message darf hier raus, sie enthält + // keine SMTP-Zugangsdaten (nur MailKit-Fehlertext wie "Authentication failed"/"Connection + // refused") und ist genau das, was zum Debuggen der Email:*-Konfiguration gebraucht wird. + _logger.LogError(ex, "Test-Mail konnte nicht gesendet werden an {ToAddress}", request.ToAddress); + return StatusCode(StatusCodes.Status502BadGateway, new { error = "send_failed", message = ex.Message }); + } return NoContent(); } diff --git a/omsorgCore/src/OmsorgCore.Api/Controllers/AuthController.cs b/omsorgCore/src/OmsorgCore.Api/Controllers/AuthController.cs index e9bb5c6..ff595e0 100644 --- a/omsorgCore/src/OmsorgCore.Api/Controllers/AuthController.cs +++ b/omsorgCore/src/OmsorgCore.Api/Controllers/AuthController.cs @@ -13,12 +13,17 @@ namespace OmsorgCore.Api.Controllers; [Route("api/auth")] public class AuthController : ControllerBase { + private const string RefreshTokenCookieName = "refreshToken"; + private readonly IAuthService _authService; private readonly ICurrentUserService _currentUserService; private readonly IPasswordResetService _passwordResetService; private readonly IUserService _userService; private readonly IDomainEventDispatcher _dispatcher; private readonly PasswordPolicyOptions _passwordPolicyOptions; + private readonly RefreshTokenOptions _refreshTokenOptions; + private readonly IWebHostEnvironment _environment; + private readonly ILogger _logger; public AuthController( IAuthService authService, @@ -26,7 +31,10 @@ public class AuthController : ControllerBase IPasswordResetService passwordResetService, IUserService userService, IDomainEventDispatcher dispatcher, - IOptions passwordPolicyOptions) + IOptions passwordPolicyOptions, + IOptions refreshTokenOptions, + IWebHostEnvironment environment, + ILogger logger) { _authService = authService; _currentUserService = currentUserService; @@ -34,6 +42,25 @@ public class AuthController : ControllerBase _userService = userService; _dispatcher = dispatcher; _passwordPolicyOptions = passwordPolicyOptions.Value; + _refreshTokenOptions = refreshTokenOptions.Value; + _environment = environment; + _logger = logger; + } + + // HttpOnly, damit ein Browser-Frontend den Refresh-Token nie per JS lesen kann (XSS-Schutz) - + // Path auf /api/auth eingeschränkt, da nur login/refresh/logout ihn brauchen. Secure nur außerhalb + // von Development, weil der lokale Dev-Server per launchSettings.json standardmäßig nur über + // http:// läuft (kein https-Profil default) - ein Secure-Cookie würde der Browser dort nie setzen. + private void SetRefreshTokenCookie(string refreshToken) + { + Response.Cookies.Append(RefreshTokenCookieName, refreshToken, new CookieOptions + { + HttpOnly = true, + Secure = !_environment.IsDevelopment(), + SameSite = SameSiteMode.Lax, + Expires = DateTimeOffset.UtcNow.AddDays(_refreshTokenOptions.ExpiryDays), + Path = "/api/auth" + }); } [HttpPost("login")] @@ -54,25 +81,37 @@ public class AuthController : ControllerBase } await _dispatcher.DispatchAsync(new AuditEvent(result.UserId, result.Username, ipAddress, "Login"), cancellationToken); - return Ok(new LoginResponse(result.Token, result.RefreshToken, result.ExpiresAt.Value, result.MustChangePassword)); + SetRefreshTokenCookie(result.RefreshToken); + return Ok(new LoginResponse(result.Token, result.ExpiresAt.Value, result.MustChangePassword)); } [HttpPost("refresh")] - public async Task> Refresh(RefreshRequest request, CancellationToken cancellationToken) + public async Task> Refresh(CancellationToken cancellationToken) { - var result = await _authService.RefreshAsync(request.RefreshToken, cancellationToken); + if (!Request.Cookies.TryGetValue(RefreshTokenCookieName, out var refreshToken) || string.IsNullOrEmpty(refreshToken)) + { + return Unauthorized(); + } + + var result = await _authService.RefreshAsync(refreshToken, cancellationToken); if (!result.Success || result.Token is null || result.RefreshToken is null || result.ExpiresAt is null) { return Unauthorized(); } - return Ok(new LoginResponse(result.Token, result.RefreshToken, result.ExpiresAt.Value, result.MustChangePassword)); + SetRefreshTokenCookie(result.RefreshToken); + return Ok(new LoginResponse(result.Token, result.ExpiresAt.Value, result.MustChangePassword)); } [HttpPost("logout")] - public async Task Logout(LogoutRequest request, CancellationToken cancellationToken) + public async Task Logout(CancellationToken cancellationToken) { - await _authService.RevokeAsync(request.RefreshToken, cancellationToken); + if (Request.Cookies.TryGetValue(RefreshTokenCookieName, out var refreshToken) && !string.IsNullOrEmpty(refreshToken)) + { + await _authService.RevokeAsync(refreshToken, cancellationToken); + } + + Response.Cookies.Delete(RefreshTokenCookieName, new CookieOptions { Path = "/api/auth" }); await _dispatcher.DispatchAsync( new AuditEvent(_currentUserService.UserId, _currentUserService.Username, _currentUserService.IpAddress, "Logout"), cancellationToken); @@ -95,7 +134,7 @@ public class AuthController : ControllerBase } var permissions = profile.Permissions - .Select(p => new PermissionDto(p.Module, p.Action)) + .Select(p => new PermissionDto(p.Module, p.Action, p.Scope)) .ToList(); return Ok(new MeResponse( @@ -142,8 +181,22 @@ public class AuthController : ControllerBase var result = await _passwordResetService.RequestResetAsync(request.Username, cancellationToken); if (result.Status == PasswordResetRequestStatus.Sent && result.Email is not null && result.RawPin is not null) { - await _dispatcher.DispatchAsync( - new PasswordResetRequestedEvent(result.Email, result.RawPin), cancellationToken); + try + { + await _dispatcher.DispatchAsync( + new PasswordResetRequestedEvent(result.Email, result.RawPin), cancellationToken); + } + catch (Exception ex) + { + // Der Reset-Code wurde bereits in der DB angelegt (PasswordResetService.RequestResetAsync) - + // nur der E-Mail-Versand ist fehlgeschlagen (z.B. SMTP nicht erreichbar/falsch konfiguriert). + // Client bekommt "email_unavailable" statt "sent", damit die UI ehrlich anzeigt, dass gerade + // kein Code angekommen ist, statt den Nutzer auf einen leeren PIN-Eingabe-Schritt zu schicken. + // Kein zusätzliches Enumeration-Risiko ggü. heute: "sent" vs. "cannot_reset" unterscheidet + // bereits, ob der Username existiert (siehe ForgotPasswordRequestResponse-Doku). + _logger.LogError(ex, "Passwort-Reset-E-Mail konnte nicht versendet werden für UserId {UserId}", result.UserId); + return Ok(new ForgotPasswordRequestResponse("email_unavailable")); + } } var status = result.Status == PasswordResetRequestStatus.Sent ? "sent" : "cannot_reset"; diff --git a/omsorgCore/src/OmsorgCore.Api/Controllers/ContractsController.cs b/omsorgCore/src/OmsorgCore.Api/Controllers/ContractsController.cs index cc11787..428b274 100644 --- a/omsorgCore/src/OmsorgCore.Api/Controllers/ContractsController.cs +++ b/omsorgCore/src/OmsorgCore.Api/Controllers/ContractsController.cs @@ -151,6 +151,14 @@ public class ContractsController : ControllerBase return updated is null ? NotFound() : Ok(ToResponse(updated)); } + [HttpDelete("{id:guid}")] + [RequirePermission(ModuleType.Contracts, PermissionAction.Delete)] + public async Task Delete(Guid id, CancellationToken cancellationToken) + { + var deleted = await _contractService.DeleteAsync(id, cancellationToken); + return deleted ? NoContent() : NotFound(); + } + private async Task ValidateFieldsAsync( string contractType, Guid? employeeId, diff --git a/omsorgCore/src/OmsorgCore.Api/Controllers/DocumentsController.cs b/omsorgCore/src/OmsorgCore.Api/Controllers/DocumentsController.cs new file mode 100644 index 0000000..d9fb602 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Controllers/DocumentsController.cs @@ -0,0 +1,177 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using OmsorgCore.Api.Contracts; +using OmsorgCore.Api.Security; +using OmsorgCore.Application.Abstractions; +using OmsorgCore.Application.Services; +using OmsorgCore.Domain.Entities; +using OmsorgCore.Domain.Enums; +using OmsorgCore.Engine.Events; + +namespace OmsorgCore.Api.Controllers; + +[ApiController] +[Authorize] +[Route("api/documents")] +public class DocumentsController : ControllerBase +{ + private readonly IDocumentService _documentService; + private readonly IEmployeeRepository _employeeRepository; + private readonly ICurrentUserService _currentUserService; + private readonly IDomainEventDispatcher _dispatcher; + + public DocumentsController( + IDocumentService documentService, + IEmployeeRepository employeeRepository, + ICurrentUserService currentUserService, + IDomainEventDispatcher dispatcher) + { + _documentService = documentService; + _employeeRepository = employeeRepository; + _currentUserService = currentUserService; + _dispatcher = dispatcher; + } + + [HttpGet] + [RequirePermission(ModuleType.Documents, PermissionAction.View)] + public async Task>> GetByEntity( + [FromQuery] string entityType, + [FromQuery] Guid entityId, + CancellationToken cancellationToken) + { + if (!Enum.TryParse(entityType, out _)) + { + return BadRequest($"entityType muss einer der folgenden Werte sein: {string.Join(", ", Enum.GetNames())}."); + } + + var documents = await _documentService.GetByEntityAsync(entityType, entityId, cancellationToken); + return Ok(documents.Select(ToResponse).ToList()); + } + + [HttpPost] + [RequirePermission(ModuleType.Documents, PermissionAction.Create)] + [Consumes("multipart/form-data")] + public async Task> Upload([FromForm] UploadDocumentRequest request, CancellationToken cancellationToken) + { + if (!Enum.TryParse(request.EntityType, out var entityType)) + { + return BadRequest($"EntityType muss einer der folgenden Werte sein: {string.Join(", ", Enum.GetNames())}."); + } + + if (entityType == DocumentEntityType.Employee) + { + var employee = await _employeeRepository.GetByIdAsync(request.EntityId, cancellationToken); + if (employee is null) + { + return BadRequest("EntityId verweist auf keinen existierenden Mitarbeiter."); + } + } + else + { + return BadRequest("Dokumente sind aktuell nur für EntityType=Employee möglich."); + } + + if (request.File is null || request.File.Length == 0) + { + return BadRequest("File ist erforderlich."); + } + + await using var stream = request.File.OpenReadStream(); + var result = await _documentService.UploadAsync( + request.EntityType, + request.EntityId, + request.Category, + request.Description, + request.File.FileName, + request.File.ContentType, + request.File.Length, + stream, + _currentUserService.UserId!.Value, + cancellationToken); + + if (result.Error != DocumentUploadError.None || result.Document is null) + { + return BadRequest(ToErrorMessage(result.Error)); + } + + var created = await _documentService.GetByIdAsync(result.Document.Id, cancellationToken) ?? result.Document; + return CreatedAtAction(nameof(GetByEntity), new { entityType = created.EntityType, entityId = created.EntityId }, ToResponse(created)); + } + + [HttpPut("{id:guid}")] + [RequirePermission(ModuleType.Documents, PermissionAction.Edit)] + public async Task> Update(Guid id, UpdateDocumentRequest request, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(request.FileName) || request.FileName.Length > 260) + { + return BadRequest("FileName ist erforderlich und darf maximal 260 Zeichen lang sein."); + } + + var result = await _documentService.UpdateAsync(id, request.Category, request.Description, request.FileName, cancellationToken); + + return result.Error switch + { + DocumentUpdateError.NotFound => NotFound(), + DocumentUpdateError.InvalidCategory => BadRequest("Category ist ungültig."), + _ => Ok(ToResponse(result.Document!)) + }; + } + + [HttpGet("{id:guid}/download")] + [RequirePermission(ModuleType.Documents, PermissionAction.View)] + public async Task Download(Guid id, CancellationToken cancellationToken) + { + var document = await _documentService.GetByIdAsync(id, cancellationToken); + if (document is null) + { + return NotFound(); + } + + var stream = await _documentService.OpenForDownloadAsync(id, cancellationToken); + if (stream is null) + { + return NotFound(); + } + + await _dispatcher.DispatchAsync( + new AuditEvent( + _currentUserService.UserId, + _currentUserService.Username, + _currentUserService.IpAddress, + "DocumentDownloaded", + $"{{\"documentId\":\"{document.Id}\",\"fileName\":\"{document.FileName}\"}}"), + cancellationToken); + + return File(stream, document.ContentType, document.FileName); + } + + [HttpDelete("{id:guid}")] + [RequirePermission(ModuleType.Documents, PermissionAction.Delete)] + public async Task Delete(Guid id, CancellationToken cancellationToken) + { + var deleted = await _documentService.DeleteAsync(id, cancellationToken); + return deleted ? NoContent() : NotFound(); + } + + private static string ToErrorMessage(DocumentUploadError error) => error switch + { + DocumentUploadError.InvalidCategory => "Category ist ungültig.", + DocumentUploadError.FileTooLarge => "Die Datei überschreitet die maximal erlaubte Größe.", + DocumentUploadError.ContentTypeNotAllowed => "Dieser Dateityp ist nicht erlaubt.", + _ => "Upload fehlgeschlagen." + }; + + private static DocumentResponse ToResponse(Document document) + => new( + document.Id, + document.EntityType, + document.EntityId, + document.Category, + document.FileName, + document.ContentType, + document.SizeBytes, + document.Description, + document.UploadedByUserId, + document.UploadedByUser?.Username, + document.CreatedAt); +} diff --git a/omsorgCore/src/OmsorgCore.Api/Controllers/EmployeesController.cs b/omsorgCore/src/OmsorgCore.Api/Controllers/EmployeesController.cs index d039324..9691583 100644 --- a/omsorgCore/src/OmsorgCore.Api/Controllers/EmployeesController.cs +++ b/omsorgCore/src/OmsorgCore.Api/Controllers/EmployeesController.cs @@ -17,6 +17,7 @@ public class EmployeesController : ControllerBase { private const string StatusListKey = "EmployeeStatus"; private const string EmploymentTypeListKey = "EmploymentType"; + private const string QualificationListKey = "Qualification"; private readonly IEmployeeService _employeeService; private readonly IValueListRepository _valueListRepository; @@ -220,6 +221,14 @@ public class EmployeesController : ControllerBase return updated is null ? NotFound() : Ok(ToResponse(updated)); } + [HttpDelete("{id:guid}")] + [RequirePermission(ModuleType.Employees, PermissionAction.Delete)] + public async Task Delete(Guid id, CancellationToken cancellationToken) + { + var deleted = await _employeeService.DeleteAsync(id, cancellationToken); + return deleted ? NoContent() : NotFound(); + } + private static string? ValidateAddressFields(string? street, string? postalCode, string? city, string? country) { if (street is { Length: > 200 }) @@ -277,9 +286,13 @@ public class EmployeesController : ControllerBase } } - if (qualification is { Length: > 500 }) + if (qualification is not null) { - return "Qualification darf maximal 500 Zeichen lang sein."; + var allowedQualifications = await _valueListRepository.GetActiveValuesAsync(QualificationListKey, cancellationToken); + if (!allowedQualifications.Contains(qualification)) + { + return $"Qualification muss einer der folgenden Werte sein: {string.Join(", ", allowedQualifications)}."; + } } return null; diff --git a/omsorgCore/src/OmsorgCore.Api/Controllers/FacilitiesController.cs b/omsorgCore/src/OmsorgCore.Api/Controllers/FacilitiesController.cs index 19741d9..85c264c 100644 --- a/omsorgCore/src/OmsorgCore.Api/Controllers/FacilitiesController.cs +++ b/omsorgCore/src/OmsorgCore.Api/Controllers/FacilitiesController.cs @@ -17,6 +17,8 @@ public class FacilitiesController : ControllerBase { private const string CrmStatusListKey = "CrmStatus"; private const string FacilityTypeListKey = "FacilityType"; + private const string FollowUpPeriodsListKey = "FollowUpPeriods"; + private const string BillingIntervalListKey = "BillingInterval"; private readonly IFacilityService _facilityService; private readonly IValueListRepository _valueListRepository; @@ -34,6 +36,7 @@ public class FacilitiesController : ControllerBase public async Task>> GetAll( [FromQuery] string? search, [FromQuery] string? crmStatus, + [FromQuery] bool followUpDueOnly = false, [FromQuery] int page = 1, [FromQuery] int pageSize = 20, CancellationToken cancellationToken = default) @@ -41,7 +44,7 @@ public class FacilitiesController : ControllerBase page = Math.Max(page, 1); pageSize = Math.Clamp(pageSize, 1, 100); - var (items, totalCount) = await _facilityService.GetPagedAsync(search, crmStatus, page, pageSize, cancellationToken); + var (items, totalCount) = await _facilityService.GetPagedAsync(search, crmStatus, followUpDueOnly, page, pageSize, cancellationToken); return Ok(new PagedResponse(items.Select(ToResponse).ToList(), totalCount, page, pageSize)); } @@ -118,6 +121,12 @@ public class FacilitiesController : ControllerBase [RequirePermission(ModuleType.Facilities, PermissionAction.Edit)] public async Task> Update(Guid id, UpdateFacilityRequest request, CancellationToken cancellationToken) { + var existing = await _facilityService.GetByIdAsync(id, cancellationToken); + if (existing is null) + { + return NotFound(); + } + if (string.IsNullOrWhiteSpace(request.Name) || request.Name.Length > 300) { return BadRequest("Name ist erforderlich und darf maximal 300 Zeichen lang sein."); @@ -128,10 +137,37 @@ public class FacilitiesController : ControllerBase return BadRequest("CrmStatus ist erforderlich und darf maximal 50 Zeichen lang sein."); } - var allowedCrmStatuses = await _valueListRepository.GetActiveValuesAsync(CrmStatusListKey, cancellationToken); - if (!allowedCrmStatuses.Contains(request.CrmStatus)) + var crmStatusItems = await _valueListRepository.GetItemsAsync(CrmStatusListKey, cancellationToken); + var selectedCrmStatusItem = crmStatusItems.FirstOrDefault(i => i.Value == request.CrmStatus); + if (selectedCrmStatusItem is null) { - return BadRequest($"CrmStatus muss einer der folgenden Werte sein: {string.Join(", ", allowedCrmStatuses)}."); + return BadRequest($"CrmStatus muss einer der folgenden Werte sein: {string.Join(", ", crmStatusItems.Select(i => i.Value))}."); + } + + var currentCrmStatusItem = crmStatusItems.FirstOrDefault(i => i.Value == existing.CrmStatus); + if (currentCrmStatusItem is not null + && !await _valueListRepository.CanTransitionAsync(currentCrmStatusItem.Id, selectedCrmStatusItem.Id, cancellationToken)) + { + return BadRequest("Der Statuswechsel ist nicht zulässig."); + } + + DateTime? followUpDueDate = null; + if (selectedCrmStatusItem.TriggersFollowUp) + { + if (existing.CrmStatus == request.CrmStatus) + { + followUpDueDate = existing.FollowUpDueDate; + } + else + { + var allowedFollowUpPeriods = await _valueListRepository.GetActiveValuesAsync(FollowUpPeriodsListKey, cancellationToken); + if (request.FollowUpDays is null || !allowedFollowUpPeriods.Contains(request.FollowUpDays.Value.ToString())) + { + return BadRequest($"FollowUpDays ist bei CrmStatus \"{request.CrmStatus}\" erforderlich und muss einer der folgenden Werte sein: {string.Join(", ", allowedFollowUpPeriods)}."); + } + + followUpDueDate = DateTime.UtcNow.AddDays(request.FollowUpDays.Value); + } } if (request.FacilityType is { Length: > 100 }) @@ -165,10 +201,53 @@ public class FacilitiesController : ControllerBase return BadRequest(billingAddressError); } + if (request.BreakPolicy is { Length: > 1000 }) + { + return BadRequest("BreakPolicy darf maximal 1000 Zeichen lang sein."); + } + + if (request.IndividualAgreements is { Length: > 2000 }) + { + return BadRequest("IndividualAgreements darf maximal 2000 Zeichen lang sein."); + } + + if (request.BillingInterval is not null) + { + var allowedBillingIntervals = await _valueListRepository.GetActiveValuesAsync(BillingIntervalListKey, cancellationToken); + if (!allowedBillingIntervals.Contains(request.BillingInterval)) + { + return BadRequest($"BillingInterval muss einer der folgenden Werte sein: {string.Join(", ", allowedBillingIntervals)}."); + } + } + + if (request.BillingRate is < 0 + || request.TravelCostRate is < 0 + || request.MinimumHours is < 0 + || request.NightSurchargePercent is < 0 + || request.SaturdaySurchargePercent is < 0 + || request.SundaySurchargePercent is < 0 + || request.HolidaySurchargePercent is < 0 + || request.PaymentTermDays is < 0) + { + return BadRequest("Konditionswerte dürfen nicht negativ sein."); + } + var updates = new Facility { Name = request.Name, CrmStatus = request.CrmStatus, + FollowUpDueDate = followUpDueDate, + BillingRate = request.BillingRate, + NightSurchargePercent = request.NightSurchargePercent, + SaturdaySurchargePercent = request.SaturdaySurchargePercent, + SundaySurchargePercent = request.SundaySurchargePercent, + HolidaySurchargePercent = request.HolidaySurchargePercent, + TravelCostRate = request.TravelCostRate, + MinimumHours = request.MinimumHours, + BreakPolicy = request.BreakPolicy, + BillingInterval = request.BillingInterval, + PaymentTermDays = request.PaymentTermDays, + IndividualAgreements = request.IndividualAgreements, FacilityType = request.FacilityType, Website = request.Website, Street = request.Street, @@ -185,6 +264,14 @@ public class FacilitiesController : ControllerBase return updated is null ? NotFound() : Ok(ToResponse(updated)); } + [HttpDelete("{id:guid}")] + [RequirePermission(ModuleType.Facilities, PermissionAction.Delete)] + public async Task Delete(Guid id, CancellationToken cancellationToken) + { + var deleted = await _facilityService.DeleteAsync(id, cancellationToken); + return deleted ? NoContent() : NotFound(); + } + private static string? ValidateAddressFields(string? street, string? postalCode, string? city, string? country, string prefix) { if (street is { Length: > 200 }) @@ -224,5 +311,17 @@ public class FacilitiesController : ControllerBase facility.BillingStreet, facility.BillingPostalCode, facility.BillingCity, - facility.BillingCountry); + facility.BillingCountry, + facility.FollowUpDueDate, + facility.BillingRate, + facility.NightSurchargePercent, + facility.SaturdaySurchargePercent, + facility.SundaySurchargePercent, + facility.HolidaySurchargePercent, + facility.TravelCostRate, + facility.MinimumHours, + facility.BreakPolicy, + facility.BillingInterval, + facility.PaymentTermDays, + facility.IndividualAgreements); } diff --git a/omsorgCore/src/OmsorgCore.Api/Controllers/FacilityContactsController.cs b/omsorgCore/src/OmsorgCore.Api/Controllers/FacilityContactsController.cs index 40eda21..83df41e 100644 --- a/omsorgCore/src/OmsorgCore.Api/Controllers/FacilityContactsController.cs +++ b/omsorgCore/src/OmsorgCore.Api/Controllers/FacilityContactsController.cs @@ -100,6 +100,20 @@ public class FacilityContactsController : ControllerBase return updated is null ? NotFound() : Ok(ToResponse(updated)); } + [HttpDelete("{id:guid}")] + [RequirePermission(ModuleType.Facilities, PermissionAction.Delete)] + public async Task Delete(Guid facilityId, Guid id, CancellationToken cancellationToken) + { + var existing = await _facilityContactService.GetByIdAsync(id, cancellationToken); + if (existing is null || existing.FacilityId != facilityId) + { + return NotFound(); + } + + var deleted = await _facilityContactService.DeleteAsync(id, cancellationToken); + return deleted ? NoContent() : NotFound(); + } + private static string? ValidateRequest(string name, string? role, string? department, string? phoneNumber, string? email, string? notes) { if (string.IsNullOrWhiteSpace(name) || name.Length > 200) diff --git a/omsorgCore/src/OmsorgCore.Api/Controllers/FacilityQualificationRatesController.cs b/omsorgCore/src/OmsorgCore.Api/Controllers/FacilityQualificationRatesController.cs new file mode 100644 index 0000000..244ced3 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Controllers/FacilityQualificationRatesController.cs @@ -0,0 +1,144 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using OmsorgCore.Api.Contracts; +using OmsorgCore.Api.Security; +using OmsorgCore.Application.Abstractions; +using OmsorgCore.Application.Services; +using OmsorgCore.Domain.Entities; +using OmsorgCore.Domain.Enums; + +namespace OmsorgCore.Api.Controllers; + +/// +/// Qualifikationsabhängige Verrechnungssätze sind eine 1:n-Unterressource von Facility (FR-EIN-4) — +/// kein eigenständiges Core-Objekt, daher unter /api/facilities/{facilityId}/qualification-rates und +/// mit den gleichen Facilities-Rechten gegated statt einem eigenen ModuleType (analog FacilityContact). +/// +[ApiController] +[Authorize] +[Route("api/facilities/{facilityId:guid}/qualification-rates")] +public class FacilityQualificationRatesController : ControllerBase +{ + private const string QualificationListKey = "Qualification"; + + private readonly IFacilityService _facilityService; + private readonly IFacilityQualificationRateService _facilityQualificationRateService; + private readonly IValueListRepository _valueListRepository; + + public FacilityQualificationRatesController( + IFacilityService facilityService, + IFacilityQualificationRateService facilityQualificationRateService, + IValueListRepository valueListRepository) + { + _facilityService = facilityService; + _facilityQualificationRateService = facilityQualificationRateService; + _valueListRepository = valueListRepository; + } + + [HttpGet] + [RequirePermission(ModuleType.Facilities, PermissionAction.View)] + public async Task>> GetAll(Guid facilityId, CancellationToken cancellationToken) + { + if (await _facilityService.GetByIdAsync(facilityId, cancellationToken) is null) + { + return NotFound(); + } + + var rates = await _facilityQualificationRateService.GetByFacilityIdAsync(facilityId, cancellationToken); + return Ok(rates.Select(ToResponse).ToList()); + } + + [HttpPost] + [RequirePermission(ModuleType.Facilities, PermissionAction.Create)] + public async Task> Create(Guid facilityId, CreateFacilityQualificationRateRequest request, CancellationToken cancellationToken) + { + if (await _facilityService.GetByIdAsync(facilityId, cancellationToken) is null) + { + return NotFound(); + } + + var fieldError = await ValidateRequestAsync(request.Qualification, request.Rate, cancellationToken); + if (fieldError is not null) + { + return BadRequest(fieldError); + } + + var rate = new FacilityQualificationRate + { + FacilityId = facilityId, + Qualification = request.Qualification, + Rate = request.Rate + }; + + var created = await _facilityQualificationRateService.CreateAsync(rate, cancellationToken); + return CreatedAtAction(nameof(GetAll), new { facilityId }, ToResponse(created)); + } + + [HttpPut("{id:guid}")] + [RequirePermission(ModuleType.Facilities, PermissionAction.Edit)] + public async Task> Update(Guid facilityId, Guid id, UpdateFacilityQualificationRateRequest request, CancellationToken cancellationToken) + { + var existing = await _facilityQualificationRateService.GetByIdAsync(id, cancellationToken); + if (existing is null || existing.FacilityId != facilityId) + { + return NotFound(); + } + + var fieldError = await ValidateRequestAsync(request.Qualification, request.Rate, cancellationToken); + if (fieldError is not null) + { + return BadRequest(fieldError); + } + + var updates = new FacilityQualificationRate + { + Qualification = request.Qualification, + Rate = request.Rate + }; + + var updated = await _facilityQualificationRateService.UpdateAsync(id, updates, cancellationToken); + return updated is null ? NotFound() : Ok(ToResponse(updated)); + } + + [HttpDelete("{id:guid}")] + [RequirePermission(ModuleType.Facilities, PermissionAction.Delete)] + public async Task Delete(Guid facilityId, Guid id, CancellationToken cancellationToken) + { + var existing = await _facilityQualificationRateService.GetByIdAsync(id, cancellationToken); + if (existing is null || existing.FacilityId != facilityId) + { + return NotFound(); + } + + var deleted = await _facilityQualificationRateService.DeleteAsync(id, cancellationToken); + return deleted ? NoContent() : NotFound(); + } + + private async Task ValidateRequestAsync(string qualification, decimal rate, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(qualification) || qualification.Length > 200) + { + return "Qualification ist erforderlich und darf maximal 200 Zeichen lang sein."; + } + + var allowedQualifications = await _valueListRepository.GetActiveValuesAsync(QualificationListKey, cancellationToken); + if (!allowedQualifications.Contains(qualification)) + { + return $"Qualification muss einer der folgenden Werte sein: {string.Join(", ", allowedQualifications)}."; + } + + if (rate < 0) + { + return "Rate darf nicht negativ sein."; + } + + return null; + } + + private static FacilityQualificationRateResponse ToResponse(FacilityQualificationRate rate) + => new( + rate.Id, + rate.FacilityId, + rate.Qualification, + rate.Rate); +} diff --git a/omsorgCore/src/OmsorgCore.Api/Controllers/OrdersController.cs b/omsorgCore/src/OmsorgCore.Api/Controllers/OrdersController.cs index 18d37ea..16aed20 100644 --- a/omsorgCore/src/OmsorgCore.Api/Controllers/OrdersController.cs +++ b/omsorgCore/src/OmsorgCore.Api/Controllers/OrdersController.cs @@ -15,6 +15,9 @@ namespace OmsorgCore.Api.Controllers; public class OrdersController : ControllerBase { private const string StatusListKey = "OrderStatus"; + private const string QualificationListKey = "Qualification"; + private const string ShiftTypeListKey = "ShiftType"; + private const string PriorityListKey = "Priority"; private readonly IOrderService _orderService; private readonly IFacilityContactService _facilityContactService; @@ -39,6 +42,9 @@ public class OrdersController : ControllerBase [FromQuery] string? search, [FromQuery] Guid? statusId, [FromQuery] Guid? facilityId, + [FromQuery] string? priority, + [FromQuery] string? requiredQualification, + [FromQuery] string? shiftType, [FromQuery] int page = 1, [FromQuery] int pageSize = 20, CancellationToken cancellationToken = default) @@ -46,7 +52,8 @@ public class OrdersController : ControllerBase page = Math.Max(page, 1); pageSize = Math.Clamp(pageSize, 1, 100); - var (items, totalCount) = await _orderService.GetPagedAsync(search, statusId, facilityId, page, pageSize, cancellationToken); + var (items, totalCount) = await _orderService.GetPagedAsync( + search, statusId, facilityId, priority, requiredQualification, shiftType, page, pageSize, cancellationToken); return Ok(new PagedResponse(items.Select(ToResponse).ToList(), totalCount, page, pageSize)); } @@ -62,7 +69,7 @@ public class OrdersController : ControllerBase [RequirePermission(ModuleType.Orders, PermissionAction.Create)] public async Task> Create(CreateOrderRequest request, CancellationToken cancellationToken) { - var fieldError = ValidateFields( + var fieldError = await ValidateFieldsAsync( request.FacilityId, request.StartDate, request.EndDate, @@ -70,7 +77,8 @@ public class OrdersController : ControllerBase request.ShiftType, request.RequiredHeadcount, request.Conditions, - request.Priority); + request.Priority, + cancellationToken); if (fieldError is not null) { return BadRequest(fieldError); @@ -106,7 +114,7 @@ public class OrdersController : ControllerBase [RequirePermission(ModuleType.Orders, PermissionAction.Edit)] public async Task> Update(Guid id, UpdateOrderRequest request, CancellationToken cancellationToken) { - var fieldError = ValidateFields( + var fieldError = await ValidateFieldsAsync( request.FacilityId, request.StartDate, request.EndDate, @@ -114,7 +122,8 @@ public class OrdersController : ControllerBase request.ShiftType, request.RequiredHeadcount, request.Conditions, - request.Priority); + request.Priority, + cancellationToken); if (fieldError is not null) { return BadRequest(fieldError); @@ -160,6 +169,14 @@ public class OrdersController : ControllerBase return Ok(ToResponse(result.Order!)); } + [HttpDelete("{id:guid}")] + [RequirePermission(ModuleType.Orders, PermissionAction.Delete)] + public async Task Delete(Guid id, CancellationToken cancellationToken) + { + var deleted = await _orderService.DeleteAsync(id, cancellationToken); + return deleted ? NoContent() : NotFound(); + } + private async Task ValidateFacilityContactAsync(Guid? facilityContactId, Guid facilityId, CancellationToken cancellationToken) { if (facilityContactId is null) @@ -176,7 +193,7 @@ public class OrdersController : ControllerBase return null; } - private static string? ValidateFields( + private async Task ValidateFieldsAsync( Guid facilityId, DateOnly startDate, DateOnly? endDate, @@ -184,7 +201,8 @@ public class OrdersController : ControllerBase string? shiftType, int requiredHeadcount, string? conditions, - string priority) + string priority, + CancellationToken cancellationToken) { if (facilityId == Guid.Empty) { @@ -206,19 +224,33 @@ public class OrdersController : ControllerBase return "RequiredHeadcount muss mindestens 1 sein."; } - if (string.IsNullOrWhiteSpace(priority) || priority.Length > 50) + if (string.IsNullOrWhiteSpace(priority)) { - return "Priority ist erforderlich und darf maximal 50 Zeichen lang sein."; + return "Priority ist erforderlich."; } - if (requiredQualification is { Length: > 200 }) + var priorityItems = await _valueListService.GetItemsAsync(PriorityListKey, cancellationToken); + if (!priorityItems.Any(i => i.Value == priority)) { - return "RequiredQualification darf maximal 200 Zeichen lang sein."; + return $"Priority muss einer der folgenden Werte sein: {string.Join(", ", priorityItems.Select(i => i.Value))}."; } - if (shiftType is { Length: > 100 }) + if (requiredQualification is not null) { - return "ShiftType darf maximal 100 Zeichen lang sein."; + var qualificationItems = await _valueListService.GetItemsAsync(QualificationListKey, cancellationToken); + if (!qualificationItems.Any(i => i.Value == requiredQualification)) + { + return $"RequiredQualification muss einer der folgenden Werte sein: {string.Join(", ", qualificationItems.Select(i => i.Value))}."; + } + } + + if (shiftType is not null) + { + var shiftTypeItems = await _valueListService.GetItemsAsync(ShiftTypeListKey, cancellationToken); + if (!shiftTypeItems.Any(i => i.Value == shiftType)) + { + return $"ShiftType muss einer der folgenden Werte sein: {string.Join(", ", shiftTypeItems.Select(i => i.Value))}."; + } } if (conditions is { Length: > 500 }) diff --git a/omsorgCore/src/OmsorgCore.Api/Controllers/RolesController.cs b/omsorgCore/src/OmsorgCore.Api/Controllers/RolesController.cs index 811eb86..f7f38e3 100644 --- a/omsorgCore/src/OmsorgCore.Api/Controllers/RolesController.cs +++ b/omsorgCore/src/OmsorgCore.Api/Controllers/RolesController.cs @@ -60,7 +60,7 @@ public class RolesController : ControllerBase } var permissions = role.RolePermissions - .Select(rp => new PermissionDto(rp.Module, rp.Action)) + .Select(rp => new PermissionDto(rp.Module, rp.Action, rp.Scope)) .ToList(); return Ok(new RolePermissionsResponse(role.Id, role.Name, permissions)); @@ -70,7 +70,7 @@ public class RolesController : ControllerBase [RequirePermission(ModuleType.UserManagement, PermissionAction.Edit)] public async Task UpdatePermissions(Guid id, UpdateRolePermissionsRequest request, CancellationToken cancellationToken) { - var parsed = request.Permissions.Select(dto => (dto.Module, dto.Action)).ToList(); + var parsed = request.Permissions.Select(dto => (dto.Module, dto.Action, dto.Scope)).ToList(); var result = await _roleService.UpdatePermissionsAsync(id, parsed, cancellationToken); if (!result.Success) diff --git a/omsorgCore/src/OmsorgCore.Api/Controllers/TimeEntriesController.cs b/omsorgCore/src/OmsorgCore.Api/Controllers/TimeEntriesController.cs new file mode 100644 index 0000000..4a7a915 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Controllers/TimeEntriesController.cs @@ -0,0 +1,204 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using OmsorgCore.Api.Contracts; +using OmsorgCore.Api.Security; +using OmsorgCore.Application.Services; +using OmsorgCore.Domain.Entities; +using OmsorgCore.Domain.Enums; + +namespace OmsorgCore.Api.Controllers; + +/// +/// Strukturierte Zeiterfassung pro Schicht (FR-ZE-1) mit Statuspipeline +/// Entwurf -> Eingereicht -> Prüfung -> Rückfrage -> Freigegeben -> Abgerechnet (FR-ZE-2). Außendienst +/// darf nur Create/View/Edit mit PermissionScope.Own (eigene Einträge, EmployeeId wird serverseitig +/// aus dem JWT gesetzt, siehe TimeEntryService.CreateAsync) und die Selbst-Einreichungs-Kante über +/// auslösen; Büro-Rollen entscheiden über (Approve). +/// +[ApiController] +[Authorize] +[Route("api/time-entries")] +public class TimeEntriesController : ControllerBase +{ + private readonly ITimeEntryService _timeEntryService; + + public TimeEntriesController(ITimeEntryService timeEntryService) + { + _timeEntryService = timeEntryService; + } + + [HttpGet] + [RequirePermission(ModuleType.TimeEntries, PermissionAction.View)] + public async Task>> GetAll( + [FromQuery] Guid? statusId, + [FromQuery] Guid? employeeId, + [FromQuery] Guid? orderId, + [FromQuery] int page = 1, + [FromQuery] int pageSize = 20, + CancellationToken cancellationToken = default) + { + page = Math.Max(page, 1); + pageSize = Math.Clamp(pageSize, 1, 100); + + var (items, totalCount) = await _timeEntryService.GetPagedAsync(statusId, employeeId, orderId, page, pageSize, cancellationToken); + return Ok(new PagedResponse(items.Select(ToResponse).ToList(), totalCount, page, pageSize)); + } + + [HttpGet("{id:guid}")] + [RequirePermission(ModuleType.TimeEntries, PermissionAction.View)] + public async Task> GetById(Guid id, CancellationToken cancellationToken) + { + var timeEntry = await _timeEntryService.GetByIdAsync(id, cancellationToken); + return timeEntry is null ? NotFound() : Ok(ToResponse(timeEntry)); + } + + [HttpPost] + [RequirePermission(ModuleType.TimeEntries, PermissionAction.Create)] + public async Task> Create(CreateTimeEntryRequest request, CancellationToken cancellationToken) + { + var fieldError = ValidateFields(request.Start, request.End, request.NightHours, request.SaturdayHours, request.SundayHours, request.HolidayHours); + if (fieldError is not null) + { + return BadRequest(fieldError); + } + + var timeEntry = new TimeEntry + { + OrderId = request.OrderId, + Date = request.Date, + Start = request.Start, + End = request.End, + BreakDuration = request.BreakDuration, + NightHours = request.NightHours, + SaturdayHours = request.SaturdayHours, + SundayHours = request.SundayHours, + HolidayHours = request.HolidayHours + }; + + var created = await _timeEntryService.CreateAsync(timeEntry, cancellationToken); + if (created is null) + { + return BadRequest("Zeiterfassung konnte nicht angelegt werden - entweder kein verknüpfter Mitarbeiter oder der Auftrag existiert nicht."); + } + + var reloaded = await _timeEntryService.GetByIdAsync(created.Id, cancellationToken); + return CreatedAtAction(nameof(GetById), new { id = created.Id }, ToResponse(reloaded!)); + } + + [HttpPut("{id:guid}")] + [RequirePermission(ModuleType.TimeEntries, PermissionAction.Edit)] + public async Task> Update(Guid id, UpdateTimeEntryRequest request, CancellationToken cancellationToken) + { + var fieldError = ValidateFields(request.Start, request.End, request.NightHours, request.SaturdayHours, request.SundayHours, request.HolidayHours); + if (fieldError is not null) + { + return BadRequest(fieldError); + } + + var updates = new TimeEntry + { + OrderId = request.OrderId, + Date = request.Date, + Start = request.Start, + End = request.End, + BreakDuration = request.BreakDuration, + NightHours = request.NightHours, + SaturdayHours = request.SaturdayHours, + SundayHours = request.SundayHours, + HolidayHours = request.HolidayHours + }; + + var result = await _timeEntryService.UpdateAsync(id, updates, cancellationToken); + if (!result.Success) + { + return result.FailureReason switch + { + UpdateTimeEntryFailureReason.NotEditable => BadRequest("Die Zeiterfassung wurde bereits zur Prüfung übergeben und kann nicht mehr bearbeitet werden."), + UpdateTimeEntryFailureReason.OrderNotFound => BadRequest("Der angegebene Auftrag existiert nicht."), + _ => NotFound() + }; + } + + var reloaded = await _timeEntryService.GetByIdAsync(result.TimeEntry!.Id, cancellationToken); + return Ok(ToResponse(reloaded!)); + } + + [HttpPost("{id:guid}/submit")] + [RequirePermission(ModuleType.TimeEntries, PermissionAction.Edit)] + public async Task> Submit(Guid id, CancellationToken cancellationToken) + { + var result = await _timeEntryService.SubmitAsync(id, cancellationToken); + if (!result.Success) + { + return result.FailureReason == SubmitTimeEntryFailureReason.NoSelfServiceTransition + ? BadRequest("Aus dem aktuellen Status ist keine Einreichung möglich.") + : NotFound(); + } + + var reloaded = await _timeEntryService.GetByIdAsync(result.TimeEntry!.Id, cancellationToken); + return Ok(ToResponse(reloaded!)); + } + + [HttpPost("{id:guid}/decision")] + [RequirePermission(ModuleType.TimeEntries, PermissionAction.Approve)] + public async Task> Decide(Guid id, TimeEntryDecisionRequest request, CancellationToken cancellationToken) + { + if (request.AdminNote is { Length: > 500 }) + { + return BadRequest("AdminNote darf maximal 500 Zeichen lang sein."); + } + + var result = await _timeEntryService.DecideAsync(id, request.StatusId, request.AdminNote, cancellationToken); + if (!result.Success) + { + return result.FailureReason == DecideTimeEntryFailureReason.InvalidStatusTransition + ? BadRequest("Der Statuswechsel ist nicht zulässig.") + : NotFound(); + } + + var reloaded = await _timeEntryService.GetByIdAsync(result.TimeEntry!.Id, cancellationToken); + return Ok(ToResponse(reloaded!)); + } + + [HttpDelete("{id:guid}")] + [RequirePermission(ModuleType.TimeEntries, PermissionAction.Delete)] + public async Task Delete(Guid id, CancellationToken cancellationToken) + { + var deleted = await _timeEntryService.DeleteAsync(id, cancellationToken); + return deleted ? NoContent() : NotFound(); + } + + // End < Start ist bewusst erlaubt (Nachtschichten, die über Mitternacht gehen) - keine + // Start/Ende-Reihenfolge-Prüfung wie bei Absence.StartDate/EndDate. + private static string? ValidateFields(TimeOnly start, TimeOnly end, decimal nightHours, decimal saturdayHours, decimal sundayHours, decimal holidayHours) + { + if (nightHours < 0 || saturdayHours < 0 || sundayHours < 0 || holidayHours < 0) + { + return "Zuschlagsstunden dürfen nicht negativ sein."; + } + + return null; + } + + private static TimeEntryResponse ToResponse(TimeEntry timeEntry) + => new( + timeEntry.Id, + timeEntry.EmployeeId, + timeEntry.Employee is null ? string.Empty : $"{timeEntry.Employee.FirstName} {timeEntry.Employee.LastName}", + timeEntry.OrderId, + timeEntry.Order?.FacilityId ?? Guid.Empty, + timeEntry.Order?.Facility?.Name ?? string.Empty, + timeEntry.Date, + timeEntry.Start, + timeEntry.End, + timeEntry.BreakDuration, + timeEntry.NightHours, + timeEntry.SaturdayHours, + timeEntry.SundayHours, + timeEntry.HolidayHours, + timeEntry.StatusId, + timeEntry.Status?.Value ?? string.Empty, + timeEntry.Status?.IsEditableByOwner ?? false, + timeEntry.AdminNote, + timeEntry.CreatedAt); +} diff --git a/omsorgCore/src/OmsorgCore.Api/Controllers/TrashController.cs b/omsorgCore/src/OmsorgCore.Api/Controllers/TrashController.cs new file mode 100644 index 0000000..4386ce8 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Controllers/TrashController.cs @@ -0,0 +1,179 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using OmsorgCore.Api.Contracts; +using OmsorgCore.Api.Security; +using OmsorgCore.Application.Services; +using OmsorgCore.Domain.Entities; +using OmsorgCore.Domain.Enums; + +namespace OmsorgCore.Api.Controllers; + +/// +/// Papierkorb: listet und stellt soft-gelöschte Datensätze der 5 Core-Objekte mit vollem CRUD +/// wieder her. Reine API-Gruppierung für die Papierkorb-Seite in omsorgapp — kein eigener +/// ModuleType/eigenes Recht, jede Route ist über das Recht des jeweiligen Objekts gegated +/// (z. B. ModuleType.Employees + PermissionAction.Recover), analog zu den bestehenden +/// Delete-Endpoints in EmployeesController/FacilitiesController/etc. +/// +[ApiController] +[Authorize] +[Route("api/trash")] +public class TrashController : ControllerBase +{ + private readonly IEmployeeService _employeeService; + private readonly IFacilityService _facilityService; + private readonly IContractService _contractService; + private readonly IOrderService _orderService; + private readonly IFacilityContactService _facilityContactService; + private readonly IFacilityQualificationRateService _facilityQualificationRateService; + private readonly IAbsenceService _absenceService; + private readonly ITimeEntryService _timeEntryService; + + public TrashController( + IEmployeeService employeeService, + IFacilityService facilityService, + IContractService contractService, + IOrderService orderService, + IFacilityContactService facilityContactService, + IFacilityQualificationRateService facilityQualificationRateService, + IAbsenceService absenceService, + ITimeEntryService timeEntryService) + { + _employeeService = employeeService; + _facilityService = facilityService; + _contractService = contractService; + _orderService = orderService; + _facilityContactService = facilityContactService; + _facilityQualificationRateService = facilityQualificationRateService; + _absenceService = absenceService; + _timeEntryService = timeEntryService; + } + + [HttpGet("employees")] + [RequirePermission(ModuleType.Employees, PermissionAction.Recover)] + public async Task>> GetDeletedEmployees([FromQuery] string? search, CancellationToken cancellationToken) + { + var employees = await _employeeService.GetDeletedAsync(search, cancellationToken); + return Ok(employees.Select(e => new TrashEmployeeResponse(e.Id, e.FirstName, e.LastName, e.DeletedAt)).ToList()); + } + + [HttpPost("employees/{id:guid}/restore")] + [RequirePermission(ModuleType.Employees, PermissionAction.Recover)] + public async Task RestoreEmployee(Guid id, CancellationToken cancellationToken) + { + var restored = await _employeeService.RestoreAsync(id, cancellationToken); + return restored ? NoContent() : NotFound(); + } + + [HttpGet("facilities")] + [RequirePermission(ModuleType.Facilities, PermissionAction.Recover)] + public async Task>> GetDeletedFacilities([FromQuery] string? search, CancellationToken cancellationToken) + { + var facilities = await _facilityService.GetDeletedAsync(search, cancellationToken); + return Ok(facilities.Select(f => new TrashFacilityResponse(f.Id, f.Name, f.DeletedAt)).ToList()); + } + + [HttpPost("facilities/{id:guid}/restore")] + [RequirePermission(ModuleType.Facilities, PermissionAction.Recover)] + public async Task RestoreFacility(Guid id, CancellationToken cancellationToken) + { + var restored = await _facilityService.RestoreAsync(id, cancellationToken); + return restored ? NoContent() : NotFound(); + } + + [HttpGet("contracts")] + [RequirePermission(ModuleType.Contracts, PermissionAction.Recover)] + public async Task>> GetDeletedContracts([FromQuery] string? search, CancellationToken cancellationToken) + { + var contracts = await _contractService.GetDeletedAsync(search, cancellationToken); + return Ok(contracts.Select(c => new TrashContractResponse(c.Id, c.ContractType, c.DeletedAt)).ToList()); + } + + [HttpPost("contracts/{id:guid}/restore")] + [RequirePermission(ModuleType.Contracts, PermissionAction.Recover)] + public async Task RestoreContract(Guid id, CancellationToken cancellationToken) + { + var restored = await _contractService.RestoreAsync(id, cancellationToken); + return restored ? NoContent() : NotFound(); + } + + [HttpGet("orders")] + [RequirePermission(ModuleType.Orders, PermissionAction.Recover)] + public async Task>> GetDeletedOrders([FromQuery] string? search, CancellationToken cancellationToken) + { + var orders = await _orderService.GetDeletedAsync(search, cancellationToken); + return Ok(orders.Select(o => new TrashOrderResponse(o.Id, o.RequiredQualification, o.DeletedAt)).ToList()); + } + + [HttpPost("orders/{id:guid}/restore")] + [RequirePermission(ModuleType.Orders, PermissionAction.Recover)] + public async Task RestoreOrder(Guid id, CancellationToken cancellationToken) + { + var restored = await _orderService.RestoreAsync(id, cancellationToken); + return restored ? NoContent() : NotFound(); + } + + [HttpGet("facility-contacts")] + [RequirePermission(ModuleType.Facilities, PermissionAction.Recover)] + public async Task>> GetDeletedFacilityContacts([FromQuery] string? search, CancellationToken cancellationToken) + { + var contacts = await _facilityContactService.GetDeletedAsync(search, cancellationToken); + return Ok(contacts.Select(c => new TrashFacilityContactResponse(c.Id, c.FacilityId, c.Name, c.DeletedAt)).ToList()); + } + + [HttpPost("facility-contacts/{id:guid}/restore")] + [RequirePermission(ModuleType.Facilities, PermissionAction.Recover)] + public async Task RestoreFacilityContact(Guid id, CancellationToken cancellationToken) + { + var restored = await _facilityContactService.RestoreAsync(id, cancellationToken); + return restored ? NoContent() : NotFound(); + } + + [HttpGet("facility-qualification-rates")] + [RequirePermission(ModuleType.Facilities, PermissionAction.Recover)] + public async Task>> GetDeletedFacilityQualificationRates([FromQuery] string? search, CancellationToken cancellationToken) + { + var rates = await _facilityQualificationRateService.GetDeletedAsync(search, cancellationToken); + return Ok(rates.Select(r => new TrashFacilityQualificationRateResponse(r.Id, r.FacilityId, r.Qualification, r.DeletedAt)).ToList()); + } + + [HttpPost("facility-qualification-rates/{id:guid}/restore")] + [RequirePermission(ModuleType.Facilities, PermissionAction.Recover)] + public async Task RestoreFacilityQualificationRate(Guid id, CancellationToken cancellationToken) + { + var restored = await _facilityQualificationRateService.RestoreAsync(id, cancellationToken); + return restored ? NoContent() : NotFound(); + } + + [HttpGet("absences")] + [RequirePermission(ModuleType.Absences, PermissionAction.Recover)] + public async Task>> GetDeletedAbsences([FromQuery] string? search, CancellationToken cancellationToken) + { + var absences = await _absenceService.GetDeletedAsync(search, cancellationToken); + return Ok(absences.Select(a => new TrashAbsenceResponse(a.Id, a.Type, a.DeletedAt)).ToList()); + } + + [HttpPost("absences/{id:guid}/restore")] + [RequirePermission(ModuleType.Absences, PermissionAction.Recover)] + public async Task RestoreAbsence(Guid id, CancellationToken cancellationToken) + { + var restored = await _absenceService.RestoreAsync(id, cancellationToken); + return restored ? NoContent() : NotFound(); + } + + [HttpGet("time-entries")] + [RequirePermission(ModuleType.TimeEntries, PermissionAction.Recover)] + public async Task>> GetDeletedTimeEntries([FromQuery] string? search, CancellationToken cancellationToken) + { + var timeEntries = await _timeEntryService.GetDeletedAsync(search, cancellationToken); + return Ok(timeEntries.Select(t => new TrashTimeEntryResponse(t.Id, t.Date, t.DeletedAt)).ToList()); + } + + [HttpPost("time-entries/{id:guid}/restore")] + [RequirePermission(ModuleType.TimeEntries, PermissionAction.Recover)] + public async Task RestoreTimeEntry(Guid id, CancellationToken cancellationToken) + { + var restored = await _timeEntryService.RestoreAsync(id, cancellationToken); + return restored ? NoContent() : NotFound(); + } +} diff --git a/omsorgCore/src/OmsorgCore.Api/Controllers/UsersController.cs b/omsorgCore/src/OmsorgCore.Api/Controllers/UsersController.cs index 289f012..51a2d2b 100644 --- a/omsorgCore/src/OmsorgCore.Api/Controllers/UsersController.cs +++ b/omsorgCore/src/OmsorgCore.Api/Controllers/UsersController.cs @@ -32,7 +32,7 @@ public class UsersController : ControllerBase } [HttpGet] - [RequirePermission(ModuleType.UserManagement, PermissionAction.View)] + [RequirePermission(ModuleType.Users, PermissionAction.View)] public async Task>> GetAll(CancellationToken cancellationToken) { var users = await _userService.GetAllAsync(cancellationToken); @@ -40,7 +40,7 @@ public class UsersController : ControllerBase } [HttpPost] - [RequirePermission(ModuleType.UserManagement, PermissionAction.Create)] + [RequirePermission(ModuleType.Users, PermissionAction.Create)] public async Task> Create(CreateUserRequest request, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(request.Username) || request.Username.Length > 100) @@ -86,7 +86,7 @@ public class UsersController : ControllerBase } [HttpPost("{id:guid}/reset-password")] - [RequirePermission(ModuleType.UserManagement, PermissionAction.Edit)] + [RequirePermission(ModuleType.Users, PermissionAction.Edit)] public async Task ResetPassword(Guid id, ResetUserPasswordRequest request, CancellationToken cancellationToken) { var (parseError, mode, pinValidity) = ParseModeAndPinValidity(request.Mode, request.PinValidityDays, request.InitialPassword); @@ -120,7 +120,7 @@ public class UsersController : ControllerBase } [HttpPut("{id:guid}")] - [RequirePermission(ModuleType.UserManagement, PermissionAction.Edit)] + [RequirePermission(ModuleType.Users, PermissionAction.Edit)] public async Task Update(Guid id, UpdateUserRequest request, CancellationToken cancellationToken) { var result = await _userService.UpdateAsync(id, request.RoleId, request.IsActive, cancellationToken); @@ -177,7 +177,7 @@ public class UsersController : ControllerBase } return Ok(overrides.Select(o => new UserPermissionOverrideResponse( - o.Id, o.Module, o.Action, o.Effect)).ToList()); + o.Id, o.Module, o.Action, o.Effect, o.Scope)).ToList()); } [HttpPost("{id:guid}/permission-overrides")] @@ -185,7 +185,7 @@ public class UsersController : ControllerBase public async Task> AddPermissionOverride( Guid id, AddUserPermissionOverrideRequest request, CancellationToken cancellationToken) { - var result = await _userService.AddPermissionOverrideAsync(id, request.Module, request.Action, request.Effect, cancellationToken); + var result = await _userService.AddPermissionOverrideAsync(id, request.Module, request.Action, request.Effect, request.Scope, cancellationToken); if (!result.Success) { return result.FailureReason switch @@ -196,7 +196,7 @@ public class UsersController : ControllerBase } var o = result.Override!; - return Ok(new UserPermissionOverrideResponse(o.Id, o.Module, o.Action, o.Effect)); + return Ok(new UserPermissionOverrideResponse(o.Id, o.Module, o.Action, o.Effect, o.Scope)); } [HttpDelete("{id:guid}/permission-overrides/{overrideId:guid}")] diff --git a/omsorgCore/src/OmsorgCore.Api/Controllers/ValueListsController.cs b/omsorgCore/src/OmsorgCore.Api/Controllers/ValueListsController.cs index 68242a5..083108a 100644 --- a/omsorgCore/src/OmsorgCore.Api/Controllers/ValueListsController.cs +++ b/omsorgCore/src/OmsorgCore.Api/Controllers/ValueListsController.cs @@ -12,8 +12,8 @@ namespace OmsorgCore.Api.Controllers; /// Verwaltet die konfigurierbaren Auswahllisten (Mitarbeiterstatus, Beschäftigungsart, CRM-Status, /// Einrichtungstyp, Vertragstyp/-status, Auftragsstatus) — siehe omsorgCore/CLAUDE.md, Abschnitt /// "Konfigurierbare Auswahllisten". Lesen ist für jeden eingeloggten Nutzer erlaubt (die aufrufenden -/// Formulare gehören zu unterschiedlichen Modulen), Schreiben ist eine Admin-Funktion und läuft über -/// dasselbe Recht wie die übrige "Einstellungen"-Seite (). +/// Formulare gehören zu unterschiedlichen Modulen), Schreiben ist eine eigene Admin-Funktion +/// (), getrennt von der Benutzer-/Rechteverwaltung. /// [ApiController] [Authorize] @@ -42,7 +42,7 @@ public class ValueListsController : ControllerBase } [HttpPost("{key}/items")] - [RequirePermission(ModuleType.UserManagement, PermissionAction.Edit)] + [RequirePermission(ModuleType.Configuration, PermissionAction.Edit)] public async Task> CreateItem(string key, CreateValueListItemRequest request, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(request.Value) || request.Value.Length > 100) @@ -53,7 +53,7 @@ public class ValueListsController : ControllerBase try { var item = await _valueListService.CreateItemAsync( - key, request.Value, request.SortOrder, request.IsDefault, request.IsInitial, request.IsTerminal, cancellationToken); + key, request.Value, request.SortOrder, request.IsDefault, request.IsInitial, request.IsTerminal, request.TriggersFollowUp, cancellationToken); return Ok(ToResponse(item)); } catch (InvalidOperationException ex) @@ -63,7 +63,7 @@ public class ValueListsController : ControllerBase } [HttpPut("{key}/items/{id:guid}")] - [RequirePermission(ModuleType.UserManagement, PermissionAction.Edit)] + [RequirePermission(ModuleType.Configuration, PermissionAction.Edit)] public async Task> UpdateItem(string key, Guid id, UpdateValueListItemRequest request, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(request.Value) || request.Value.Length > 100) @@ -72,12 +72,12 @@ public class ValueListsController : ControllerBase } var item = await _valueListService.UpdateItemAsync( - id, request.Value, request.SortOrder, request.IsDefault, request.IsInitial, request.IsTerminal, cancellationToken); + id, request.Value, request.SortOrder, request.IsDefault, request.IsInitial, request.IsTerminal, request.TriggersFollowUp, cancellationToken); return item is null ? NotFound() : Ok(ToResponse(item)); } [HttpDelete("{key}/items/{id:guid}")] - [RequirePermission(ModuleType.UserManagement, PermissionAction.Edit)] + [RequirePermission(ModuleType.Configuration, PermissionAction.Edit)] public async Task DeleteItem(string key, Guid id, CancellationToken cancellationToken) { var result = await _valueListService.DeleteItemAsync(id, cancellationToken); @@ -97,7 +97,7 @@ public class ValueListsController : ControllerBase } [HttpGet("{key}/items/{id:guid}/usages")] - [RequirePermission(ModuleType.UserManagement, PermissionAction.Edit)] + [RequirePermission(ModuleType.Configuration, PermissionAction.Edit)] public async Task>> GetUsages(string key, Guid id, CancellationToken cancellationToken) { var usages = await _valueListService.GetUsagesAsync(id, cancellationToken); @@ -108,11 +108,11 @@ public class ValueListsController : ControllerBase public async Task>> GetTransitions(string key, CancellationToken cancellationToken) { var transitions = await _valueListService.GetTransitionsAsync(key, cancellationToken); - return Ok(transitions.Select(t => new ValueListTransitionResponse(t.Id, t.FromItemId, t.ToItemId)).ToList()); + return Ok(transitions.Select(t => new ValueListTransitionResponse(t.Id, t.FromItemId, t.ToItemId, t.RequiresApproval)).ToList()); } [HttpPut("{key}/transitions")] - [RequirePermission(ModuleType.UserManagement, PermissionAction.Edit)] + [RequirePermission(ModuleType.Configuration, PermissionAction.Edit)] public async Task ReplaceTransitions(string key, List request, CancellationToken cancellationToken) { await _valueListService.ReplaceTransitionsAsync(key, request.Select(t => (t.FromItemId, t.ToItemId)), cancellationToken); @@ -120,5 +120,5 @@ public class ValueListsController : ControllerBase } private static ValueListItemResponse ToResponse(ValueListItem item) - => new(item.Id, item.Value, item.SortOrder, item.IsDefault, item.IsInitial, item.IsTerminal); + => new(item.Id, item.Value, item.SortOrder, item.IsDefault, item.IsInitial, item.IsTerminal, item.TriggersFollowUp); } diff --git a/omsorgCore/src/OmsorgCore.Api/Program.cs b/omsorgCore/src/OmsorgCore.Api/Program.cs index adbdaa4..6de8755 100644 --- a/omsorgCore/src/OmsorgCore.Api/Program.cs +++ b/omsorgCore/src/OmsorgCore.Api/Program.cs @@ -50,6 +50,7 @@ builder.Services.AddSwaggerGen(options => // Schichten verdrahten: Domain kennt niemanden, Application kennt nur Domain, // Infrastructure implementiert Application-Interfaces, Engine ist die Event-Schicht // auf denselben Daten, Api verdrahtet alles nur hier. +builder.Services.AddMemoryCache(); builder.Services.AddApplication(); builder.Services.AddInfrastructure(builder.Configuration); builder.Services.AddEngine(); @@ -58,6 +59,18 @@ builder.Services.AddEmail(builder.Configuration); builder.Services.AddHttpContextAccessor(); builder.Services.AddScoped(); +// Nötig, seit omsorgapp als Browser-SPA statt Electron läuft: der Refresh-Token geht per +// HttpOnly-Cookie, dafür muss der Browser die Cross-Origin-Antwort mit Credentials akzeptieren. +var corsOrigins = builder.Configuration.GetSection("Cors:AllowedOrigins").Get() ?? Array.Empty(); +builder.Services.AddCors(options => +{ + options.AddPolicy("Frontend", policy => policy + .WithOrigins(corsOrigins) + .AllowAnyHeader() + .AllowAnyMethod() + .AllowCredentials()); +}); + var jwtOptions = builder.Configuration.GetSection(JwtOptions.SectionName).Get() ?? new JwtOptions(); builder.Services @@ -183,6 +196,8 @@ if (app.Environment.IsDevelopment()) app.UseHttpsRedirection(); +app.UseCors("Frontend"); + app.UseAuthentication(); app.UseAuthorization(); diff --git a/omsorgCore/src/OmsorgCore.Api/Security/CurrentUserService.cs b/omsorgCore/src/OmsorgCore.Api/Security/CurrentUserService.cs index a21b394..ee409bd 100644 --- a/omsorgCore/src/OmsorgCore.Api/Security/CurrentUserService.cs +++ b/omsorgCore/src/OmsorgCore.Api/Security/CurrentUserService.cs @@ -30,4 +30,13 @@ public class CurrentUserService : ICurrentUserService public string? RoleName => _httpContextAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.Role); public string? IpAddress => _httpContextAccessor.HttpContext?.Connection.RemoteIpAddress?.ToString(); + + public Guid? EmployeeId + { + get + { + var value = _httpContextAccessor.HttpContext?.User.FindFirstValue("employeeId"); + return Guid.TryParse(value, out var id) ? id : null; + } + } } diff --git a/omsorgCore/src/OmsorgCore.Api/appsettings.json b/omsorgCore/src/OmsorgCore.Api/appsettings.json index 23ba850..b01a004 100644 --- a/omsorgCore/src/OmsorgCore.Api/appsettings.json +++ b/omsorgCore/src/OmsorgCore.Api/appsettings.json @@ -6,6 +6,9 @@ } }, "AllowedHosts": "*", + "Cors": { + "AllowedOrigins": [] + }, "Jwt": { "Issuer": "OmsorgCore", "Audience": "OmsorgClients", @@ -21,6 +24,11 @@ "MaxLoginFailures": 5, "LoginLockoutMinutes": 10 }, + "Storage": { + "DocumentsRootPath": "App_Data/documents", + "MaxDocumentSizeBytes": 20971520, + "AllowedDocumentContentTypes": "application/pdf,image/jpeg,image/png" + }, "Email": { "PinExpiryMinutes": 5, "MaxAttempts": 3, diff --git a/omsorgCore/src/OmsorgCore.Application/Abstractions/IAbsenceRepository.cs b/omsorgCore/src/OmsorgCore.Application/Abstractions/IAbsenceRepository.cs new file mode 100644 index 0000000..96436e9 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Abstractions/IAbsenceRepository.cs @@ -0,0 +1,22 @@ +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Application.Abstractions; + +public interface IAbsenceRepository +{ + Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default); + Task<(IReadOnlyList Items, int TotalCount)> GetPagedAsync( + string? status, + string? type, + Guid? employeeId, + int page, + int pageSize, + CancellationToken cancellationToken = default, + Guid? restrictToEmployeeId = null); + Task AddAsync(Absence absence, CancellationToken cancellationToken = default); + Task UpdateAsync(Absence absence, CancellationToken cancellationToken = default); + Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default); + Task RestoreAsync(Guid id, CancellationToken cancellationToken = default); + Task SaveChangesAsync(CancellationToken cancellationToken = default); +} diff --git a/omsorgCore/src/OmsorgCore.Application/Abstractions/IContractRepository.cs b/omsorgCore/src/OmsorgCore.Application/Abstractions/IContractRepository.cs index a04d7aa..f8b2be3 100644 --- a/omsorgCore/src/OmsorgCore.Application/Abstractions/IContractRepository.cs +++ b/omsorgCore/src/OmsorgCore.Application/Abstractions/IContractRepository.cs @@ -16,5 +16,8 @@ public interface IContractRepository CancellationToken cancellationToken = default); Task AddAsync(Contract contract, CancellationToken cancellationToken = default); Task UpdateAsync(Contract contract, CancellationToken cancellationToken = default); + Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default); + Task RestoreAsync(Guid id, CancellationToken cancellationToken = default); Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/omsorgCore/src/OmsorgCore.Application/Abstractions/ICurrentUserService.cs b/omsorgCore/src/OmsorgCore.Application/Abstractions/ICurrentUserService.cs index 7ffee1f..7931f25 100644 --- a/omsorgCore/src/OmsorgCore.Application/Abstractions/ICurrentUserService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Abstractions/ICurrentUserService.cs @@ -12,4 +12,12 @@ public interface ICurrentUserService string? Username { get; } string? RoleName { get; } string? IpAddress { get; } + + /// + /// Verknüpfte Mitarbeiter-Id () dieses Users, falls + /// vorhanden - aus dem JWT-Claim "employeeId", nicht per DB-Read. Wird nach Verknüpfen/Lösen + /// erst mit dem nächsten Token-Refresh aktuell (Staleness-Fenster = Access-Token-Laufzeit). + /// Anker für Own-Scope-Datenfilterung, siehe . + /// + Guid? EmployeeId { get; } } diff --git a/omsorgCore/src/OmsorgCore.Application/Abstractions/IDocumentRepository.cs b/omsorgCore/src/OmsorgCore.Application/Abstractions/IDocumentRepository.cs new file mode 100644 index 0000000..e74dae2 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Abstractions/IDocumentRepository.cs @@ -0,0 +1,14 @@ +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Application.Abstractions; + +public interface IDocumentRepository +{ + Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetByEntityAsync(string entityType, Guid entityId, CancellationToken cancellationToken = default); + Task AddAsync(Document document, CancellationToken cancellationToken = default); + Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default); + Task RestoreAsync(Guid id, CancellationToken cancellationToken = default); + Task SaveChangesAsync(CancellationToken cancellationToken = default); +} diff --git a/omsorgCore/src/OmsorgCore.Application/Abstractions/IDocumentStorage.cs b/omsorgCore/src/OmsorgCore.Application/Abstractions/IDocumentStorage.cs new file mode 100644 index 0000000..9fd1654 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Abstractions/IDocumentStorage.cs @@ -0,0 +1,17 @@ +namespace OmsorgCore.Application.Abstractions; + +/// +/// Port für die physische Dateiablage eines — getrennt vom +/// Repository (das nur Metadaten in der DB verwaltet), weil die Bytes bewusst NICHT als Blob in +/// Postgres liegen, sondern auf dem Dateisystem (siehe omsorgCore/CLAUDE.md, Dokumentenarchiv). +/// +public interface IDocumentStorage +{ + /// + /// Schreibt den Inhalt auf die Storage und liefert den relativen StorageKey, unter dem + /// er später wiedergefunden wird. + /// + Task SaveAsync(string entityType, Guid entityId, Guid documentId, string originalFileName, Stream content, CancellationToken cancellationToken = default); + + Task OpenReadAsync(string storageKey, CancellationToken cancellationToken = default); +} diff --git a/omsorgCore/src/OmsorgCore.Application/Abstractions/IDocumentUploadPolicy.cs b/omsorgCore/src/OmsorgCore.Application/Abstractions/IDocumentUploadPolicy.cs new file mode 100644 index 0000000..c02ccc7 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Abstractions/IDocumentUploadPolicy.cs @@ -0,0 +1,14 @@ +namespace OmsorgCore.Application.Abstractions; + +/// +/// Einzige Stelle, die entscheidet, ob ein Upload die konfigurierte Größen-/Dateityp-Grenze +/// einhält (Storage:MaxDocumentSizeBytes/Storage:AllowedDocumentContentTypes) - analog zu +/// , damit die Regel nicht mehrfach im Controller/Service dupliziert wird. +/// +public interface IDocumentUploadPolicy +{ + long MaxSizeBytes { get; } + + bool IsSizeAllowed(long sizeBytes); + bool IsContentTypeAllowed(string? contentType); +} diff --git a/omsorgCore/src/OmsorgCore.Application/Abstractions/IEmployeeRepository.cs b/omsorgCore/src/OmsorgCore.Application/Abstractions/IEmployeeRepository.cs index cb04dd4..394f5f8 100644 --- a/omsorgCore/src/OmsorgCore.Application/Abstractions/IEmployeeRepository.cs +++ b/omsorgCore/src/OmsorgCore.Application/Abstractions/IEmployeeRepository.cs @@ -6,14 +6,22 @@ public interface IEmployeeRepository { Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default); Task> GetAllAsync(CancellationToken cancellationToken = default); + /// + /// Own-Scope-Filterung (siehe IPermissionService.GetScopeAsync): liefert bei Angabe nur den + /// Datensatz mit dieser Id, unabhängig von //. + /// Task<(IReadOnlyList Items, int TotalCount)> GetPagedAsync( string? search, string? status, string? employmentType, int page, int pageSize, - CancellationToken cancellationToken = default); + CancellationToken cancellationToken = default, + Guid? restrictToEmployeeId = null); Task AddAsync(Employee employee, CancellationToken cancellationToken = default); Task UpdateAsync(Employee employee, CancellationToken cancellationToken = default); + Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default); + Task RestoreAsync(Guid id, CancellationToken cancellationToken = default); Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/omsorgCore/src/OmsorgCore.Application/Abstractions/IFacilityContactRepository.cs b/omsorgCore/src/OmsorgCore.Application/Abstractions/IFacilityContactRepository.cs index 0511264..0215d73 100644 --- a/omsorgCore/src/OmsorgCore.Application/Abstractions/IFacilityContactRepository.cs +++ b/omsorgCore/src/OmsorgCore.Application/Abstractions/IFacilityContactRepository.cs @@ -8,5 +8,8 @@ public interface IFacilityContactRepository Task> GetByFacilityIdAsync(Guid facilityId, CancellationToken cancellationToken = default); Task AddAsync(FacilityContact contact, CancellationToken cancellationToken = default); Task UpdateAsync(FacilityContact contact, CancellationToken cancellationToken = default); + Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default); + Task RestoreAsync(Guid id, CancellationToken cancellationToken = default); Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/omsorgCore/src/OmsorgCore.Application/Abstractions/IFacilityQualificationRateRepository.cs b/omsorgCore/src/OmsorgCore.Application/Abstractions/IFacilityQualificationRateRepository.cs new file mode 100644 index 0000000..32ae612 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Abstractions/IFacilityQualificationRateRepository.cs @@ -0,0 +1,15 @@ +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Application.Abstractions; + +public interface IFacilityQualificationRateRepository +{ + Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetByFacilityIdAsync(Guid facilityId, CancellationToken cancellationToken = default); + Task AddAsync(FacilityQualificationRate rate, CancellationToken cancellationToken = default); + Task UpdateAsync(FacilityQualificationRate rate, CancellationToken cancellationToken = default); + Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default); + Task RestoreAsync(Guid id, CancellationToken cancellationToken = default); + Task SaveChangesAsync(CancellationToken cancellationToken = default); +} diff --git a/omsorgCore/src/OmsorgCore.Application/Abstractions/IFacilityRepository.cs b/omsorgCore/src/OmsorgCore.Application/Abstractions/IFacilityRepository.cs index c84b80e..6823d18 100644 --- a/omsorgCore/src/OmsorgCore.Application/Abstractions/IFacilityRepository.cs +++ b/omsorgCore/src/OmsorgCore.Application/Abstractions/IFacilityRepository.cs @@ -9,10 +9,14 @@ public interface IFacilityRepository Task<(IReadOnlyList Items, int TotalCount)> GetPagedAsync( string? search, string? crmStatus, + bool followUpDueOnly, int page, int pageSize, CancellationToken cancellationToken = default); Task AddAsync(Facility facility, CancellationToken cancellationToken = default); Task UpdateAsync(Facility facility, CancellationToken cancellationToken = default); + Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default); + Task RestoreAsync(Guid id, CancellationToken cancellationToken = default); Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/omsorgCore/src/OmsorgCore.Application/Abstractions/IOrderRepository.cs b/omsorgCore/src/OmsorgCore.Application/Abstractions/IOrderRepository.cs index 2116a42..a77b926 100644 --- a/omsorgCore/src/OmsorgCore.Application/Abstractions/IOrderRepository.cs +++ b/omsorgCore/src/OmsorgCore.Application/Abstractions/IOrderRepository.cs @@ -10,10 +10,16 @@ public interface IOrderRepository string? search, Guid? statusId, Guid? facilityId, + string? priority, + string? requiredQualification, + string? shiftType, int page, int pageSize, CancellationToken cancellationToken = default); Task AddAsync(Order order, CancellationToken cancellationToken = default); Task UpdateAsync(Order order, CancellationToken cancellationToken = default); + Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default); + Task RestoreAsync(Guid id, CancellationToken cancellationToken = default); Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/omsorgCore/src/OmsorgCore.Application/Abstractions/IPermissionService.cs b/omsorgCore/src/OmsorgCore.Application/Abstractions/IPermissionService.cs index d1146ca..0049515 100644 --- a/omsorgCore/src/OmsorgCore.Application/Abstractions/IPermissionService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Abstractions/IPermissionService.cs @@ -12,6 +12,20 @@ public interface IPermissionService { Task HasPermissionAsync(Guid userId, ModuleType module, PermissionAction action, CancellationToken cancellationToken = default); + /// + /// Liefert den effektiven für Modul/Aktion dieses Users, oder + /// null wenn gar nicht gewährt. Scope-unabhängig vom reinen Endpunkt-Gate + /// () — wird von datenzugreifenden Application-Services + /// konsultiert, um Own-Scope-Filterung anzuwenden (siehe REQUIREMENTS.md FR-MA-6). + /// + Task GetScopeAsync(Guid userId, ModuleType module, PermissionAction action, CancellationToken cancellationToken = default); + /// Alle tatsächlich gewährten Modul/Aktion-Kombinationen dieses Users (Rollen-Default + Overrides aufgelöst). Task> GetGrantedPermissionsAsync(Guid userId, CancellationToken cancellationToken = default); + + /// Entfernt den gecachten Rechte-Stand dieses einzelnen Users - nach einem individuellen Override oder Rollenwechsel. + void InvalidateUserPermissions(Guid userId); + + /// Entfernt den gecachten Rechte-Stand aller User mit dieser Rolle - nach einer Änderung der Rollen-Rechte-Matrix. + Task InvalidateRolePermissionsAsync(Guid roleId, CancellationToken cancellationToken = default); } diff --git a/omsorgCore/src/OmsorgCore.Application/Abstractions/ITimeEntryRepository.cs b/omsorgCore/src/OmsorgCore.Application/Abstractions/ITimeEntryRepository.cs new file mode 100644 index 0000000..7aec326 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Abstractions/ITimeEntryRepository.cs @@ -0,0 +1,22 @@ +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Application.Abstractions; + +public interface ITimeEntryRepository +{ + Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default); + Task<(IReadOnlyList Items, int TotalCount)> GetPagedAsync( + Guid? statusId, + Guid? employeeId, + Guid? orderId, + int page, + int pageSize, + CancellationToken cancellationToken = default, + Guid? restrictToEmployeeId = null); + Task AddAsync(TimeEntry timeEntry, CancellationToken cancellationToken = default); + Task UpdateAsync(TimeEntry timeEntry, CancellationToken cancellationToken = default); + Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default); + Task RestoreAsync(Guid id, CancellationToken cancellationToken = default); + Task SaveChangesAsync(CancellationToken cancellationToken = default); +} diff --git a/omsorgCore/src/OmsorgCore.Application/Abstractions/IUserRepository.cs b/omsorgCore/src/OmsorgCore.Application/Abstractions/IUserRepository.cs index 8f69062..11dc923 100644 --- a/omsorgCore/src/OmsorgCore.Application/Abstractions/IUserRepository.cs +++ b/omsorgCore/src/OmsorgCore.Application/Abstractions/IUserRepository.cs @@ -6,9 +6,12 @@ public interface IUserRepository { Task GetByUsernameAsync(string username, CancellationToken cancellationToken = default); - /// Inklusive Role+RolePermissions+PermissionOverrides (für die Rechteauflösung) und Employee (für /api/auth/me). + /// Inklusive Role+RolePermissions+PermissionOverrides (für die Rechteauflösung) und Employee (für /api/auth/me). Getrackt - für Mutationsflows (Override hinzufügen/entfernen). Task GetByIdWithPermissionsAsync(Guid id, CancellationToken cancellationToken = default); + /// Wie , aber AsNoTracking - für PermissionService's Cache (nie mutiert, wird über Request-Grenzen hinweg gehalten). + Task GetByIdWithPermissionsNoTrackingAsync(Guid id, CancellationToken cancellationToken = default); + /// Schlanker Lookup ohne Includes - für den SecurityStamp-Check bei jedem Request. Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default); @@ -17,6 +20,9 @@ public interface IUserRepository Task> GetAllAsync(CancellationToken cancellationToken = default); + /// Nur die Ids der User mit dieser Rolle - für die Cache-Invalidierung bei Rollen-Rechte-Änderungen. + Task> GetUserIdsByRoleAsync(Guid roleId, CancellationToken cancellationToken = default); + Task GetByEmployeeIdAsync(Guid employeeId, CancellationToken cancellationToken = default); Task ExistsByUsernameAsync(string username, CancellationToken cancellationToken = default); diff --git a/omsorgCore/src/OmsorgCore.Application/Abstractions/IValueListRepository.cs b/omsorgCore/src/OmsorgCore.Application/Abstractions/IValueListRepository.cs index 36560a8..590f056 100644 --- a/omsorgCore/src/OmsorgCore.Application/Abstractions/IValueListRepository.cs +++ b/omsorgCore/src/OmsorgCore.Application/Abstractions/IValueListRepository.cs @@ -19,6 +19,8 @@ public interface IValueListRepository Task CanTransitionAsync(Guid fromItemId, Guid toItemId, CancellationToken cancellationToken = default); Task> GetTransitionsAsync(string key, CancellationToken cancellationToken = default); Task ReplaceTransitionsAsync(string key, IEnumerable<(Guid FromItemId, Guid ToItemId)> transitions, CancellationToken cancellationToken = default); + Task GetSelfServiceTransitionAsync(Guid fromItemId, CancellationToken cancellationToken = default); + Task GetTransitionAsync(Guid fromItemId, Guid toItemId, CancellationToken cancellationToken = default); Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/omsorgCore/src/OmsorgCore.Application/DependencyInjection.cs b/omsorgCore/src/OmsorgCore.Application/DependencyInjection.cs index 7444a6b..5b8904c 100644 --- a/omsorgCore/src/OmsorgCore.Application/DependencyInjection.cs +++ b/omsorgCore/src/OmsorgCore.Application/DependencyInjection.cs @@ -13,8 +13,12 @@ public static class DependencyInjection services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/omsorgCore/src/OmsorgCore.Application/Models/PermissionGrant.cs b/omsorgCore/src/OmsorgCore.Application/Models/PermissionGrant.cs index 7a15a99..84631bf 100644 --- a/omsorgCore/src/OmsorgCore.Application/Models/PermissionGrant.cs +++ b/omsorgCore/src/OmsorgCore.Application/Models/PermissionGrant.cs @@ -3,4 +3,4 @@ using OmsorgCore.Domain.Enums; namespace OmsorgCore.Application.Models; /// Eine für einen User tatsächlich gewährte Modul/Aktion-Kombination (Ergebnis der Rechte-Auflösung). -public record PermissionGrant(ModuleType Module, PermissionAction Action); +public record PermissionGrant(ModuleType Module, PermissionAction Action, PermissionScope Scope); diff --git a/omsorgCore/src/OmsorgCore.Application/Models/PermissionOverrideSummary.cs b/omsorgCore/src/OmsorgCore.Application/Models/PermissionOverrideSummary.cs index b7dbbc1..6e1f7a5 100644 --- a/omsorgCore/src/OmsorgCore.Application/Models/PermissionOverrideSummary.cs +++ b/omsorgCore/src/OmsorgCore.Application/Models/PermissionOverrideSummary.cs @@ -3,4 +3,4 @@ using OmsorgCore.Domain.Enums; namespace OmsorgCore.Application.Models; /// Ein einzelner UserPermissionOverride-Eintrag für die Admin-Ansicht/-Bearbeitung. -public record PermissionOverrideSummary(Guid Id, ModuleType Module, PermissionAction Action, PermissionEffect Effect); +public record PermissionOverrideSummary(Guid Id, ModuleType Module, PermissionAction Action, PermissionEffect Effect, PermissionScope Scope); diff --git a/omsorgCore/src/OmsorgCore.Application/OmsorgCore.Application.csproj b/omsorgCore/src/OmsorgCore.Application/OmsorgCore.Application.csproj index e15c016..e577740 100644 --- a/omsorgCore/src/OmsorgCore.Application/OmsorgCore.Application.csproj +++ b/omsorgCore/src/OmsorgCore.Application/OmsorgCore.Application.csproj @@ -6,6 +6,7 @@ + diff --git a/omsorgCore/src/OmsorgCore.Application/Services/AbsenceService.cs b/omsorgCore/src/OmsorgCore.Application/Services/AbsenceService.cs new file mode 100644 index 0000000..3b89a97 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Services/AbsenceService.cs @@ -0,0 +1,198 @@ +using OmsorgCore.Application.Abstractions; +using OmsorgCore.Domain.Entities; +using OmsorgCore.Domain.Enums; + +namespace OmsorgCore.Application.Services; + +public class AbsenceService : IAbsenceService +{ + private const string StatusListKey = "AbsenceStatus"; + + private readonly IAbsenceRepository _absenceRepository; + private readonly IPermissionService _permissionService; + private readonly ICurrentUserService _currentUserService; + private readonly IValueListRepository _valueListRepository; + + public AbsenceService( + IAbsenceRepository absenceRepository, + IPermissionService permissionService, + ICurrentUserService currentUserService, + IValueListRepository valueListRepository) + { + _absenceRepository = absenceRepository; + _permissionService = permissionService; + _currentUserService = currentUserService; + _valueListRepository = valueListRepository; + } + + public async Task<(IReadOnlyList Items, int TotalCount)> GetPagedAsync( + string? status, + string? type, + Guid? employeeId, + int page, + int pageSize, + CancellationToken cancellationToken = default) + { + Guid? restrictToEmployeeId = await ResolveOwnScopeRestrictionAsync(PermissionAction.View, cancellationToken); + return await _absenceRepository.GetPagedAsync(status, type, employeeId, page, pageSize, cancellationToken, restrictToEmployeeId); + } + + public async Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) + { + var absence = await _absenceRepository.GetByIdAsync(id, cancellationToken); + if (absence is null) + { + return null; + } + + Guid? restrictToEmployeeId = await ResolveOwnScopeRestrictionAsync(PermissionAction.View, cancellationToken); + if (restrictToEmployeeId.HasValue && absence.EmployeeId != restrictToEmployeeId.Value) + { + return null; + } + + return absence; + } + + public async Task CreateAsync(Absence absence, CancellationToken cancellationToken = default) + { + Guid? restrictToEmployeeId = await ResolveOwnScopeRestrictionAsync(PermissionAction.Create, cancellationToken); + if (restrictToEmployeeId.HasValue) + { + if (restrictToEmployeeId.Value == Guid.Empty) + { + // Own-Scope, aber kein User.EmployeeId verknüpft - fail-closed statt "für niemanden" anzulegen. + return null; + } + + absence.EmployeeId = restrictToEmployeeId.Value; + } + else + { + // All-Scope-Aufrufer (z. B. Büro-Rollen/Administrator): CreateAbsenceRequest hat bewusst + // kein employeeId-Feld ("im Namen von" ist nicht Teil dieser ersten UI, siehe + // omsorgCore/CLAUDE.md). Own-Scope ist nur eine Sichtbarkeits-/Anlege-Einschränkung + // ("nur eigene Daten"), kein Ausschlusskriterium dafür, ob man überhaupt einen eigenen + // Antrag stellen darf - auch Büro-Rollen sind Mitarbeiter und wollen eigenen Urlaub + // beantragen können (Vorfall 2026-08-10: Admin mit verknüpftem Mitarbeiter bekam grundlos + // 400). Deshalb hier Fallback auf die eigene verknüpfte Mitarbeiter-Id; nur wenn die + // Aufrufer:in selbst gar keinen Mitarbeiter verknüpft hat, bleibt es ein harter Fehler. + var ownEmployeeId = _currentUserService.EmployeeId; + if (ownEmployeeId is null || ownEmployeeId == Guid.Empty) + { + return null; + } + + absence.EmployeeId = ownEmployeeId.Value; + } + + // Nicht den Entity-Default hartkodiert übernehmen - der Wert kommt aus der admin-editierbaren + // ValueList "AbsenceStatus" (IsInitial-Flag, von DbSeeder.SeedValueListsAsync gesetzt), damit + // ein Umbenennen über die Status-Verwaltung nicht lautlos bricht (analog OrderService.CreateAsync). + absence.Status = await GetInitialStatusValueAsync(cancellationToken); + + await _absenceRepository.AddAsync(absence, cancellationToken); + await _absenceRepository.SaveChangesAsync(cancellationToken); + return absence; + } + + public async Task UpdateAsync(Guid id, Absence updates, CancellationToken cancellationToken = default) + { + var absence = await _absenceRepository.GetByIdAsync(id, cancellationToken); + if (absence is null) + { + return UpdateAbsenceResult.Fail(UpdateAbsenceFailureReason.NotFound); + } + + Guid? restrictToEmployeeId = await ResolveOwnScopeRestrictionAsync(PermissionAction.Edit, cancellationToken); + if (restrictToEmployeeId.HasValue && absence.EmployeeId != restrictToEmployeeId.Value) + { + // Wie GetByIdAsync: fremden Own-Scope-Antrag als "nicht gefunden" behandeln statt 403, + // um nicht zu verraten, dass die Id überhaupt existiert. + return UpdateAbsenceResult.Fail(UpdateAbsenceFailureReason.NotFound); + } + + if (absence.Status != await GetInitialStatusValueAsync(cancellationToken)) + { + return UpdateAbsenceResult.Fail(UpdateAbsenceFailureReason.AlreadyDecided); + } + + absence.Type = updates.Type; + absence.StartDate = updates.StartDate; + absence.EndDate = updates.EndDate; + absence.Reason = updates.Reason; + absence.Substitute = updates.Substitute; + absence.Note = updates.Note; + absence.UpdatedAt = DateTime.UtcNow; + + await _absenceRepository.UpdateAsync(absence, cancellationToken); + await _absenceRepository.SaveChangesAsync(cancellationToken); + return UpdateAbsenceResult.Ok(absence); + } + + public async Task DecideAsync(Guid id, string status, string? adminNote, CancellationToken cancellationToken = default) + { + var absence = await _absenceRepository.GetByIdAsync(id, cancellationToken); + if (absence is null) + { + return null; + } + + absence.Status = status; + absence.AdminNote = adminNote; + absence.UpdatedAt = DateTime.UtcNow; + + await _absenceRepository.UpdateAsync(absence, cancellationToken); + await _absenceRepository.SaveChangesAsync(cancellationToken); + return absence; + } + + public async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var deleted = await _absenceRepository.SoftDeleteAsync(id, cancellationToken); + if (deleted) + { + await _absenceRepository.SaveChangesAsync(cancellationToken); + } + + return deleted; + } + + public Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + => _absenceRepository.GetDeletedAsync(search, cancellationToken); + + public async Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var restored = await _absenceRepository.RestoreAsync(id, cancellationToken); + if (restored) + { + await _absenceRepository.SaveChangesAsync(cancellationToken); + } + + return restored; + } + + /// + /// Der aktuell als "initial"/pending markierte Status-Wert der ValueList "AbsenceStatus" + /// (per Default "Eingereicht", aber nicht hartkodiert - über Status-Verwaltung umbenennbar, + /// ohne dass diese Logik oder der Controller (Decide-Endpoint) mitgeändert werden müssten). + /// + public async Task GetInitialStatusValueAsync(CancellationToken cancellationToken = default) + { + var initial = await _valueListRepository.GetInitialItemAsync(StatusListKey, cancellationToken); + return initial?.Value + ?? throw new InvalidOperationException("Kein initialer Abwesenheitsstatus konfiguriert (DbSeeder.SeedValueListsAsync fehlt)."); + } + + /// Own-Scope-Filterung, siehe EmployeeService.ResolveOwnScopeRestrictionAsync (analoges Muster für ModuleType.Absences). + private async Task ResolveOwnScopeRestrictionAsync(PermissionAction action, CancellationToken cancellationToken) + { + if (_currentUserService.UserId is not { } userId) + { + return null; + } + + var scope = await _permissionService.GetScopeAsync(userId, ModuleType.Absences, action, cancellationToken); + return scope == PermissionScope.Own ? (_currentUserService.EmployeeId ?? Guid.Empty) : null; + } +} diff --git a/omsorgCore/src/OmsorgCore.Application/Services/ContractService.cs b/omsorgCore/src/OmsorgCore.Application/Services/ContractService.cs index d18d9e3..ae46113 100644 --- a/omsorgCore/src/OmsorgCore.Application/Services/ContractService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Services/ContractService.cs @@ -1,21 +1,29 @@ using OmsorgCore.Application.Abstractions; using OmsorgCore.Domain.Entities; +using OmsorgCore.Domain.Enums; namespace OmsorgCore.Application.Services; public class ContractService : IContractService { private readonly IContractRepository _contractRepository; + private readonly IPermissionService _permissionService; + private readonly ICurrentUserService _currentUserService; - public ContractService(IContractRepository contractRepository) + public ContractService( + IContractRepository contractRepository, + IPermissionService permissionService, + ICurrentUserService currentUserService) { _contractRepository = contractRepository; + _permissionService = permissionService; + _currentUserService = currentUserService; } public Task> GetAllAsync(CancellationToken cancellationToken = default) => _contractRepository.GetAllAsync(cancellationToken); - public Task<(IReadOnlyList Items, int TotalCount)> GetPagedAsync( + public async Task<(IReadOnlyList Items, int TotalCount)> GetPagedAsync( string? search, string? status, Guid? employeeId, @@ -23,10 +31,43 @@ public class ContractService : IContractService int page, int pageSize, CancellationToken cancellationToken = default) - => _contractRepository.GetPagedAsync(search, status, employeeId, facilityId, page, pageSize, cancellationToken); + { + Guid? ownRestriction = await ResolveOwnScopeRestrictionAsync(cancellationToken); + // Own-Scope erzwingt die eigene EmployeeId und überschreibt einen ggf. angeforderten + // fremden employeeId-Filter - sonst könnte ein Own-User über den Query-Parameter fremde + // Verträge abfragen. + Guid? effectiveEmployeeId = ownRestriction ?? employeeId; + return await _contractRepository.GetPagedAsync(search, status, effectiveEmployeeId, facilityId, page, pageSize, cancellationToken); + } - public Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) - => _contractRepository.GetByIdAsync(id, cancellationToken); + public async Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) + { + var contract = await _contractRepository.GetByIdAsync(id, cancellationToken); + if (contract is null) + { + return null; + } + + Guid? ownRestriction = await ResolveOwnScopeRestrictionAsync(cancellationToken); + if (ownRestriction.HasValue && contract.EmployeeId != ownRestriction.Value) + { + return null; + } + + return contract; + } + + /// Own-Scope-Filterung, siehe EmployeeService.ResolveOwnScopeRestrictionAsync (analoges Muster für ModuleType.Contracts). + private async Task ResolveOwnScopeRestrictionAsync(CancellationToken cancellationToken) + { + if (_currentUserService.UserId is not { } userId) + { + return null; + } + + var scope = await _permissionService.GetScopeAsync(userId, ModuleType.Contracts, PermissionAction.View, cancellationToken); + return scope == PermissionScope.Own ? (_currentUserService.EmployeeId ?? Guid.Empty) : null; + } public async Task CreateAsync(Contract contract, CancellationToken cancellationToken = default) { @@ -61,4 +102,29 @@ public class ContractService : IContractService await _contractRepository.SaveChangesAsync(cancellationToken); return contract; } + + public async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var deleted = await _contractRepository.SoftDeleteAsync(id, cancellationToken); + if (deleted) + { + await _contractRepository.SaveChangesAsync(cancellationToken); + } + + return deleted; + } + + public Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + => _contractRepository.GetDeletedAsync(search, cancellationToken); + + public async Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var restored = await _contractRepository.RestoreAsync(id, cancellationToken); + if (restored) + { + await _contractRepository.SaveChangesAsync(cancellationToken); + } + + return restored; + } } diff --git a/omsorgCore/src/OmsorgCore.Application/Services/DecideTimeEntryResult.cs b/omsorgCore/src/OmsorgCore.Application/Services/DecideTimeEntryResult.cs new file mode 100644 index 0000000..72f2586 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Services/DecideTimeEntryResult.cs @@ -0,0 +1,28 @@ +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Application.Services; + +public enum DecideTimeEntryFailureReason +{ + NotFound, + InvalidStatusTransition +} + +public class DecideTimeEntryResult +{ + public bool Success { get; init; } + public DecideTimeEntryFailureReason? FailureReason { get; init; } + public TimeEntry? TimeEntry { get; init; } + + public static DecideTimeEntryResult Fail(DecideTimeEntryFailureReason reason) => new() + { + Success = false, + FailureReason = reason + }; + + public static DecideTimeEntryResult Ok(TimeEntry timeEntry) => new() + { + Success = true, + TimeEntry = timeEntry + }; +} diff --git a/omsorgCore/src/OmsorgCore.Application/Services/DocumentService.cs b/omsorgCore/src/OmsorgCore.Application/Services/DocumentService.cs new file mode 100644 index 0000000..e72a208 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Services/DocumentService.cs @@ -0,0 +1,145 @@ +using OmsorgCore.Application.Abstractions; +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Application.Services; + +public class DocumentService : IDocumentService +{ + private const string DocumentCategoryListKey = "DocumentCategory"; + + private readonly IDocumentRepository _documentRepository; + private readonly IDocumentStorage _documentStorage; + private readonly IDocumentUploadPolicy _uploadPolicy; + private readonly IValueListRepository _valueListRepository; + + public DocumentService( + IDocumentRepository documentRepository, + IDocumentStorage documentStorage, + IDocumentUploadPolicy uploadPolicy, + IValueListRepository valueListRepository) + { + _documentRepository = documentRepository; + _documentStorage = documentStorage; + _uploadPolicy = uploadPolicy; + _valueListRepository = valueListRepository; + } + + public async Task UploadAsync( + string entityType, + Guid entityId, + string category, + string? description, + string fileName, + string contentType, + long sizeBytes, + Stream content, + Guid uploadedByUserId, + CancellationToken cancellationToken = default) + { + if (!_uploadPolicy.IsSizeAllowed(sizeBytes)) + { + return new DocumentUploadResult(null, DocumentUploadError.FileTooLarge); + } + + if (!_uploadPolicy.IsContentTypeAllowed(contentType)) + { + return new DocumentUploadResult(null, DocumentUploadError.ContentTypeNotAllowed); + } + + var allowedCategories = await _valueListRepository.GetActiveValuesAsync(DocumentCategoryListKey, cancellationToken); + if (!allowedCategories.Contains(category)) + { + return new DocumentUploadResult(null, DocumentUploadError.InvalidCategory); + } + + var document = new Document + { + EntityType = entityType, + EntityId = entityId, + Category = category, + Description = description, + FileName = fileName, + ContentType = contentType, + SizeBytes = sizeBytes, + UploadedByUserId = uploadedByUserId + }; + + document.StorageKey = await _documentStorage.SaveAsync(entityType, entityId, document.Id, fileName, content, cancellationToken); + + await _documentRepository.AddAsync(document, cancellationToken); + await _documentRepository.SaveChangesAsync(cancellationToken); + + return new DocumentUploadResult(document, DocumentUploadError.None); + } + + public async Task UpdateAsync( + Guid id, + string category, + string? description, + string fileName, + CancellationToken cancellationToken = default) + { + var document = await _documentRepository.GetByIdAsync(id, cancellationToken); + if (document is null) + { + return new DocumentUpdateResult(null, DocumentUpdateError.NotFound); + } + + var allowedCategories = await _valueListRepository.GetActiveValuesAsync(DocumentCategoryListKey, cancellationToken); + if (!allowedCategories.Contains(category)) + { + return new DocumentUpdateResult(null, DocumentUpdateError.InvalidCategory); + } + + document.Category = category; + document.Description = description; + document.FileName = fileName; + document.UpdatedAt = DateTime.UtcNow; + + await _documentRepository.SaveChangesAsync(cancellationToken); + + return new DocumentUpdateResult(document, DocumentUpdateError.None); + } + + public Task> GetByEntityAsync(string entityType, Guid entityId, CancellationToken cancellationToken = default) + => _documentRepository.GetByEntityAsync(entityType, entityId, cancellationToken); + + public Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) + => _documentRepository.GetByIdAsync(id, cancellationToken); + + public async Task OpenForDownloadAsync(Guid id, CancellationToken cancellationToken = default) + { + var document = await _documentRepository.GetByIdAsync(id, cancellationToken); + if (document is null) + { + return null; + } + + return await _documentStorage.OpenReadAsync(document.StorageKey, cancellationToken); + } + + public async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var deleted = await _documentRepository.SoftDeleteAsync(id, cancellationToken); + if (deleted) + { + await _documentRepository.SaveChangesAsync(cancellationToken); + } + + return deleted; + } + + public Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + => _documentRepository.GetDeletedAsync(search, cancellationToken); + + public async Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var restored = await _documentRepository.RestoreAsync(id, cancellationToken); + if (restored) + { + await _documentRepository.SaveChangesAsync(cancellationToken); + } + + return restored; + } +} diff --git a/omsorgCore/src/OmsorgCore.Application/Services/EmployeeService.cs b/omsorgCore/src/OmsorgCore.Application/Services/EmployeeService.cs index 9c6f20f..0759736 100644 --- a/omsorgCore/src/OmsorgCore.Application/Services/EmployeeService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Services/EmployeeService.cs @@ -1,31 +1,74 @@ using OmsorgCore.Application.Abstractions; using OmsorgCore.Domain.Entities; +using OmsorgCore.Domain.Enums; namespace OmsorgCore.Application.Services; public class EmployeeService : IEmployeeService { private readonly IEmployeeRepository _employeeRepository; + private readonly IPermissionService _permissionService; + private readonly ICurrentUserService _currentUserService; - public EmployeeService(IEmployeeRepository employeeRepository) + public EmployeeService( + IEmployeeRepository employeeRepository, + IPermissionService permissionService, + ICurrentUserService currentUserService) { _employeeRepository = employeeRepository; + _permissionService = permissionService; + _currentUserService = currentUserService; } public Task> GetAllAsync(CancellationToken cancellationToken = default) => _employeeRepository.GetAllAsync(cancellationToken); - public Task<(IReadOnlyList Items, int TotalCount)> GetPagedAsync( + public async Task<(IReadOnlyList Items, int TotalCount)> GetPagedAsync( string? search, string? status, string? employmentType, int page, int pageSize, CancellationToken cancellationToken = default) - => _employeeRepository.GetPagedAsync(search, status, employmentType, page, pageSize, cancellationToken); + { + Guid? restrictToEmployeeId = await ResolveOwnScopeRestrictionAsync(PermissionAction.View, cancellationToken); + return await _employeeRepository.GetPagedAsync(search, status, employmentType, page, pageSize, cancellationToken, restrictToEmployeeId); + } - public Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) - => _employeeRepository.GetByIdAsync(id, cancellationToken); + public async Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) + { + var employee = await _employeeRepository.GetByIdAsync(id, cancellationToken); + if (employee is null) + { + return null; + } + + Guid? restrictToEmployeeId = await ResolveOwnScopeRestrictionAsync(PermissionAction.View, cancellationToken); + if (restrictToEmployeeId.HasValue && employee.Id != restrictToEmployeeId.Value) + { + return null; + } + + return employee; + } + + /// + /// Own-Scope-Filterung (siehe IPermissionService.GetScopeAsync): liefert die eigene + /// EmployeeId, wenn der aktuelle User für Employees.<action> nur "Own" gewährt bekommt + /// (Guid.Empty statt null, falls kein User.EmployeeId verknüpft ist — Own ohne Anker sieht + /// dann nichts statt versehentlich alles). Null = keine Einschränkung (Scope "All" oder kein + /// eingeloggter User, letzteres blockiert der [RequirePermission]-Endpunkt-Gate ohnehin schon). + /// + private async Task ResolveOwnScopeRestrictionAsync(PermissionAction action, CancellationToken cancellationToken) + { + if (_currentUserService.UserId is not { } userId) + { + return null; + } + + var scope = await _permissionService.GetScopeAsync(userId, ModuleType.Employees, action, cancellationToken); + return scope == PermissionScope.Own ? (_currentUserService.EmployeeId ?? Guid.Empty) : null; + } public async Task CreateAsync(Employee employee, CancellationToken cancellationToken = default) { @@ -66,4 +109,29 @@ public class EmployeeService : IEmployeeService await _employeeRepository.SaveChangesAsync(cancellationToken); return employee; } + + public async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var deleted = await _employeeRepository.SoftDeleteAsync(id, cancellationToken); + if (deleted) + { + await _employeeRepository.SaveChangesAsync(cancellationToken); + } + + return deleted; + } + + public Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + => _employeeRepository.GetDeletedAsync(search, cancellationToken); + + public async Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var restored = await _employeeRepository.RestoreAsync(id, cancellationToken); + if (restored) + { + await _employeeRepository.SaveChangesAsync(cancellationToken); + } + + return restored; + } } diff --git a/omsorgCore/src/OmsorgCore.Application/Services/FacilityContactService.cs b/omsorgCore/src/OmsorgCore.Application/Services/FacilityContactService.cs index bbda233..c772d1a 100644 --- a/omsorgCore/src/OmsorgCore.Application/Services/FacilityContactService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Services/FacilityContactService.cs @@ -45,4 +45,29 @@ public class FacilityContactService : IFacilityContactService await _facilityContactRepository.SaveChangesAsync(cancellationToken); return contact; } + + public async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var deleted = await _facilityContactRepository.SoftDeleteAsync(id, cancellationToken); + if (deleted) + { + await _facilityContactRepository.SaveChangesAsync(cancellationToken); + } + + return deleted; + } + + public Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + => _facilityContactRepository.GetDeletedAsync(search, cancellationToken); + + public async Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var restored = await _facilityContactRepository.RestoreAsync(id, cancellationToken); + if (restored) + { + await _facilityContactRepository.SaveChangesAsync(cancellationToken); + } + + return restored; + } } diff --git a/omsorgCore/src/OmsorgCore.Application/Services/FacilityQualificationRateService.cs b/omsorgCore/src/OmsorgCore.Application/Services/FacilityQualificationRateService.cs new file mode 100644 index 0000000..17e6649 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Services/FacilityQualificationRateService.cs @@ -0,0 +1,69 @@ +using OmsorgCore.Application.Abstractions; +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Application.Services; + +public class FacilityQualificationRateService : IFacilityQualificationRateService +{ + private readonly IFacilityQualificationRateRepository _facilityQualificationRateRepository; + + public FacilityQualificationRateService(IFacilityQualificationRateRepository facilityQualificationRateRepository) + { + _facilityQualificationRateRepository = facilityQualificationRateRepository; + } + + public Task> GetByFacilityIdAsync(Guid facilityId, CancellationToken cancellationToken = default) + => _facilityQualificationRateRepository.GetByFacilityIdAsync(facilityId, cancellationToken); + + public Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) + => _facilityQualificationRateRepository.GetByIdAsync(id, cancellationToken); + + public async Task CreateAsync(FacilityQualificationRate rate, CancellationToken cancellationToken = default) + { + await _facilityQualificationRateRepository.AddAsync(rate, cancellationToken); + await _facilityQualificationRateRepository.SaveChangesAsync(cancellationToken); + return rate; + } + + public async Task UpdateAsync(Guid id, FacilityQualificationRate updates, CancellationToken cancellationToken = default) + { + var rate = await _facilityQualificationRateRepository.GetByIdAsync(id, cancellationToken); + if (rate is null) + { + return null; + } + + rate.Qualification = updates.Qualification; + rate.Rate = updates.Rate; + rate.UpdatedAt = DateTime.UtcNow; + + await _facilityQualificationRateRepository.UpdateAsync(rate, cancellationToken); + await _facilityQualificationRateRepository.SaveChangesAsync(cancellationToken); + return rate; + } + + public async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var deleted = await _facilityQualificationRateRepository.SoftDeleteAsync(id, cancellationToken); + if (deleted) + { + await _facilityQualificationRateRepository.SaveChangesAsync(cancellationToken); + } + + return deleted; + } + + public Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + => _facilityQualificationRateRepository.GetDeletedAsync(search, cancellationToken); + + public async Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var restored = await _facilityQualificationRateRepository.RestoreAsync(id, cancellationToken); + if (restored) + { + await _facilityQualificationRateRepository.SaveChangesAsync(cancellationToken); + } + + return restored; + } +} diff --git a/omsorgCore/src/OmsorgCore.Application/Services/FacilityService.cs b/omsorgCore/src/OmsorgCore.Application/Services/FacilityService.cs index 99bd3a1..e1e097e 100644 --- a/omsorgCore/src/OmsorgCore.Application/Services/FacilityService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Services/FacilityService.cs @@ -18,10 +18,11 @@ public class FacilityService : IFacilityService public Task<(IReadOnlyList Items, int TotalCount)> GetPagedAsync( string? search, string? crmStatus, + bool followUpDueOnly, int page, int pageSize, CancellationToken cancellationToken = default) - => _facilityRepository.GetPagedAsync(search, crmStatus, page, pageSize, cancellationToken); + => _facilityRepository.GetPagedAsync(search, crmStatus, followUpDueOnly, page, pageSize, cancellationToken); public Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) => _facilityRepository.GetByIdAsync(id, cancellationToken); @@ -53,10 +54,36 @@ public class FacilityService : IFacilityService facility.BillingCity = updates.BillingCity; facility.BillingCountry = updates.BillingCountry; facility.CrmStatus = updates.CrmStatus; + facility.FollowUpDueDate = updates.FollowUpDueDate; facility.UpdatedAt = DateTime.UtcNow; await _facilityRepository.UpdateAsync(facility, cancellationToken); await _facilityRepository.SaveChangesAsync(cancellationToken); return facility; } + + public async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var deleted = await _facilityRepository.SoftDeleteAsync(id, cancellationToken); + if (deleted) + { + await _facilityRepository.SaveChangesAsync(cancellationToken); + } + + return deleted; + } + + public Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + => _facilityRepository.GetDeletedAsync(search, cancellationToken); + + public async Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var restored = await _facilityRepository.RestoreAsync(id, cancellationToken); + if (restored) + { + await _facilityRepository.SaveChangesAsync(cancellationToken); + } + + return restored; + } } diff --git a/omsorgCore/src/OmsorgCore.Application/Services/IAbsenceService.cs b/omsorgCore/src/OmsorgCore.Application/Services/IAbsenceService.cs new file mode 100644 index 0000000..aad80b8 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Services/IAbsenceService.cs @@ -0,0 +1,39 @@ +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Application.Services; + +public interface IAbsenceService +{ + Task<(IReadOnlyList Items, int TotalCount)> GetPagedAsync( + string? status, + string? type, + Guid? employeeId, + int page, + int pageSize, + CancellationToken cancellationToken = default); + Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default); + + /// + /// Legt den Antrag an. Ist der aktuelle Nutzer nur mit Own-Scope berechtigt, wird + /// .EmployeeId ignoriert und serverseitig auf die eigene, + /// per JWT verknüpfte Mitarbeiter-Id gesetzt - der Client kann sich nie als jemand + /// anderes ausgeben. Gibt null zurück, wenn Own-Scope greift, aber kein Mitarbeiter + /// verknüpft ist (fail-closed). + /// + Task CreateAsync(Absence absence, CancellationToken cancellationToken = default); + + /// + /// Bearbeitet Zeitraum/Art/Grund/Vertretung/Nachricht - nur solange der Antrag noch nicht + /// entschieden wurde (Status "Eingereicht"), sonst . + /// Own-Scope-Aufrufer dürfen nur ihre eigenen Anträge bearbeiten (wie bei GetByIdAsync). + /// + Task UpdateAsync(Guid id, Absence updates, CancellationToken cancellationToken = default); + + Task DecideAsync(Guid id, string status, string? adminNote, CancellationToken cancellationToken = default); + + /// Der aktuell als "initial"/pending markierte Wert der ValueList "AbsenceStatus" (nicht hartkodiert, siehe AbsenceService). + Task GetInitialStatusValueAsync(CancellationToken cancellationToken = default); + Task DeleteAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default); + Task RestoreAsync(Guid id, CancellationToken cancellationToken = default); +} diff --git a/omsorgCore/src/OmsorgCore.Application/Services/IContractService.cs b/omsorgCore/src/OmsorgCore.Application/Services/IContractService.cs index 1dd7aed..e9fa4dd 100644 --- a/omsorgCore/src/OmsorgCore.Application/Services/IContractService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Services/IContractService.cs @@ -16,4 +16,7 @@ public interface IContractService Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default); Task CreateAsync(Contract contract, CancellationToken cancellationToken = default); Task UpdateAsync(Guid id, Contract updates, CancellationToken cancellationToken = default); + Task DeleteAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default); + Task RestoreAsync(Guid id, CancellationToken cancellationToken = default); } diff --git a/omsorgCore/src/OmsorgCore.Application/Services/IDocumentService.cs b/omsorgCore/src/OmsorgCore.Application/Services/IDocumentService.cs new file mode 100644 index 0000000..5aa8f3d --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Services/IDocumentService.cs @@ -0,0 +1,51 @@ +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Application.Services; + +public enum DocumentUploadError +{ + None, + InvalidCategory, + FileTooLarge, + ContentTypeNotAllowed +} + +public record DocumentUploadResult(Document? Document, DocumentUploadError Error); + +public enum DocumentUpdateError +{ + None, + NotFound, + InvalidCategory +} + +public record DocumentUpdateResult(Document? Document, DocumentUpdateError Error); + +public interface IDocumentService +{ + Task UploadAsync( + string entityType, + Guid entityId, + string category, + string? description, + string fileName, + string contentType, + long sizeBytes, + Stream content, + Guid uploadedByUserId, + CancellationToken cancellationToken = default); + + Task UpdateAsync( + Guid id, + string category, + string? description, + string fileName, + CancellationToken cancellationToken = default); + + Task> GetByEntityAsync(string entityType, Guid entityId, CancellationToken cancellationToken = default); + Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default); + Task OpenForDownloadAsync(Guid id, CancellationToken cancellationToken = default); + Task DeleteAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default); + Task RestoreAsync(Guid id, CancellationToken cancellationToken = default); +} diff --git a/omsorgCore/src/OmsorgCore.Application/Services/IEmployeeService.cs b/omsorgCore/src/OmsorgCore.Application/Services/IEmployeeService.cs index 84fc905..1a476b1 100644 --- a/omsorgCore/src/OmsorgCore.Application/Services/IEmployeeService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Services/IEmployeeService.cs @@ -15,4 +15,7 @@ public interface IEmployeeService Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default); Task CreateAsync(Employee employee, CancellationToken cancellationToken = default); Task UpdateAsync(Guid id, Employee updates, CancellationToken cancellationToken = default); + Task DeleteAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default); + Task RestoreAsync(Guid id, CancellationToken cancellationToken = default); } diff --git a/omsorgCore/src/OmsorgCore.Application/Services/IFacilityContactService.cs b/omsorgCore/src/OmsorgCore.Application/Services/IFacilityContactService.cs index db14499..4fe6079 100644 --- a/omsorgCore/src/OmsorgCore.Application/Services/IFacilityContactService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Services/IFacilityContactService.cs @@ -8,4 +8,7 @@ public interface IFacilityContactService Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default); Task CreateAsync(FacilityContact contact, CancellationToken cancellationToken = default); Task UpdateAsync(Guid id, FacilityContact updates, CancellationToken cancellationToken = default); + Task DeleteAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default); + Task RestoreAsync(Guid id, CancellationToken cancellationToken = default); } diff --git a/omsorgCore/src/OmsorgCore.Application/Services/IFacilityQualificationRateService.cs b/omsorgCore/src/OmsorgCore.Application/Services/IFacilityQualificationRateService.cs new file mode 100644 index 0000000..645490e --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Services/IFacilityQualificationRateService.cs @@ -0,0 +1,14 @@ +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Application.Services; + +public interface IFacilityQualificationRateService +{ + Task> GetByFacilityIdAsync(Guid facilityId, CancellationToken cancellationToken = default); + Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default); + Task CreateAsync(FacilityQualificationRate rate, CancellationToken cancellationToken = default); + Task UpdateAsync(Guid id, FacilityQualificationRate updates, CancellationToken cancellationToken = default); + Task DeleteAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default); + Task RestoreAsync(Guid id, CancellationToken cancellationToken = default); +} diff --git a/omsorgCore/src/OmsorgCore.Application/Services/IFacilityService.cs b/omsorgCore/src/OmsorgCore.Application/Services/IFacilityService.cs index 71a3cfc..40b519e 100644 --- a/omsorgCore/src/OmsorgCore.Application/Services/IFacilityService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Services/IFacilityService.cs @@ -8,10 +8,14 @@ public interface IFacilityService Task<(IReadOnlyList Items, int TotalCount)> GetPagedAsync( string? search, string? crmStatus, + bool followUpDueOnly, int page, int pageSize, CancellationToken cancellationToken = default); Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default); Task CreateAsync(Facility facility, CancellationToken cancellationToken = default); Task UpdateAsync(Guid id, Facility updates, CancellationToken cancellationToken = default); + Task DeleteAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default); + Task RestoreAsync(Guid id, CancellationToken cancellationToken = default); } diff --git a/omsorgCore/src/OmsorgCore.Application/Services/IOrderService.cs b/omsorgCore/src/OmsorgCore.Application/Services/IOrderService.cs index 967e281..cec2005 100644 --- a/omsorgCore/src/OmsorgCore.Application/Services/IOrderService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Services/IOrderService.cs @@ -9,10 +9,16 @@ public interface IOrderService string? search, Guid? statusId, Guid? facilityId, + string? priority, + string? requiredQualification, + string? shiftType, int page, int pageSize, CancellationToken cancellationToken = default); Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default); Task CreateAsync(Order order, CancellationToken cancellationToken = default); Task UpdateAsync(Guid id, Order updates, CancellationToken cancellationToken = default); + Task DeleteAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default); + Task RestoreAsync(Guid id, CancellationToken cancellationToken = default); } diff --git a/omsorgCore/src/OmsorgCore.Application/Services/IRoleService.cs b/omsorgCore/src/OmsorgCore.Application/Services/IRoleService.cs index 32023f2..889c52e 100644 --- a/omsorgCore/src/OmsorgCore.Application/Services/IRoleService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Services/IRoleService.cs @@ -14,6 +14,6 @@ public interface IRoleService /// Ersetzt die komplette RolePermission-Menge der Rolle durch die übergebene Menge (kein inkrementelles Patchen). Task UpdatePermissionsAsync( Guid roleId, - IReadOnlyList<(ModuleType Module, PermissionAction Action)> permissions, + IReadOnlyList<(ModuleType Module, PermissionAction Action, PermissionScope Scope)> permissions, CancellationToken cancellationToken = default); } diff --git a/omsorgCore/src/OmsorgCore.Application/Services/ITimeEntryService.cs b/omsorgCore/src/OmsorgCore.Application/Services/ITimeEntryService.cs new file mode 100644 index 0000000..63cff9e --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Services/ITimeEntryService.cs @@ -0,0 +1,48 @@ +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Application.Services; + +public interface ITimeEntryService +{ + Task<(IReadOnlyList Items, int TotalCount)> GetPagedAsync( + Guid? statusId, + Guid? employeeId, + Guid? orderId, + int page, + int pageSize, + CancellationToken cancellationToken = default); + Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default); + + /// + /// Legt die Zeiterfassung an. Ist der aktuelle Nutzer nur mit Own-Scope berechtigt, wird + /// .EmployeeId ignoriert und serverseitig auf die eigene, per JWT + /// verknüpfte Mitarbeiter-Id gesetzt (analog AbsenceService.CreateAsync). StatusId wird immer + /// serverseitig auf den initialen Status ("Entwurf") gesetzt. Gibt null zurück, wenn Own-Scope + /// greift, aber kein Mitarbeiter verknüpft ist (fail-closed). + /// + Task CreateAsync(TimeEntry timeEntry, CancellationToken cancellationToken = default); + + /// + /// Bearbeitet Auftrag/Datum/Zeiten/Zuschlagsstunden - nur solange der aktuelle Status + /// ist. Own-Scope-Aufrufer dürfen nur eigene + /// Einträge bearbeiten (wie bei GetByIdAsync). + /// + Task UpdateAsync(Guid id, TimeEntry updates, CancellationToken cancellationToken = default); + + /// + /// Löst die einzige Selbst-Einreichungs-Kante (RequiresApproval=false) ab dem aktuellen Status + /// aus (Entwurf/Rückfrage -> Eingereicht). Kein Body nötig. + /// + Task SubmitAsync(Guid id, CancellationToken cancellationToken = default); + + /// + /// Büro-Entscheidung entlang der Statuspipeline (z. B. ->Prüfung/->Rückfrage/->Freigegeben/->Abgerechnet). + /// Validiert die Ziel-Transition über CanTransitionAsync und lehnt Kanten mit RequiresApproval=false ab + /// (die gehören zu SubmitAsync, nicht hierher). + /// + Task DecideAsync(Guid id, Guid statusId, string? adminNote, CancellationToken cancellationToken = default); + + Task DeleteAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default); + Task RestoreAsync(Guid id, CancellationToken cancellationToken = default); +} diff --git a/omsorgCore/src/OmsorgCore.Application/Services/IUserService.cs b/omsorgCore/src/OmsorgCore.Application/Services/IUserService.cs index ff8e5d9..e725de7 100644 --- a/omsorgCore/src/OmsorgCore.Application/Services/IUserService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Services/IUserService.cs @@ -42,12 +42,13 @@ public interface IUserService Guid userId, CancellationToken cancellationToken = default); - /// Upsert: existiert bereits ein Override für (module, action) bei diesem User, wird dessen Effect aktualisiert statt dupliziert. + /// Upsert: existiert bereits ein Override für (module, action) bei diesem User, werden dessen Effect und Scope aktualisiert statt dupliziert. Task AddPermissionOverrideAsync( Guid userId, ModuleType module, PermissionAction action, PermissionEffect effect, + PermissionScope scope, CancellationToken cancellationToken = default); Task RemovePermissionOverrideAsync( diff --git a/omsorgCore/src/OmsorgCore.Application/Services/IValueListService.cs b/omsorgCore/src/OmsorgCore.Application/Services/IValueListService.cs index 76690f0..ed8b4c0 100644 --- a/omsorgCore/src/OmsorgCore.Application/Services/IValueListService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Services/IValueListService.cs @@ -8,8 +8,8 @@ public interface IValueListService Task> GetListsAsync(CancellationToken cancellationToken = default); Task> GetItemsAsync(string key, CancellationToken cancellationToken = default); - Task CreateItemAsync(string key, string value, int sortOrder, bool isDefault, bool isInitial, bool isTerminal, CancellationToken cancellationToken = default); - Task UpdateItemAsync(Guid id, string value, int sortOrder, bool isDefault, bool isInitial, bool isTerminal, CancellationToken cancellationToken = default); + Task CreateItemAsync(string key, string value, int sortOrder, bool isDefault, bool isInitial, bool isTerminal, bool triggersFollowUp, CancellationToken cancellationToken = default); + Task UpdateItemAsync(Guid id, string value, int sortOrder, bool isDefault, bool isInitial, bool isTerminal, bool triggersFollowUp, CancellationToken cancellationToken = default); Task DeleteItemAsync(Guid id, CancellationToken cancellationToken = default); Task> GetUsagesAsync(Guid id, CancellationToken cancellationToken = default); diff --git a/omsorgCore/src/OmsorgCore.Application/Services/OrderService.cs b/omsorgCore/src/OmsorgCore.Application/Services/OrderService.cs index 1a6c1f6..7ca63f2 100644 --- a/omsorgCore/src/OmsorgCore.Application/Services/OrderService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Services/OrderService.cs @@ -23,10 +23,13 @@ public class OrderService : IOrderService string? search, Guid? statusId, Guid? facilityId, + string? priority, + string? requiredQualification, + string? shiftType, int page, int pageSize, CancellationToken cancellationToken = default) - => _orderRepository.GetPagedAsync(search, statusId, facilityId, page, pageSize, cancellationToken); + => _orderRepository.GetPagedAsync(search, statusId, facilityId, priority, requiredQualification, shiftType, page, pageSize, cancellationToken); public Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) => _orderRepository.GetByIdAsync(id, cancellationToken); @@ -71,4 +74,29 @@ public class OrderService : IOrderService await _orderRepository.SaveChangesAsync(cancellationToken); return UpdateOrderResult.Ok(order); } + + public async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var deleted = await _orderRepository.SoftDeleteAsync(id, cancellationToken); + if (deleted) + { + await _orderRepository.SaveChangesAsync(cancellationToken); + } + + return deleted; + } + + public Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + => _orderRepository.GetDeletedAsync(search, cancellationToken); + + public async Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var restored = await _orderRepository.RestoreAsync(id, cancellationToken); + if (restored) + { + await _orderRepository.SaveChangesAsync(cancellationToken); + } + + return restored; + } } diff --git a/omsorgCore/src/OmsorgCore.Application/Services/PermissionService.cs b/omsorgCore/src/OmsorgCore.Application/Services/PermissionService.cs index 89934a0..5724c99 100644 --- a/omsorgCore/src/OmsorgCore.Application/Services/PermissionService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Services/PermissionService.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.Caching.Memory; using OmsorgCore.Application.Abstractions; using OmsorgCore.Application.Models; using OmsorgCore.Domain.Entities; @@ -8,30 +9,53 @@ namespace OmsorgCore.Application.Services; /// /// Implementiert die Regel aus REQUIREMENTS.md Abschnitt 3/7 und Blueprint 6.5: /// Rollen-Default gilt, ein individueller Override (Grant oder Revoke) gewinnt immer. +/// +/// Der Rechte-Join (Role→RolePermissions + PermissionOverrides + Employee) ist die teuerste, +/// pro Request wiederholte Query der gesamten API (jeder [RequirePermission]-Endpunkt löst sie +/// aus). Deshalb wird das Ergebnis 60s in einem IMemoryCache gehalten. Der SecurityStamp-Check +/// (Session-Killswitch, siehe Program.cs OnTokenValidated) bleibt bewusst außen vor - der muss +/// laut CLAUDE.md "sofort" wirken, unabhängig von jeder TTL. Die TTL hier ist nur ein +/// Sicherheitsnetz; der Normalfall ist aktive Invalidierung bei jeder Rechte-Mutation +/// (InvalidateUserPermissions/InvalidateRolePermissionsAsync, aufgerufen aus RoleService/UserService). /// public class PermissionService : IPermissionService { - private readonly IUserRepository _userRepository; + private static readonly TimeSpan CacheDuration = TimeSpan.FromSeconds(60); - public PermissionService(IUserRepository userRepository) + private readonly IUserRepository _userRepository; + private readonly IMemoryCache _cache; + + public PermissionService(IUserRepository userRepository, IMemoryCache cache) { _userRepository = userRepository; + _cache = cache; } public async Task HasPermissionAsync(Guid userId, ModuleType module, PermissionAction action, CancellationToken cancellationToken = default) { - User? user = await _userRepository.GetByIdWithPermissionsAsync(userId, cancellationToken); + User? user = await GetCachedUserAsync(userId, cancellationToken); if (user is null || !user.IsActive) { return false; } - return IsGranted(user, module, action); + return ResolveScope(user, module, action) is not null; + } + + public async Task GetScopeAsync(Guid userId, ModuleType module, PermissionAction action, CancellationToken cancellationToken = default) + { + User? user = await GetCachedUserAsync(userId, cancellationToken); + if (user is null || !user.IsActive) + { + return null; + } + + return ResolveScope(user, module, action); } public async Task> GetGrantedPermissionsAsync(Guid userId, CancellationToken cancellationToken = default) { - User? user = await _userRepository.GetByIdWithPermissionsAsync(userId, cancellationToken); + User? user = await GetCachedUserAsync(userId, cancellationToken); if (user is null || !user.IsActive) { return Array.Empty(); @@ -42,9 +66,10 @@ public class PermissionService : IPermissionService { foreach (PermissionAction action in Enum.GetValues()) { - if (IsGranted(user, module, action)) + PermissionScope? scope = ResolveScope(user, module, action); + if (scope is not null) { - grants.Add(new PermissionGrant(module, action)); + grants.Add(new PermissionGrant(module, action, scope.Value)); } } } @@ -52,16 +77,50 @@ public class PermissionService : IPermissionService return grants; } - private static bool IsGranted(User user, ModuleType module, PermissionAction action) + public void InvalidateUserPermissions(Guid userId) + => _cache.Remove(CacheKey(userId)); + + public async Task InvalidateRolePermissionsAsync(Guid roleId, CancellationToken cancellationToken = default) + { + var userIds = await _userRepository.GetUserIdsByRoleAsync(roleId, cancellationToken); + foreach (var userId in userIds) + { + InvalidateUserPermissions(userId); + } + } + + private async Task GetCachedUserAsync(Guid userId, CancellationToken cancellationToken) + { + if (_cache.TryGetValue(CacheKey(userId), out User? cached)) + { + return cached; + } + + User? user = await _userRepository.GetByIdWithPermissionsNoTrackingAsync(userId, cancellationToken); + _cache.Set(CacheKey(userId), user, CacheDuration); + return user; + } + + private static string CacheKey(Guid userId) => $"user-permissions:{userId}"; + + /// + /// Löst Modul+Aktion für diesen User zu einem Scope auf, oder null wenn nicht gewährt. + /// Ein Override ersetzt die Zelle vollständig (Grant+Scope) — es wird nicht mit dem + /// Rollen-Scope gemergt, analog zur bestehenden Effect-Semantik. + /// + private static PermissionScope? ResolveScope(User user, ModuleType module, PermissionAction action) { UserPermissionOverride? override_ = user.PermissionOverrides .FirstOrDefault(o => o.Module == module && o.Action == action); if (override_ is not null) { - return override_.Effect == PermissionEffect.Grant; + return override_.Effect == PermissionEffect.Grant ? override_.Scope : null; } - return user.Role.RolePermissions.Any(rp => rp.Module == module && rp.Action == action); + RolePermission? rolePermission = user.Role.RolePermissions + .FirstOrDefault(rp => rp.Module == module && rp.Action == action); + + return rolePermission?.Scope; } } diff --git a/omsorgCore/src/OmsorgCore.Application/Services/RoleService.cs b/omsorgCore/src/OmsorgCore.Application/Services/RoleService.cs index c536e0d..cef4cab 100644 --- a/omsorgCore/src/OmsorgCore.Application/Services/RoleService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Services/RoleService.cs @@ -7,10 +7,12 @@ namespace OmsorgCore.Application.Services; public class RoleService : IRoleService { private readonly IRoleRepository _roleRepository; + private readonly IPermissionService _permissionService; - public RoleService(IRoleRepository roleRepository) + public RoleService(IRoleRepository roleRepository, IPermissionService permissionService) { _roleRepository = roleRepository; + _permissionService = permissionService; } public Task> GetAllAsync(CancellationToken cancellationToken = default) @@ -40,7 +42,7 @@ public class RoleService : IRoleService public async Task UpdatePermissionsAsync( Guid roleId, - IReadOnlyList<(ModuleType Module, PermissionAction Action)> permissions, + IReadOnlyList<(ModuleType Module, PermissionAction Action, PermissionScope Scope)> permissions, CancellationToken cancellationToken = default) { var role = await _roleRepository.GetByIdWithPermissionsAsync(roleId, cancellationToken); @@ -52,11 +54,12 @@ public class RoleService : IRoleService role.RolePermissions.Clear(); await _roleRepository.SaveChangesAsync(cancellationToken); - var newPermissions = permissions.Distinct() - .Select(p => new RolePermission { RoleId = role.Id, Module = p.Module, Action = p.Action }) + var newPermissions = permissions.DistinctBy(p => (p.Module, p.Action)) + .Select(p => new RolePermission { RoleId = role.Id, Module = p.Module, Action = p.Action, Scope = p.Scope }) .ToList(); await _roleRepository.AddPermissionRangeAsync(newPermissions, cancellationToken); await _roleRepository.SaveChangesAsync(cancellationToken); + await _permissionService.InvalidateRolePermissionsAsync(roleId, cancellationToken); return UpdateRolePermissionsResult.Ok(); } } diff --git a/omsorgCore/src/OmsorgCore.Application/Services/SubmitTimeEntryResult.cs b/omsorgCore/src/OmsorgCore.Application/Services/SubmitTimeEntryResult.cs new file mode 100644 index 0000000..8acb958 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Services/SubmitTimeEntryResult.cs @@ -0,0 +1,28 @@ +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Application.Services; + +public enum SubmitTimeEntryFailureReason +{ + NotFound, + NoSelfServiceTransition +} + +public class SubmitTimeEntryResult +{ + public bool Success { get; init; } + public SubmitTimeEntryFailureReason? FailureReason { get; init; } + public TimeEntry? TimeEntry { get; init; } + + public static SubmitTimeEntryResult Fail(SubmitTimeEntryFailureReason reason) => new() + { + Success = false, + FailureReason = reason + }; + + public static SubmitTimeEntryResult Ok(TimeEntry timeEntry) => new() + { + Success = true, + TimeEntry = timeEntry + }; +} diff --git a/omsorgCore/src/OmsorgCore.Application/Services/TimeEntryService.cs b/omsorgCore/src/OmsorgCore.Application/Services/TimeEntryService.cs new file mode 100644 index 0000000..b580c5c --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Services/TimeEntryService.cs @@ -0,0 +1,226 @@ +using OmsorgCore.Application.Abstractions; +using OmsorgCore.Domain.Entities; +using OmsorgCore.Domain.Enums; + +namespace OmsorgCore.Application.Services; + +public class TimeEntryService : ITimeEntryService +{ + private const string StatusListKey = "TimeEntryStatus"; + + private readonly ITimeEntryRepository _timeEntryRepository; + private readonly IOrderRepository _orderRepository; + private readonly IPermissionService _permissionService; + private readonly ICurrentUserService _currentUserService; + private readonly IValueListRepository _valueListRepository; + + public TimeEntryService( + ITimeEntryRepository timeEntryRepository, + IOrderRepository orderRepository, + IPermissionService permissionService, + ICurrentUserService currentUserService, + IValueListRepository valueListRepository) + { + _timeEntryRepository = timeEntryRepository; + _orderRepository = orderRepository; + _permissionService = permissionService; + _currentUserService = currentUserService; + _valueListRepository = valueListRepository; + } + + public async Task<(IReadOnlyList Items, int TotalCount)> GetPagedAsync( + Guid? statusId, + Guid? employeeId, + Guid? orderId, + int page, + int pageSize, + CancellationToken cancellationToken = default) + { + Guid? restrictToEmployeeId = await ResolveOwnScopeRestrictionAsync(PermissionAction.View, cancellationToken); + return await _timeEntryRepository.GetPagedAsync(statusId, employeeId, orderId, page, pageSize, cancellationToken, restrictToEmployeeId); + } + + public async Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) + { + var timeEntry = await _timeEntryRepository.GetByIdAsync(id, cancellationToken); + if (timeEntry is null) + { + return null; + } + + Guid? restrictToEmployeeId = await ResolveOwnScopeRestrictionAsync(PermissionAction.View, cancellationToken); + if (restrictToEmployeeId.HasValue && timeEntry.EmployeeId != restrictToEmployeeId.Value) + { + return null; + } + + return timeEntry; + } + + public async Task CreateAsync(TimeEntry timeEntry, CancellationToken cancellationToken = default) + { + if (await _orderRepository.GetByIdAsync(timeEntry.OrderId, cancellationToken) is null) + { + return null; + } + + Guid? restrictToEmployeeId = await ResolveOwnScopeRestrictionAsync(PermissionAction.Create, cancellationToken); + if (restrictToEmployeeId.HasValue) + { + if (restrictToEmployeeId.Value == Guid.Empty) + { + // Own-Scope, aber kein User.EmployeeId verknüpft - fail-closed (analog AbsenceService). + return null; + } + + timeEntry.EmployeeId = restrictToEmployeeId.Value; + } + else + { + var ownEmployeeId = _currentUserService.EmployeeId; + if (ownEmployeeId is null || ownEmployeeId == Guid.Empty) + { + return null; + } + + timeEntry.EmployeeId = ownEmployeeId.Value; + } + + var initialStatus = await _valueListRepository.GetInitialItemAsync(StatusListKey, cancellationToken) + ?? throw new InvalidOperationException("Kein initialer Zeiterfassungsstatus konfiguriert (DbSeeder.SeedValueListsAsync fehlt)."); + timeEntry.StatusId = initialStatus.Id; + + await _timeEntryRepository.AddAsync(timeEntry, cancellationToken); + await _timeEntryRepository.SaveChangesAsync(cancellationToken); + return timeEntry; + } + + public async Task UpdateAsync(Guid id, TimeEntry updates, CancellationToken cancellationToken = default) + { + var timeEntry = await _timeEntryRepository.GetByIdAsync(id, cancellationToken); + if (timeEntry is null) + { + return UpdateTimeEntryResult.Fail(UpdateTimeEntryFailureReason.NotFound); + } + + Guid? restrictToEmployeeId = await ResolveOwnScopeRestrictionAsync(PermissionAction.Edit, cancellationToken); + if (restrictToEmployeeId.HasValue && timeEntry.EmployeeId != restrictToEmployeeId.Value) + { + return UpdateTimeEntryResult.Fail(UpdateTimeEntryFailureReason.NotFound); + } + + var currentStatus = await _valueListRepository.GetItemByIdAsync(timeEntry.StatusId, cancellationToken); + if (currentStatus is null || !currentStatus.IsEditableByOwner) + { + return UpdateTimeEntryResult.Fail(UpdateTimeEntryFailureReason.NotEditable); + } + + if (updates.OrderId != timeEntry.OrderId && await _orderRepository.GetByIdAsync(updates.OrderId, cancellationToken) is null) + { + return UpdateTimeEntryResult.Fail(UpdateTimeEntryFailureReason.OrderNotFound); + } + + timeEntry.OrderId = updates.OrderId; + timeEntry.Date = updates.Date; + timeEntry.Start = updates.Start; + timeEntry.End = updates.End; + timeEntry.BreakDuration = updates.BreakDuration; + timeEntry.NightHours = updates.NightHours; + timeEntry.SaturdayHours = updates.SaturdayHours; + timeEntry.SundayHours = updates.SundayHours; + timeEntry.HolidayHours = updates.HolidayHours; + timeEntry.UpdatedAt = DateTime.UtcNow; + + await _timeEntryRepository.UpdateAsync(timeEntry, cancellationToken); + await _timeEntryRepository.SaveChangesAsync(cancellationToken); + return UpdateTimeEntryResult.Ok(timeEntry); + } + + public async Task SubmitAsync(Guid id, CancellationToken cancellationToken = default) + { + var timeEntry = await _timeEntryRepository.GetByIdAsync(id, cancellationToken); + if (timeEntry is null) + { + return SubmitTimeEntryResult.Fail(SubmitTimeEntryFailureReason.NotFound); + } + + Guid? restrictToEmployeeId = await ResolveOwnScopeRestrictionAsync(PermissionAction.Edit, cancellationToken); + if (restrictToEmployeeId.HasValue && timeEntry.EmployeeId != restrictToEmployeeId.Value) + { + return SubmitTimeEntryResult.Fail(SubmitTimeEntryFailureReason.NotFound); + } + + var transition = await _valueListRepository.GetSelfServiceTransitionAsync(timeEntry.StatusId, cancellationToken); + if (transition is null) + { + return SubmitTimeEntryResult.Fail(SubmitTimeEntryFailureReason.NoSelfServiceTransition); + } + + timeEntry.StatusId = transition.ToItemId; + timeEntry.UpdatedAt = DateTime.UtcNow; + + await _timeEntryRepository.UpdateAsync(timeEntry, cancellationToken); + await _timeEntryRepository.SaveChangesAsync(cancellationToken); + return SubmitTimeEntryResult.Ok(timeEntry); + } + + public async Task DecideAsync(Guid id, Guid statusId, string? adminNote, CancellationToken cancellationToken = default) + { + var timeEntry = await _timeEntryRepository.GetByIdAsync(id, cancellationToken); + if (timeEntry is null) + { + return DecideTimeEntryResult.Fail(DecideTimeEntryFailureReason.NotFound); + } + + var transition = await _valueListRepository.GetTransitionAsync(timeEntry.StatusId, statusId, cancellationToken); + if (transition is null || !transition.RequiresApproval) + { + return DecideTimeEntryResult.Fail(DecideTimeEntryFailureReason.InvalidStatusTransition); + } + + timeEntry.StatusId = statusId; + timeEntry.AdminNote = adminNote; + timeEntry.UpdatedAt = DateTime.UtcNow; + + await _timeEntryRepository.UpdateAsync(timeEntry, cancellationToken); + await _timeEntryRepository.SaveChangesAsync(cancellationToken); + return DecideTimeEntryResult.Ok(timeEntry); + } + + public async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var deleted = await _timeEntryRepository.SoftDeleteAsync(id, cancellationToken); + if (deleted) + { + await _timeEntryRepository.SaveChangesAsync(cancellationToken); + } + + return deleted; + } + + public Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + => _timeEntryRepository.GetDeletedAsync(search, cancellationToken); + + public async Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var restored = await _timeEntryRepository.RestoreAsync(id, cancellationToken); + if (restored) + { + await _timeEntryRepository.SaveChangesAsync(cancellationToken); + } + + return restored; + } + + /// Own-Scope-Filterung, siehe AbsenceService.ResolveOwnScopeRestrictionAsync (analoges Muster für ModuleType.TimeEntries). + private async Task ResolveOwnScopeRestrictionAsync(PermissionAction action, CancellationToken cancellationToken) + { + if (_currentUserService.UserId is not { } userId) + { + return null; + } + + var scope = await _permissionService.GetScopeAsync(userId, ModuleType.TimeEntries, action, cancellationToken); + return scope == PermissionScope.Own ? (_currentUserService.EmployeeId ?? Guid.Empty) : null; + } +} diff --git a/omsorgCore/src/OmsorgCore.Application/Services/UpdateAbsenceResult.cs b/omsorgCore/src/OmsorgCore.Application/Services/UpdateAbsenceResult.cs new file mode 100644 index 0000000..c83453a --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Services/UpdateAbsenceResult.cs @@ -0,0 +1,28 @@ +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Application.Services; + +public enum UpdateAbsenceFailureReason +{ + NotFound, + AlreadyDecided +} + +public class UpdateAbsenceResult +{ + public bool Success { get; init; } + public UpdateAbsenceFailureReason? FailureReason { get; init; } + public Absence? Absence { get; init; } + + public static UpdateAbsenceResult Fail(UpdateAbsenceFailureReason reason) => new() + { + Success = false, + FailureReason = reason + }; + + public static UpdateAbsenceResult Ok(Absence absence) => new() + { + Success = true, + Absence = absence + }; +} diff --git a/omsorgCore/src/OmsorgCore.Application/Services/UpdateTimeEntryResult.cs b/omsorgCore/src/OmsorgCore.Application/Services/UpdateTimeEntryResult.cs new file mode 100644 index 0000000..626258d --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Services/UpdateTimeEntryResult.cs @@ -0,0 +1,29 @@ +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Application.Services; + +public enum UpdateTimeEntryFailureReason +{ + NotFound, + NotEditable, + OrderNotFound +} + +public class UpdateTimeEntryResult +{ + public bool Success { get; init; } + public UpdateTimeEntryFailureReason? FailureReason { get; init; } + public TimeEntry? TimeEntry { get; init; } + + public static UpdateTimeEntryResult Fail(UpdateTimeEntryFailureReason reason) => new() + { + Success = false, + FailureReason = reason + }; + + public static UpdateTimeEntryResult Ok(TimeEntry timeEntry) => new() + { + Success = true, + TimeEntry = timeEntry + }; +} diff --git a/omsorgCore/src/OmsorgCore.Application/Services/UserService.cs b/omsorgCore/src/OmsorgCore.Application/Services/UserService.cs index fae765e..0dbba12 100644 --- a/omsorgCore/src/OmsorgCore.Application/Services/UserService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Services/UserService.cs @@ -14,6 +14,7 @@ public class UserService : IUserService private readonly IPasswordResetService _passwordResetService; private readonly IRefreshTokenRepository _refreshTokenRepository; private readonly IPasswordPolicy _passwordPolicy; + private readonly IPermissionService _permissionService; public UserService( IUserRepository userRepository, @@ -22,7 +23,8 @@ public class UserService : IUserService IPasswordHasher passwordHasher, IPasswordResetService passwordResetService, IRefreshTokenRepository refreshTokenRepository, - IPasswordPolicy passwordPolicy) + IPasswordPolicy passwordPolicy, + IPermissionService permissionService) { _userRepository = userRepository; _employeeRepository = employeeRepository; @@ -31,6 +33,7 @@ public class UserService : IUserService _passwordResetService = passwordResetService; _refreshTokenRepository = refreshTokenRepository; _passwordPolicy = passwordPolicy; + _permissionService = permissionService; } public async Task> GetAllAsync(CancellationToken cancellationToken = default) @@ -220,6 +223,7 @@ public class UserService : IUserService } user.RoleId = roleId; + _permissionService.InvalidateUserPermissions(userId); var wasActive = user.IsActive; user.IsActive = isActive; @@ -250,7 +254,7 @@ public class UserService : IUserService } return user.PermissionOverrides - .Select(o => new PermissionOverrideSummary(o.Id, o.Module, o.Action, o.Effect)) + .Select(o => new PermissionOverrideSummary(o.Id, o.Module, o.Action, o.Effect, o.Scope)) .ToList(); } @@ -259,6 +263,7 @@ public class UserService : IUserService ModuleType module, PermissionAction action, PermissionEffect effect, + PermissionScope scope, CancellationToken cancellationToken = default) { var user = await _userRepository.GetByIdWithPermissionsAsync(userId, cancellationToken); @@ -273,15 +278,17 @@ public class UserService : IUserService if (existing is not null) { existing.Effect = effect; + existing.Scope = scope; } else { - existing = new UserPermissionOverride { UserId = user.Id, Module = module, Action = action, Effect = effect }; + existing = new UserPermissionOverride { UserId = user.Id, Module = module, Action = action, Effect = effect, Scope = scope }; await _userRepository.AddPermissionOverrideAsync(existing, cancellationToken); } await _userRepository.SaveChangesAsync(cancellationToken); - return AddPermissionOverrideResult.Ok(new PermissionOverrideSummary(existing.Id, module, action, effect)); + _permissionService.InvalidateUserPermissions(userId); + return AddPermissionOverrideResult.Ok(new PermissionOverrideSummary(existing.Id, module, action, effect, scope)); } public async Task RemovePermissionOverrideAsync( @@ -301,6 +308,7 @@ public class UserService : IUserService user.PermissionOverrides.Remove(existing); await _userRepository.SaveChangesAsync(cancellationToken); + _permissionService.InvalidateUserPermissions(userId); return RemovePermissionOverrideResult.Ok(); } } diff --git a/omsorgCore/src/OmsorgCore.Application/Services/ValueListService.cs b/omsorgCore/src/OmsorgCore.Application/Services/ValueListService.cs index 6b0572e..fb33c1f 100644 --- a/omsorgCore/src/OmsorgCore.Application/Services/ValueListService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Services/ValueListService.cs @@ -20,7 +20,7 @@ public class ValueListService : IValueListService public Task> GetItemsAsync(string key, CancellationToken cancellationToken = default) => _valueListRepository.GetItemsAsync(key, cancellationToken); - public async Task CreateItemAsync(string key, string value, int sortOrder, bool isDefault, bool isInitial, bool isTerminal, CancellationToken cancellationToken = default) + public async Task CreateItemAsync(string key, string value, int sortOrder, bool isDefault, bool isInitial, bool isTerminal, bool triggersFollowUp, CancellationToken cancellationToken = default) { var list = await _valueListRepository.GetListByKeyAsync(key, cancellationToken) ?? throw new InvalidOperationException($"Unbekannte Auswahlliste '{key}'."); @@ -32,7 +32,8 @@ public class ValueListService : IValueListService SortOrder = sortOrder, IsDefault = isDefault, IsInitial = isInitial, - IsTerminal = isTerminal + IsTerminal = isTerminal, + TriggersFollowUp = triggersFollowUp }; await _valueListRepository.AddItemAsync(item, cancellationToken); @@ -40,7 +41,7 @@ public class ValueListService : IValueListService return item; } - public async Task UpdateItemAsync(Guid id, string value, int sortOrder, bool isDefault, bool isInitial, bool isTerminal, CancellationToken cancellationToken = default) + public async Task UpdateItemAsync(Guid id, string value, int sortOrder, bool isDefault, bool isInitial, bool isTerminal, bool triggersFollowUp, CancellationToken cancellationToken = default) { var item = await _valueListRepository.GetItemByIdAsync(id, cancellationToken); if (item is null) @@ -53,6 +54,7 @@ public class ValueListService : IValueListService item.IsDefault = isDefault; item.IsInitial = isInitial; item.IsTerminal = isTerminal; + item.TriggersFollowUp = triggersFollowUp; await _valueListRepository.UpdateItemAsync(item, cancellationToken); await _valueListRepository.SaveChangesAsync(cancellationToken); diff --git a/omsorgCore/src/OmsorgCore.Domain/Entities/Absence.cs b/omsorgCore/src/OmsorgCore.Domain/Entities/Absence.cs new file mode 100644 index 0000000..663bd87 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Domain/Entities/Absence.cs @@ -0,0 +1,28 @@ +using OmsorgCore.Domain.Common; + +namespace OmsorgCore.Domain.Entities; + +/// +/// Abwesenheits-/Urlaubs-/Krankmeldungsantrag eines Mitarbeiters (FR-CON-1, Datenbasis für die +/// spätere Verfügbarkeitsprüfung in FR-EM-3). unterscheidet Urlaub/Krankmeldung/ +/// Sonstige über die ValueList "AbsenceType" statt über getrennte Entitäten, da alle übrigen Felder +/// identisch sind (siehe Root-CLAUDE.md, "jede Information wird nur einmal gespeichert"). +/// +public class Absence : AuditableEntity +{ + public Guid EmployeeId { get; set; } + public Employee? Employee { get; set; } + + public string Type { get; set; } = string.Empty; + public DateOnly StartDate { get; set; } + public DateOnly EndDate { get; set; } + public string? Reason { get; set; } + public string? Substitute { get; set; } + public string? Note { get; set; } + + // Kein hartkodierter Default hier (anders als z.B. Contract.Status) - der initiale Wert kommt + // aus der admin-editierbaren ValueList "AbsenceStatus" (IsInitial-Flag) und wird in + // AbsenceService.CreateAsync gesetzt, bevor der Antrag gespeichert wird. + public string Status { get; set; } = string.Empty; + public string? AdminNote { get; set; } +} diff --git a/omsorgCore/src/OmsorgCore.Domain/Entities/Document.cs b/omsorgCore/src/OmsorgCore.Domain/Entities/Document.cs new file mode 100644 index 0000000..c0026dd --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Domain/Entities/Document.cs @@ -0,0 +1,25 @@ +using OmsorgCore.Domain.Common; + +namespace OmsorgCore.Domain.Entities; + +/// +/// Dokumentenarchiv-Eintrag (FR-MA-3) — Bezug zu einer beliebigen Kern-Entität über +/// / statt eines festen FK, damit künftig auch +/// Facility-/Contract-/Order-Dokumente ohne neue Migration möglich sind. Die Datei-Bytes selbst +/// liegen auf dem Dateisystem (siehe IDocumentStorage) — hier nur Metadaten + Pfad. +/// +public class Document : AuditableEntity +{ + public string EntityType { get; set; } = string.Empty; + public Guid EntityId { get; set; } + + public string Category { get; set; } = string.Empty; + public string FileName { get; set; } = string.Empty; + public string ContentType { get; set; } = string.Empty; + public long SizeBytes { get; set; } + public string StorageKey { get; set; } = string.Empty; + public string? Description { get; set; } + + public Guid UploadedByUserId { get; set; } + public User? UploadedByUser { get; set; } +} diff --git a/omsorgCore/src/OmsorgCore.Domain/Entities/Facility.cs b/omsorgCore/src/OmsorgCore.Domain/Entities/Facility.cs index aa16ba9..f854514 100644 --- a/omsorgCore/src/OmsorgCore.Domain/Entities/Facility.cs +++ b/omsorgCore/src/OmsorgCore.Domain/Entities/Facility.cs @@ -6,6 +6,8 @@ namespace OmsorgCore.Domain.Entities; /// Core-Objekt "Einrichtung" — CRM-/Kundendatensatz (REQUIREMENTS.md Abschnitt 6, Blueprint 19.2). /// Adresse und Rechnungsadresse sind getrennte Adressen (z. B. Einsatzort vs. zentrale Buchhaltung /// eines Trägers), daher jeweils eigene strukturierte Felder statt eines gemeinsamen Freitextfelds. +/// Konditionen (FR-EIN-4, Blueprint 19.2) sind ebenfalls flache, nullable Felder direkt auf dieser +/// Entität — Ausnahme ist die qualifikationsabhängige Bepreisung, siehe . /// public class Facility : AuditableEntity { @@ -24,4 +26,17 @@ public class Facility : AuditableEntity public string? BillingCountry { get; set; } public string CrmStatus { get; set; } = "Lead"; + public DateTime? FollowUpDueDate { get; set; } + + public decimal? BillingRate { get; set; } + public decimal? NightSurchargePercent { get; set; } + public decimal? SaturdaySurchargePercent { get; set; } + public decimal? SundaySurchargePercent { get; set; } + public decimal? HolidaySurchargePercent { get; set; } + public decimal? TravelCostRate { get; set; } + public decimal? MinimumHours { get; set; } + public string? BreakPolicy { get; set; } + public string? BillingInterval { get; set; } + public int? PaymentTermDays { get; set; } + public string? IndividualAgreements { get; set; } } diff --git a/omsorgCore/src/OmsorgCore.Domain/Entities/FacilityQualificationRate.cs b/omsorgCore/src/OmsorgCore.Domain/Entities/FacilityQualificationRate.cs new file mode 100644 index 0000000..8d68574 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Domain/Entities/FacilityQualificationRate.cs @@ -0,0 +1,18 @@ +using OmsorgCore.Domain.Common; + +namespace OmsorgCore.Domain.Entities; + +/// +/// Qualifikationsabhängiger Verrechnungssatz einer Einrichtung (FR-EIN-4, Blueprint 19.2) — 1:n-Beziehung +/// zu , kein eigenständiges Core-Objekt. wird gegen die +/// admin-editierbare ValueList "Qualification" validiert (dieselbe Liste wie Employee.Qualification/ +/// Order.RequiredQualification). +/// +public class FacilityQualificationRate : AuditableEntity +{ + public Guid FacilityId { get; set; } + public Facility Facility { get; set; } = null!; + + public string Qualification { get; set; } = string.Empty; + public decimal Rate { get; set; } +} diff --git a/omsorgCore/src/OmsorgCore.Domain/Entities/RolePermission.cs b/omsorgCore/src/OmsorgCore.Domain/Entities/RolePermission.cs index e565240..70bd1f0 100644 --- a/omsorgCore/src/OmsorgCore.Domain/Entities/RolePermission.cs +++ b/omsorgCore/src/OmsorgCore.Domain/Entities/RolePermission.cs @@ -13,4 +13,5 @@ public class RolePermission : Entity public ModuleType Module { get; set; } public PermissionAction Action { get; set; } + public PermissionScope Scope { get; set; } } diff --git a/omsorgCore/src/OmsorgCore.Domain/Entities/TimeEntry.cs b/omsorgCore/src/OmsorgCore.Domain/Entities/TimeEntry.cs index 1042f70..bff379e 100644 --- a/omsorgCore/src/OmsorgCore.Domain/Entities/TimeEntry.cs +++ b/omsorgCore/src/OmsorgCore.Domain/Entities/TimeEntry.cs @@ -20,5 +20,12 @@ public class TimeEntry : AuditableEntity public TimeOnly End { get; set; } public TimeSpan BreakDuration { get; set; } - public string Status { get; set; } = "Entwurf"; + public decimal NightHours { get; set; } + public decimal SaturdayHours { get; set; } + public decimal SundayHours { get; set; } + public decimal HolidayHours { get; set; } + + public Guid StatusId { get; set; } + public ValueListItem Status { get; set; } = null!; + public string? AdminNote { get; set; } } diff --git a/omsorgCore/src/OmsorgCore.Domain/Entities/UserPermissionOverride.cs b/omsorgCore/src/OmsorgCore.Domain/Entities/UserPermissionOverride.cs index 0054505..e8aece6 100644 --- a/omsorgCore/src/OmsorgCore.Domain/Entities/UserPermissionOverride.cs +++ b/omsorgCore/src/OmsorgCore.Domain/Entities/UserPermissionOverride.cs @@ -16,4 +16,5 @@ public class UserPermissionOverride : Entity public ModuleType Module { get; set; } public PermissionAction Action { get; set; } public PermissionEffect Effect { get; set; } + public PermissionScope Scope { get; set; } } diff --git a/omsorgCore/src/OmsorgCore.Domain/Entities/ValueListItem.cs b/omsorgCore/src/OmsorgCore.Domain/Entities/ValueListItem.cs index 4a8e29b..8dc89de 100644 --- a/omsorgCore/src/OmsorgCore.Domain/Entities/ValueListItem.cs +++ b/omsorgCore/src/OmsorgCore.Domain/Entities/ValueListItem.cs @@ -6,7 +6,10 @@ namespace OmsorgCore.Domain.Entities; /// Ein einzelner Wert innerhalb einer (z. B. "Aktiv" in der Liste /// "EmployeeStatus"). / sind nur für Listen mit /// Übergangsregeln relevant (aktuell nur "OrderStatus", siehe ) -/// und bei allen anderen Listen einfach false. +/// und bei allen anderen Listen einfach false. ist nur für den +/// "Kein Bedarf"-Eintrag der Liste "CrmStatus" relevant (siehe FacilitiesController) und entkoppelt +/// die automatische Wiedervorlage vom konkreten Anzeigetext, damit ein Umbenennen über die +/// Status-Verwaltung die Automatik nicht lautlos bricht. /// public class ValueListItem : Entity { @@ -18,4 +21,6 @@ public class ValueListItem : Entity public bool IsDefault { get; set; } public bool IsInitial { get; set; } public bool IsTerminal { get; set; } + public bool TriggersFollowUp { get; set; } + public bool IsEditableByOwner { get; set; } } diff --git a/omsorgCore/src/OmsorgCore.Domain/Entities/ValueListItemTransition.cs b/omsorgCore/src/OmsorgCore.Domain/Entities/ValueListItemTransition.cs index 019ecd3..6a559de 100644 --- a/omsorgCore/src/OmsorgCore.Domain/Entities/ValueListItemTransition.cs +++ b/omsorgCore/src/OmsorgCore.Domain/Entities/ValueListItemTransition.cs @@ -14,4 +14,11 @@ public class ValueListItemTransition : Entity public Guid ToItemId { get; set; } public ValueListItem ToItem { get; set; } = null!; + + /// + /// Nur für "TimeEntryStatus" ausgewertet: false markiert eine Selbst-Einreichungs-Kante, + /// die der Ersteller ohne Büro-Freigabe auslösen darf (z. B. Entwurf→Eingereicht). Für alle + /// anderen Listen (u. a. "OrderStatus") bleibt der Default true ohne Verhaltensänderung. + /// + public bool RequiresApproval { get; set; } = true; } diff --git a/omsorgCore/src/OmsorgCore.Domain/Enums/DocumentEntityType.cs b/omsorgCore/src/OmsorgCore.Domain/Enums/DocumentEntityType.cs new file mode 100644 index 0000000..cb29b5f --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Domain/Enums/DocumentEntityType.cs @@ -0,0 +1,15 @@ +namespace OmsorgCore.Domain.Enums; + +/// +/// Welche Art von Kern-Entität ein beschreibt (siehe FR-MA-3). +/// Nur hat in diesem Schritt tatsächlich Upload-/Validierungscode - +/// die übrigen Werte sind für ein künftiges Facility-/Contract-/Order-Dokumentenarchiv vorgesehen, +/// damit dafür keine neue Migration nötig wird. +/// +public enum DocumentEntityType +{ + Employee, + Facility, + Contract, + Order +} diff --git a/omsorgCore/src/OmsorgCore.Domain/Enums/ModuleType.cs b/omsorgCore/src/OmsorgCore.Domain/Enums/ModuleType.cs index 4c48ef9..4256f91 100644 --- a/omsorgCore/src/OmsorgCore.Domain/Enums/ModuleType.cs +++ b/omsorgCore/src/OmsorgCore.Domain/Enums/ModuleType.cs @@ -14,5 +14,9 @@ public enum ModuleType Recruiting, Controlling, UserManagement, - AuditLog + AuditLog, + Documents, + Users, + Configuration, + Absences } diff --git a/omsorgCore/src/OmsorgCore.Domain/Enums/PermissionAction.cs b/omsorgCore/src/OmsorgCore.Domain/Enums/PermissionAction.cs index bd28035..a630e19 100644 --- a/omsorgCore/src/OmsorgCore.Domain/Enums/PermissionAction.cs +++ b/omsorgCore/src/OmsorgCore.Domain/Enums/PermissionAction.cs @@ -10,5 +10,6 @@ public enum PermissionAction Edit, Delete, Export, - Approve + Approve, + Recover } diff --git a/omsorgCore/src/OmsorgCore.Domain/Enums/PermissionScope.cs b/omsorgCore/src/OmsorgCore.Domain/Enums/PermissionScope.cs new file mode 100644 index 0000000..e2ac1f5 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Domain/Enums/PermissionScope.cs @@ -0,0 +1,16 @@ +namespace OmsorgCore.Domain.Enums; + +/// +/// Datenebenen-Einschränkung eines Modul/Aktion-Rechts: sieht ein Nutzer alle Datensätze des +/// Moduls, oder nur die eigenen (verknüpft über )? +/// Ergänzt / als dritte Dimension der +/// Rechtematrix (siehe REQUIREMENTS.md FR-MA-6, Blueprint 4.2). All ist bewusst der +/// Ordinalwert 0, damit bestehende / +/// -Zeilen nach einer Migration unverändertes +/// Verhalten behalten. +/// +public enum PermissionScope +{ + All, + Own +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/DependencyInjection.cs b/omsorgCore/src/OmsorgCore.Infrastructure/DependencyInjection.cs index 9525780..6923918 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/DependencyInjection.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/DependencyInjection.cs @@ -5,6 +5,7 @@ using OmsorgCore.Application.Abstractions; using OmsorgCore.Infrastructure.Persistence; using OmsorgCore.Infrastructure.Repositories; using OmsorgCore.Infrastructure.Security; +using OmsorgCore.Infrastructure.Storage; namespace OmsorgCore.Infrastructure; @@ -28,12 +29,19 @@ public static class DependencyInjection services.Configure(configuration.GetSection(SeedOptions.SectionName)); services.Configure(configuration.GetSection(LoginAttemptOptions.SectionName)); services.Configure(configuration.GetSection(PasswordPolicyOptions.SectionName)); + services.Configure(configuration.GetSection(StorageOptions.SectionName)); services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddScoped(); RegisterValueListUsageCheckers(services); services.AddScoped(); @@ -91,5 +99,39 @@ public static class DependencyInjection .Select(c => new ValueListUsageEntry("Contract", c.Id, $"Vertrag {c.Id}")).ToListAsync(ct))); services.AddScoped(); + + services.AddScoped(sp => new StringFieldValueListUsageChecker( + sp.GetRequiredService(), "DocumentCategory", + (db, value, ct) => db.Documents.AsNoTracking().Where(d => d.Category == value) + .Select(d => new ValueListUsageEntry("Document", d.Id, d.FileName)).ToListAsync(ct))); + + services.AddScoped(); + + services.AddScoped(sp => new StringFieldValueListUsageChecker( + sp.GetRequiredService(), "ShiftType", + (db, value, ct) => db.Orders.AsNoTracking().Where(o => o.ShiftType == value) + .Select(o => new ValueListUsageEntry("Order", o.Id, $"Auftrag {o.Id}")).ToListAsync(ct))); + + services.AddScoped(sp => new StringFieldValueListUsageChecker( + sp.GetRequiredService(), "Priority", + (db, value, ct) => db.Orders.AsNoTracking().Where(o => o.Priority == value) + .Select(o => new ValueListUsageEntry("Order", o.Id, $"Auftrag {o.Id}")).ToListAsync(ct))); + + services.AddScoped(sp => new StringFieldValueListUsageChecker( + sp.GetRequiredService(), "BillingInterval", + (db, value, ct) => db.Facilities.AsNoTracking().Where(f => f.BillingInterval == value) + .Select(f => new ValueListUsageEntry("Facility", f.Id, f.Name)).ToListAsync(ct))); + + services.AddScoped(sp => new StringFieldValueListUsageChecker( + sp.GetRequiredService(), "AbsenceType", + (db, value, ct) => db.Absences.AsNoTracking().Where(a => a.Type == value) + .Select(a => new ValueListUsageEntry("Absence", a.Id, $"Antrag {a.Id}")).ToListAsync(ct))); + + services.AddScoped(sp => new StringFieldValueListUsageChecker( + sp.GetRequiredService(), "AbsenceStatus", + (db, value, ct) => db.Absences.AsNoTracking().Where(a => a.Status == value) + .Select(a => new ValueListUsageEntry("Absence", a.Id, $"Antrag {a.Id}")).ToListAsync(ct))); + + services.AddScoped(); } } diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/AbsenceConfiguration.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/AbsenceConfiguration.cs new file mode 100644 index 0000000..3c0499c --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/AbsenceConfiguration.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Infrastructure.Persistence.Configurations; + +public class AbsenceConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("absences"); + builder.HasKey(a => a.Id); + builder.Property(a => a.Type).IsRequired().HasMaxLength(50); + builder.Property(a => a.Status).IsRequired().HasMaxLength(50); + builder.Property(a => a.Reason).HasMaxLength(500); + builder.Property(a => a.Substitute).HasMaxLength(200); + builder.Property(a => a.Note).HasMaxLength(500); + builder.Property(a => a.AdminNote).HasMaxLength(500); + + builder.HasOne(a => a.Employee) + .WithMany() + .HasForeignKey(a => a.EmployeeId) + .OnDelete(DeleteBehavior.Restrict); + + builder.HasQueryFilter(a => !a.IsDeleted); + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/DocumentConfiguration.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/DocumentConfiguration.cs new file mode 100644 index 0000000..538a816 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/DocumentConfiguration.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Infrastructure.Persistence.Configurations; + +public class DocumentConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("documents"); + builder.HasKey(d => d.Id); + + builder.Property(d => d.EntityType).IsRequired().HasMaxLength(50); + builder.Property(d => d.Category).IsRequired().HasMaxLength(100); + builder.Property(d => d.FileName).IsRequired().HasMaxLength(260); + builder.Property(d => d.ContentType).IsRequired().HasMaxLength(150); + builder.Property(d => d.StorageKey).IsRequired().HasMaxLength(500); + builder.Property(d => d.Description).HasMaxLength(1000); + + builder.HasIndex(d => new { d.EntityType, d.EntityId }); + + builder.HasOne(d => d.UploadedByUser) + .WithMany() + .HasForeignKey(d => d.UploadedByUserId) + .OnDelete(DeleteBehavior.Restrict); + + builder.HasQueryFilter(d => !d.IsDeleted); + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/FacilityConfiguration.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/FacilityConfiguration.cs index 450fd9f..ceecde8 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/FacilityConfiguration.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/FacilityConfiguration.cs @@ -23,6 +23,17 @@ public class FacilityConfiguration : IEntityTypeConfiguration builder.Property(f => f.BillingCountry).HasMaxLength(100); builder.Property(f => f.CrmStatus).IsRequired().HasMaxLength(50); + builder.Property(f => f.BillingRate).HasColumnType("decimal(10,2)"); + builder.Property(f => f.NightSurchargePercent).HasColumnType("decimal(5,2)"); + builder.Property(f => f.SaturdaySurchargePercent).HasColumnType("decimal(5,2)"); + builder.Property(f => f.SundaySurchargePercent).HasColumnType("decimal(5,2)"); + builder.Property(f => f.HolidaySurchargePercent).HasColumnType("decimal(5,2)"); + builder.Property(f => f.TravelCostRate).HasColumnType("decimal(10,2)"); + builder.Property(f => f.MinimumHours).HasColumnType("decimal(5,2)"); + builder.Property(f => f.BreakPolicy).HasMaxLength(1000); + builder.Property(f => f.BillingInterval).HasMaxLength(50); + builder.Property(f => f.IndividualAgreements).HasMaxLength(2000); + // Soft-gelöschte Einrichtungen sind für alle normalen Queries unsichtbar. builder.HasQueryFilter(f => !f.IsDeleted); } diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/FacilityQualificationRateConfiguration.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/FacilityQualificationRateConfiguration.cs new file mode 100644 index 0000000..7a0ae16 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/FacilityQualificationRateConfiguration.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Infrastructure.Persistence.Configurations; + +public class FacilityQualificationRateConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("facility_qualification_rates"); + builder.HasKey(r => r.Id); + builder.Property(r => r.Qualification).IsRequired().HasMaxLength(200); + builder.Property(r => r.Rate).IsRequired().HasColumnType("decimal(10,2)"); + + builder.HasOne(r => r.Facility) + .WithMany() + .HasForeignKey(r => r.FacilityId) + .OnDelete(DeleteBehavior.Restrict); + + // Soft-gelöschte Qualifikationspreise sind für alle normalen Queries unsichtbar. + builder.HasQueryFilter(r => !r.IsDeleted); + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/InvoiceConfiguration.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/InvoiceConfiguration.cs index 54d5f0c..3ad4476 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/InvoiceConfiguration.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/InvoiceConfiguration.cs @@ -20,5 +20,8 @@ public class InvoiceConfiguration : IEntityTypeConfiguration .WithMany() .HasForeignKey(i => i.FacilityId) .OnDelete(DeleteBehavior.Restrict); + + // Soft-gelöschte Rechnungen sind für alle normalen Queries unsichtbar. + builder.HasQueryFilter(i => !i.IsDeleted); } } diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/TimeEntryConfiguration.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/TimeEntryConfiguration.cs index 2122ce9..42212bc 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/TimeEntryConfiguration.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/TimeEntryConfiguration.cs @@ -10,7 +10,11 @@ public class TimeEntryConfiguration : IEntityTypeConfiguration { builder.ToTable("time_entries"); builder.HasKey(t => t.Id); - builder.Property(t => t.Status).IsRequired().HasMaxLength(50); + builder.Property(t => t.NightHours).HasColumnType("numeric(6,2)"); + builder.Property(t => t.SaturdayHours).HasColumnType("numeric(6,2)"); + builder.Property(t => t.SundayHours).HasColumnType("numeric(6,2)"); + builder.Property(t => t.HolidayHours).HasColumnType("numeric(6,2)"); + builder.Property(t => t.AdminNote).HasMaxLength(500); builder.HasOne(t => t.Employee) .WithMany() @@ -21,5 +25,13 @@ public class TimeEntryConfiguration : IEntityTypeConfiguration .WithMany() .HasForeignKey(t => t.OrderId) .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(t => t.Status) + .WithMany() + .HasForeignKey(t => t.StatusId) + .OnDelete(DeleteBehavior.Restrict); + + // Soft-gelöschte Zeiterfassungen sind für alle normalen Queries unsichtbar. + builder.HasQueryFilter(t => !t.IsDeleted); } } diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/ValueListItemTransitionConfiguration.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/ValueListItemTransitionConfiguration.cs index 6138c75..2ad8206 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/ValueListItemTransitionConfiguration.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/ValueListItemTransitionConfiguration.cs @@ -11,6 +11,7 @@ public class ValueListItemTransitionConfiguration : IEntityTypeConfiguration t.Id); builder.HasIndex(t => new { t.FromItemId, t.ToItemId }).IsUnique(); + builder.Property(t => t.RequiresApproval).HasDefaultValue(true); builder.HasOne(t => t.FromItem) .WithMany() diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/DbSeeder.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/DbSeeder.cs index 1ce7ad0..0ddb1e2 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/DbSeeder.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/DbSeeder.cs @@ -34,7 +34,7 @@ public static class DbSeeder { foreach (PermissionAction action in Enum.GetValues()) { - role.RolePermissions.Add(new RolePermission { Role = role, Module = module, Action = action }); + role.RolePermissions.Add(new RolePermission { Role = role, Module = module, Action = action, Scope = PermissionScope.All }); } } } @@ -64,37 +64,48 @@ public static class DbSeeder /// selben Change aktualisiert werden (siehe Pflegehinweis in omsorgCore/CLAUDE.md, Abschnitt /// "Rechtesystem"). "Verträge" hat in der Rechtematrix keine eigene Zeile - hier wie /// Mitarbeiter/Einrichtungen behandelt (siehe FR-MA-2: Vertragsdaten für Sabina/Malik/Sabrina). - /// "Außendienst" bekommt bewusst keine Modul-Rechte: OMSORG Connect spricht noch nicht gegen - /// dieses Backend (eigene MySQL-Datenhaltung, siehe omsorgWeb/CLAUDE.md), und "nur eigene Daten" - /// ist mit dem aktuell rein modulweiten RBAC ohnehin nicht abbildbar (Datenebene, kein Rollen-Recht). - /// "Geschäftsführung" bekommt AuditLog automatisch mit (iteriert alle ModuleType-Werte generisch, + /// "Außendienst" bekommt Employees/Contracts.View mit PermissionScope.Own (siehe + /// IPermissionService.GetScopeAsync) - sieht nur die eigene Personalakte/eigene Verträge über + /// User.EmployeeId. Ebenso Absences.{Create,View,Edit} mit PermissionScope.Own (FR-CON-1/FR-EM-3): + /// der Außendienst stellt eigene Abwesenheits-/Urlaubs-/Krankmeldungsanträge über + /// `omsorgWeb/mitarbeiter-app` gegen dieses Backend, Genehmigen/Ablehnen bleibt Büro-Rollen + /// vorbehalten (kein Approve-Recht hier). "Geschäftsführung" bekommt AuditLog automatisch mit + /// (iteriert alle ModuleType-Werte generisch, /// siehe unten) - alle anderen Rollen listen ihre Module explizit auf und bekommen AuditLog dadurch /// bewusst NICHT (Audit-Log ist per Default nur für Geschäftsführung sichtbar). /// public static async Task SeedBaseRolesAsync(OmsorgCoreDbContext db, CancellationToken cancellationToken = default) { - await SeedRoleIfMissingAsync(db, "Geschäftsführung", Enum.GetValues().Select(m => (m, AllActions)), cancellationToken); + await SeedRoleIfMissingAsync(db, "Geschäftsführung", Enum.GetValues().Select(m => (m, AllActions, PermissionScope.All)), cancellationToken); - await SeedRoleIfMissingAsync(db, "Disposition/Buchhaltung", new (ModuleType Module, PermissionAction[] Actions)[] + await SeedRoleIfMissingAsync(db, "Disposition/Buchhaltung", new (ModuleType Module, PermissionAction[] Actions, PermissionScope Scope)[] { - (ModuleType.Employees, AllActions), - (ModuleType.Facilities, AllActions), - (ModuleType.Contracts, AllActions), - (ModuleType.Orders, AllActions), - (ModuleType.TimeEntries, new[] { PermissionAction.View, PermissionAction.Approve }), - (ModuleType.Invoices, new[] { PermissionAction.View, PermissionAction.Create, PermissionAction.Approve }), - (ModuleType.Recruiting, new[] { PermissionAction.View, PermissionAction.Create, PermissionAction.Edit }), - (ModuleType.Controlling, new[] { PermissionAction.View }) + (ModuleType.Employees, AllActions, PermissionScope.All), + (ModuleType.Facilities, AllActions, PermissionScope.All), + (ModuleType.Contracts, AllActions, PermissionScope.All), + (ModuleType.Orders, AllActions, PermissionScope.All), + (ModuleType.TimeEntries, AllActions, PermissionScope.All), + (ModuleType.Invoices, new[] { PermissionAction.View, PermissionAction.Create, PermissionAction.Approve }, PermissionScope.All), + (ModuleType.Recruiting, new[] { PermissionAction.View, PermissionAction.Create, PermissionAction.Edit }, PermissionScope.All), + (ModuleType.Controlling, new[] { PermissionAction.View }, PermissionScope.All), + (ModuleType.Documents, AllActions, PermissionScope.All), + (ModuleType.Absences, AllActions, PermissionScope.All) }, cancellationToken); - await SeedRoleIfMissingAsync(db, "Recruiting", new (ModuleType Module, PermissionAction[] Actions)[] + await SeedRoleIfMissingAsync(db, "Recruiting", new (ModuleType Module, PermissionAction[] Actions, PermissionScope Scope)[] { - (ModuleType.Employees, new[] { PermissionAction.View }), - (ModuleType.Facilities, new[] { PermissionAction.View, PermissionAction.Create, PermissionAction.Edit }), - (ModuleType.Recruiting, AllActions) + (ModuleType.Employees, new[] { PermissionAction.View }, PermissionScope.All), + (ModuleType.Facilities, new[] { PermissionAction.View, PermissionAction.Create, PermissionAction.Edit }, PermissionScope.All), + (ModuleType.Recruiting, AllActions, PermissionScope.All) }, cancellationToken); - await SeedRoleIfMissingAsync(db, "Außendienst", Array.Empty<(ModuleType, PermissionAction[])>(), cancellationToken); + await SeedRoleIfMissingAsync(db, "Außendienst", new (ModuleType Module, PermissionAction[] Actions, PermissionScope Scope)[] + { + (ModuleType.Employees, new[] { PermissionAction.View }, PermissionScope.Own), + (ModuleType.Contracts, new[] { PermissionAction.View }, PermissionScope.Own), + (ModuleType.Absences, new[] { PermissionAction.Create, PermissionAction.View, PermissionAction.Edit }, PermissionScope.Own), + (ModuleType.TimeEntries, new[] { PermissionAction.Create, PermissionAction.View, PermissionAction.Edit }, PermissionScope.Own) + }, cancellationToken); } /// @@ -124,6 +135,9 @@ public static class DbSeeder await SeedSimpleListIfMissingAsync(db, "CrmStatus", "CRM-Status", new (string Value, bool IsDefault)[] { ("Lead", true), ("Kontaktiert", false), ("Kein Bedarf", false), ("Wiedervorlage", false), ("Interesse", false), ("Angebot", false), ("Kunde", false), ("Bestandskunde", false) }, cancellationToken); + await SeedSimpleListIfMissingAsync(db, "FollowUpPeriods", "Wiedervorlage-Fristen", + new (string Value, bool IsDefault)[] { ("7", false), ("14", true), ("21", false), ("31", false) }, cancellationToken); + await SeedSimpleListIfMissingAsync(db, "FacilityType", "Einrichtungstyp", new (string Value, bool IsDefault)[] { ("Pflegeheim", false), ("Ambulanter Pflegedienst", false), ("Krankenhaus", false), ("Betreutes Wohnen", false), ("Sonstige", false) }, cancellationToken); @@ -133,7 +147,62 @@ public static class DbSeeder await SeedSimpleListIfMissingAsync(db, "ContractStatus", "Vertragsstatus", new (string Value, bool IsDefault)[] { ("Entwurf", true), ("Aktiv", false), ("Beendet", false) }, cancellationToken); + await SeedSimpleListIfMissingAsync(db, "DocumentCategory", "Dokumentkategorie", + new (string Value, bool IsDefault)[] { ("Qualifikation", false), ("Fortbildung", false), ("Führerschein", false), ("Gesundheitsnachweis", false), ("Notiz", false), ("Sonstiges", true) }, cancellationToken); + + // Aufsteigend nach Qualifikationsniveau (SortOrder = Level) - genutzt sowohl für + // Employee.Qualification als auch für Order.RequiredQualification (FR-EM-1/FR-EM-3), damit + // beide Seiten auf denselben Werten und derselben Rangfolge basieren. + await SeedSimpleListIfMissingAsync(db, "Qualification", "Qualifikation", + new (string Value, bool IsDefault)[] + { + ("Ungelernte Kraft", false), + ("Betreuungskraft (§ 43b/53c SGB XI)", false), + ("Pflegehelfer/in", false), + ("Pflegefachassistent/in", false), + ("Altenpfleger/in", false), + ("Gesundheits- und Krankenpfleger/in", false), + ("Pflegefachkraft mit Leitungsfunktion", false) + }, cancellationToken); + + await SeedSimpleListIfMissingAsync(db, "ShiftType", "Schichtart", + new (string Value, bool IsDefault)[] + { + ("Frühdienst", false), + ("Spätdienst", false), + ("Nachtdienst", false), + ("Tagdienst", false), + ("Bereitschaftsdienst", false), + ("Sonstige", false) + }, cancellationToken); + + await SeedSimpleListIfMissingAsync(db, "Priority", "Priorität", + new (string Value, bool IsDefault)[] + { + ("Niedrig", false), + ("Normal", true), + ("Hoch", false), + ("Dringend", false) + }, cancellationToken); + + // FR-EIN-4: Abrechnungsintervall der Konditionen einer Einrichtung (Facility.BillingInterval). + await SeedSimpleListIfMissingAsync(db, "BillingInterval", "Abrechnungsintervall", + new (string Value, bool IsDefault)[] { ("Wöchentlich", false), ("Monatlich", true), ("Quartalsweise", false) }, cancellationToken); + + // FR-CON-1/FR-EM-3: Abwesenheits-/Urlaubs-/Krankmeldungsanträge (Absence.Type/Status). + // Einfacher String-Status ohne ValueListItemTransition (wie ContractStatus) - "wer darf + // wohin" ergibt sich vollständig aus den Rechten (Außendienst nur Create, Büro-Rollen + // Approve), nicht aus einem Übergangsgraphen. + await SeedSimpleListIfMissingAsync(db, "AbsenceType", "Abwesenheitsart", + new (string Value, bool IsDefault)[] { ("Urlaub", true), ("Krankmeldung", false), ("Sonstige", false) }, cancellationToken); + + await SeedSimpleListIfMissingAsync(db, "AbsenceStatus", "Antragsstatus", + new (string Value, bool IsDefault)[] { ("Eingereicht", true), ("Genehmigt", false), ("Abgelehnt", false) }, cancellationToken); + await SeedAbsenceStatusInitialFlagIfMissingAsync(db, cancellationToken); + await SeedOrderStatusListIfMissingAsync(db, cancellationToken); + await SeedCrmStatusTransitionsIfMissingAsync(db, cancellationToken); + await SeedTimeEntryStatusListIfMissingAsync(db, cancellationToken); async Task SeedSimpleListIfMissingAsync(OmsorgCoreDbContext context, string key, string displayName, (string Value, bool IsDefault)[] values, CancellationToken ct) { @@ -225,12 +294,152 @@ public static class DbSeeder await context.SaveChangesAsync(ct); } + + // FR-EIN-3: CRM-Status-Übergänge - locker statt streng linear (anders als OrderStatus), weil + // ein CRM-Kontakt jederzeit abspringen oder auf Eis gelegt werden kann. Vorwärtskette entlang + // der bestehenden SortOrder-Reihenfolge, plus von JEDEM Status aus jederzeit nach "Kein Bedarf" + // und "Wiedervorlage". Läuft nur einmal (Liste selbst wird oben bereits idempotent geseedet, + // hier zählt nur, ob schon Transitions für "CrmStatus" existieren). + async Task SeedCrmStatusTransitionsIfMissingAsync(OmsorgCoreDbContext context, CancellationToken ct) + { + if (await context.ValueListItemTransitions.AnyAsync(t => t.FromItem.ValueList.Key == "CrmStatus", ct)) + { + return; + } + + var items = await context.ValueListItems + .Where(i => i.ValueList.Key == "CrmStatus") + .OrderBy(i => i.SortOrder) + .ToListAsync(ct); + + if (items.Count == 0) + { + return; + } + + Guid IdOf(string value) => items.First(i => i.Value == value).Id; + + var pairs = new HashSet<(Guid From, Guid To)>(); + + for (var i = 0; i < items.Count - 1; i++) + { + pairs.Add((items[i].Id, items[i + 1].Id)); + } + + var keinBedarfId = IdOf("Kein Bedarf"); + var wiedervorlageId = IdOf("Wiedervorlage"); + foreach (var item in items) + { + if (item.Id != keinBedarfId) + { + pairs.Add((item.Id, keinBedarfId)); + } + if (item.Id != wiedervorlageId) + { + pairs.Add((item.Id, wiedervorlageId)); + } + } + + foreach (var (from, to) in pairs) + { + context.ValueListItemTransitions.Add(new ValueListItemTransition { FromItemId = from, ToItemId = to }); + } + + await context.SaveChangesAsync(ct); + } + + // FR-ZE-2: Statuspipeline der Zeiterfassung (Entwurf -> eingereicht -> Prüfung -> Rückfrage -> + // freigegeben -> abgerechnet). Anders als OrderStatus braucht diese Liste zusätzlich + // RequiresApproval je Transition, damit TimeEntryService.SubmitAsync/DecideAsync unterscheiden + // können, ob eine Kante vom Ersteller selbst ausgelöst werden darf (Entwurf/Rückfrage -> + // Eingereicht) oder eine Büro-Entscheidung braucht (alle übrigen). + async Task SeedTimeEntryStatusListIfMissingAsync(OmsorgCoreDbContext context, CancellationToken ct) + { + if (await context.ValueListItems.AnyAsync(i => i.ValueList.Key == "TimeEntryStatus", ct)) + { + return; + } + + var list = new ValueList { Key = "TimeEntryStatus", DisplayName = "Zeiterfassungsstatus" }; + context.ValueLists.Add(list); + + var definitions = new (string Name, bool IsInitial, bool IsTerminal, bool IsEditableByOwner)[] + { + ("Entwurf", true, false, true), + ("Eingereicht", false, false, true), + ("Prüfung", false, false, false), + ("Rückfrage", false, false, true), + ("Freigegeben", false, false, false), + ("Abgerechnet", false, true, false) + }; + + var statuses = definitions + .Select((d, index) => new ValueListItem + { + ValueListId = list.Id, + Value = d.Name, + SortOrder = index, + IsInitial = d.IsInitial, + IsTerminal = d.IsTerminal, + IsEditableByOwner = d.IsEditableByOwner + }) + .ToList(); + context.ValueListItems.AddRange(statuses); + + Guid IdOf(string name) => statuses.First(s => s.Value == name).Id; + + var transitions = new (string From, string To, bool RequiresApproval)[] + { + ("Entwurf", "Eingereicht", false), + ("Eingereicht", "Prüfung", true), + ("Prüfung", "Rückfrage", true), + ("Rückfrage", "Eingereicht", false), + ("Prüfung", "Freigegeben", true), + ("Freigegeben", "Abgerechnet", true) + }; + + foreach (var (from, to, requiresApproval) in transitions) + { + context.ValueListItemTransitions.Add(new ValueListItemTransition + { + FromItemId = IdOf(from), + ToItemId = IdOf(to), + RequiresApproval = requiresApproval + }); + } + + await context.SaveChangesAsync(ct); + } + + // "Eingereicht" ist der Ausgangszustand jedes Antrags (AbsenceService.CreateAsync/UpdateAsync + // lesen darüber, statt den Anzeigetext hartzukodieren, siehe dortige Kommentare) - IsInitial + // markiert das strukturell, genau wie bei "OrderStatus", nicht per Admin-UI wie + // TriggersFollowUp bei CrmStatus. Läuft idempotent (kein erneutes Setzen, sobald irgendein + // Item der Liste bereits IsInitial trägt), damit ein späteres manuelles Umflaggen über die + // Status-Verwaltung nicht bei jedem Start zurückgesetzt wird. + async Task SeedAbsenceStatusInitialFlagIfMissingAsync(OmsorgCoreDbContext context, CancellationToken ct) + { + if (await context.ValueListItems.AnyAsync(i => i.ValueList.Key == "AbsenceStatus" && i.IsInitial, ct)) + { + return; + } + + var item = await context.ValueListItems + .FirstOrDefaultAsync(i => i.ValueList.Key == "AbsenceStatus" && i.Value == "Eingereicht", ct); + if (item is null) + { + return; + } + + item.IsInitial = true; + await context.SaveChangesAsync(ct); + } } private static async Task SeedRoleIfMissingAsync( OmsorgCoreDbContext db, string roleName, - IEnumerable<(ModuleType Module, PermissionAction[] Actions)> permissions, + IEnumerable<(ModuleType Module, PermissionAction[] Actions, PermissionScope Scope)> permissions, CancellationToken cancellationToken) { if (await db.Roles.AnyAsync(r => r.Name == roleName, cancellationToken)) @@ -239,11 +448,11 @@ public static class DbSeeder } var role = new Role { Name = roleName }; - foreach (var (module, actions) in permissions) + foreach (var (module, actions, scope) in permissions) { foreach (var action in actions) { - role.RolePermissions.Add(new RolePermission { Role = role, Module = module, Action = action }); + role.RolePermissions.Add(new RolePermission { Role = role, Module = module, Action = action, Scope = scope }); } } diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809071507_AddFacilityFollowUpDueDate.Designer.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809071507_AddFacilityFollowUpDueDate.Designer.cs new file mode 100644 index 0000000..7c70257 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809071507_AddFacilityFollowUpDueDate.Designer.cs @@ -0,0 +1,1058 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using OmsorgCore.Infrastructure.Persistence; + +#nullable disable + +namespace OmsorgCore.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(OmsorgCoreDbContext))] + [Migration("20260809071507_AddFacilityFollowUpDueDate")] + partial class AddFacilityFollowUpDueDate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.AuditLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ActorUserId") + .HasColumnType("uuid"); + + b.Property("ActorUsername") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Category") + .HasColumnType("integer"); + + b.Property("Details") + .HasColumnType("jsonb"); + + b.Property("EntityId") + .HasColumnType("uuid"); + + b.Property("EntityType") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("IpAddress") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("OccurredAtUtc") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ActorUserId"); + + b.HasIndex("OccurredAtUtc"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("audit_log_entries", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Contract", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AllowancesDescription") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ContractType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("HourlyWage") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("OvertimeRules") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ProbationPeriodMonths") + .HasColumnType("integer"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("VacationDaysPerYear") + .HasColumnType("integer"); + + b.Property("WeeklyHours") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("FacilityId"); + + b.ToTable("contracts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Employee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AvatarFileName") + .HasColumnType("text"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DateOfBirth") + .HasColumnType("date"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EmergencyContactName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EmergencyContactPhone") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("EmergencyContactRelation") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("EmploymentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("EntryDate") + .HasColumnType("date"); + + b.Property("ExitDate") + .HasColumnType("date"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("PhoneNumber") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Qualification") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Street") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("employees", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Facility", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BillingCity") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("BillingCountry") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("BillingPostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("BillingStreet") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CrmStatus") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FacilityType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("FollowUpDueDate") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Street") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Website") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.HasKey("Id"); + + b.ToTable("facilities", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityContact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Department") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Email") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("PhoneNumber") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Role") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.ToTable("facility_contacts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Invoice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BillingPeriodEnd") + .HasColumnType("date"); + + b.Property("BillingPeriodStart") + .HasColumnType("date"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("GrossAmount") + .HasColumnType("numeric(12,2)"); + + b.Property("InvoiceNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("NetAmount") + .HasColumnType("numeric(12,2)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.HasIndex("InvoiceNumber") + .IsUnique(); + + b.ToTable("invoices", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.LoginAttempt", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttemptedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Succeeded") + .HasColumnType("boolean"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("IpAddress", "AttemptedAt"); + + b.ToTable("login_attempts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Conditions") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("FacilityContactId") + .HasColumnType("uuid"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Priority") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RequiredHeadcount") + .HasColumnType("integer"); + + b.Property("RequiredQualification") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ShiftType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("StatusId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityContactId"); + + b.HasIndex("FacilityId"); + + b.HasIndex("StatusId"); + + b.ToTable("orders", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.PasswordResetCode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttemptCount") + .HasColumnType("integer"); + + b.Property("CodeHash") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ConsumedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("InvalidatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ResetTokenExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ResetTokenHash") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ResetTokenUsedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ResetTokenHash"); + + b.HasIndex("UserId"); + + b.ToTable("password_reset_codes", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ReplacedByTokenId") + .HasColumnType("uuid"); + + b.Property("RevokedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("TokenHash") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ReplacedByTokenId"); + + b.HasIndex("TokenHash") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("refresh_tokens", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("roles", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RolePermission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("integer"); + + b.Property("Module") + .HasColumnType("integer"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId", "Module", "Action") + .IsUnique(); + + b.ToTable("role_permissions", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.TimeEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BreakDuration") + .HasColumnType("interval"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("End") + .HasColumnType("time without time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("OrderId") + .HasColumnType("uuid"); + + b.Property("Start") + .HasColumnType("time without time zone"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("OrderId"); + + b.ToTable("time_entries", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("MustChangePassword") + .HasColumnType("boolean"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("SecurityStamp") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("RoleId"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("users", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.UserPermissionOverride", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("integer"); + + b.Property("Effect") + .HasColumnType("integer"); + + b.Property("Module") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "Module", "Action") + .IsUnique(); + + b.ToTable("user_permission_overrides", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueList", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("value_lists", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IsDefault") + .HasColumnType("boolean"); + + b.Property("IsInitial") + .HasColumnType("boolean"); + + b.Property("IsTerminal") + .HasColumnType("boolean"); + + b.Property("SortOrder") + .HasColumnType("integer"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ValueListId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ValueListId", "Value") + .IsUnique(); + + b.ToTable("value_list_items", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItemTransition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("FromItemId") + .HasColumnType("uuid"); + + b.Property("ToItemId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ToItemId"); + + b.HasIndex("FromItemId", "ToItemId") + .IsUnique(); + + b.ToTable("value_list_item_transitions", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Contract", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Employee"); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityContact", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Invoice", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Order", b => + { + b.HasOne("OmsorgCore.Domain.Entities.FacilityContact", "FacilityContact") + .WithMany() + .HasForeignKey("FacilityContactId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "Status") + .WithMany() + .HasForeignKey("StatusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + + b.Navigation("FacilityContact"); + + b.Navigation("Status"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.PasswordResetCode", b => + { + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RefreshToken", b => + { + b.HasOne("OmsorgCore.Domain.Entities.RefreshToken", null) + .WithMany() + .HasForeignKey("ReplacedByTokenId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RolePermission", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Role", "Role") + .WithMany("RolePermissions") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.TimeEntry", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.Order", "Order") + .WithMany() + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("OmsorgCore.Domain.Entities.Role", "Role") + .WithMany("Users") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.UserPermissionOverride", b => + { + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany("PermissionOverrides") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItem", b => + { + b.HasOne("OmsorgCore.Domain.Entities.ValueList", "ValueList") + .WithMany("Items") + .HasForeignKey("ValueListId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ValueList"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItemTransition", b => + { + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "FromItem") + .WithMany() + .HasForeignKey("FromItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "ToItem") + .WithMany() + .HasForeignKey("ToItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("FromItem"); + + b.Navigation("ToItem"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Role", b => + { + b.Navigation("RolePermissions"); + + b.Navigation("Users"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.Navigation("PermissionOverrides"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueList", b => + { + b.Navigation("Items"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809071507_AddFacilityFollowUpDueDate.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809071507_AddFacilityFollowUpDueDate.cs new file mode 100644 index 0000000..158bdb9 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809071507_AddFacilityFollowUpDueDate.cs @@ -0,0 +1,29 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace OmsorgCore.Infrastructure.Persistence.Migrations +{ + /// + public partial class AddFacilityFollowUpDueDate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "FollowUpDueDate", + table: "facilities", + type: "timestamp with time zone", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "FollowUpDueDate", + table: "facilities"); + } + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809075239_AddValueListItemTriggersFollowUp.Designer.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809075239_AddValueListItemTriggersFollowUp.Designer.cs new file mode 100644 index 0000000..a363d58 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809075239_AddValueListItemTriggersFollowUp.Designer.cs @@ -0,0 +1,1061 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using OmsorgCore.Infrastructure.Persistence; + +#nullable disable + +namespace OmsorgCore.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(OmsorgCoreDbContext))] + [Migration("20260809075239_AddValueListItemTriggersFollowUp")] + partial class AddValueListItemTriggersFollowUp + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.AuditLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ActorUserId") + .HasColumnType("uuid"); + + b.Property("ActorUsername") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Category") + .HasColumnType("integer"); + + b.Property("Details") + .HasColumnType("jsonb"); + + b.Property("EntityId") + .HasColumnType("uuid"); + + b.Property("EntityType") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("IpAddress") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("OccurredAtUtc") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ActorUserId"); + + b.HasIndex("OccurredAtUtc"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("audit_log_entries", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Contract", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AllowancesDescription") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ContractType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("HourlyWage") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("OvertimeRules") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ProbationPeriodMonths") + .HasColumnType("integer"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("VacationDaysPerYear") + .HasColumnType("integer"); + + b.Property("WeeklyHours") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("FacilityId"); + + b.ToTable("contracts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Employee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AvatarFileName") + .HasColumnType("text"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DateOfBirth") + .HasColumnType("date"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EmergencyContactName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EmergencyContactPhone") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("EmergencyContactRelation") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("EmploymentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("EntryDate") + .HasColumnType("date"); + + b.Property("ExitDate") + .HasColumnType("date"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("PhoneNumber") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Qualification") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Street") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("employees", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Facility", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BillingCity") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("BillingCountry") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("BillingPostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("BillingStreet") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CrmStatus") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FacilityType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("FollowUpDueDate") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Street") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Website") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.HasKey("Id"); + + b.ToTable("facilities", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityContact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Department") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Email") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("PhoneNumber") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Role") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.ToTable("facility_contacts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Invoice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BillingPeriodEnd") + .HasColumnType("date"); + + b.Property("BillingPeriodStart") + .HasColumnType("date"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("GrossAmount") + .HasColumnType("numeric(12,2)"); + + b.Property("InvoiceNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("NetAmount") + .HasColumnType("numeric(12,2)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.HasIndex("InvoiceNumber") + .IsUnique(); + + b.ToTable("invoices", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.LoginAttempt", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttemptedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Succeeded") + .HasColumnType("boolean"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("IpAddress", "AttemptedAt"); + + b.ToTable("login_attempts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Conditions") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("FacilityContactId") + .HasColumnType("uuid"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Priority") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RequiredHeadcount") + .HasColumnType("integer"); + + b.Property("RequiredQualification") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ShiftType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("StatusId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityContactId"); + + b.HasIndex("FacilityId"); + + b.HasIndex("StatusId"); + + b.ToTable("orders", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.PasswordResetCode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttemptCount") + .HasColumnType("integer"); + + b.Property("CodeHash") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ConsumedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("InvalidatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ResetTokenExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ResetTokenHash") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ResetTokenUsedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ResetTokenHash"); + + b.HasIndex("UserId"); + + b.ToTable("password_reset_codes", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ReplacedByTokenId") + .HasColumnType("uuid"); + + b.Property("RevokedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("TokenHash") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ReplacedByTokenId"); + + b.HasIndex("TokenHash") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("refresh_tokens", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("roles", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RolePermission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("integer"); + + b.Property("Module") + .HasColumnType("integer"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId", "Module", "Action") + .IsUnique(); + + b.ToTable("role_permissions", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.TimeEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BreakDuration") + .HasColumnType("interval"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("End") + .HasColumnType("time without time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("OrderId") + .HasColumnType("uuid"); + + b.Property("Start") + .HasColumnType("time without time zone"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("OrderId"); + + b.ToTable("time_entries", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("MustChangePassword") + .HasColumnType("boolean"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("SecurityStamp") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("RoleId"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("users", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.UserPermissionOverride", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("integer"); + + b.Property("Effect") + .HasColumnType("integer"); + + b.Property("Module") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "Module", "Action") + .IsUnique(); + + b.ToTable("user_permission_overrides", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueList", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("value_lists", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IsDefault") + .HasColumnType("boolean"); + + b.Property("IsInitial") + .HasColumnType("boolean"); + + b.Property("IsTerminal") + .HasColumnType("boolean"); + + b.Property("SortOrder") + .HasColumnType("integer"); + + b.Property("TriggersFollowUp") + .HasColumnType("boolean"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ValueListId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ValueListId", "Value") + .IsUnique(); + + b.ToTable("value_list_items", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItemTransition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("FromItemId") + .HasColumnType("uuid"); + + b.Property("ToItemId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ToItemId"); + + b.HasIndex("FromItemId", "ToItemId") + .IsUnique(); + + b.ToTable("value_list_item_transitions", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Contract", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Employee"); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityContact", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Invoice", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Order", b => + { + b.HasOne("OmsorgCore.Domain.Entities.FacilityContact", "FacilityContact") + .WithMany() + .HasForeignKey("FacilityContactId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "Status") + .WithMany() + .HasForeignKey("StatusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + + b.Navigation("FacilityContact"); + + b.Navigation("Status"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.PasswordResetCode", b => + { + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RefreshToken", b => + { + b.HasOne("OmsorgCore.Domain.Entities.RefreshToken", null) + .WithMany() + .HasForeignKey("ReplacedByTokenId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RolePermission", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Role", "Role") + .WithMany("RolePermissions") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.TimeEntry", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.Order", "Order") + .WithMany() + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("OmsorgCore.Domain.Entities.Role", "Role") + .WithMany("Users") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.UserPermissionOverride", b => + { + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany("PermissionOverrides") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItem", b => + { + b.HasOne("OmsorgCore.Domain.Entities.ValueList", "ValueList") + .WithMany("Items") + .HasForeignKey("ValueListId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ValueList"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItemTransition", b => + { + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "FromItem") + .WithMany() + .HasForeignKey("FromItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "ToItem") + .WithMany() + .HasForeignKey("ToItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("FromItem"); + + b.Navigation("ToItem"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Role", b => + { + b.Navigation("RolePermissions"); + + b.Navigation("Users"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.Navigation("PermissionOverrides"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueList", b => + { + b.Navigation("Items"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809075239_AddValueListItemTriggersFollowUp.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809075239_AddValueListItemTriggersFollowUp.cs new file mode 100644 index 0000000..fe16761 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809075239_AddValueListItemTriggersFollowUp.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace OmsorgCore.Infrastructure.Persistence.Migrations +{ + /// + public partial class AddValueListItemTriggersFollowUp : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "TriggersFollowUp", + table: "value_list_items", + type: "boolean", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "TriggersFollowUp", + table: "value_list_items"); + } + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809133244_AddDocuments.Designer.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809133244_AddDocuments.Designer.cs new file mode 100644 index 0000000..11db44b --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809133244_AddDocuments.Designer.cs @@ -0,0 +1,1137 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using OmsorgCore.Infrastructure.Persistence; + +#nullable disable + +namespace OmsorgCore.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(OmsorgCoreDbContext))] + [Migration("20260809133244_AddDocuments")] + partial class AddDocuments + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.AuditLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ActorUserId") + .HasColumnType("uuid"); + + b.Property("ActorUsername") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Category") + .HasColumnType("integer"); + + b.Property("Details") + .HasColumnType("jsonb"); + + b.Property("EntityId") + .HasColumnType("uuid"); + + b.Property("EntityType") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("IpAddress") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("OccurredAtUtc") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ActorUserId"); + + b.HasIndex("OccurredAtUtc"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("audit_log_entries", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Contract", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AllowancesDescription") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ContractType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("HourlyWage") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("OvertimeRules") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ProbationPeriodMonths") + .HasColumnType("integer"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("VacationDaysPerYear") + .HasColumnType("integer"); + + b.Property("WeeklyHours") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("FacilityId"); + + b.ToTable("contracts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Document", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("EntityId") + .HasColumnType("uuid"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("character varying(260)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StorageKey") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UploadedByUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UploadedByUserId"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("documents", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Employee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AvatarFileName") + .HasColumnType("text"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DateOfBirth") + .HasColumnType("date"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EmergencyContactName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EmergencyContactPhone") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("EmergencyContactRelation") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("EmploymentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("EntryDate") + .HasColumnType("date"); + + b.Property("ExitDate") + .HasColumnType("date"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("PhoneNumber") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Qualification") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Street") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("employees", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Facility", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BillingCity") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("BillingCountry") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("BillingPostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("BillingStreet") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CrmStatus") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FacilityType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("FollowUpDueDate") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Street") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Website") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.HasKey("Id"); + + b.ToTable("facilities", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityContact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Department") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Email") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("PhoneNumber") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Role") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.ToTable("facility_contacts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Invoice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BillingPeriodEnd") + .HasColumnType("date"); + + b.Property("BillingPeriodStart") + .HasColumnType("date"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("GrossAmount") + .HasColumnType("numeric(12,2)"); + + b.Property("InvoiceNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("NetAmount") + .HasColumnType("numeric(12,2)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.HasIndex("InvoiceNumber") + .IsUnique(); + + b.ToTable("invoices", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.LoginAttempt", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttemptedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Succeeded") + .HasColumnType("boolean"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("IpAddress", "AttemptedAt"); + + b.ToTable("login_attempts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Conditions") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("FacilityContactId") + .HasColumnType("uuid"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Priority") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RequiredHeadcount") + .HasColumnType("integer"); + + b.Property("RequiredQualification") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ShiftType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("StatusId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityContactId"); + + b.HasIndex("FacilityId"); + + b.HasIndex("StatusId"); + + b.ToTable("orders", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.PasswordResetCode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttemptCount") + .HasColumnType("integer"); + + b.Property("CodeHash") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ConsumedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("InvalidatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ResetTokenExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ResetTokenHash") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ResetTokenUsedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ResetTokenHash"); + + b.HasIndex("UserId"); + + b.ToTable("password_reset_codes", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ReplacedByTokenId") + .HasColumnType("uuid"); + + b.Property("RevokedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("TokenHash") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ReplacedByTokenId"); + + b.HasIndex("TokenHash") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("refresh_tokens", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("roles", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RolePermission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("integer"); + + b.Property("Module") + .HasColumnType("integer"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId", "Module", "Action") + .IsUnique(); + + b.ToTable("role_permissions", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.TimeEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BreakDuration") + .HasColumnType("interval"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("End") + .HasColumnType("time without time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("OrderId") + .HasColumnType("uuid"); + + b.Property("Start") + .HasColumnType("time without time zone"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("OrderId"); + + b.ToTable("time_entries", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("MustChangePassword") + .HasColumnType("boolean"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("SecurityStamp") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("RoleId"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("users", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.UserPermissionOverride", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("integer"); + + b.Property("Effect") + .HasColumnType("integer"); + + b.Property("Module") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "Module", "Action") + .IsUnique(); + + b.ToTable("user_permission_overrides", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueList", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("value_lists", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IsDefault") + .HasColumnType("boolean"); + + b.Property("IsInitial") + .HasColumnType("boolean"); + + b.Property("IsTerminal") + .HasColumnType("boolean"); + + b.Property("SortOrder") + .HasColumnType("integer"); + + b.Property("TriggersFollowUp") + .HasColumnType("boolean"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ValueListId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ValueListId", "Value") + .IsUnique(); + + b.ToTable("value_list_items", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItemTransition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("FromItemId") + .HasColumnType("uuid"); + + b.Property("ToItemId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ToItemId"); + + b.HasIndex("FromItemId", "ToItemId") + .IsUnique(); + + b.ToTable("value_list_item_transitions", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Contract", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Employee"); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Document", b => + { + b.HasOne("OmsorgCore.Domain.Entities.User", "UploadedByUser") + .WithMany() + .HasForeignKey("UploadedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("UploadedByUser"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityContact", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Invoice", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Order", b => + { + b.HasOne("OmsorgCore.Domain.Entities.FacilityContact", "FacilityContact") + .WithMany() + .HasForeignKey("FacilityContactId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "Status") + .WithMany() + .HasForeignKey("StatusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + + b.Navigation("FacilityContact"); + + b.Navigation("Status"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.PasswordResetCode", b => + { + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RefreshToken", b => + { + b.HasOne("OmsorgCore.Domain.Entities.RefreshToken", null) + .WithMany() + .HasForeignKey("ReplacedByTokenId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RolePermission", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Role", "Role") + .WithMany("RolePermissions") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.TimeEntry", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.Order", "Order") + .WithMany() + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("OmsorgCore.Domain.Entities.Role", "Role") + .WithMany("Users") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.UserPermissionOverride", b => + { + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany("PermissionOverrides") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItem", b => + { + b.HasOne("OmsorgCore.Domain.Entities.ValueList", "ValueList") + .WithMany("Items") + .HasForeignKey("ValueListId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ValueList"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItemTransition", b => + { + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "FromItem") + .WithMany() + .HasForeignKey("FromItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "ToItem") + .WithMany() + .HasForeignKey("ToItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("FromItem"); + + b.Navigation("ToItem"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Role", b => + { + b.Navigation("RolePermissions"); + + b.Navigation("Users"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.Navigation("PermissionOverrides"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueList", b => + { + b.Navigation("Items"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809133244_AddDocuments.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809133244_AddDocuments.cs new file mode 100644 index 0000000..8195760 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809133244_AddDocuments.cs @@ -0,0 +1,62 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace OmsorgCore.Infrastructure.Persistence.Migrations +{ + /// + public partial class AddDocuments : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "documents", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + EntityType = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + EntityId = table.Column(type: "uuid", nullable: false), + Category = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + FileName = table.Column(type: "character varying(260)", maxLength: 260, nullable: false), + ContentType = table.Column(type: "character varying(150)", maxLength: 150, nullable: false), + SizeBytes = table.Column(type: "bigint", nullable: false), + StorageKey = table.Column(type: "character varying(500)", maxLength: 500, nullable: false), + Description = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: true), + UploadedByUserId = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), + IsDeleted = table.Column(type: "boolean", nullable: false), + DeletedAt = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_documents", x => x.Id); + table.ForeignKey( + name: "FK_documents_users_UploadedByUserId", + column: x => x.UploadedByUserId, + principalTable: "users", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_documents_EntityType_EntityId", + table: "documents", + columns: new[] { "EntityType", "EntityId" }); + + migrationBuilder.CreateIndex( + name: "IX_documents_UploadedByUserId", + table: "documents", + column: "UploadedByUserId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "documents"); + } + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809153800_AddPermissionScope.Designer.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809153800_AddPermissionScope.Designer.cs new file mode 100644 index 0000000..20b73d5 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809153800_AddPermissionScope.Designer.cs @@ -0,0 +1,1143 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using OmsorgCore.Infrastructure.Persistence; + +#nullable disable + +namespace OmsorgCore.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(OmsorgCoreDbContext))] + [Migration("20260809153800_AddPermissionScope")] + partial class AddPermissionScope + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.AuditLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ActorUserId") + .HasColumnType("uuid"); + + b.Property("ActorUsername") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Category") + .HasColumnType("integer"); + + b.Property("Details") + .HasColumnType("jsonb"); + + b.Property("EntityId") + .HasColumnType("uuid"); + + b.Property("EntityType") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("IpAddress") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("OccurredAtUtc") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ActorUserId"); + + b.HasIndex("OccurredAtUtc"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("audit_log_entries", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Contract", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AllowancesDescription") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ContractType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("HourlyWage") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("OvertimeRules") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ProbationPeriodMonths") + .HasColumnType("integer"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("VacationDaysPerYear") + .HasColumnType("integer"); + + b.Property("WeeklyHours") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("FacilityId"); + + b.ToTable("contracts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Document", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("EntityId") + .HasColumnType("uuid"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("character varying(260)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StorageKey") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UploadedByUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UploadedByUserId"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("documents", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Employee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AvatarFileName") + .HasColumnType("text"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DateOfBirth") + .HasColumnType("date"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EmergencyContactName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EmergencyContactPhone") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("EmergencyContactRelation") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("EmploymentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("EntryDate") + .HasColumnType("date"); + + b.Property("ExitDate") + .HasColumnType("date"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("PhoneNumber") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Qualification") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Street") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("employees", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Facility", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BillingCity") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("BillingCountry") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("BillingPostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("BillingStreet") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CrmStatus") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FacilityType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("FollowUpDueDate") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Street") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Website") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.HasKey("Id"); + + b.ToTable("facilities", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityContact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Department") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Email") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("PhoneNumber") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Role") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.ToTable("facility_contacts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Invoice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BillingPeriodEnd") + .HasColumnType("date"); + + b.Property("BillingPeriodStart") + .HasColumnType("date"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("GrossAmount") + .HasColumnType("numeric(12,2)"); + + b.Property("InvoiceNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("NetAmount") + .HasColumnType("numeric(12,2)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.HasIndex("InvoiceNumber") + .IsUnique(); + + b.ToTable("invoices", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.LoginAttempt", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttemptedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Succeeded") + .HasColumnType("boolean"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("IpAddress", "AttemptedAt"); + + b.ToTable("login_attempts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Conditions") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("FacilityContactId") + .HasColumnType("uuid"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Priority") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RequiredHeadcount") + .HasColumnType("integer"); + + b.Property("RequiredQualification") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ShiftType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("StatusId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityContactId"); + + b.HasIndex("FacilityId"); + + b.HasIndex("StatusId"); + + b.ToTable("orders", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.PasswordResetCode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttemptCount") + .HasColumnType("integer"); + + b.Property("CodeHash") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ConsumedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("InvalidatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ResetTokenExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ResetTokenHash") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ResetTokenUsedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ResetTokenHash"); + + b.HasIndex("UserId"); + + b.ToTable("password_reset_codes", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ReplacedByTokenId") + .HasColumnType("uuid"); + + b.Property("RevokedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("TokenHash") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ReplacedByTokenId"); + + b.HasIndex("TokenHash") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("refresh_tokens", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("roles", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RolePermission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("integer"); + + b.Property("Module") + .HasColumnType("integer"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("Scope") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("RoleId", "Module", "Action") + .IsUnique(); + + b.ToTable("role_permissions", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.TimeEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BreakDuration") + .HasColumnType("interval"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("End") + .HasColumnType("time without time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("OrderId") + .HasColumnType("uuid"); + + b.Property("Start") + .HasColumnType("time without time zone"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("OrderId"); + + b.ToTable("time_entries", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("MustChangePassword") + .HasColumnType("boolean"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("SecurityStamp") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("RoleId"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("users", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.UserPermissionOverride", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("integer"); + + b.Property("Effect") + .HasColumnType("integer"); + + b.Property("Module") + .HasColumnType("integer"); + + b.Property("Scope") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "Module", "Action") + .IsUnique(); + + b.ToTable("user_permission_overrides", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueList", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("value_lists", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IsDefault") + .HasColumnType("boolean"); + + b.Property("IsInitial") + .HasColumnType("boolean"); + + b.Property("IsTerminal") + .HasColumnType("boolean"); + + b.Property("SortOrder") + .HasColumnType("integer"); + + b.Property("TriggersFollowUp") + .HasColumnType("boolean"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ValueListId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ValueListId", "Value") + .IsUnique(); + + b.ToTable("value_list_items", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItemTransition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("FromItemId") + .HasColumnType("uuid"); + + b.Property("ToItemId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ToItemId"); + + b.HasIndex("FromItemId", "ToItemId") + .IsUnique(); + + b.ToTable("value_list_item_transitions", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Contract", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Employee"); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Document", b => + { + b.HasOne("OmsorgCore.Domain.Entities.User", "UploadedByUser") + .WithMany() + .HasForeignKey("UploadedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("UploadedByUser"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityContact", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Invoice", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Order", b => + { + b.HasOne("OmsorgCore.Domain.Entities.FacilityContact", "FacilityContact") + .WithMany() + .HasForeignKey("FacilityContactId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "Status") + .WithMany() + .HasForeignKey("StatusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + + b.Navigation("FacilityContact"); + + b.Navigation("Status"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.PasswordResetCode", b => + { + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RefreshToken", b => + { + b.HasOne("OmsorgCore.Domain.Entities.RefreshToken", null) + .WithMany() + .HasForeignKey("ReplacedByTokenId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RolePermission", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Role", "Role") + .WithMany("RolePermissions") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.TimeEntry", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.Order", "Order") + .WithMany() + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("OmsorgCore.Domain.Entities.Role", "Role") + .WithMany("Users") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.UserPermissionOverride", b => + { + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany("PermissionOverrides") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItem", b => + { + b.HasOne("OmsorgCore.Domain.Entities.ValueList", "ValueList") + .WithMany("Items") + .HasForeignKey("ValueListId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ValueList"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItemTransition", b => + { + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "FromItem") + .WithMany() + .HasForeignKey("FromItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "ToItem") + .WithMany() + .HasForeignKey("ToItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("FromItem"); + + b.Navigation("ToItem"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Role", b => + { + b.Navigation("RolePermissions"); + + b.Navigation("Users"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.Navigation("PermissionOverrides"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueList", b => + { + b.Navigation("Items"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809153800_AddPermissionScope.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809153800_AddPermissionScope.cs new file mode 100644 index 0000000..b687d8e --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260809153800_AddPermissionScope.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace OmsorgCore.Infrastructure.Persistence.Migrations +{ + /// + public partial class AddPermissionScope : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Scope", + table: "user_permission_overrides", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "Scope", + table: "role_permissions", + type: "integer", + nullable: false, + defaultValue: 0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Scope", + table: "user_permission_overrides"); + + migrationBuilder.DropColumn( + name: "Scope", + table: "role_permissions"); + } + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810043919_AddFacilityConditionsAndQualificationRates.Designer.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810043919_AddFacilityConditionsAndQualificationRates.Designer.cs new file mode 100644 index 0000000..3726f2f --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810043919_AddFacilityConditionsAndQualificationRates.Designer.cs @@ -0,0 +1,1226 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using OmsorgCore.Infrastructure.Persistence; + +#nullable disable + +namespace OmsorgCore.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(OmsorgCoreDbContext))] + [Migration("20260810043919_AddFacilityConditionsAndQualificationRates")] + partial class AddFacilityConditionsAndQualificationRates + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.AuditLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ActorUserId") + .HasColumnType("uuid"); + + b.Property("ActorUsername") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Category") + .HasColumnType("integer"); + + b.Property("Details") + .HasColumnType("jsonb"); + + b.Property("EntityId") + .HasColumnType("uuid"); + + b.Property("EntityType") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("IpAddress") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("OccurredAtUtc") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ActorUserId"); + + b.HasIndex("OccurredAtUtc"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("audit_log_entries", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Contract", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AllowancesDescription") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ContractType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("HourlyWage") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("OvertimeRules") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ProbationPeriodMonths") + .HasColumnType("integer"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("VacationDaysPerYear") + .HasColumnType("integer"); + + b.Property("WeeklyHours") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("FacilityId"); + + b.ToTable("contracts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Document", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("EntityId") + .HasColumnType("uuid"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("character varying(260)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StorageKey") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UploadedByUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UploadedByUserId"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("documents", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Employee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AvatarFileName") + .HasColumnType("text"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DateOfBirth") + .HasColumnType("date"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EmergencyContactName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EmergencyContactPhone") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("EmergencyContactRelation") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("EmploymentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("EntryDate") + .HasColumnType("date"); + + b.Property("ExitDate") + .HasColumnType("date"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("PhoneNumber") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Qualification") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Street") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("employees", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Facility", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BillingCity") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("BillingCountry") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("BillingInterval") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BillingPostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("BillingRate") + .HasColumnType("decimal(10,2)"); + + b.Property("BillingStreet") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("BreakPolicy") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CrmStatus") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FacilityType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("FollowUpDueDate") + .HasColumnType("timestamp with time zone"); + + b.Property("HolidaySurchargePercent") + .HasColumnType("decimal(5,2)"); + + b.Property("IndividualAgreements") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("MinimumHours") + .HasColumnType("decimal(5,2)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("NightSurchargePercent") + .HasColumnType("decimal(5,2)"); + + b.Property("PaymentTermDays") + .HasColumnType("integer"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("SaturdaySurchargePercent") + .HasColumnType("decimal(5,2)"); + + b.Property("Street") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("SundaySurchargePercent") + .HasColumnType("decimal(5,2)"); + + b.Property("TravelCostRate") + .HasColumnType("decimal(10,2)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Website") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.HasKey("Id"); + + b.ToTable("facilities", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityContact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Department") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Email") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("PhoneNumber") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Role") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.ToTable("facility_contacts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityQualificationRate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Qualification") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Rate") + .HasColumnType("decimal(10,2)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.ToTable("facility_qualification_rates", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Invoice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BillingPeriodEnd") + .HasColumnType("date"); + + b.Property("BillingPeriodStart") + .HasColumnType("date"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("GrossAmount") + .HasColumnType("numeric(12,2)"); + + b.Property("InvoiceNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("NetAmount") + .HasColumnType("numeric(12,2)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.HasIndex("InvoiceNumber") + .IsUnique(); + + b.ToTable("invoices", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.LoginAttempt", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttemptedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Succeeded") + .HasColumnType("boolean"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("IpAddress", "AttemptedAt"); + + b.ToTable("login_attempts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Conditions") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("FacilityContactId") + .HasColumnType("uuid"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Priority") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RequiredHeadcount") + .HasColumnType("integer"); + + b.Property("RequiredQualification") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ShiftType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("StatusId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityContactId"); + + b.HasIndex("FacilityId"); + + b.HasIndex("StatusId"); + + b.ToTable("orders", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.PasswordResetCode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttemptCount") + .HasColumnType("integer"); + + b.Property("CodeHash") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ConsumedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("InvalidatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ResetTokenExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ResetTokenHash") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ResetTokenUsedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ResetTokenHash"); + + b.HasIndex("UserId"); + + b.ToTable("password_reset_codes", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ReplacedByTokenId") + .HasColumnType("uuid"); + + b.Property("RevokedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("TokenHash") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ReplacedByTokenId"); + + b.HasIndex("TokenHash") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("refresh_tokens", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("roles", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RolePermission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("integer"); + + b.Property("Module") + .HasColumnType("integer"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("Scope") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("RoleId", "Module", "Action") + .IsUnique(); + + b.ToTable("role_permissions", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.TimeEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BreakDuration") + .HasColumnType("interval"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("End") + .HasColumnType("time without time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("OrderId") + .HasColumnType("uuid"); + + b.Property("Start") + .HasColumnType("time without time zone"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("OrderId"); + + b.ToTable("time_entries", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("MustChangePassword") + .HasColumnType("boolean"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("SecurityStamp") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("RoleId"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("users", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.UserPermissionOverride", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("integer"); + + b.Property("Effect") + .HasColumnType("integer"); + + b.Property("Module") + .HasColumnType("integer"); + + b.Property("Scope") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "Module", "Action") + .IsUnique(); + + b.ToTable("user_permission_overrides", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueList", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("value_lists", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IsDefault") + .HasColumnType("boolean"); + + b.Property("IsInitial") + .HasColumnType("boolean"); + + b.Property("IsTerminal") + .HasColumnType("boolean"); + + b.Property("SortOrder") + .HasColumnType("integer"); + + b.Property("TriggersFollowUp") + .HasColumnType("boolean"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ValueListId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ValueListId", "Value") + .IsUnique(); + + b.ToTable("value_list_items", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItemTransition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("FromItemId") + .HasColumnType("uuid"); + + b.Property("ToItemId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ToItemId"); + + b.HasIndex("FromItemId", "ToItemId") + .IsUnique(); + + b.ToTable("value_list_item_transitions", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Contract", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Employee"); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Document", b => + { + b.HasOne("OmsorgCore.Domain.Entities.User", "UploadedByUser") + .WithMany() + .HasForeignKey("UploadedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("UploadedByUser"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityContact", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityQualificationRate", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Invoice", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Order", b => + { + b.HasOne("OmsorgCore.Domain.Entities.FacilityContact", "FacilityContact") + .WithMany() + .HasForeignKey("FacilityContactId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "Status") + .WithMany() + .HasForeignKey("StatusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + + b.Navigation("FacilityContact"); + + b.Navigation("Status"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.PasswordResetCode", b => + { + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RefreshToken", b => + { + b.HasOne("OmsorgCore.Domain.Entities.RefreshToken", null) + .WithMany() + .HasForeignKey("ReplacedByTokenId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RolePermission", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Role", "Role") + .WithMany("RolePermissions") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.TimeEntry", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.Order", "Order") + .WithMany() + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("OmsorgCore.Domain.Entities.Role", "Role") + .WithMany("Users") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.UserPermissionOverride", b => + { + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany("PermissionOverrides") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItem", b => + { + b.HasOne("OmsorgCore.Domain.Entities.ValueList", "ValueList") + .WithMany("Items") + .HasForeignKey("ValueListId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ValueList"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItemTransition", b => + { + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "FromItem") + .WithMany() + .HasForeignKey("FromItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "ToItem") + .WithMany() + .HasForeignKey("ToItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("FromItem"); + + b.Navigation("ToItem"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Role", b => + { + b.Navigation("RolePermissions"); + + b.Navigation("Users"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.Navigation("PermissionOverrides"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueList", b => + { + b.Navigation("Items"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810043919_AddFacilityConditionsAndQualificationRates.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810043919_AddFacilityConditionsAndQualificationRates.cs new file mode 100644 index 0000000..16b4ed9 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810043919_AddFacilityConditionsAndQualificationRates.cs @@ -0,0 +1,164 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace OmsorgCore.Infrastructure.Persistence.Migrations +{ + /// + public partial class AddFacilityConditionsAndQualificationRates : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "BillingInterval", + table: "facilities", + type: "character varying(50)", + maxLength: 50, + nullable: true); + + migrationBuilder.AddColumn( + name: "BillingRate", + table: "facilities", + type: "numeric(10,2)", + nullable: true); + + migrationBuilder.AddColumn( + name: "BreakPolicy", + table: "facilities", + type: "character varying(1000)", + maxLength: 1000, + nullable: true); + + migrationBuilder.AddColumn( + name: "HolidaySurchargePercent", + table: "facilities", + type: "numeric(5,2)", + nullable: true); + + migrationBuilder.AddColumn( + name: "IndividualAgreements", + table: "facilities", + type: "character varying(2000)", + maxLength: 2000, + nullable: true); + + migrationBuilder.AddColumn( + name: "MinimumHours", + table: "facilities", + type: "numeric(5,2)", + nullable: true); + + migrationBuilder.AddColumn( + name: "NightSurchargePercent", + table: "facilities", + type: "numeric(5,2)", + nullable: true); + + migrationBuilder.AddColumn( + name: "PaymentTermDays", + table: "facilities", + type: "integer", + nullable: true); + + migrationBuilder.AddColumn( + name: "SaturdaySurchargePercent", + table: "facilities", + type: "numeric(5,2)", + nullable: true); + + migrationBuilder.AddColumn( + name: "SundaySurchargePercent", + table: "facilities", + type: "numeric(5,2)", + nullable: true); + + migrationBuilder.AddColumn( + name: "TravelCostRate", + table: "facilities", + type: "numeric(10,2)", + nullable: true); + + migrationBuilder.CreateTable( + name: "facility_qualification_rates", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + FacilityId = table.Column(type: "uuid", nullable: false), + Qualification = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + Rate = table.Column(type: "numeric(10,2)", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), + IsDeleted = table.Column(type: "boolean", nullable: false), + DeletedAt = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_facility_qualification_rates", x => x.Id); + table.ForeignKey( + name: "FK_facility_qualification_rates_facilities_FacilityId", + column: x => x.FacilityId, + principalTable: "facilities", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_facility_qualification_rates_FacilityId", + table: "facility_qualification_rates", + column: "FacilityId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "facility_qualification_rates"); + + migrationBuilder.DropColumn( + name: "BillingInterval", + table: "facilities"); + + migrationBuilder.DropColumn( + name: "BillingRate", + table: "facilities"); + + migrationBuilder.DropColumn( + name: "BreakPolicy", + table: "facilities"); + + migrationBuilder.DropColumn( + name: "HolidaySurchargePercent", + table: "facilities"); + + migrationBuilder.DropColumn( + name: "IndividualAgreements", + table: "facilities"); + + migrationBuilder.DropColumn( + name: "MinimumHours", + table: "facilities"); + + migrationBuilder.DropColumn( + name: "NightSurchargePercent", + table: "facilities"); + + migrationBuilder.DropColumn( + name: "PaymentTermDays", + table: "facilities"); + + migrationBuilder.DropColumn( + name: "SaturdaySurchargePercent", + table: "facilities"); + + migrationBuilder.DropColumn( + name: "SundaySurchargePercent", + table: "facilities"); + + migrationBuilder.DropColumn( + name: "TravelCostRate", + table: "facilities"); + } + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810055938_AddAbsences.Designer.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810055938_AddAbsences.Designer.cs new file mode 100644 index 0000000..6d9e5ef --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810055938_AddAbsences.Designer.cs @@ -0,0 +1,1297 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using OmsorgCore.Infrastructure.Persistence; + +#nullable disable + +namespace OmsorgCore.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(OmsorgCoreDbContext))] + [Migration("20260810055938_AddAbsences")] + partial class AddAbsences + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Absence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AdminNote") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Substitute") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.ToTable("absences", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.AuditLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ActorUserId") + .HasColumnType("uuid"); + + b.Property("ActorUsername") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Category") + .HasColumnType("integer"); + + b.Property("Details") + .HasColumnType("jsonb"); + + b.Property("EntityId") + .HasColumnType("uuid"); + + b.Property("EntityType") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("IpAddress") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("OccurredAtUtc") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ActorUserId"); + + b.HasIndex("OccurredAtUtc"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("audit_log_entries", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Contract", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AllowancesDescription") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ContractType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("HourlyWage") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("OvertimeRules") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ProbationPeriodMonths") + .HasColumnType("integer"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("VacationDaysPerYear") + .HasColumnType("integer"); + + b.Property("WeeklyHours") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("FacilityId"); + + b.ToTable("contracts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Document", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("EntityId") + .HasColumnType("uuid"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("character varying(260)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StorageKey") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UploadedByUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UploadedByUserId"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("documents", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Employee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AvatarFileName") + .HasColumnType("text"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DateOfBirth") + .HasColumnType("date"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EmergencyContactName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EmergencyContactPhone") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("EmergencyContactRelation") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("EmploymentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("EntryDate") + .HasColumnType("date"); + + b.Property("ExitDate") + .HasColumnType("date"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("PhoneNumber") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Qualification") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Street") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("employees", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Facility", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BillingCity") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("BillingCountry") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("BillingInterval") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BillingPostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("BillingRate") + .HasColumnType("decimal(10,2)"); + + b.Property("BillingStreet") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("BreakPolicy") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CrmStatus") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FacilityType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("FollowUpDueDate") + .HasColumnType("timestamp with time zone"); + + b.Property("HolidaySurchargePercent") + .HasColumnType("decimal(5,2)"); + + b.Property("IndividualAgreements") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("MinimumHours") + .HasColumnType("decimal(5,2)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("NightSurchargePercent") + .HasColumnType("decimal(5,2)"); + + b.Property("PaymentTermDays") + .HasColumnType("integer"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("SaturdaySurchargePercent") + .HasColumnType("decimal(5,2)"); + + b.Property("Street") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("SundaySurchargePercent") + .HasColumnType("decimal(5,2)"); + + b.Property("TravelCostRate") + .HasColumnType("decimal(10,2)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Website") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.HasKey("Id"); + + b.ToTable("facilities", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityContact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Department") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Email") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("PhoneNumber") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Role") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.ToTable("facility_contacts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityQualificationRate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Qualification") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Rate") + .HasColumnType("decimal(10,2)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.ToTable("facility_qualification_rates", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Invoice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BillingPeriodEnd") + .HasColumnType("date"); + + b.Property("BillingPeriodStart") + .HasColumnType("date"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("GrossAmount") + .HasColumnType("numeric(12,2)"); + + b.Property("InvoiceNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("NetAmount") + .HasColumnType("numeric(12,2)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.HasIndex("InvoiceNumber") + .IsUnique(); + + b.ToTable("invoices", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.LoginAttempt", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttemptedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Succeeded") + .HasColumnType("boolean"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("IpAddress", "AttemptedAt"); + + b.ToTable("login_attempts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Conditions") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("FacilityContactId") + .HasColumnType("uuid"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Priority") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RequiredHeadcount") + .HasColumnType("integer"); + + b.Property("RequiredQualification") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ShiftType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("StatusId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityContactId"); + + b.HasIndex("FacilityId"); + + b.HasIndex("StatusId"); + + b.ToTable("orders", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.PasswordResetCode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttemptCount") + .HasColumnType("integer"); + + b.Property("CodeHash") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ConsumedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("InvalidatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ResetTokenExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ResetTokenHash") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ResetTokenUsedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ResetTokenHash"); + + b.HasIndex("UserId"); + + b.ToTable("password_reset_codes", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ReplacedByTokenId") + .HasColumnType("uuid"); + + b.Property("RevokedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("TokenHash") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ReplacedByTokenId"); + + b.HasIndex("TokenHash") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("refresh_tokens", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("roles", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RolePermission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("integer"); + + b.Property("Module") + .HasColumnType("integer"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("Scope") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("RoleId", "Module", "Action") + .IsUnique(); + + b.ToTable("role_permissions", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.TimeEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BreakDuration") + .HasColumnType("interval"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("End") + .HasColumnType("time without time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("OrderId") + .HasColumnType("uuid"); + + b.Property("Start") + .HasColumnType("time without time zone"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("OrderId"); + + b.ToTable("time_entries", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("MustChangePassword") + .HasColumnType("boolean"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("SecurityStamp") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("RoleId"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("users", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.UserPermissionOverride", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("integer"); + + b.Property("Effect") + .HasColumnType("integer"); + + b.Property("Module") + .HasColumnType("integer"); + + b.Property("Scope") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "Module", "Action") + .IsUnique(); + + b.ToTable("user_permission_overrides", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueList", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("value_lists", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IsDefault") + .HasColumnType("boolean"); + + b.Property("IsInitial") + .HasColumnType("boolean"); + + b.Property("IsTerminal") + .HasColumnType("boolean"); + + b.Property("SortOrder") + .HasColumnType("integer"); + + b.Property("TriggersFollowUp") + .HasColumnType("boolean"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ValueListId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ValueListId", "Value") + .IsUnique(); + + b.ToTable("value_list_items", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItemTransition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("FromItemId") + .HasColumnType("uuid"); + + b.Property("ToItemId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ToItemId"); + + b.HasIndex("FromItemId", "ToItemId") + .IsUnique(); + + b.ToTable("value_list_item_transitions", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Absence", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Contract", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Employee"); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Document", b => + { + b.HasOne("OmsorgCore.Domain.Entities.User", "UploadedByUser") + .WithMany() + .HasForeignKey("UploadedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("UploadedByUser"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityContact", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityQualificationRate", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Invoice", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Order", b => + { + b.HasOne("OmsorgCore.Domain.Entities.FacilityContact", "FacilityContact") + .WithMany() + .HasForeignKey("FacilityContactId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "Status") + .WithMany() + .HasForeignKey("StatusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + + b.Navigation("FacilityContact"); + + b.Navigation("Status"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.PasswordResetCode", b => + { + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RefreshToken", b => + { + b.HasOne("OmsorgCore.Domain.Entities.RefreshToken", null) + .WithMany() + .HasForeignKey("ReplacedByTokenId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RolePermission", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Role", "Role") + .WithMany("RolePermissions") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.TimeEntry", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.Order", "Order") + .WithMany() + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("OmsorgCore.Domain.Entities.Role", "Role") + .WithMany("Users") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.UserPermissionOverride", b => + { + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany("PermissionOverrides") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItem", b => + { + b.HasOne("OmsorgCore.Domain.Entities.ValueList", "ValueList") + .WithMany("Items") + .HasForeignKey("ValueListId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ValueList"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItemTransition", b => + { + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "FromItem") + .WithMany() + .HasForeignKey("FromItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "ToItem") + .WithMany() + .HasForeignKey("ToItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("FromItem"); + + b.Navigation("ToItem"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Role", b => + { + b.Navigation("RolePermissions"); + + b.Navigation("Users"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.Navigation("PermissionOverrides"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueList", b => + { + b.Navigation("Items"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810055938_AddAbsences.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810055938_AddAbsences.cs new file mode 100644 index 0000000..2ca69b5 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810055938_AddAbsences.cs @@ -0,0 +1,57 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace OmsorgCore.Infrastructure.Persistence.Migrations +{ + /// + public partial class AddAbsences : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "absences", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + EmployeeId = table.Column(type: "uuid", nullable: false), + Type = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + StartDate = table.Column(type: "date", nullable: false), + EndDate = table.Column(type: "date", nullable: false), + Reason = table.Column(type: "character varying(500)", maxLength: 500, nullable: true), + Substitute = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), + Note = table.Column(type: "character varying(500)", maxLength: 500, nullable: true), + Status = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + AdminNote = table.Column(type: "character varying(500)", maxLength: 500, nullable: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), + IsDeleted = table.Column(type: "boolean", nullable: false), + DeletedAt = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_absences", x => x.Id); + table.ForeignKey( + name: "FK_absences_employees_EmployeeId", + column: x => x.EmployeeId, + principalTable: "employees", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_absences_EmployeeId", + table: "absences", + column: "EmployeeId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "absences"); + } + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810103935_AddTimeEntryStatusAndSurchargeHours.Designer.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810103935_AddTimeEntryStatusAndSurchargeHours.Designer.cs new file mode 100644 index 0000000..ac060d9 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810103935_AddTimeEntryStatusAndSurchargeHours.Designer.cs @@ -0,0 +1,1329 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using OmsorgCore.Infrastructure.Persistence; + +#nullable disable + +namespace OmsorgCore.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(OmsorgCoreDbContext))] + [Migration("20260810103935_AddTimeEntryStatusAndSurchargeHours")] + partial class AddTimeEntryStatusAndSurchargeHours + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Absence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AdminNote") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Substitute") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.ToTable("absences", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.AuditLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ActorUserId") + .HasColumnType("uuid"); + + b.Property("ActorUsername") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Category") + .HasColumnType("integer"); + + b.Property("Details") + .HasColumnType("jsonb"); + + b.Property("EntityId") + .HasColumnType("uuid"); + + b.Property("EntityType") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("IpAddress") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("OccurredAtUtc") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ActorUserId"); + + b.HasIndex("OccurredAtUtc"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("audit_log_entries", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Contract", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AllowancesDescription") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ContractType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("HourlyWage") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("OvertimeRules") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ProbationPeriodMonths") + .HasColumnType("integer"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("VacationDaysPerYear") + .HasColumnType("integer"); + + b.Property("WeeklyHours") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("FacilityId"); + + b.ToTable("contracts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Document", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("EntityId") + .HasColumnType("uuid"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("character varying(260)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StorageKey") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UploadedByUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UploadedByUserId"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("documents", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Employee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AvatarFileName") + .HasColumnType("text"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DateOfBirth") + .HasColumnType("date"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EmergencyContactName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EmergencyContactPhone") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("EmergencyContactRelation") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("EmploymentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("EntryDate") + .HasColumnType("date"); + + b.Property("ExitDate") + .HasColumnType("date"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("PhoneNumber") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Qualification") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Street") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("employees", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Facility", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BillingCity") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("BillingCountry") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("BillingInterval") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BillingPostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("BillingRate") + .HasColumnType("decimal(10,2)"); + + b.Property("BillingStreet") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("BreakPolicy") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CrmStatus") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FacilityType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("FollowUpDueDate") + .HasColumnType("timestamp with time zone"); + + b.Property("HolidaySurchargePercent") + .HasColumnType("decimal(5,2)"); + + b.Property("IndividualAgreements") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("MinimumHours") + .HasColumnType("decimal(5,2)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("NightSurchargePercent") + .HasColumnType("decimal(5,2)"); + + b.Property("PaymentTermDays") + .HasColumnType("integer"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("SaturdaySurchargePercent") + .HasColumnType("decimal(5,2)"); + + b.Property("Street") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("SundaySurchargePercent") + .HasColumnType("decimal(5,2)"); + + b.Property("TravelCostRate") + .HasColumnType("decimal(10,2)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Website") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.HasKey("Id"); + + b.ToTable("facilities", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityContact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Department") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Email") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("PhoneNumber") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Role") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.ToTable("facility_contacts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityQualificationRate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Qualification") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Rate") + .HasColumnType("decimal(10,2)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.ToTable("facility_qualification_rates", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Invoice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BillingPeriodEnd") + .HasColumnType("date"); + + b.Property("BillingPeriodStart") + .HasColumnType("date"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("GrossAmount") + .HasColumnType("numeric(12,2)"); + + b.Property("InvoiceNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("NetAmount") + .HasColumnType("numeric(12,2)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.HasIndex("InvoiceNumber") + .IsUnique(); + + b.ToTable("invoices", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.LoginAttempt", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttemptedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Succeeded") + .HasColumnType("boolean"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("IpAddress", "AttemptedAt"); + + b.ToTable("login_attempts", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Conditions") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("FacilityContactId") + .HasColumnType("uuid"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Priority") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RequiredHeadcount") + .HasColumnType("integer"); + + b.Property("RequiredQualification") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ShiftType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("StatusId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityContactId"); + + b.HasIndex("FacilityId"); + + b.HasIndex("StatusId"); + + b.ToTable("orders", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.PasswordResetCode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttemptCount") + .HasColumnType("integer"); + + b.Property("CodeHash") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ConsumedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("InvalidatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ResetTokenExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ResetTokenHash") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ResetTokenUsedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ResetTokenHash"); + + b.HasIndex("UserId"); + + b.ToTable("password_reset_codes", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ReplacedByTokenId") + .HasColumnType("uuid"); + + b.Property("RevokedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("TokenHash") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ReplacedByTokenId"); + + b.HasIndex("TokenHash") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("refresh_tokens", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("roles", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RolePermission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("integer"); + + b.Property("Module") + .HasColumnType("integer"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("Scope") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("RoleId", "Module", "Action") + .IsUnique(); + + b.ToTable("role_permissions", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.TimeEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AdminNote") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("BreakDuration") + .HasColumnType("interval"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("End") + .HasColumnType("time without time zone"); + + b.Property("HolidayHours") + .HasColumnType("numeric(6,2)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("NightHours") + .HasColumnType("numeric(6,2)"); + + b.Property("OrderId") + .HasColumnType("uuid"); + + b.Property("SaturdayHours") + .HasColumnType("numeric(6,2)"); + + b.Property("Start") + .HasColumnType("time without time zone"); + + b.Property("StatusId") + .HasColumnType("uuid"); + + b.Property("SundayHours") + .HasColumnType("numeric(6,2)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("OrderId"); + + b.HasIndex("StatusId"); + + b.ToTable("time_entries", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("MustChangePassword") + .HasColumnType("boolean"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("SecurityStamp") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("RoleId"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("users", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.UserPermissionOverride", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Action") + .HasColumnType("integer"); + + b.Property("Effect") + .HasColumnType("integer"); + + b.Property("Module") + .HasColumnType("integer"); + + b.Property("Scope") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "Module", "Action") + .IsUnique(); + + b.ToTable("user_permission_overrides", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueList", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("value_lists", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IsDefault") + .HasColumnType("boolean"); + + b.Property("IsEditableByOwner") + .HasColumnType("boolean"); + + b.Property("IsInitial") + .HasColumnType("boolean"); + + b.Property("IsTerminal") + .HasColumnType("boolean"); + + b.Property("SortOrder") + .HasColumnType("integer"); + + b.Property("TriggersFollowUp") + .HasColumnType("boolean"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ValueListId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ValueListId", "Value") + .IsUnique(); + + b.ToTable("value_list_items", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItemTransition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("FromItemId") + .HasColumnType("uuid"); + + b.Property("RequiresApproval") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("ToItemId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ToItemId"); + + b.HasIndex("FromItemId", "ToItemId") + .IsUnique(); + + b.ToTable("value_list_item_transitions", (string)null); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Absence", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Contract", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Employee"); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Document", b => + { + b.HasOne("OmsorgCore.Domain.Entities.User", "UploadedByUser") + .WithMany() + .HasForeignKey("UploadedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("UploadedByUser"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityContact", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityQualificationRate", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Invoice", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Order", b => + { + b.HasOne("OmsorgCore.Domain.Entities.FacilityContact", "FacilityContact") + .WithMany() + .HasForeignKey("FacilityContactId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "Status") + .WithMany() + .HasForeignKey("StatusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + + b.Navigation("FacilityContact"); + + b.Navigation("Status"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.PasswordResetCode", b => + { + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RefreshToken", b => + { + b.HasOne("OmsorgCore.Domain.Entities.RefreshToken", null) + .WithMany() + .HasForeignKey("ReplacedByTokenId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.RolePermission", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Role", "Role") + .WithMany("RolePermissions") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.TimeEntry", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.Order", "Order") + .WithMany() + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "Status") + .WithMany() + .HasForeignKey("StatusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("Order"); + + b.Navigation("Status"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("OmsorgCore.Domain.Entities.Role", "Role") + .WithMany("Users") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.UserPermissionOverride", b => + { + b.HasOne("OmsorgCore.Domain.Entities.User", "User") + .WithMany("PermissionOverrides") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItem", b => + { + b.HasOne("OmsorgCore.Domain.Entities.ValueList", "ValueList") + .WithMany("Items") + .HasForeignKey("ValueListId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ValueList"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueListItemTransition", b => + { + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "FromItem") + .WithMany() + .HasForeignKey("FromItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "ToItem") + .WithMany() + .HasForeignKey("ToItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("FromItem"); + + b.Navigation("ToItem"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.Role", b => + { + b.Navigation("RolePermissions"); + + b.Navigation("Users"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => + { + b.Navigation("PermissionOverrides"); + }); + + modelBuilder.Entity("OmsorgCore.Domain.Entities.ValueList", b => + { + b.Navigation("Items"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810103935_AddTimeEntryStatusAndSurchargeHours.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810103935_AddTimeEntryStatusAndSurchargeHours.cs new file mode 100644 index 0000000..1b483ca --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810103935_AddTimeEntryStatusAndSurchargeHours.cs @@ -0,0 +1,140 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace OmsorgCore.Infrastructure.Persistence.Migrations +{ + /// + public partial class AddTimeEntryStatusAndSurchargeHours : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Status", + table: "time_entries"); + + migrationBuilder.AddColumn( + name: "IsEditableByOwner", + table: "value_list_items", + type: "boolean", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "RequiresApproval", + table: "value_list_item_transitions", + type: "boolean", + nullable: false, + defaultValue: true); + + migrationBuilder.AddColumn( + name: "AdminNote", + table: "time_entries", + type: "character varying(500)", + maxLength: 500, + nullable: true); + + migrationBuilder.AddColumn( + name: "HolidayHours", + table: "time_entries", + type: "numeric(6,2)", + nullable: false, + defaultValue: 0m); + + migrationBuilder.AddColumn( + name: "NightHours", + table: "time_entries", + type: "numeric(6,2)", + nullable: false, + defaultValue: 0m); + + migrationBuilder.AddColumn( + name: "SaturdayHours", + table: "time_entries", + type: "numeric(6,2)", + nullable: false, + defaultValue: 0m); + + migrationBuilder.AddColumn( + name: "StatusId", + table: "time_entries", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "SundayHours", + table: "time_entries", + type: "numeric(6,2)", + nullable: false, + defaultValue: 0m); + + migrationBuilder.CreateIndex( + name: "IX_time_entries_StatusId", + table: "time_entries", + column: "StatusId"); + + migrationBuilder.AddForeignKey( + name: "FK_time_entries_value_list_items_StatusId", + table: "time_entries", + column: "StatusId", + principalTable: "value_list_items", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_time_entries_value_list_items_StatusId", + table: "time_entries"); + + migrationBuilder.DropIndex( + name: "IX_time_entries_StatusId", + table: "time_entries"); + + migrationBuilder.DropColumn( + name: "IsEditableByOwner", + table: "value_list_items"); + + migrationBuilder.DropColumn( + name: "RequiresApproval", + table: "value_list_item_transitions"); + + migrationBuilder.DropColumn( + name: "AdminNote", + table: "time_entries"); + + migrationBuilder.DropColumn( + name: "HolidayHours", + table: "time_entries"); + + migrationBuilder.DropColumn( + name: "NightHours", + table: "time_entries"); + + migrationBuilder.DropColumn( + name: "SaturdayHours", + table: "time_entries"); + + migrationBuilder.DropColumn( + name: "StatusId", + table: "time_entries"); + + migrationBuilder.DropColumn( + name: "SundayHours", + table: "time_entries"); + + migrationBuilder.AddColumn( + name: "Status", + table: "time_entries", + type: "character varying(50)", + maxLength: 50, + nullable: false, + defaultValue: ""); + } + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/OmsorgCoreDbContextModelSnapshot.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/OmsorgCoreDbContextModelSnapshot.cs index 156aaa9..f20e4fa 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/OmsorgCoreDbContextModelSnapshot.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/OmsorgCoreDbContextModelSnapshot.cs @@ -22,6 +22,66 @@ namespace OmsorgCore.Infrastructure.Persistence.Migrations NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("OmsorgCore.Domain.Entities.Absence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AdminNote") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Substitute") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.ToTable("absences", (string)null); + }); + modelBuilder.Entity("OmsorgCore.Domain.Entities.AuditLogEntry", b => { b.Property("Id") @@ -142,6 +202,71 @@ namespace OmsorgCore.Infrastructure.Persistence.Migrations b.ToTable("contracts", (string)null); }); + modelBuilder.Entity("OmsorgCore.Domain.Entities.Document", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("EntityId") + .HasColumnType("uuid"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("character varying(260)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("StorageKey") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UploadedByUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UploadedByUserId"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("documents", (string)null); + }); + modelBuilder.Entity("OmsorgCore.Domain.Entities.Employee", b => { b.Property("Id") @@ -250,14 +375,25 @@ namespace OmsorgCore.Infrastructure.Persistence.Migrations .HasMaxLength(100) .HasColumnType("character varying(100)"); + b.Property("BillingInterval") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + b.Property("BillingPostalCode") .HasMaxLength(10) .HasColumnType("character varying(10)"); + b.Property("BillingRate") + .HasColumnType("decimal(10,2)"); + b.Property("BillingStreet") .HasMaxLength(200) .HasColumnType("character varying(200)"); + b.Property("BreakPolicy") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + b.Property("City") .HasMaxLength(100) .HasColumnType("character varying(100)"); @@ -281,22 +417,50 @@ namespace OmsorgCore.Infrastructure.Persistence.Migrations .HasMaxLength(100) .HasColumnType("character varying(100)"); + b.Property("FollowUpDueDate") + .HasColumnType("timestamp with time zone"); + + b.Property("HolidaySurchargePercent") + .HasColumnType("decimal(5,2)"); + + b.Property("IndividualAgreements") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + b.Property("IsDeleted") .HasColumnType("boolean"); + b.Property("MinimumHours") + .HasColumnType("decimal(5,2)"); + b.Property("Name") .IsRequired() .HasMaxLength(300) .HasColumnType("character varying(300)"); + b.Property("NightSurchargePercent") + .HasColumnType("decimal(5,2)"); + + b.Property("PaymentTermDays") + .HasColumnType("integer"); + b.Property("PostalCode") .HasMaxLength(10) .HasColumnType("character varying(10)"); + b.Property("SaturdaySurchargePercent") + .HasColumnType("decimal(5,2)"); + b.Property("Street") .HasMaxLength(200) .HasColumnType("character varying(200)"); + b.Property("SundaySurchargePercent") + .HasColumnType("decimal(5,2)"); + + b.Property("TravelCostRate") + .HasColumnType("decimal(10,2)"); + b.Property("UpdatedAt") .HasColumnType("timestamp with time zone"); @@ -362,6 +526,42 @@ namespace OmsorgCore.Infrastructure.Persistence.Migrations b.ToTable("facility_contacts", (string)null); }); + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityQualificationRate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Qualification") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Rate") + .HasColumnType("decimal(10,2)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("FacilityId"); + + b.ToTable("facility_qualification_rates", (string)null); + }); + modelBuilder.Entity("OmsorgCore.Domain.Entities.Invoice", b => { b.Property("Id") @@ -631,6 +831,9 @@ namespace OmsorgCore.Infrastructure.Persistence.Migrations b.Property("RoleId") .HasColumnType("uuid"); + b.Property("Scope") + .HasColumnType("integer"); + b.HasKey("Id"); b.HasIndex("RoleId", "Module", "Action") @@ -645,6 +848,10 @@ namespace OmsorgCore.Infrastructure.Persistence.Migrations .ValueGeneratedOnAdd() .HasColumnType("uuid"); + b.Property("AdminNote") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + b.Property("BreakDuration") .HasColumnType("interval"); @@ -663,19 +870,29 @@ namespace OmsorgCore.Infrastructure.Persistence.Migrations b.Property("End") .HasColumnType("time without time zone"); + b.Property("HolidayHours") + .HasColumnType("numeric(6,2)"); + b.Property("IsDeleted") .HasColumnType("boolean"); + b.Property("NightHours") + .HasColumnType("numeric(6,2)"); + b.Property("OrderId") .HasColumnType("uuid"); + b.Property("SaturdayHours") + .HasColumnType("numeric(6,2)"); + b.Property("Start") .HasColumnType("time without time zone"); - b.Property("Status") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); + b.Property("StatusId") + .HasColumnType("uuid"); + + b.Property("SundayHours") + .HasColumnType("numeric(6,2)"); b.Property("UpdatedAt") .HasColumnType("timestamp with time zone"); @@ -686,6 +903,8 @@ namespace OmsorgCore.Infrastructure.Persistence.Migrations b.HasIndex("OrderId"); + b.HasIndex("StatusId"); + b.ToTable("time_entries", (string)null); }); @@ -758,6 +977,9 @@ namespace OmsorgCore.Infrastructure.Persistence.Migrations b.Property("Module") .HasColumnType("integer"); + b.Property("Scope") + .HasColumnType("integer"); + b.Property("UserId") .HasColumnType("uuid"); @@ -802,6 +1024,9 @@ namespace OmsorgCore.Infrastructure.Persistence.Migrations b.Property("IsDefault") .HasColumnType("boolean"); + b.Property("IsEditableByOwner") + .HasColumnType("boolean"); + b.Property("IsInitial") .HasColumnType("boolean"); @@ -811,6 +1036,9 @@ namespace OmsorgCore.Infrastructure.Persistence.Migrations b.Property("SortOrder") .HasColumnType("integer"); + b.Property("TriggersFollowUp") + .HasColumnType("boolean"); + b.Property("Value") .IsRequired() .HasMaxLength(100) @@ -836,6 +1064,11 @@ namespace OmsorgCore.Infrastructure.Persistence.Migrations b.Property("FromItemId") .HasColumnType("uuid"); + b.Property("RequiresApproval") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + b.Property("ToItemId") .HasColumnType("uuid"); @@ -849,6 +1082,17 @@ namespace OmsorgCore.Infrastructure.Persistence.Migrations b.ToTable("value_list_item_transitions", (string)null); }); + modelBuilder.Entity("OmsorgCore.Domain.Entities.Absence", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + modelBuilder.Entity("OmsorgCore.Domain.Entities.Contract", b => { b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") @@ -866,6 +1110,17 @@ namespace OmsorgCore.Infrastructure.Persistence.Migrations b.Navigation("Facility"); }); + modelBuilder.Entity("OmsorgCore.Domain.Entities.Document", b => + { + b.HasOne("OmsorgCore.Domain.Entities.User", "UploadedByUser") + .WithMany() + .HasForeignKey("UploadedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("UploadedByUser"); + }); + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityContact", b => { b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") @@ -877,6 +1132,17 @@ namespace OmsorgCore.Infrastructure.Persistence.Migrations b.Navigation("Facility"); }); + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityQualificationRate", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Facility"); + }); + modelBuilder.Entity("OmsorgCore.Domain.Entities.Invoice", b => { b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") @@ -966,9 +1232,17 @@ namespace OmsorgCore.Infrastructure.Persistence.Migrations .OnDelete(DeleteBehavior.Restrict) .IsRequired(); + b.HasOne("OmsorgCore.Domain.Entities.ValueListItem", "Status") + .WithMany() + .HasForeignKey("StatusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + b.Navigation("Employee"); b.Navigation("Order"); + + b.Navigation("Status"); }); modelBuilder.Entity("OmsorgCore.Domain.Entities.User", b => diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/OmsorgCoreDbContext.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/OmsorgCoreDbContext.cs index c10187e..d458fbe 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/OmsorgCoreDbContext.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/OmsorgCoreDbContext.cs @@ -12,13 +12,16 @@ public class OmsorgCoreDbContext : DbContext public DbSet Employees => Set(); public DbSet Facilities => Set(); public DbSet FacilityContacts => Set(); + public DbSet FacilityQualificationRates => Set(); public DbSet Contracts => Set(); public DbSet Orders => Set(); + public DbSet Absences => Set(); public DbSet ValueLists => Set(); public DbSet ValueListItems => Set(); public DbSet ValueListItemTransitions => Set(); public DbSet TimeEntries => Set(); public DbSet Invoices => Set(); + public DbSet Documents => Set(); public DbSet Users => Set(); public DbSet Roles => Set(); diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/AbsenceRepository.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/AbsenceRepository.cs new file mode 100644 index 0000000..a688f59 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/AbsenceRepository.cs @@ -0,0 +1,113 @@ +using Microsoft.EntityFrameworkCore; +using OmsorgCore.Application.Abstractions; +using OmsorgCore.Domain.Entities; +using OmsorgCore.Infrastructure.Persistence; + +namespace OmsorgCore.Infrastructure.Repositories; + +public class AbsenceRepository : IAbsenceRepository +{ + private readonly OmsorgCoreDbContext _db; + + public AbsenceRepository(OmsorgCoreDbContext db) + { + _db = db; + } + + public Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) + => _db.Absences.Include(a => a.Employee).FirstOrDefaultAsync(a => a.Id == id, cancellationToken); + + public async Task<(IReadOnlyList Items, int TotalCount)> GetPagedAsync( + string? status, + string? type, + Guid? employeeId, + int page, + int pageSize, + CancellationToken cancellationToken = default, + Guid? restrictToEmployeeId = null) + { + var query = _db.Absences.AsNoTracking().Include(a => a.Employee).Where(a => !a.IsDeleted); + + if (restrictToEmployeeId.HasValue) + { + query = query.Where(a => a.EmployeeId == restrictToEmployeeId.Value); + } + else if (employeeId.HasValue) + { + query = query.Where(a => a.EmployeeId == employeeId.Value); + } + + if (!string.IsNullOrWhiteSpace(status)) + { + query = query.Where(a => a.Status == status); + } + + if (!string.IsNullOrWhiteSpace(type)) + { + query = query.Where(a => a.Type == type); + } + + var totalCount = await query.CountAsync(cancellationToken); + + var items = await query + .OrderByDescending(a => a.CreatedAt) + .Skip((page - 1) * pageSize) + .Take(pageSize) + .ToListAsync(cancellationToken); + + return (items, totalCount); + } + + public async Task AddAsync(Absence absence, CancellationToken cancellationToken = default) + => await _db.Absences.AddAsync(absence, cancellationToken); + + public Task UpdateAsync(Absence absence, CancellationToken cancellationToken = default) + { + _db.Absences.Update(absence); + return Task.CompletedTask; + } + + public async Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var absence = await _db.Absences.FirstOrDefaultAsync(a => a.Id == id, cancellationToken); + if (absence is null) + { + return false; + } + + absence.IsDeleted = true; + absence.DeletedAt = DateTime.UtcNow; + return true; + } + + public async Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + { + var query = _db.Absences.IgnoreQueryFilters().AsNoTracking().Include(a => a.Employee).Where(a => a.IsDeleted); + + if (!string.IsNullOrWhiteSpace(search)) + { + var pattern = $"%{search.Trim()}%"; + query = query.Where(a => + EF.Functions.ILike(a.Type, pattern) || + (a.Employee != null && (EF.Functions.ILike(a.Employee.FirstName, pattern) || EF.Functions.ILike(a.Employee.LastName, pattern)))); + } + + return await query.OrderByDescending(a => a.DeletedAt).ToListAsync(cancellationToken); + } + + public async Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var absence = await _db.Absences.IgnoreQueryFilters().FirstOrDefaultAsync(a => a.Id == id && a.IsDeleted, cancellationToken); + if (absence is null) + { + return false; + } + + absence.IsDeleted = false; + absence.DeletedAt = null; + return true; + } + + public Task SaveChangesAsync(CancellationToken cancellationToken = default) + => _db.SaveChangesAsync(cancellationToken); +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/ContractRepository.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/ContractRepository.cs index ab7059f..11e91db 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/ContractRepository.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/ContractRepository.cs @@ -72,6 +72,45 @@ public class ContractRepository : IContractRepository return Task.CompletedTask; } + public async Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var contract = await _db.Contracts.FirstOrDefaultAsync(c => c.Id == id, cancellationToken); + if (contract is null) + { + return false; + } + + contract.IsDeleted = true; + contract.DeletedAt = DateTime.UtcNow; + return true; + } + + public async Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + { + var query = _db.Contracts.IgnoreQueryFilters().AsNoTracking().Where(c => c.IsDeleted); + + if (!string.IsNullOrWhiteSpace(search)) + { + var pattern = $"%{search.Trim()}%"; + query = query.Where(c => EF.Functions.ILike(c.ContractType, pattern)); + } + + return await query.OrderByDescending(c => c.DeletedAt).ToListAsync(cancellationToken); + } + + public async Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var contract = await _db.Contracts.IgnoreQueryFilters().FirstOrDefaultAsync(c => c.Id == id && c.IsDeleted, cancellationToken); + if (contract is null) + { + return false; + } + + contract.IsDeleted = false; + contract.DeletedAt = null; + return true; + } + public Task SaveChangesAsync(CancellationToken cancellationToken = default) => _db.SaveChangesAsync(cancellationToken); } diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/DocumentRepository.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/DocumentRepository.cs new file mode 100644 index 0000000..ae2ed80 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/DocumentRepository.cs @@ -0,0 +1,71 @@ +using Microsoft.EntityFrameworkCore; +using OmsorgCore.Application.Abstractions; +using OmsorgCore.Domain.Entities; +using OmsorgCore.Infrastructure.Persistence; + +namespace OmsorgCore.Infrastructure.Repositories; + +public class DocumentRepository : IDocumentRepository +{ + private readonly OmsorgCoreDbContext _db; + + public DocumentRepository(OmsorgCoreDbContext db) + { + _db = db; + } + + public Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) + => _db.Documents.Include(d => d.UploadedByUser).FirstOrDefaultAsync(d => d.Id == id, cancellationToken); + + public async Task> GetByEntityAsync(string entityType, Guid entityId, CancellationToken cancellationToken = default) + => await _db.Documents.AsNoTracking() + .Include(d => d.UploadedByUser) + .Where(d => d.EntityType == entityType && d.EntityId == entityId) + .OrderByDescending(d => d.CreatedAt) + .ToListAsync(cancellationToken); + + public async Task AddAsync(Document document, CancellationToken cancellationToken = default) + => await _db.Documents.AddAsync(document, cancellationToken); + + public async Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var document = await _db.Documents.FirstOrDefaultAsync(d => d.Id == id, cancellationToken); + if (document is null) + { + return false; + } + + document.IsDeleted = true; + document.DeletedAt = DateTime.UtcNow; + return true; + } + + public async Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + { + var query = _db.Documents.IgnoreQueryFilters().AsNoTracking().Where(d => d.IsDeleted); + + if (!string.IsNullOrWhiteSpace(search)) + { + var pattern = $"%{search.Trim()}%"; + query = query.Where(d => EF.Functions.ILike(d.FileName, pattern)); + } + + return await query.OrderByDescending(d => d.DeletedAt).ToListAsync(cancellationToken); + } + + public async Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var document = await _db.Documents.IgnoreQueryFilters().FirstOrDefaultAsync(d => d.Id == id && d.IsDeleted, cancellationToken); + if (document is null) + { + return false; + } + + document.IsDeleted = false; + document.DeletedAt = null; + return true; + } + + public Task SaveChangesAsync(CancellationToken cancellationToken = default) + => _db.SaveChangesAsync(cancellationToken); +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/EmployeeRepository.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/EmployeeRepository.cs index 42b05d0..9168412 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/EmployeeRepository.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/EmployeeRepository.cs @@ -26,10 +26,16 @@ public class EmployeeRepository : IEmployeeRepository string? employmentType, int page, int pageSize, - CancellationToken cancellationToken = default) + CancellationToken cancellationToken = default, + Guid? restrictToEmployeeId = null) { var query = _db.Employees.AsNoTracking().Where(e => !e.IsDeleted); + if (restrictToEmployeeId.HasValue) + { + query = query.Where(e => e.Id == restrictToEmployeeId.Value); + } + if (!string.IsNullOrWhiteSpace(search)) { var pattern = $"%{search.Trim()}%"; @@ -70,6 +76,48 @@ public class EmployeeRepository : IEmployeeRepository return Task.CompletedTask; } + public async Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var employee = await _db.Employees.FirstOrDefaultAsync(e => e.Id == id, cancellationToken); + if (employee is null) + { + return false; + } + + employee.IsDeleted = true; + employee.DeletedAt = DateTime.UtcNow; + return true; + } + + public async Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + { + var query = _db.Employees.IgnoreQueryFilters().AsNoTracking().Where(e => e.IsDeleted); + + if (!string.IsNullOrWhiteSpace(search)) + { + var pattern = $"%{search.Trim()}%"; + query = query.Where(e => + EF.Functions.ILike(e.FirstName, pattern) || + EF.Functions.ILike(e.LastName, pattern) || + (e.Email != null && EF.Functions.ILike(e.Email, pattern))); + } + + return await query.OrderByDescending(e => e.DeletedAt).ToListAsync(cancellationToken); + } + + public async Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var employee = await _db.Employees.IgnoreQueryFilters().FirstOrDefaultAsync(e => e.Id == id && e.IsDeleted, cancellationToken); + if (employee is null) + { + return false; + } + + employee.IsDeleted = false; + employee.DeletedAt = null; + return true; + } + public Task SaveChangesAsync(CancellationToken cancellationToken = default) => _db.SaveChangesAsync(cancellationToken); } diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/FacilityContactRepository.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/FacilityContactRepository.cs index 153da6c..70a0607 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/FacilityContactRepository.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/FacilityContactRepository.cs @@ -33,6 +33,45 @@ public class FacilityContactRepository : IFacilityContactRepository return Task.CompletedTask; } + public async Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var contact = await _db.FacilityContacts.FirstOrDefaultAsync(c => c.Id == id, cancellationToken); + if (contact is null) + { + return false; + } + + contact.IsDeleted = true; + contact.DeletedAt = DateTime.UtcNow; + return true; + } + + public async Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + { + var query = _db.FacilityContacts.IgnoreQueryFilters().AsNoTracking().Where(c => c.IsDeleted); + + if (!string.IsNullOrWhiteSpace(search)) + { + var pattern = $"%{search.Trim()}%"; + query = query.Where(c => EF.Functions.ILike(c.Name, pattern)); + } + + return await query.OrderByDescending(c => c.DeletedAt).ToListAsync(cancellationToken); + } + + public async Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var contact = await _db.FacilityContacts.IgnoreQueryFilters().FirstOrDefaultAsync(c => c.Id == id && c.IsDeleted, cancellationToken); + if (contact is null) + { + return false; + } + + contact.IsDeleted = false; + contact.DeletedAt = null; + return true; + } + public Task SaveChangesAsync(CancellationToken cancellationToken = default) => _db.SaveChangesAsync(cancellationToken); } diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/FacilityQualificationRateRepository.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/FacilityQualificationRateRepository.cs new file mode 100644 index 0000000..b116bf5 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/FacilityQualificationRateRepository.cs @@ -0,0 +1,77 @@ +using Microsoft.EntityFrameworkCore; +using OmsorgCore.Application.Abstractions; +using OmsorgCore.Domain.Entities; +using OmsorgCore.Infrastructure.Persistence; + +namespace OmsorgCore.Infrastructure.Repositories; + +public class FacilityQualificationRateRepository : IFacilityQualificationRateRepository +{ + private readonly OmsorgCoreDbContext _db; + + public FacilityQualificationRateRepository(OmsorgCoreDbContext db) + { + _db = db; + } + + public Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) + => _db.FacilityQualificationRates.FirstOrDefaultAsync(r => r.Id == id, cancellationToken); + + public async Task> GetByFacilityIdAsync(Guid facilityId, CancellationToken cancellationToken = default) + => await _db.FacilityQualificationRates + .AsNoTracking() + .Where(r => r.FacilityId == facilityId) + .OrderBy(r => r.Qualification) + .ToListAsync(cancellationToken); + + public async Task AddAsync(FacilityQualificationRate rate, CancellationToken cancellationToken = default) + => await _db.FacilityQualificationRates.AddAsync(rate, cancellationToken); + + public Task UpdateAsync(FacilityQualificationRate rate, CancellationToken cancellationToken = default) + { + _db.FacilityQualificationRates.Update(rate); + return Task.CompletedTask; + } + + public async Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var rate = await _db.FacilityQualificationRates.FirstOrDefaultAsync(r => r.Id == id, cancellationToken); + if (rate is null) + { + return false; + } + + rate.IsDeleted = true; + rate.DeletedAt = DateTime.UtcNow; + return true; + } + + public async Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + { + var query = _db.FacilityQualificationRates.IgnoreQueryFilters().AsNoTracking().Where(r => r.IsDeleted); + + if (!string.IsNullOrWhiteSpace(search)) + { + var pattern = $"%{search.Trim()}%"; + query = query.Where(r => EF.Functions.ILike(r.Qualification, pattern)); + } + + return await query.OrderByDescending(r => r.DeletedAt).ToListAsync(cancellationToken); + } + + public async Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var rate = await _db.FacilityQualificationRates.IgnoreQueryFilters().FirstOrDefaultAsync(r => r.Id == id && r.IsDeleted, cancellationToken); + if (rate is null) + { + return false; + } + + rate.IsDeleted = false; + rate.DeletedAt = null; + return true; + } + + public Task SaveChangesAsync(CancellationToken cancellationToken = default) + => _db.SaveChangesAsync(cancellationToken); +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/FacilityRepository.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/FacilityRepository.cs index 489e42b..fb68725 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/FacilityRepository.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/FacilityRepository.cs @@ -23,6 +23,7 @@ public class FacilityRepository : IFacilityRepository public async Task<(IReadOnlyList Items, int TotalCount)> GetPagedAsync( string? search, string? crmStatus, + bool followUpDueOnly, int page, int pageSize, CancellationToken cancellationToken = default) @@ -40,10 +41,18 @@ public class FacilityRepository : IFacilityRepository query = query.Where(f => f.CrmStatus == crmStatus); } + if (followUpDueOnly) + { + query = query.Where(f => f.FollowUpDueDate != null); + } + var totalCount = await query.CountAsync(cancellationToken); - var items = await query - .OrderBy(f => f.Name) + var orderedQuery = followUpDueOnly + ? query.OrderBy(f => f.FollowUpDueDate) + : query.OrderBy(f => f.Name); + + var items = await orderedQuery .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(cancellationToken); @@ -60,6 +69,45 @@ public class FacilityRepository : IFacilityRepository return Task.CompletedTask; } + public async Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var facility = await _db.Facilities.FirstOrDefaultAsync(f => f.Id == id, cancellationToken); + if (facility is null) + { + return false; + } + + facility.IsDeleted = true; + facility.DeletedAt = DateTime.UtcNow; + return true; + } + + public async Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + { + var query = _db.Facilities.IgnoreQueryFilters().AsNoTracking().Where(f => f.IsDeleted); + + if (!string.IsNullOrWhiteSpace(search)) + { + var pattern = $"%{search.Trim()}%"; + query = query.Where(f => EF.Functions.ILike(f.Name, pattern)); + } + + return await query.OrderByDescending(f => f.DeletedAt).ToListAsync(cancellationToken); + } + + public async Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var facility = await _db.Facilities.IgnoreQueryFilters().FirstOrDefaultAsync(f => f.Id == id && f.IsDeleted, cancellationToken); + if (facility is null) + { + return false; + } + + facility.IsDeleted = false; + facility.DeletedAt = null; + return true; + } + public Task SaveChangesAsync(CancellationToken cancellationToken = default) => _db.SaveChangesAsync(cancellationToken); } diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/OrderRepository.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/OrderRepository.cs index 4c82a42..d51d60e 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/OrderRepository.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/OrderRepository.cs @@ -24,6 +24,9 @@ public class OrderRepository : IOrderRepository string? search, Guid? statusId, Guid? facilityId, + string? priority, + string? requiredQualification, + string? shiftType, int page, int pageSize, CancellationToken cancellationToken = default) @@ -46,6 +49,21 @@ public class OrderRepository : IOrderRepository query = query.Where(o => o.FacilityId == facilityId.Value); } + if (!string.IsNullOrWhiteSpace(priority)) + { + query = query.Where(o => o.Priority == priority); + } + + if (!string.IsNullOrWhiteSpace(requiredQualification)) + { + query = query.Where(o => o.RequiredQualification == requiredQualification); + } + + if (!string.IsNullOrWhiteSpace(shiftType)) + { + query = query.Where(o => o.ShiftType == shiftType); + } + var totalCount = await query.CountAsync(cancellationToken); var items = await query @@ -66,6 +84,45 @@ public class OrderRepository : IOrderRepository return Task.CompletedTask; } + public async Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var order = await _db.Orders.FirstOrDefaultAsync(o => o.Id == id, cancellationToken); + if (order is null) + { + return false; + } + + order.IsDeleted = true; + order.DeletedAt = DateTime.UtcNow; + return true; + } + + public async Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + { + var query = _db.Orders.IgnoreQueryFilters().AsNoTracking().Include(o => o.Status).Where(o => o.IsDeleted); + + if (!string.IsNullOrWhiteSpace(search)) + { + var pattern = $"%{search.Trim()}%"; + query = query.Where(o => o.RequiredQualification != null && EF.Functions.ILike(o.RequiredQualification, pattern)); + } + + return await query.OrderByDescending(o => o.DeletedAt).ToListAsync(cancellationToken); + } + + public async Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var order = await _db.Orders.IgnoreQueryFilters().FirstOrDefaultAsync(o => o.Id == id && o.IsDeleted, cancellationToken); + if (order is null) + { + return false; + } + + order.IsDeleted = false; + order.DeletedAt = null; + return true; + } + public Task SaveChangesAsync(CancellationToken cancellationToken = default) => _db.SaveChangesAsync(cancellationToken); } diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/QualificationValueListUsageChecker.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/QualificationValueListUsageChecker.cs new file mode 100644 index 0000000..cd998ef --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/QualificationValueListUsageChecker.cs @@ -0,0 +1,46 @@ +using Microsoft.EntityFrameworkCore; +using OmsorgCore.Application.Abstractions; +using OmsorgCore.Infrastructure.Persistence; + +namespace OmsorgCore.Infrastructure.Repositories; + +/// +/// Verwendungsprüfung für die Liste "Qualification" — anders als die übrigen String-Feld-Listen +/// () wird ihr Wert von DREI Entitäten referenziert +/// (Employee.Qualification, Order.RequiredQualification, FacilityQualificationRate.Qualification). +/// ValueListService.FindUsagesAsync wählt +/// pro Key nur den ERSTEN registrierten Checker aus, daher eine eigene Klasse statt zwei separater +/// StringFieldValueListUsageChecker-Registrierungen mit demselben Key (die zweite würde sonst nie +/// befragt und das Löschen eines noch verwendeten Werts fälschlich erlauben). +/// +public class QualificationValueListUsageChecker : IValueListUsageChecker +{ + private readonly OmsorgCoreDbContext _db; + + public string Key => "Qualification"; + + public QualificationValueListUsageChecker(OmsorgCoreDbContext db) + { + _db = db; + } + + public async Task> FindUsagesAsync(Guid itemId, string itemValue, CancellationToken cancellationToken = default) + { + var employeeUsages = await _db.Employees.AsNoTracking() + .Where(e => e.Qualification == itemValue) + .Select(e => new ValueListUsageEntry("Employee", e.Id, $"{e.FirstName} {e.LastName}")) + .ToListAsync(cancellationToken); + + var orderUsages = await _db.Orders.AsNoTracking() + .Where(o => o.RequiredQualification == itemValue) + .Select(o => new ValueListUsageEntry("Order", o.Id, $"Auftrag {o.Id}")) + .ToListAsync(cancellationToken); + + var facilityQualificationRateUsages = await _db.FacilityQualificationRates.AsNoTracking() + .Where(r => r.Qualification == itemValue) + .Select(r => new ValueListUsageEntry("FacilityQualificationRate", r.Id, $"Qualifikationspreis {r.Id}")) + .ToListAsync(cancellationToken); + + return employeeUsages.Concat(orderUsages).Concat(facilityQualificationRateUsages).ToList(); + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/TimeEntryRepository.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/TimeEntryRepository.cs new file mode 100644 index 0000000..12186d7 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/TimeEntryRepository.cs @@ -0,0 +1,119 @@ +using Microsoft.EntityFrameworkCore; +using OmsorgCore.Application.Abstractions; +using OmsorgCore.Domain.Entities; +using OmsorgCore.Infrastructure.Persistence; + +namespace OmsorgCore.Infrastructure.Repositories; + +public class TimeEntryRepository : ITimeEntryRepository +{ + private readonly OmsorgCoreDbContext _db; + + public TimeEntryRepository(OmsorgCoreDbContext db) + { + _db = db; + } + + public Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) + => _db.TimeEntries.Include(t => t.Employee).Include(t => t.Order).ThenInclude(o => o.Facility).Include(t => t.Status) + .FirstOrDefaultAsync(t => t.Id == id, cancellationToken); + + public async Task<(IReadOnlyList Items, int TotalCount)> GetPagedAsync( + Guid? statusId, + Guid? employeeId, + Guid? orderId, + int page, + int pageSize, + CancellationToken cancellationToken = default, + Guid? restrictToEmployeeId = null) + { + var query = _db.TimeEntries.AsNoTracking() + .Include(t => t.Employee) + .Include(t => t.Order).ThenInclude(o => o.Facility) + .Include(t => t.Status) + .Where(t => !t.IsDeleted); + + if (restrictToEmployeeId.HasValue) + { + query = query.Where(t => t.EmployeeId == restrictToEmployeeId.Value); + } + else if (employeeId.HasValue) + { + query = query.Where(t => t.EmployeeId == employeeId.Value); + } + + if (orderId.HasValue) + { + query = query.Where(t => t.OrderId == orderId.Value); + } + + if (statusId.HasValue) + { + query = query.Where(t => t.StatusId == statusId.Value); + } + + var totalCount = await query.CountAsync(cancellationToken); + + var items = await query + .OrderByDescending(t => t.CreatedAt) + .Skip((page - 1) * pageSize) + .Take(pageSize) + .ToListAsync(cancellationToken); + + return (items, totalCount); + } + + public async Task AddAsync(TimeEntry timeEntry, CancellationToken cancellationToken = default) + => await _db.TimeEntries.AddAsync(timeEntry, cancellationToken); + + public Task UpdateAsync(TimeEntry timeEntry, CancellationToken cancellationToken = default) + { + _db.TimeEntries.Update(timeEntry); + return Task.CompletedTask; + } + + public async Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var timeEntry = await _db.TimeEntries.FirstOrDefaultAsync(t => t.Id == id, cancellationToken); + if (timeEntry is null) + { + return false; + } + + timeEntry.IsDeleted = true; + timeEntry.DeletedAt = DateTime.UtcNow; + return true; + } + + public async Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + { + var query = _db.TimeEntries.IgnoreQueryFilters().AsNoTracking() + .Include(t => t.Employee).Include(t => t.Order).Include(t => t.Status) + .Where(t => t.IsDeleted); + + if (!string.IsNullOrWhiteSpace(search)) + { + var pattern = $"%{search.Trim()}%"; + query = query.Where(t => + (t.Employee != null && (EF.Functions.ILike(t.Employee.FirstName, pattern) || EF.Functions.ILike(t.Employee.LastName, pattern)))); + } + + return await query.OrderByDescending(t => t.DeletedAt).ToListAsync(cancellationToken); + } + + public async Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var timeEntry = await _db.TimeEntries.IgnoreQueryFilters().FirstOrDefaultAsync(t => t.Id == id && t.IsDeleted, cancellationToken); + if (timeEntry is null) + { + return false; + } + + timeEntry.IsDeleted = false; + timeEntry.DeletedAt = null; + return true; + } + + public Task SaveChangesAsync(CancellationToken cancellationToken = default) + => _db.SaveChangesAsync(cancellationToken); +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/TimeEntryStatusValueListUsageChecker.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/TimeEntryStatusValueListUsageChecker.cs new file mode 100644 index 0000000..1834d55 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/TimeEntryStatusValueListUsageChecker.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore; +using OmsorgCore.Application.Abstractions; +using OmsorgCore.Infrastructure.Persistence; + +namespace OmsorgCore.Infrastructure.Repositories; + +/// +/// Verwendungsprüfung für die Liste "TimeEntryStatus" — wie "OrderStatus" eine echte +/// Fremdschlüsselbeziehung (TimeEntry.StatusId) plus mögliche Übergangsregeln. +/// +public class TimeEntryStatusValueListUsageChecker : IValueListUsageChecker +{ + private readonly OmsorgCoreDbContext _db; + + public string Key => "TimeEntryStatus"; + + public TimeEntryStatusValueListUsageChecker(OmsorgCoreDbContext db) + { + _db = db; + } + + public async Task> FindUsagesAsync(Guid itemId, string itemValue, CancellationToken cancellationToken = default) + { + var timeEntryUsages = await _db.TimeEntries.AsNoTracking() + .Where(t => t.StatusId == itemId) + .Select(t => new ValueListUsageEntry("TimeEntry", t.Id, $"Zeiterfassung {t.Id}")) + .ToListAsync(cancellationToken); + + var transitionUsages = await _db.ValueListItemTransitions.AsNoTracking() + .Where(t => t.FromItemId == itemId || t.ToItemId == itemId) + .Select(t => new ValueListUsageEntry("TimeEntryStatusTransition", t.Id, $"Übergang {t.FromItem.Value} → {t.ToItem.Value}")) + .ToListAsync(cancellationToken); + + return timeEntryUsages.Concat(transitionUsages).ToList(); + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/UserRepository.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/UserRepository.cs index 0df7764..997b889 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/UserRepository.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/UserRepository.cs @@ -28,6 +28,15 @@ public class UserRepository : IUserRepository .Include(u => u.Employee) .FirstOrDefaultAsync(u => u.Id == id, cancellationToken); + public Task GetByIdWithPermissionsNoTrackingAsync(Guid id, CancellationToken cancellationToken = default) + => _db.Users + .AsNoTracking() + .Include(u => u.Role) + .ThenInclude(r => r.RolePermissions) + .Include(u => u.PermissionOverrides) + .Include(u => u.Employee) + .FirstOrDefaultAsync(u => u.Id == id, cancellationToken); + public Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) => _db.Users.FirstOrDefaultAsync(u => u.Id == id, cancellationToken); @@ -40,6 +49,9 @@ public class UserRepository : IUserRepository public async Task> GetAllAsync(CancellationToken cancellationToken = default) => await _db.Users.Include(u => u.Role).ToListAsync(cancellationToken); + public async Task> GetUserIdsByRoleAsync(Guid roleId, CancellationToken cancellationToken = default) + => await _db.Users.Where(u => u.RoleId == roleId).Select(u => u.Id).ToListAsync(cancellationToken); + public Task GetByEmployeeIdAsync(Guid employeeId, CancellationToken cancellationToken = default) => _db.Users.FirstOrDefaultAsync(u => u.EmployeeId == employeeId, cancellationToken); diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/ValueListRepository.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/ValueListRepository.cs index a5d8d67..4489c2a 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/ValueListRepository.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/ValueListRepository.cs @@ -83,6 +83,14 @@ public class ValueListRepository : IValueListRepository } } + public Task GetSelfServiceTransitionAsync(Guid fromItemId, CancellationToken cancellationToken = default) + => _db.ValueListItemTransitions.AsNoTracking() + .FirstOrDefaultAsync(t => t.FromItemId == fromItemId && !t.RequiresApproval, cancellationToken); + + public Task GetTransitionAsync(Guid fromItemId, Guid toItemId, CancellationToken cancellationToken = default) + => _db.ValueListItemTransitions.AsNoTracking() + .FirstOrDefaultAsync(t => t.FromItemId == fromItemId && t.ToItemId == toItemId, cancellationToken); + public Task SaveChangesAsync(CancellationToken cancellationToken = default) => _db.SaveChangesAsync(cancellationToken); } diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Security/JwtTokenGenerator.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Security/JwtTokenGenerator.cs index 3bf5f9a..027fea0 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/Security/JwtTokenGenerator.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Security/JwtTokenGenerator.cs @@ -19,7 +19,7 @@ public class JwtTokenGenerator : IJwtTokenGenerator public (string Token, DateTime ExpiresAt) GenerateToken(User user) { - var claims = new[] + var claims = new List { new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()), new Claim(ClaimTypes.Name, user.Username), @@ -28,6 +28,11 @@ public class JwtTokenGenerator : IJwtTokenGenerator new Claim("mcp", user.MustChangePassword.ToString()) }; + if (user.EmployeeId is { } employeeId) + { + claims.Add(new Claim("employeeId", employeeId.ToString())); + } + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.Secret)); var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var expiresAt = DateTime.UtcNow.AddMinutes(_options.ExpiryMinutes); diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Storage/DocumentUploadPolicy.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Storage/DocumentUploadPolicy.cs new file mode 100644 index 0000000..d62cf4a --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Storage/DocumentUploadPolicy.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.Options; +using OmsorgCore.Application.Abstractions; + +namespace OmsorgCore.Infrastructure.Storage; + +public class DocumentUploadPolicy : IDocumentUploadPolicy +{ + private readonly StorageOptions _options; + private readonly HashSet _allowedContentTypes; + + public DocumentUploadPolicy(IOptions options) + { + _options = options.Value; + _allowedContentTypes = _options.AllowedDocumentContentTypes + .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .ToHashSet(StringComparer.OrdinalIgnoreCase); + } + + public long MaxSizeBytes => _options.MaxDocumentSizeBytes; + + public bool IsSizeAllowed(long sizeBytes) => sizeBytes > 0 && sizeBytes <= MaxSizeBytes; + + public bool IsContentTypeAllowed(string? contentType) + => !string.IsNullOrWhiteSpace(contentType) && _allowedContentTypes.Contains(contentType); +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Storage/FileSystemDocumentStorage.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Storage/FileSystemDocumentStorage.cs new file mode 100644 index 0000000..c65696a --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Storage/FileSystemDocumentStorage.cs @@ -0,0 +1,41 @@ +using Microsoft.Extensions.Options; +using OmsorgCore.Application.Abstractions; + +namespace OmsorgCore.Infrastructure.Storage; + +/// +/// Legt Dokument-Bytes auf dem Dateisystem unterhalb von Storage:DocumentsRootPath ab, unter +/// {EntityType}/{EntityId}/{DocumentId}{Extension} - EntityId/DocumentId sind Guids und +/// Extension kommt aus Path.GetExtension, kein Client-String fließt direkt in den Pfad ein +/// (kein Path-Traversal-Vektor). +/// +public class FileSystemDocumentStorage : IDocumentStorage +{ + private readonly string _rootPath; + + public FileSystemDocumentStorage(IOptions options) + { + _rootPath = Path.GetFullPath(options.Value.DocumentsRootPath); + } + + public async Task SaveAsync(string entityType, Guid entityId, Guid documentId, string originalFileName, Stream content, CancellationToken cancellationToken = default) + { + var extension = Path.GetExtension(originalFileName); + var relativePath = Path.Combine(entityType, entityId.ToString(), $"{documentId}{extension}"); + var fullPath = Path.Combine(_rootPath, relativePath); + + Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!); + + await using var fileStream = new FileStream(fullPath, FileMode.CreateNew, FileAccess.Write); + await content.CopyToAsync(fileStream, cancellationToken); + + return relativePath; + } + + public Task OpenReadAsync(string storageKey, CancellationToken cancellationToken = default) + { + var fullPath = Path.Combine(_rootPath, storageKey); + Stream stream = new FileStream(fullPath, FileMode.Open, FileAccess.Read); + return Task.FromResult(stream); + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Storage/StorageOptions.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Storage/StorageOptions.cs new file mode 100644 index 0000000..a02780c --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Storage/StorageOptions.cs @@ -0,0 +1,15 @@ +namespace OmsorgCore.Infrastructure.Storage; + +/// +/// Gebunden an den "Storage"-Abschnitt in appsettings.json (siehe CONFIGURATION.md, +/// "Dokumentenarchiv / Storage"). Steuert, wo Dokument-Uploads physisch abgelegt werden und +/// welche Größen-/Dateityp-Grenzen dafür gelten - Dateien landen NICHT als Blob in Postgres. +/// +public class StorageOptions +{ + public const string SectionName = "Storage"; + + public string DocumentsRootPath { get; set; } = "App_Data/documents"; + public long MaxDocumentSizeBytes { get; set; } = 20 * 1024 * 1024; + public string AllowedDocumentContentTypes { get; set; } = "application/pdf,image/jpeg,image/png"; +} diff --git a/omsorgCore/tests/OmsorgCore.Tests/Persistence/AuditSaveChangesInterceptorTests.cs b/omsorgCore/tests/OmsorgCore.Tests/Persistence/AuditSaveChangesInterceptorTests.cs index 9cdcc0d..a35045a 100644 --- a/omsorgCore/tests/OmsorgCore.Tests/Persistence/AuditSaveChangesInterceptorTests.cs +++ b/omsorgCore/tests/OmsorgCore.Tests/Persistence/AuditSaveChangesInterceptorTests.cs @@ -14,6 +14,7 @@ public class AuditSaveChangesInterceptorTests public string? Username { get; set; } public string? RoleName { get; set; } public string? IpAddress { get; set; } + public Guid? EmployeeId { get; set; } } private static OmsorgCoreDbContext CreateContext(ICurrentUserService currentUserService) diff --git a/omsorgCore/tests/OmsorgCore.Tests/Services/AuthServiceTests.cs b/omsorgCore/tests/OmsorgCore.Tests/Services/AuthServiceTests.cs index 0e2f812..0ec3001 100644 --- a/omsorgCore/tests/OmsorgCore.Tests/Services/AuthServiceTests.cs +++ b/omsorgCore/tests/OmsorgCore.Tests/Services/AuthServiceTests.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.Caching.Memory; using OmsorgCore.Application.Services; using OmsorgCore.Domain.Entities; using OmsorgCore.Tests.TestDoubles; @@ -30,7 +31,7 @@ public class AuthServiceTests new FakePasswordHasher(), new FakeJwtTokenGenerator(), new FakeRefreshTokenGenerator(), - new PermissionService(userRepository), + new PermissionService(userRepository, new MemoryCache(new MemoryCacheOptions())), loginAttempts ?? new FakeLoginAttemptRepository(), lockoutPolicy ?? new FakeLoginLockoutPolicy()); } diff --git a/omsorgCore/tests/OmsorgCore.Tests/Services/ContractServiceTests.cs b/omsorgCore/tests/OmsorgCore.Tests/Services/ContractServiceTests.cs index e2918f2..3720335 100644 --- a/omsorgCore/tests/OmsorgCore.Tests/Services/ContractServiceTests.cs +++ b/omsorgCore/tests/OmsorgCore.Tests/Services/ContractServiceTests.cs @@ -1,16 +1,21 @@ +using Microsoft.Extensions.Caching.Memory; using OmsorgCore.Application.Services; using OmsorgCore.Domain.Entities; +using OmsorgCore.Domain.Enums; using OmsorgCore.Tests.TestDoubles; namespace OmsorgCore.Tests.Services; public class ContractServiceTests { + private static PermissionService CreatePermissionService() + => new(new FakeUserRepository(), new MemoryCache(new MemoryCacheOptions())); + [Fact] public async Task CreateAsync_PersistsAllFields() { var repository = new FakeContractRepository(); - var sut = new ContractService(repository); + var sut = new ContractService(repository, CreatePermissionService(), new FakeCurrentUserService()); var employeeId = Guid.NewGuid(); var contract = new Contract @@ -48,7 +53,7 @@ public class ContractServiceTests public async Task GetAllAsync_ReturnsCreatedContracts() { var repository = new FakeContractRepository(); - var sut = new ContractService(repository); + var sut = new ContractService(repository, CreatePermissionService(), new FakeCurrentUserService()); await sut.CreateAsync(new Contract { ContractType = "Rahmenvertrag", FacilityId = Guid.NewGuid(), StartDate = new DateOnly(2026, 1, 1) }); @@ -61,7 +66,7 @@ public class ContractServiceTests public async Task UpdateAsync_UpdatesAllMutableFields_AndReturnsUpdatedContract() { var repository = new FakeContractRepository(); - var sut = new ContractService(repository); + var sut = new ContractService(repository, CreatePermissionService(), new FakeCurrentUserService()); var employeeId = Guid.NewGuid(); var created = await sut.CreateAsync(new Contract { @@ -99,7 +104,7 @@ public class ContractServiceTests public async Task UpdateAsync_UnknownId_ReturnsNull() { var repository = new FakeContractRepository(); - var sut = new ContractService(repository); + var sut = new ContractService(repository, CreatePermissionService(), new FakeCurrentUserService()); var updated = await sut.UpdateAsync(Guid.NewGuid(), new Contract { ContractType = "X", StartDate = new DateOnly(2026, 1, 1) }); @@ -110,7 +115,7 @@ public class ContractServiceTests public async Task GetPagedAsync_FiltersByStatusEmployeeAndFacility_AndPaginates() { var repository = new FakeContractRepository(); - var sut = new ContractService(repository); + var sut = new ContractService(repository, CreatePermissionService(), new FakeCurrentUserService()); var employeeA = Guid.NewGuid(); var employeeB = Guid.NewGuid(); var facility = Guid.NewGuid(); @@ -130,7 +135,7 @@ public class ContractServiceTests public async Task GetPagedAsync_PaginatesResults() { var repository = new FakeContractRepository(); - var sut = new ContractService(repository); + var sut = new ContractService(repository, CreatePermissionService(), new FakeCurrentUserService()); await sut.CreateAsync(new Contract { ContractType = "A", StartDate = new DateOnly(2026, 1, 1), EmployeeId = Guid.NewGuid() }); await sut.CreateAsync(new Contract { ContractType = "B", StartDate = new DateOnly(2026, 2, 1), EmployeeId = Guid.NewGuid() }); @@ -141,4 +146,58 @@ public class ContractServiceTests Assert.Equal(3, totalCount); Assert.Single(items); } + + private static (User User, FakeUserRepository Users) CreateOwnScopedAussendienstUser(Guid? linkedEmployeeId) + { + var role = new Role { Name = "Außendienst" }; + role.RolePermissions.Add(new RolePermission { Role = role, RoleId = role.Id, Module = ModuleType.Contracts, Action = PermissionAction.View, Scope = PermissionScope.Own }); + var user = new User + { + Username = "aussendienst", + PasswordHash = "hashed:secret", + IsActive = true, + RoleId = role.Id, + Role = role, + EmployeeId = linkedEmployeeId + }; + return (user, new FakeUserRepository(user)); + } + + [Fact] + public async Task GetPagedAsync_OwnScope_ReturnsOnlyOwnContractsRegardlessOfRequestedEmployeeId() + { + var repository = new FakeContractRepository(); + var ownEmployeeId = Guid.NewGuid(); + var foreignEmployeeId = Guid.NewGuid(); + var (user, users) = CreateOwnScopedAussendienstUser(ownEmployeeId); + var permissionService = new PermissionService(users, new MemoryCache(new MemoryCacheOptions())); + var currentUser = new FakeCurrentUserService { UserId = user.Id, EmployeeId = ownEmployeeId }; + var sut = new ContractService(repository, permissionService, currentUser); + + await sut.CreateAsync(new Contract { ContractType = "Arbeitsvertrag", EmployeeId = ownEmployeeId, StartDate = new DateOnly(2026, 1, 1) }); + await sut.CreateAsync(new Contract { ContractType = "Arbeitsvertrag", EmployeeId = foreignEmployeeId, StartDate = new DateOnly(2026, 1, 1) }); + + // Own-User fragt versuchsweise fremde EmployeeId ab - Backend muss trotzdem auf die eigene erzwingen. + var (items, totalCount) = await sut.GetPagedAsync(search: null, status: null, employeeId: foreignEmployeeId, facilityId: null, page: 1, pageSize: 10); + + Assert.Equal(1, totalCount); + Assert.Equal(ownEmployeeId, Assert.Single(items).EmployeeId); + } + + [Fact] + public async Task GetByIdAsync_OwnScope_ForeignContract_ReturnsNull() + { + var repository = new FakeContractRepository(); + var ownEmployeeId = Guid.NewGuid(); + var (user, users) = CreateOwnScopedAussendienstUser(ownEmployeeId); + var permissionService = new PermissionService(users, new MemoryCache(new MemoryCacheOptions())); + var currentUser = new FakeCurrentUserService { UserId = user.Id, EmployeeId = ownEmployeeId }; + var sut = new ContractService(repository, permissionService, currentUser); + + var own = await sut.CreateAsync(new Contract { ContractType = "Arbeitsvertrag", EmployeeId = ownEmployeeId, StartDate = new DateOnly(2026, 1, 1) }); + var foreign = await sut.CreateAsync(new Contract { ContractType = "Arbeitsvertrag", EmployeeId = Guid.NewGuid(), StartDate = new DateOnly(2026, 1, 1) }); + + Assert.NotNull(await sut.GetByIdAsync(own.Id)); + Assert.Null(await sut.GetByIdAsync(foreign.Id)); + } } diff --git a/omsorgCore/tests/OmsorgCore.Tests/Services/EmployeeServiceTests.cs b/omsorgCore/tests/OmsorgCore.Tests/Services/EmployeeServiceTests.cs index f061ab8..e4a5b42 100644 --- a/omsorgCore/tests/OmsorgCore.Tests/Services/EmployeeServiceTests.cs +++ b/omsorgCore/tests/OmsorgCore.Tests/Services/EmployeeServiceTests.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.Caching.Memory; using OmsorgCore.Application.Services; using OmsorgCore.Domain.Entities; using OmsorgCore.Domain.Enums; @@ -7,11 +8,14 @@ namespace OmsorgCore.Tests.Services; public class EmployeeServiceTests { + private static PermissionService CreatePermissionService() + => new(new FakeUserRepository(), new MemoryCache(new MemoryCacheOptions())); + [Fact] public async Task CreateAsync_PersistsAllStammdatenFields() { var repository = new FakeEmployeeRepository(); - var sut = new EmployeeService(repository); + var sut = new EmployeeService(repository, CreatePermissionService(), new FakeCurrentUserService()); var employee = new Employee { @@ -60,7 +64,7 @@ public class EmployeeServiceTests public async Task GetAllAsync_ReturnsCreatedEmployees() { var repository = new FakeEmployeeRepository(); - var sut = new EmployeeService(repository); + var sut = new EmployeeService(repository, CreatePermissionService(), new FakeCurrentUserService()); await sut.CreateAsync(new Employee { FirstName = "Sascha", LastName = "Recruiting" }); @@ -73,7 +77,7 @@ public class EmployeeServiceTests public async Task UpdateAsync_UpdatesAllMutableFields_AndReturnsUpdatedEmployee() { var repository = new FakeEmployeeRepository(); - var sut = new EmployeeService(repository); + var sut = new EmployeeService(repository, CreatePermissionService(), new FakeCurrentUserService()); var created = await sut.CreateAsync(new Employee { FirstName = "Sabrina", LastName = "Berggötz" }); var updates = new Employee @@ -115,7 +119,7 @@ public class EmployeeServiceTests public async Task UpdateAsync_UnknownId_ReturnsNull() { var repository = new FakeEmployeeRepository(); - var sut = new EmployeeService(repository); + var sut = new EmployeeService(repository, CreatePermissionService(), new FakeCurrentUserService()); var updated = await sut.UpdateAsync(Guid.NewGuid(), new Employee { FirstName = "X", LastName = "Y" }); @@ -126,7 +130,7 @@ public class EmployeeServiceTests public async Task GetPagedAsync_FiltersBySearchStatusAndEmploymentType_AndPaginates() { var repository = new FakeEmployeeRepository(); - var sut = new EmployeeService(repository); + var sut = new EmployeeService(repository, CreatePermissionService(), new FakeCurrentUserService()); await sut.CreateAsync(new Employee { FirstName = "Anna", LastName = "Beispiel", Status = "Aktiv", EmploymentType = "Vollzeit" }); await sut.CreateAsync(new Employee { FirstName = "Bernd", LastName = "Muster", Status = "Aktiv", EmploymentType = "Teilzeit" }); @@ -143,7 +147,7 @@ public class EmployeeServiceTests public async Task GetPagedAsync_PaginatesResults() { var repository = new FakeEmployeeRepository(); - var sut = new EmployeeService(repository); + var sut = new EmployeeService(repository, CreatePermissionService(), new FakeCurrentUserService()); await sut.CreateAsync(new Employee { FirstName = "Anna", LastName = "Eins" }); await sut.CreateAsync(new Employee { FirstName = "Bernd", LastName = "Zwei" }); @@ -168,10 +172,79 @@ public class EmployeeServiceTests Role = role }; - var permissionService = new PermissionService(new FakeUserRepository(user)); + var permissionService = new PermissionService(new FakeUserRepository(user), new MemoryCache(new MemoryCacheOptions())); var allowed = await permissionService.HasPermissionAsync(user.Id, ModuleType.Employees, PermissionAction.Create); Assert.False(allowed); } + + private static (User User, FakeUserRepository Users) CreateOwnScopedAussendienstUser(Guid? linkedEmployeeId) + { + var role = new Role { Name = "Außendienst" }; + role.RolePermissions.Add(new RolePermission { Role = role, RoleId = role.Id, Module = ModuleType.Employees, Action = PermissionAction.View, Scope = PermissionScope.Own }); + var user = new User + { + Username = "aussendienst", + PasswordHash = "hashed:secret", + IsActive = true, + RoleId = role.Id, + Role = role, + EmployeeId = linkedEmployeeId + }; + return (user, new FakeUserRepository(user)); + } + + [Fact] + public async Task GetPagedAsync_OwnScope_ReturnsOnlyLinkedEmployee() + { + var repository = new FakeEmployeeRepository(); + var (user, users) = CreateOwnScopedAussendienstUser(linkedEmployeeId: null); + var permissionService = new PermissionService(users, new MemoryCache(new MemoryCacheOptions())); + var currentUser = new FakeCurrentUserService { UserId = user.Id }; + var sut = new EmployeeService(repository, permissionService, currentUser); + + var own = await sut.CreateAsync(new Employee { FirstName = "Anna", LastName = "Eigen" }); + await sut.CreateAsync(new Employee { FirstName = "Bernd", LastName = "Fremd" }); + currentUser.EmployeeId = own.Id; + + var (items, totalCount) = await sut.GetPagedAsync(search: null, status: null, employmentType: null, page: 1, pageSize: 10); + + Assert.Equal(1, totalCount); + Assert.Equal(own.Id, Assert.Single(items).Id); + } + + [Fact] + public async Task GetPagedAsync_OwnScopeWithoutLinkedEmployee_ReturnsNothing() + { + var repository = new FakeEmployeeRepository(); + var (user, users) = CreateOwnScopedAussendienstUser(linkedEmployeeId: null); + var permissionService = new PermissionService(users, new MemoryCache(new MemoryCacheOptions())); + var currentUser = new FakeCurrentUserService { UserId = user.Id }; + var sut = new EmployeeService(repository, permissionService, currentUser); + + await sut.CreateAsync(new Employee { FirstName = "Bernd", LastName = "Fremd" }); + + var (items, totalCount) = await sut.GetPagedAsync(search: null, status: null, employmentType: null, page: 1, pageSize: 10); + + Assert.Equal(0, totalCount); + Assert.Empty(items); + } + + [Fact] + public async Task GetByIdAsync_OwnScope_ForeignEmployee_ReturnsNull() + { + var repository = new FakeEmployeeRepository(); + var (user, users) = CreateOwnScopedAussendienstUser(linkedEmployeeId: null); + var permissionService = new PermissionService(users, new MemoryCache(new MemoryCacheOptions())); + var currentUser = new FakeCurrentUserService { UserId = user.Id }; + var sut = new EmployeeService(repository, permissionService, currentUser); + + var own = await sut.CreateAsync(new Employee { FirstName = "Anna", LastName = "Eigen" }); + var foreign = await sut.CreateAsync(new Employee { FirstName = "Bernd", LastName = "Fremd" }); + currentUser.EmployeeId = own.Id; + + Assert.NotNull(await sut.GetByIdAsync(own.Id)); + Assert.Null(await sut.GetByIdAsync(foreign.Id)); + } } diff --git a/omsorgCore/tests/OmsorgCore.Tests/Services/FacilityQualificationRateServiceTests.cs b/omsorgCore/tests/OmsorgCore.Tests/Services/FacilityQualificationRateServiceTests.cs new file mode 100644 index 0000000..79ac49f --- /dev/null +++ b/omsorgCore/tests/OmsorgCore.Tests/Services/FacilityQualificationRateServiceTests.cs @@ -0,0 +1,108 @@ +using OmsorgCore.Application.Services; +using OmsorgCore.Domain.Entities; +using OmsorgCore.Tests.TestDoubles; + +namespace OmsorgCore.Tests.Services; + +public class FacilityQualificationRateServiceTests +{ + [Fact] + public async Task CreateAsync_PersistsAllFields() + { + var repository = new FakeFacilityQualificationRateRepository(); + var sut = new FacilityQualificationRateService(repository); + var facilityId = Guid.NewGuid(); + + var rate = new FacilityQualificationRate + { + FacilityId = facilityId, + Qualification = "Altenpfleger/in", + Rate = 38.50m + }; + + var created = await sut.CreateAsync(rate); + + Assert.Equal("Altenpfleger/in", created.Qualification); + Assert.Equal(38.50m, created.Rate); + Assert.Equal(facilityId, created.FacilityId); + } + + [Fact] + public async Task GetByFacilityIdAsync_ReturnsOnlyRatesOfThatFacility() + { + var repository = new FakeFacilityQualificationRateRepository(); + var sut = new FacilityQualificationRateService(repository); + var facilityIdA = Guid.NewGuid(); + var facilityIdB = Guid.NewGuid(); + + await sut.CreateAsync(new FacilityQualificationRate { FacilityId = facilityIdA, Qualification = "Pflegehelfer/in", Rate = 25 }); + await sut.CreateAsync(new FacilityQualificationRate { FacilityId = facilityIdA, Qualification = "Altenpfleger/in", Rate = 38.50m }); + await sut.CreateAsync(new FacilityQualificationRate { FacilityId = facilityIdB, Qualification = "Pflegehelfer/in", Rate = 24 }); + + var ratesForA = await sut.GetByFacilityIdAsync(facilityIdA); + + Assert.Equal(2, ratesForA.Count); + Assert.All(ratesForA, r => Assert.Equal(facilityIdA, r.FacilityId)); + } + + [Fact] + public async Task UpdateAsync_UpdatesAllMutableFields_AndReturnsUpdatedRate() + { + var repository = new FakeFacilityQualificationRateRepository(); + var sut = new FacilityQualificationRateService(repository); + var created = await sut.CreateAsync(new FacilityQualificationRate { FacilityId = Guid.NewGuid(), Qualification = "Pflegehelfer/in", Rate = 25 }); + + var updates = new FacilityQualificationRate + { + Qualification = "Altenpfleger/in", + Rate = 38.50m + }; + + var updated = await sut.UpdateAsync(created.Id, updates); + + Assert.NotNull(updated); + Assert.Equal("Altenpfleger/in", updated!.Qualification); + Assert.Equal(38.50m, updated.Rate); + Assert.NotNull(updated.UpdatedAt); + } + + [Fact] + public async Task UpdateAsync_UnknownId_ReturnsNull() + { + var repository = new FakeFacilityQualificationRateRepository(); + var sut = new FacilityQualificationRateService(repository); + + var updated = await sut.UpdateAsync(Guid.NewGuid(), new FacilityQualificationRate { Qualification = "X", Rate = 1 }); + + Assert.Null(updated); + } + + [Fact] + public async Task DeleteAsync_ThenGetDeletedAsync_ReturnsSoftDeletedRate() + { + var repository = new FakeFacilityQualificationRateRepository(); + var sut = new FacilityQualificationRateService(repository); + var created = await sut.CreateAsync(new FacilityQualificationRate { FacilityId = Guid.NewGuid(), Qualification = "Pflegehelfer/in", Rate = 25 }); + + var deleted = await sut.DeleteAsync(created.Id); + var deletedList = await sut.GetDeletedAsync(null); + + Assert.True(deleted); + Assert.Contains(deletedList, r => r.Id == created.Id); + } + + [Fact] + public async Task RestoreAsync_UndoesSoftDelete() + { + var repository = new FakeFacilityQualificationRateRepository(); + var sut = new FacilityQualificationRateService(repository); + var created = await sut.CreateAsync(new FacilityQualificationRate { FacilityId = Guid.NewGuid(), Qualification = "Pflegehelfer/in", Rate = 25 }); + await sut.DeleteAsync(created.Id); + + var restored = await sut.RestoreAsync(created.Id); + var deletedList = await sut.GetDeletedAsync(null); + + Assert.True(restored); + Assert.DoesNotContain(deletedList, r => r.Id == created.Id); + } +} diff --git a/omsorgCore/tests/OmsorgCore.Tests/Services/FacilityServiceTests.cs b/omsorgCore/tests/OmsorgCore.Tests/Services/FacilityServiceTests.cs index be9d232..45fef17 100644 --- a/omsorgCore/tests/OmsorgCore.Tests/Services/FacilityServiceTests.cs +++ b/omsorgCore/tests/OmsorgCore.Tests/Services/FacilityServiceTests.cs @@ -105,7 +105,7 @@ public class FacilityServiceTests await sut.CreateAsync(new Facility { Name = "Bernd-Zentrum", CrmStatus = "Lead" }); await sut.CreateAsync(new Facility { Name = "Anna-Zentrum", CrmStatus = "Lead" }); - var (items, totalCount) = await sut.GetPagedAsync(search: "anna", crmStatus: "Kunde", page: 1, pageSize: 10); + var (items, totalCount) = await sut.GetPagedAsync(search: "anna", crmStatus: "Kunde", followUpDueOnly: false, page: 1, pageSize: 10); Assert.Equal(1, totalCount); Assert.Single(items); @@ -122,7 +122,7 @@ public class FacilityServiceTests await sut.CreateAsync(new Facility { Name = "Zwei" }); await sut.CreateAsync(new Facility { Name = "Drei" }); - var (items, totalCount) = await sut.GetPagedAsync(search: null, crmStatus: null, page: 2, pageSize: 2); + var (items, totalCount) = await sut.GetPagedAsync(search: null, crmStatus: null, followUpDueOnly: false, page: 2, pageSize: 2); Assert.Equal(3, totalCount); Assert.Single(items); diff --git a/omsorgCore/tests/OmsorgCore.Tests/Services/OrderServiceTests.cs b/omsorgCore/tests/OmsorgCore.Tests/Services/OrderServiceTests.cs index 4514fef..792715a 100644 --- a/omsorgCore/tests/OmsorgCore.Tests/Services/OrderServiceTests.cs +++ b/omsorgCore/tests/OmsorgCore.Tests/Services/OrderServiceTests.cs @@ -170,7 +170,7 @@ public class OrderServiceTests await sut.CreateAsync(new Order { FacilityId = facilityB, StartDate = new DateOnly(2026, 2, 1), Priority = "Normal" }); await sut.UpdateAsync(first.Id, new Order { FacilityId = facilityA, StartDate = first.StartDate, Priority = "Normal", StatusId = pruefung.Id }); - var (items, totalCount) = await sut.GetPagedAsync(search: null, statusId: pruefung.Id, facilityId: facilityA, page: 1, pageSize: 10); + var (items, totalCount) = await sut.GetPagedAsync(search: null, statusId: pruefung.Id, facilityId: facilityA, priority: null, requiredQualification: null, shiftType: null, page: 1, pageSize: 10); Assert.Equal(1, totalCount); Assert.Single(items); diff --git a/omsorgCore/tests/OmsorgCore.Tests/Services/PermissionServiceTests.cs b/omsorgCore/tests/OmsorgCore.Tests/Services/PermissionServiceTests.cs index 6ec620a..b01ff08 100644 --- a/omsorgCore/tests/OmsorgCore.Tests/Services/PermissionServiceTests.cs +++ b/omsorgCore/tests/OmsorgCore.Tests/Services/PermissionServiceTests.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.Caching.Memory; using OmsorgCore.Application.Services; using OmsorgCore.Domain.Entities; using OmsorgCore.Domain.Enums; @@ -36,7 +37,7 @@ public class PermissionServiceTests role.RolePermissions.Add(new RolePermission { Role = role, RoleId = role.Id, Module = ModuleType.Orders, Action = PermissionAction.Create }); var user = CreateUser(role); - var sut = new PermissionService(new FakeUserRepository(user)); + var sut = new PermissionService(new FakeUserRepository(user), new MemoryCache(new MemoryCacheOptions())); var grants = await sut.GetGrantedPermissionsAsync(user.Id); @@ -57,7 +58,7 @@ public class PermissionServiceTests Action = PermissionAction.Delete, Effect = PermissionEffect.Revoke }); - var sut = new PermissionService(new FakeUserRepository(user)); + var sut = new PermissionService(new FakeUserRepository(user), new MemoryCache(new MemoryCacheOptions())); var grants = await sut.GetGrantedPermissionsAsync(user.Id); @@ -75,7 +76,7 @@ public class PermissionServiceTests Action = PermissionAction.View, Effect = PermissionEffect.Grant }); - var sut = new PermissionService(new FakeUserRepository(user)); + var sut = new PermissionService(new FakeUserRepository(user), new MemoryCache(new MemoryCacheOptions())); var grants = await sut.GetGrantedPermissionsAsync(user.Id); @@ -87,7 +88,7 @@ public class PermissionServiceTests { var role = new Role { Name = "Recruiting" }; var user = CreateUser(role); - var sut = new PermissionService(new FakeUserRepository(user)); + var sut = new PermissionService(new FakeUserRepository(user), new MemoryCache(new MemoryCacheOptions())); var allowed = await sut.HasPermissionAsync(user.Id, ModuleType.UserManagement, PermissionAction.View); diff --git a/omsorgCore/tests/OmsorgCore.Tests/Services/RoleServiceTests.cs b/omsorgCore/tests/OmsorgCore.Tests/Services/RoleServiceTests.cs index 8747f27..26036d7 100644 --- a/omsorgCore/tests/OmsorgCore.Tests/Services/RoleServiceTests.cs +++ b/omsorgCore/tests/OmsorgCore.Tests/Services/RoleServiceTests.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.Caching.Memory; using OmsorgCore.Application.Services; using OmsorgCore.Domain.Entities; using OmsorgCore.Domain.Enums; @@ -7,11 +8,14 @@ namespace OmsorgCore.Tests.Services; public class RoleServiceTests { + private static PermissionService CreatePermissionService() + => new(new FakeUserRepository(), new MemoryCache(new MemoryCacheOptions())); + [Fact] public async Task CreateAsync_WithNewName_CreatesRole() { var roles = new FakeRoleRepository(); - var sut = new RoleService(roles); + var sut = new RoleService(roles, CreatePermissionService()); var result = await sut.CreateAsync("Aussendienst"); @@ -25,7 +29,7 @@ public class RoleServiceTests public async Task CreateAsync_WithExistingName_Fails() { var roles = new FakeRoleRepository(new Role { Name = "Aussendienst" }); - var sut = new RoleService(roles); + var sut = new RoleService(roles, CreatePermissionService()); var result = await sut.CreateAsync("Aussendienst"); @@ -36,7 +40,7 @@ public class RoleServiceTests [Fact] public async Task CreateAsync_WithEmptyName_Fails() { - var sut = new RoleService(new FakeRoleRepository()); + var sut = new RoleService(new FakeRoleRepository(), CreatePermissionService()); var result = await sut.CreateAsync(" "); @@ -50,23 +54,39 @@ public class RoleServiceTests var role = new Role { Name = "Disposition" }; role.RolePermissions.Add(new RolePermission { RoleId = role.Id, Role = role, Module = ModuleType.Employees, Action = PermissionAction.View }); var roles = new FakeRoleRepository(role); - var sut = new RoleService(roles); + var sut = new RoleService(roles, CreatePermissionService()); - var result = await sut.UpdatePermissionsAsync(role.Id, new[] { (ModuleType.Invoices, PermissionAction.Edit) }); + var result = await sut.UpdatePermissionsAsync(role.Id, new[] { (ModuleType.Invoices, PermissionAction.Edit, PermissionScope.All) }); Assert.True(result.Success); var updated = await roles.GetByIdWithPermissionsAsync(role.Id); var permission = Assert.Single(updated!.RolePermissions); Assert.Equal(ModuleType.Invoices, permission.Module); Assert.Equal(PermissionAction.Edit, permission.Action); + Assert.Equal(PermissionScope.All, permission.Scope); + } + + [Fact] + public async Task UpdatePermissionsAsync_WithOwnScope_PersistsScope() + { + var role = new Role { Name = "Außendienst" }; + var roles = new FakeRoleRepository(role); + var sut = new RoleService(roles, CreatePermissionService()); + + var result = await sut.UpdatePermissionsAsync(role.Id, new[] { (ModuleType.Employees, PermissionAction.View, PermissionScope.Own) }); + + Assert.True(result.Success); + var updated = await roles.GetByIdWithPermissionsAsync(role.Id); + var permission = Assert.Single(updated!.RolePermissions); + Assert.Equal(PermissionScope.Own, permission.Scope); } [Fact] public async Task UpdatePermissionsAsync_UnknownRole_Fails() { - var sut = new RoleService(new FakeRoleRepository()); + var sut = new RoleService(new FakeRoleRepository(), CreatePermissionService()); - var result = await sut.UpdatePermissionsAsync(Guid.NewGuid(), new[] { (ModuleType.Employees, PermissionAction.View) }); + var result = await sut.UpdatePermissionsAsync(Guid.NewGuid(), new[] { (ModuleType.Employees, PermissionAction.View, PermissionScope.All) }); Assert.False(result.Success); Assert.Equal(UpdateRolePermissionsFailureReason.RoleNotFound, result.FailureReason); diff --git a/omsorgCore/tests/OmsorgCore.Tests/Services/UserServiceTests.cs b/omsorgCore/tests/OmsorgCore.Tests/Services/UserServiceTests.cs index c2c1f8b..91fa82e 100644 --- a/omsorgCore/tests/OmsorgCore.Tests/Services/UserServiceTests.cs +++ b/omsorgCore/tests/OmsorgCore.Tests/Services/UserServiceTests.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.Caching.Memory; using OmsorgCore.Application.Services; using OmsorgCore.Domain.Entities; using OmsorgCore.Domain.Enums; @@ -30,7 +31,8 @@ public class UserServiceTests return new UserService( users, employees, roles, new FakePasswordHasher(), - passwordResetService, actualRefreshTokens, passwordPolicy); + passwordResetService, actualRefreshTokens, passwordPolicy, + new PermissionService(users, new MemoryCache(new MemoryCacheOptions()))); } [Fact] @@ -416,7 +418,7 @@ public class UserServiceTests var users = new FakeUserRepository(user); var sut = CreateSut(users, new FakeEmployeeRepository(), new FakeRoleRepository(role)); - var result = await sut.AddPermissionOverrideAsync(user.Id, ModuleType.Invoices, PermissionAction.View, PermissionEffect.Grant); + var result = await sut.AddPermissionOverrideAsync(user.Id, ModuleType.Invoices, PermissionAction.View, PermissionEffect.Grant, PermissionScope.All); Assert.True(result.Success); Assert.Single(user.PermissionOverrides); @@ -435,7 +437,7 @@ public class UserServiceTests var users = new FakeUserRepository(user); var sut = CreateSut(users, new FakeEmployeeRepository(), new FakeRoleRepository(role)); - var result = await sut.AddPermissionOverrideAsync(user.Id, ModuleType.Invoices, PermissionAction.View, PermissionEffect.Revoke); + var result = await sut.AddPermissionOverrideAsync(user.Id, ModuleType.Invoices, PermissionAction.View, PermissionEffect.Revoke, PermissionScope.All); Assert.True(result.Success); var single = Assert.Single(user.PermissionOverrides); @@ -447,7 +449,7 @@ public class UserServiceTests { var sut = CreateSut(new FakeUserRepository(), new FakeEmployeeRepository(), new FakeRoleRepository()); - var result = await sut.AddPermissionOverrideAsync(Guid.NewGuid(), ModuleType.Invoices, PermissionAction.View, PermissionEffect.Grant); + var result = await sut.AddPermissionOverrideAsync(Guid.NewGuid(), ModuleType.Invoices, PermissionAction.View, PermissionEffect.Grant, PermissionScope.All); Assert.False(result.Success); Assert.Equal(AddPermissionOverrideFailureReason.UserNotFound, result.FailureReason); diff --git a/omsorgCore/tests/OmsorgCore.Tests/TestDoubles/FakeRepositories.cs b/omsorgCore/tests/OmsorgCore.Tests/TestDoubles/FakeRepositories.cs index 9361c82..b129551 100644 --- a/omsorgCore/tests/OmsorgCore.Tests/TestDoubles/FakeRepositories.cs +++ b/omsorgCore/tests/OmsorgCore.Tests/TestDoubles/FakeRepositories.cs @@ -19,6 +19,9 @@ public class FakeUserRepository : IUserRepository public Task GetByIdWithPermissionsAsync(Guid id, CancellationToken cancellationToken = default) => Task.FromResult(_users.FirstOrDefault(u => u.Id == id)); + public Task GetByIdWithPermissionsNoTrackingAsync(Guid id, CancellationToken cancellationToken = default) + => Task.FromResult(_users.FirstOrDefault(u => u.Id == id)); + public Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) => Task.FromResult(_users.FirstOrDefault(u => u.Id == id)); @@ -28,6 +31,9 @@ public class FakeUserRepository : IUserRepository public Task> GetAllAsync(CancellationToken cancellationToken = default) => Task.FromResult>(_users.ToList()); + public Task> GetUserIdsByRoleAsync(Guid roleId, CancellationToken cancellationToken = default) + => Task.FromResult>(_users.Where(u => u.RoleId == roleId).Select(u => u.Id).ToList()); + public Task GetByEmployeeIdAsync(Guid employeeId, CancellationToken cancellationToken = default) => Task.FromResult(_users.FirstOrDefault(u => u.EmployeeId == employeeId)); @@ -105,10 +111,16 @@ public class FakeEmployeeRepository : IEmployeeRepository string? employmentType, int page, int pageSize, - CancellationToken cancellationToken = default) + CancellationToken cancellationToken = default, + Guid? restrictToEmployeeId = null) { IEnumerable query = _employees.Where(e => !e.IsDeleted); + if (restrictToEmployeeId.HasValue) + { + query = query.Where(e => e.Id == restrictToEmployeeId.Value); + } + if (!string.IsNullOrWhiteSpace(search)) { var term = search.Trim(); @@ -144,6 +156,36 @@ public class FakeEmployeeRepository : IEmployeeRepository public Task UpdateAsync(Employee employee, CancellationToken cancellationToken = default) => Task.CompletedTask; + public Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var employee = _employees.FirstOrDefault(e => e.Id == id); + if (employee is null) + { + return Task.FromResult(false); + } + + employee.IsDeleted = true; + employee.DeletedAt = DateTime.UtcNow; + return Task.FromResult(true); + } + + public Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + => Task.FromResult>( + _employees.Where(e => e.IsDeleted).OrderByDescending(e => e.DeletedAt).ToList()); + + public Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var employee = _employees.FirstOrDefault(e => e.Id == id && e.IsDeleted); + if (employee is null) + { + return Task.FromResult(false); + } + + employee.IsDeleted = false; + employee.DeletedAt = null; + return Task.FromResult(true); + } + public Task SaveChangesAsync(CancellationToken cancellationToken = default) => Task.CompletedTask; } @@ -161,6 +203,7 @@ public class FakeFacilityRepository : IFacilityRepository public Task<(IReadOnlyList Items, int TotalCount)> GetPagedAsync( string? search, string? crmStatus, + bool followUpDueOnly, int page, int pageSize, CancellationToken cancellationToken = default) @@ -178,7 +221,14 @@ public class FakeFacilityRepository : IFacilityRepository query = query.Where(f => f.CrmStatus == crmStatus); } - var ordered = query.OrderBy(f => f.Name).ToList(); + if (followUpDueOnly) + { + query = query.Where(f => f.FollowUpDueDate != null); + } + + var ordered = followUpDueOnly + ? query.OrderBy(f => f.FollowUpDueDate).ToList() + : query.OrderBy(f => f.Name).ToList(); var totalCount = ordered.Count; var items = ordered.Skip((page - 1) * pageSize).Take(pageSize).ToList(); @@ -194,6 +244,36 @@ public class FakeFacilityRepository : IFacilityRepository public Task UpdateAsync(Facility facility, CancellationToken cancellationToken = default) => Task.CompletedTask; + public Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var facility = _facilities.FirstOrDefault(f => f.Id == id); + if (facility is null) + { + return Task.FromResult(false); + } + + facility.IsDeleted = true; + facility.DeletedAt = DateTime.UtcNow; + return Task.FromResult(true); + } + + public Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + => Task.FromResult>( + _facilities.Where(f => f.IsDeleted).OrderByDescending(f => f.DeletedAt).ToList()); + + public Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var facility = _facilities.FirstOrDefault(f => f.Id == id && f.IsDeleted); + if (facility is null) + { + return Task.FromResult(false); + } + + facility.IsDeleted = false; + facility.DeletedAt = null; + return Task.FromResult(true); + } + public Task SaveChangesAsync(CancellationToken cancellationToken = default) => Task.CompletedTask; } @@ -218,6 +298,90 @@ public class FakeFacilityContactRepository : IFacilityContactRepository public Task UpdateAsync(FacilityContact contact, CancellationToken cancellationToken = default) => Task.CompletedTask; + public Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var contact = _contacts.FirstOrDefault(c => c.Id == id); + if (contact is null) + { + return Task.FromResult(false); + } + + contact.IsDeleted = true; + contact.DeletedAt = DateTime.UtcNow; + return Task.FromResult(true); + } + + public Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + => Task.FromResult>( + _contacts.Where(c => c.IsDeleted).OrderByDescending(c => c.DeletedAt).ToList()); + + public Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var contact = _contacts.FirstOrDefault(c => c.Id == id && c.IsDeleted); + if (contact is null) + { + return Task.FromResult(false); + } + + contact.IsDeleted = false; + contact.DeletedAt = null; + return Task.FromResult(true); + } + + public Task SaveChangesAsync(CancellationToken cancellationToken = default) + => Task.CompletedTask; +} + +public class FakeFacilityQualificationRateRepository : IFacilityQualificationRateRepository +{ + private readonly List _rates = new(); + + public Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) + => Task.FromResult(_rates.FirstOrDefault(r => r.Id == id)); + + public Task> GetByFacilityIdAsync(Guid facilityId, CancellationToken cancellationToken = default) + => Task.FromResult>( + _rates.Where(r => r.FacilityId == facilityId).OrderBy(r => r.Qualification).ToList()); + + public Task AddAsync(FacilityQualificationRate rate, CancellationToken cancellationToken = default) + { + _rates.Add(rate); + return Task.CompletedTask; + } + + public Task UpdateAsync(FacilityQualificationRate rate, CancellationToken cancellationToken = default) + => Task.CompletedTask; + + public Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var rate = _rates.FirstOrDefault(r => r.Id == id); + if (rate is null) + { + return Task.FromResult(false); + } + + rate.IsDeleted = true; + rate.DeletedAt = DateTime.UtcNow; + return Task.FromResult(true); + } + + public Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + => Task.FromResult>( + _rates.Where(r => r.IsDeleted).OrderByDescending(r => r.DeletedAt).ToList()); + + public Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var rate = _rates.FirstOrDefault(r => r.Id == id && r.IsDeleted); + if (rate is null) + { + return Task.FromResult(false); + } + + rate.IsDeleted = false; + rate.DeletedAt = null; + return Task.FromResult(true); + } + public Task SaveChangesAsync(CancellationToken cancellationToken = default) => Task.CompletedTask; } @@ -280,6 +444,36 @@ public class FakeContractRepository : IContractRepository public Task UpdateAsync(Contract contract, CancellationToken cancellationToken = default) => Task.CompletedTask; + public Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var contract = _contracts.FirstOrDefault(c => c.Id == id); + if (contract is null) + { + return Task.FromResult(false); + } + + contract.IsDeleted = true; + contract.DeletedAt = DateTime.UtcNow; + return Task.FromResult(true); + } + + public Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + => Task.FromResult>( + _contracts.Where(c => c.IsDeleted).OrderByDescending(c => c.DeletedAt).ToList()); + + public Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var contract = _contracts.FirstOrDefault(c => c.Id == id && c.IsDeleted); + if (contract is null) + { + return Task.FromResult(false); + } + + contract.IsDeleted = false; + contract.DeletedAt = null; + return Task.FromResult(true); + } + public Task SaveChangesAsync(CancellationToken cancellationToken = default) => Task.CompletedTask; } @@ -493,6 +687,9 @@ public class FakeOrderRepository : IOrderRepository string? search, Guid? statusId, Guid? facilityId, + string? priority, + string? requiredQualification, + string? shiftType, int page, int pageSize, CancellationToken cancellationToken = default) @@ -515,6 +712,21 @@ public class FakeOrderRepository : IOrderRepository query = query.Where(o => o.FacilityId == facilityId.Value); } + if (!string.IsNullOrWhiteSpace(priority)) + { + query = query.Where(o => o.Priority == priority); + } + + if (!string.IsNullOrWhiteSpace(requiredQualification)) + { + query = query.Where(o => o.RequiredQualification == requiredQualification); + } + + if (!string.IsNullOrWhiteSpace(shiftType)) + { + query = query.Where(o => o.ShiftType == shiftType); + } + var ordered = query.OrderBy(o => o.StartDate).ToList(); var totalCount = ordered.Count; var items = ordered.Skip((page - 1) * pageSize).Take(pageSize).ToList(); @@ -531,6 +743,36 @@ public class FakeOrderRepository : IOrderRepository public Task UpdateAsync(Order order, CancellationToken cancellationToken = default) => Task.CompletedTask; + public Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var order = _orders.FirstOrDefault(o => o.Id == id); + if (order is null) + { + return Task.FromResult(false); + } + + order.IsDeleted = true; + order.DeletedAt = DateTime.UtcNow; + return Task.FromResult(true); + } + + public Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + => Task.FromResult>( + _orders.Where(o => o.IsDeleted).OrderByDescending(o => o.DeletedAt).ToList()); + + public Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var order = _orders.FirstOrDefault(o => o.Id == id && o.IsDeleted); + if (order is null) + { + return Task.FromResult(false); + } + + order.IsDeleted = false; + order.DeletedAt = null; + return Task.FromResult(true); + } + public Task SaveChangesAsync(CancellationToken cancellationToken = default) => Task.CompletedTask; } @@ -563,8 +805,8 @@ public class FakeValueListRepository : IValueListRepository return item; } - public void AllowTransition(Guid fromItemId, Guid toItemId) - => _transitions.Add(new ValueListItemTransition { FromItemId = fromItemId, ToItemId = toItemId }); + public void AllowTransition(Guid fromItemId, Guid toItemId, bool requiresApproval = true) + => _transitions.Add(new ValueListItemTransition { FromItemId = fromItemId, ToItemId = toItemId, RequiresApproval = requiresApproval }); public Task> GetAllListsAsync(CancellationToken cancellationToken = default) => Task.FromResult>(_lists.ToList()); @@ -622,6 +864,23 @@ public class FakeValueListRepository : IValueListRepository return Task.CompletedTask; } + public Task GetSelfServiceTransitionAsync(Guid fromItemId, CancellationToken cancellationToken = default) + => Task.FromResult(_transitions.FirstOrDefault(t => t.FromItemId == fromItemId && !t.RequiresApproval)); + + public Task GetTransitionAsync(Guid fromItemId, Guid toItemId, CancellationToken cancellationToken = default) + => Task.FromResult(_transitions.FirstOrDefault(t => t.FromItemId == fromItemId && t.ToItemId == toItemId)); + public Task SaveChangesAsync(CancellationToken cancellationToken = default) => Task.CompletedTask; } + +/// Konfigurierbarer Fake für ICurrentUserService - default: kein eingeloggter User (UserId null), damit scope-abhängige Services ohne Einschränkung testbar bleiben, wenn UserId nicht explizit gesetzt wird. +public class FakeCurrentUserService : ICurrentUserService +{ + public Guid? UserId { get; set; } + public bool IsAuthenticated { get; set; } + public string? Username { get; set; } + public string? RoleName { get; set; } + public string? IpAddress { get; set; } + public Guid? EmployeeId { get; set; } +} diff --git a/omsorgWeb/CLAUDE.md b/omsorgWeb/CLAUDE.md index f6c3282..520019f 100644 --- a/omsorgWeb/CLAUDE.md +++ b/omsorgWeb/CLAUDE.md @@ -9,9 +9,17 @@ This is the OMSORG website plus **OMSORG Connect**, an internal employee web app **OMSORG Connect wird gerade komplett neu aufgebaut.** `mitarbeiter-app-legacy/` ist die alte, produktiv gelaufene Version — dient nur noch als Referenz/Vorlage, wird nicht mehr weiterentwickelt. Der Rest dieser Datei beschreibt `mitarbeiter-app-legacy/`, nicht den Neuaufbau. Die aktive Entwicklung findet in `mitarbeiter-app/` statt (aktuell: Login-Flow + eigenes Passwort ändern/zurücksetzen gegen `omsorgCore`, siehe unten "Neuaufbau"). Auth in der Legacy-Version läuft **nicht mehr** über lokales bcrypt/Session-Lockout wie unten in "Entry point" beschrieben — das ist bereits auf omsorgCore-JWT-Auth umgestellt (`lib/omsorgCoreClient.php`, `lib/auth.php`), lokales MySQL dient dort nur noch als Read-Cache für Profildaten. ### Neuaufbau (`mitarbeiter-app/`) -Frischer, minimaler PHP-Login-Flow gegen `omsorgCore` — kein Framework, gleiches Deployment-Modell wie die Legacy-App. Bewusst (noch) ohne: Antragsformulare, Dienstplan, Downloads, Admin-Oberfläche, PWA-Assets, MySQL. Admin-/Mitarbeiterverwaltung gehört nicht hierher, sondern exklusiv zu OMSORG Desktop (`omsorgapp`) — Connect zeigt/bearbeitet ausschließlich Daten des eingeloggten Nutzers selbst. +Frischer, minimaler PHP-Flow gegen `omsorgCore` — kein Framework, gleiches Deployment-Modell wie die Legacy-App. Login/Passwort, das Abwesenheits-/Urlaubs-/Krankmeldungsformular und die strukturierte Zeiterfassung pro Schicht (siehe unten) sind umgesetzt; weiterhin bewusst (noch) ohne: Dienstplan, Downloads, Admin-Oberfläche, PWA-Assets, MySQL. Admin-/Mitarbeiterverwaltung gehört nicht hierher, sondern exklusiv zu OMSORG Desktop (`omsorgapp`) — Connect zeigt/bearbeitet ausschließlich Daten des eingeloggten Nutzers selbst. -**Passwort ändern/vergessen:** `pages/settings.php` (nach Login, `require_login()`) und `pages/forgot-password.php` (vor Login, 3-stufig: Code anfordern → verifizieren → neues Passwort setzen), beide über die dafür in `lib/omsorgCoreClient.php` ergänzten `omsorgcore_change_password`/`omsorgcore_forgot_password_*`-Wrapper gegen dieselben `omsorgCore`-Endpunkte wie in `mitarbeiter-app-legacy`. Die Mindestlänge kommt nicht hartcodiert, sondern über `omsorgcore_password_policy()` (`GET /api/auth/password-policy`, siehe `omsorgCore/CLAUDE.md` Abschnitt "Passwort-Mindestlänge") — sowohl für das `minlength`-Attribut der Formularfelder als auch für die serverseitige Vorab-Fehlermeldung; die eigentliche Durchsetzung passiert im Backend. Nach erfolgreichem Anlegen/Admin-Reset eines Accounts (`mustChangePassword`-Flag aus der Login-Response, siehe `lib/auth.php`) leitet `pages/dashboard.php` erzwungen zu `settings.php` weiter. +**Abwesenheits-/Urlaubs-/Krankmeldungsanträge (FR-CON-1, `pages/urlaubsantrag.php`):** einzige Seite in diesem Neuaufbau mit echtem Formular + eigener Datenliste. Formular (Art/Zeitraum/Grund/Vertretung/Nachricht) postet inline auf sich selbst (kein separates `actions/*.php` wie in der Legacy-App) über `omsorgcore_absences_create()` (`lib/omsorgCoreClient.php`) gegen `POST /api/absences` in `omsorgCore` — schickt bewusst **keine** `employeeId` mit, das Backend löst den eingeloggten Mitarbeiter serverseitig über den JWT-Claim auf (`AbsenceService.CreateAsync`, siehe `omsorgCore/CLAUDE.md`). Darunter die eigene Antragshistorie über `omsorgcore_absences_list()` (Own-Scope filtert automatisch serverseitig, kein `employeeId`-Parameter nötig). Die Art-Dropdown-Optionen kommen über den neuen generischen `omsorgcore_value_list_items($config, $token, $key)`-Wrapper (erste Nicht-Auth-Verwendung des generierten PHP-Clients hier) aus `GET /api/value-lists/AbsenceType/items`, nicht hartcodiert. Genehmigen/Ablehnen passiert ausschließlich in `omsorgapp` (`AbsencesPage`) — Connect selbst hat keine Entscheidungs-UI, nur Anlegen/Bearbeiten + eigenen Status einsehen. Zweispaltiges Layout (`.split-layout` in `app.css`, Liste links/Formular rechts, bricht unter 860px auf eine Spalte um) statt gestapelter Karten. + +**Bearbeiten eines eigenen Antrags:** jeder eigene Antrag im initialen Status bekommt in der Liste einen "Bearbeiten"-Link (`urlaubsantrag.php?edit=`) — das rechte Formular wechselt dann in den Edit-Modus (vorbefüllt aus dem passenden Eintrag der bereits geladenen `$absences`-Liste, kein extra `GET`), postet über `omsorgcore_absences_update()` gegen `PUT /api/absences/{id}` (mit `mode=edit`/`absence_id` als Hidden-Fields, um im selben Formular-Handler zwischen Anlegen und Bearbeiten zu unterscheiden) und leitet bei Erfolg per Post-Redirect-Get auf `urlaubsantrag.php?updated=1` weiter. Der initiale Status wird dynamisch über `$initialStatus` ermittelt (`omsorgcore_value_list_items(..., 'AbsenceStatus')`, das Item mit `isInitial === true` — **nicht** der Literal `"Eingereicht"`, siehe `omsorgCore/CLAUDE.md` "Abwesenheits-/Urlaubs-/Krankmeldungsanträge"; ein Umbenennen über die Status-Verwaltung in `omsorgapp` bricht diese Seite dadurch nicht). Serverseitig (nicht nur hier) gilt dieselbe Regel: nur solange der Antrag im initialen Status ist, danach `400` — ein bereits genehmigter/abgelehnter Antrag fällt deshalb defensiv aus dem Edit-Modus zurück auf "Neuer Antrag", falls doch mal ein veralteter Link aufgerufen wird. Braucht `has_permission('Absences','Edit')` zusätzlich zu `View`. + +**Zeiterfassung (FR-ZE-1/FR-ZE-2, `pages/stundenerfassung.php`):** 1:1 nach dem Muster von `urlaubsantrag.php` (Liste links/Formular rechts, inline-POST auf sich selbst, `mode=create|edit` als Hidden-Field), aber mit drei statt zwei möglichen Aktionen, weil `TimeEntryStatus` eine echte Mehrstufen-Pipeline statt einer binären Entscheidung ist (siehe `omsorgCore/CLAUDE.md` "Zeiterfassung"): Anlegen (`omsorgcore_time_entries_create()` gegen `POST /api/time-entries`, ohne `employeeId`), Bearbeiten solange `isEditableByOwner` (`omsorgcore_time_entries_update()` gegen `PUT /api/time-entries/{id}`, ohne `statusId`) und zusätzlich ein dritter `mode=submit`-Zweig im selben POST-Handler (`omsorgcore_time_entries_submit()` gegen `POST /api/time-entries/{id}/submit`, kein Payload) für den "Einreichen"-Button. Der "Einreichen"-Button erscheint nur bei Einträgen, deren `statusId` in der Menge der Selbst-Einreichungs-Kanten liegt (`omsorgcore_value_list_transitions($config, $token, 'TimeEntryStatus')`, gefiltert auf `requiresApproval === false`) — bewusst **nicht** dasselbe Kriterium wie für den "Bearbeiten"-Link (`isEditableByOwner` allein reicht hier nicht, weil auch der bereits eingereichte, aber noch nicht geprüfte Status `isEditableByOwner=true` trägt, aber keine ausgehende Selbst-Einreichungs-Kante mehr hat). Auftrags-Dropdown über `omsorgcore_orders_list()` (`GET /api/orders`) — zeigt mangels Mitarbeiter-Zuweisung auf `Order` (FR-EM-3 offen) bewusst alle aktiven Aufträge, nicht nur zugewiesene. Genehmigen/Prüfen/Freigeben passiert ausschließlich in `omsorgapp` (`TimeEntriesPage`) — Connect selbst hat keine Entscheidungs-UI. + +**Rechte im Client (`has_permission()`, `lib/auth.php`):** serverseitig ist jeder `omsorgCore`-Endpunkt ohnehin über `[RequirePermission]` gegated (siehe `omsorgCore/CLAUDE.md`, "Rechtesystem") — `has_permission(string $module, string $action): bool` ist nur die UI-Seite davon, analog zu `hasPermission()` in `omsorgapp/src/app/AuthContext.jsx`, liest `$_SESSION['omsorgcore_profile']['permissions']` (aus `GET /api/auth/me`, `PermissionDto[] { module, action, scope }`). `lib/layout.php` blendet den "Urlaub & Abwesenheit"-Tab aus, wenn `!has_permission('Absences','View')`; `pages/urlaubsantrag.php` leitet ohne dieses Recht direkt auf `dashboard.php` um (kein "leere Seite ohne Erklärung"-Fall) und blendet zusätzlich separat das Formular aus, wenn `!has_permission('Absences','Create')` (z. B. für eine Rolle mit `View`, aber ohne `Create`) — beide Prüfungen sind rein kosmetisch, ein direkt gepostetes Formular ohne UI wird serverseitig trotzdem mit `403` abgelehnt, wird hier aber zusätzlich mit einer klaren deutschen Fehlermeldung statt eines stillen Fehlschlags abgefangen. Neue Connect-Seiten mit einem Rechte-Bezug sollten `has_permission()` nach demselben Muster nutzen, statt ungegated jedem eingeloggten Nutzer alles zu zeigen. + +**Passwort ändern/vergessen:** `pages/settings.php` (nach Login, `require_login()`) und `pages/forgot-password.php` (vor Login, 3-stufig: Code anfordern → verifizieren → neues Passwort setzen), beide über die dafür in `lib/omsorgCoreClient.php` ergänzten `omsorgcore_change_password`/`omsorgcore_forgot_password_*`-Wrapper gegen dieselben `omsorgCore`-Endpunkte wie in `mitarbeiter-app-legacy`. Die Mindestlänge kommt nicht hartcodiert, sondern über `omsorgcore_password_policy()` (`GET /api/auth/password-policy`, siehe `omsorgCore/CLAUDE.md` Abschnitt "Passwort-Mindestlänge") — sowohl für das `minlength`-Attribut der Formularfelder als auch für die serverseitige Vorab-Fehlermeldung; die eigentliche Durchsetzung passiert im Backend. Nach erfolgreichem Anlegen/Admin-Reset eines Accounts (`mustChangePassword`-Flag aus der Login-Response, siehe `lib/auth.php`) leitet `pages/dashboard.php` erzwungen zu `settings.php` weiter. `forgot-password.php` geht bei Schritt "request" bewusst **immer** zu Schritt "verify" weiter, unabhängig davon, ob der Username existiert (kein Enumeration-Rückschluss, siehe `omsorgCore/CLAUDE.md` "Passwort-Reset/E-Mail-Versand") — **außer** der Status ist `"email_unavailable"` (E-Mail-Versand aktuell gestört, z. B. SMTP down): dann bleibt die Seite auf Schritt "request" und zeigt eine klare Fehlermeldung, statt den Nutzer auf eine Code-Eingabe warten zu lassen, die nie ankommt. This project is one part of the OMSORG monorepo — see the root `CLAUDE.md` (`../CLAUDE.md`) for the overall platform picture and `../REQUIREMENTS.md` for functional/non-functional requirements with FR-IDs. This app is the reference implementation for **OMSORG Connect**; its MySQL database is expected to eventually move behind the shared `omsorgCore` backend (currently empty, planned as C#/.NET + PostgreSQL) rather than staying a standalone data store. diff --git a/omsorgWeb/Dockerfile b/omsorgWeb/Dockerfile new file mode 100644 index 0000000..e55635a --- /dev/null +++ b/omsorgWeb/Dockerfile @@ -0,0 +1,32 @@ +# Build-Kontext ist der Repo-Root (siehe .gitea/workflows/docker-build.yml und docker-compose.yml) - +# nötig, um zusätzlich zu omsorgWeb/ auch die Root-.htaccess (liegt bewusst außerhalb von +# omsorgWeb/, siehe README.txt "upload directory contents to htdocs") mit ins Image zu kopieren. +FROM php:8.2-apache + +RUN apt-get update \ + && apt-get install -y --no-install-recommends libcurl4-openssl-dev libonig-dev \ + && docker-php-ext-install pdo_mysql mbstring curl \ + && a2enmod rewrite headers \ + && rm -rf /var/lib/apt/lists/* + +# Debians Standard-apache2.conf setzt AllowOverride None für /var/www/ - ohne dieses Drop-in +# würden alle .htaccess-Regeln der App stillschweigend ignoriert (siehe Datei-Kommentar). +COPY omsorgWeb/docker/allow-htaccess.conf /etc/apache2/conf-available/allow-htaccess.conf +RUN a2enconf allow-htaccess + +COPY omsorgWeb/ /var/www/html/ +COPY .htaccess /var/www/html/.htaccess + +RUN chmod +x /var/www/html/docker/docker-entrypoint.sh \ + # Schreibbare Verzeichnisse für Datei-Uploads zur Laufzeit (siehe docker-compose.yml-Volumes) - + # Besitzer auf den Apache-User setzen, damit PHP dort schreiben kann. + && chown -R www-data:www-data \ + /var/www/html/mitarbeiter-app-legacy/uploads \ + /var/www/html/mitarbeiter-app-legacy/downloads \ + /var/www/html/mitarbeiter-app-legacy/fortbildung-materials \ + /var/www/html/mitarbeiter-app-legacy/assets/avatars \ + /var/www/html/mitarbeiter-app-legacy/data + +EXPOSE 80 + +ENTRYPOINT ["/var/www/html/docker/docker-entrypoint.sh"] diff --git a/omsorgWeb/docker/allow-htaccess.conf b/omsorgWeb/docker/allow-htaccess.conf new file mode 100644 index 0000000..eff561d --- /dev/null +++ b/omsorgWeb/docker/allow-htaccess.conf @@ -0,0 +1,7 @@ +# Debians Standard-apache2.conf setzt AllowOverride None für /var/www/ - ohne diese Datei würden +# sämtliche .htaccess-Regeln der App (HTTPS-Redirect, Security-Header, Deny-All auf lib/uploads/..., +# Blockade von config.php/config.secret.php) stillschweigend ignoriert. Das wäre ein echtes +# Sicherheitsloch (config.secret.php wäre sonst direkt per HTTP abrufbar). + + AllowOverride All + diff --git a/omsorgWeb/docker/bootstrap-config.php b/omsorgWeb/docker/bootstrap-config.php new file mode 100644 index 0000000..363cc4b --- /dev/null +++ b/omsorgWeb/docker/bootstrap-config.php @@ -0,0 +1,80 @@ + $value !== null && $value !== ''); + if (empty($data)) { + // Kein Geheimwert gesetzt - config.php's array_merge() fällt einfach auf seine + // eigenen Defaults zurück (is_file()-Check dort). + if (is_file($path)) { + unlink($path); + } + return; + } + file_put_contents($path, " 'mysql',\n" + . " 'db_dsn' => " . var_export($dbDsn, true) . ",\n" + . " 'db_user' => '',\n" + . " 'db_password' => '',\n" + . " 'omsorg_core_url' => " . var_export(envOrDefault('OMSORG_CORE_URL', 'http://localhost:5245'), true) . ",\n" + . " 'mail_from' => " . var_export(envOrDefault('MAIL_FROM', 'no-reply@omsorg-pflegedienste.de'), true) . ",\n" + . " 'mail_info' => " . var_export(envOrDefault('MAIL_INFO', 'info@omsorg-pflegedienste.de'), true) . ",\n" + . " 'mail_sabrina' => " . var_export(envOrDefault('MAIL_SABRINA'), true) . ",\n" + . " 'smtp_host' => " . var_export(envOrDefault('SMTP_HOST'), true) . ",\n" + . " 'smtp_port' => " . (int) envOrDefault('SMTP_PORT', '587') . ",\n" + . " 'smtp_user' => " . var_export(envOrDefault('SMTP_USER'), true) . ",\n" + . " 'smtp_password' => '',\n" + . "], \$secret);\n"); + +writeSecret($legacyDir . '/config.secret.php', [ + 'db_user' => envOrDefault('DB_USER'), + 'db_password' => envOrDefault('DB_PASSWORD'), + 'smtp_password' => envOrDefault('SMTP_PASSWORD'), +]); + +// --- mitarbeiter-app (Rewrite, kein MySQL, nur omsorgCore) --- +$newDir = __DIR__ . '/../mitarbeiter-app/lib'; + +writeIfMissing($newDir . '/config.php', " " . var_export(envOrDefault('OMSORG_CORE_URL', 'http://localhost:5245'), true) . ",\n" + . "], \$secret);\n"); diff --git a/omsorgWeb/docker/docker-entrypoint.sh b/omsorgWeb/docker/docker-entrypoint.sh new file mode 100644 index 0000000..8f07fec --- /dev/null +++ b/omsorgWeb/docker/docker-entrypoint.sh @@ -0,0 +1,6 @@ +#!/bin/sh +set -eu + +php /var/www/html/docker/bootstrap-config.php + +exec apache2-foreground diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/.openapi-generator/FILES b/omsorgWeb/mitarbeiter-app/api-client-php/.openapi-generator/FILES index 89c9068..60a584a 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/.openapi-generator/FILES +++ b/omsorgWeb/mitarbeiter-app/api-client-php/.openapi-generator/FILES @@ -1,39 +1,52 @@ .gitignore -.openapi-generator-ignore .php-cs-fixer.dist.php .travis.yml README.md composer.json +docs/Api/AbsencesApi.md docs/Api/AdminEmailApi.md docs/Api/AdminSessionsApi.md docs/Api/AuditLogApi.md docs/Api/AuthApi.md docs/Api/ContractsApi.md +docs/Api/DocumentsApi.md docs/Api/EmployeesApi.md docs/Api/FacilitiesApi.md docs/Api/FacilityContactsApi.md +docs/Api/FacilityQualificationRatesApi.md docs/Api/HealthApi.md docs/Api/OrdersApi.md docs/Api/RolesApi.md +docs/Api/TimeEntriesApi.md +docs/Api/TrashApi.md docs/Api/UsersApi.md docs/Api/ValueListsApi.md +docs/Model/AbsenceDecisionRequest.md +docs/Model/AbsenceResponse.md +docs/Model/AbsenceResponsePagedResponse.md docs/Model/AddUserPermissionOverrideRequest.md +docs/Model/AuditEventCategory.md docs/Model/AuditLogEntryResponse.md docs/Model/AuditLogEntryResponsePagedResponse.md docs/Model/ChangePasswordRequest.md docs/Model/ContractResponse.md docs/Model/ContractResponsePagedResponse.md +docs/Model/CreateAbsenceRequest.md docs/Model/CreateContractRequest.md docs/Model/CreateEmployeeRequest.md docs/Model/CreateFacilityContactRequest.md +docs/Model/CreateFacilityQualificationRateRequest.md docs/Model/CreateFacilityRequest.md docs/Model/CreateOrderRequest.md docs/Model/CreateRoleRequest.md +docs/Model/CreateTimeEntryRequest.md docs/Model/CreateUserRequest.md docs/Model/CreateValueListItemRequest.md +docs/Model/DocumentResponse.md docs/Model/EmployeeResponse.md docs/Model/EmployeeResponsePagedResponse.md docs/Model/FacilityContactResponse.md +docs/Model/FacilityQualificationRateResponse.md docs/Model/FacilityResponse.md docs/Model/FacilityResponsePagedResponse.md docs/Model/ForgotPasswordRequestRequest.md @@ -45,23 +58,42 @@ docs/Model/LoginRequest.md docs/Model/LoginResponse.md docs/Model/LogoutRequest.md docs/Model/MeResponse.md +docs/Model/ModuleType.md docs/Model/OrderResponse.md docs/Model/OrderResponsePagedResponse.md docs/Model/PasswordPolicyResponse.md docs/Model/PasswordResetTemplateResponse.md +docs/Model/PermissionAction.md docs/Model/PermissionDto.md +docs/Model/PermissionEffect.md +docs/Model/PermissionScope.md docs/Model/RefreshRequest.md docs/Model/ResetUserPasswordRequest.md docs/Model/RolePermissionsResponse.md docs/Model/RoleResponse.md docs/Model/SendTestEmailRequest.md docs/Model/SessionResponse.md +docs/Model/TimeEntryDecisionRequest.md +docs/Model/TimeEntryResponse.md +docs/Model/TimeEntryResponsePagedResponse.md +docs/Model/TrashAbsenceResponse.md +docs/Model/TrashContractResponse.md +docs/Model/TrashEmployeeResponse.md +docs/Model/TrashFacilityContactResponse.md +docs/Model/TrashFacilityQualificationRateResponse.md +docs/Model/TrashFacilityResponse.md +docs/Model/TrashOrderResponse.md +docs/Model/TrashTimeEntryResponse.md +docs/Model/UpdateAbsenceRequest.md docs/Model/UpdateContractRequest.md +docs/Model/UpdateDocumentRequest.md docs/Model/UpdateEmployeeRequest.md docs/Model/UpdateFacilityContactRequest.md +docs/Model/UpdateFacilityQualificationRateRequest.md docs/Model/UpdateFacilityRequest.md docs/Model/UpdateOrderRequest.md docs/Model/UpdateRolePermissionsRequest.md +docs/Model/UpdateTimeEntryRequest.md docs/Model/UpdateUserRequest.md docs/Model/UpdateValueListItemRequest.md docs/Model/UserPermissionOverrideResponse.md @@ -72,40 +104,54 @@ docs/Model/ValueListTransitionRequest.md docs/Model/ValueListTransitionResponse.md docs/Model/ValueListUsageResponse.md git_push.sh +lib/Api/AbsencesApi.php lib/Api/AdminEmailApi.php lib/Api/AdminSessionsApi.php lib/Api/AuditLogApi.php lib/Api/AuthApi.php lib/Api/ContractsApi.php +lib/Api/DocumentsApi.php lib/Api/EmployeesApi.php lib/Api/FacilitiesApi.php lib/Api/FacilityContactsApi.php +lib/Api/FacilityQualificationRatesApi.php lib/Api/HealthApi.php lib/Api/OrdersApi.php lib/Api/RolesApi.php +lib/Api/TimeEntriesApi.php +lib/Api/TrashApi.php lib/Api/UsersApi.php lib/Api/ValueListsApi.php lib/ApiException.php lib/Configuration.php lib/FormDataProcessor.php lib/HeaderSelector.php +lib/Model/AbsenceDecisionRequest.php +lib/Model/AbsenceResponse.php +lib/Model/AbsenceResponsePagedResponse.php lib/Model/AddUserPermissionOverrideRequest.php +lib/Model/AuditEventCategory.php lib/Model/AuditLogEntryResponse.php lib/Model/AuditLogEntryResponsePagedResponse.php lib/Model/ChangePasswordRequest.php lib/Model/ContractResponse.php lib/Model/ContractResponsePagedResponse.php +lib/Model/CreateAbsenceRequest.php lib/Model/CreateContractRequest.php lib/Model/CreateEmployeeRequest.php lib/Model/CreateFacilityContactRequest.php +lib/Model/CreateFacilityQualificationRateRequest.php lib/Model/CreateFacilityRequest.php lib/Model/CreateOrderRequest.php lib/Model/CreateRoleRequest.php +lib/Model/CreateTimeEntryRequest.php lib/Model/CreateUserRequest.php lib/Model/CreateValueListItemRequest.php +lib/Model/DocumentResponse.php lib/Model/EmployeeResponse.php lib/Model/EmployeeResponsePagedResponse.php lib/Model/FacilityContactResponse.php +lib/Model/FacilityQualificationRateResponse.php lib/Model/FacilityResponse.php lib/Model/FacilityResponsePagedResponse.php lib/Model/ForgotPasswordRequestRequest.php @@ -118,23 +164,42 @@ lib/Model/LoginResponse.php lib/Model/LogoutRequest.php lib/Model/MeResponse.php lib/Model/ModelInterface.php +lib/Model/ModuleType.php lib/Model/OrderResponse.php lib/Model/OrderResponsePagedResponse.php lib/Model/PasswordPolicyResponse.php lib/Model/PasswordResetTemplateResponse.php +lib/Model/PermissionAction.php lib/Model/PermissionDto.php +lib/Model/PermissionEffect.php +lib/Model/PermissionScope.php lib/Model/RefreshRequest.php lib/Model/ResetUserPasswordRequest.php lib/Model/RolePermissionsResponse.php lib/Model/RoleResponse.php lib/Model/SendTestEmailRequest.php lib/Model/SessionResponse.php +lib/Model/TimeEntryDecisionRequest.php +lib/Model/TimeEntryResponse.php +lib/Model/TimeEntryResponsePagedResponse.php +lib/Model/TrashAbsenceResponse.php +lib/Model/TrashContractResponse.php +lib/Model/TrashEmployeeResponse.php +lib/Model/TrashFacilityContactResponse.php +lib/Model/TrashFacilityQualificationRateResponse.php +lib/Model/TrashFacilityResponse.php +lib/Model/TrashOrderResponse.php +lib/Model/TrashTimeEntryResponse.php +lib/Model/UpdateAbsenceRequest.php lib/Model/UpdateContractRequest.php +lib/Model/UpdateDocumentRequest.php lib/Model/UpdateEmployeeRequest.php lib/Model/UpdateFacilityContactRequest.php +lib/Model/UpdateFacilityQualificationRateRequest.php lib/Model/UpdateFacilityRequest.php lib/Model/UpdateOrderRequest.php lib/Model/UpdateRolePermissionsRequest.php +lib/Model/UpdateTimeEntryRequest.php lib/Model/UpdateUserRequest.php lib/Model/UpdateValueListItemRequest.php lib/Model/UserPermissionOverrideResponse.php @@ -146,70 +211,3 @@ lib/Model/ValueListTransitionResponse.php lib/Model/ValueListUsageResponse.php lib/ObjectSerializer.php phpunit.xml.dist -test/Api/AdminEmailApiTest.php -test/Api/AdminSessionsApiTest.php -test/Api/AuditLogApiTest.php -test/Api/AuthApiTest.php -test/Api/ContractsApiTest.php -test/Api/EmployeesApiTest.php -test/Api/FacilitiesApiTest.php -test/Api/FacilityContactsApiTest.php -test/Api/HealthApiTest.php -test/Api/OrdersApiTest.php -test/Api/RolesApiTest.php -test/Api/UsersApiTest.php -test/Api/ValueListsApiTest.php -test/Model/AddUserPermissionOverrideRequestTest.php -test/Model/AuditLogEntryResponsePagedResponseTest.php -test/Model/AuditLogEntryResponseTest.php -test/Model/ChangePasswordRequestTest.php -test/Model/ContractResponsePagedResponseTest.php -test/Model/ContractResponseTest.php -test/Model/CreateContractRequestTest.php -test/Model/CreateEmployeeRequestTest.php -test/Model/CreateFacilityContactRequestTest.php -test/Model/CreateFacilityRequestTest.php -test/Model/CreateOrderRequestTest.php -test/Model/CreateRoleRequestTest.php -test/Model/CreateUserRequestTest.php -test/Model/CreateValueListItemRequestTest.php -test/Model/EmployeeResponsePagedResponseTest.php -test/Model/EmployeeResponseTest.php -test/Model/FacilityContactResponseTest.php -test/Model/FacilityResponsePagedResponseTest.php -test/Model/FacilityResponseTest.php -test/Model/ForgotPasswordRequestRequestTest.php -test/Model/ForgotPasswordRequestResponseTest.php -test/Model/ForgotPasswordResetRequestTest.php -test/Model/ForgotPasswordVerifyRequestTest.php -test/Model/ForgotPasswordVerifyResponseTest.php -test/Model/LoginRequestTest.php -test/Model/LoginResponseTest.php -test/Model/LogoutRequestTest.php -test/Model/MeResponseTest.php -test/Model/OrderResponsePagedResponseTest.php -test/Model/OrderResponseTest.php -test/Model/PasswordPolicyResponseTest.php -test/Model/PasswordResetTemplateResponseTest.php -test/Model/PermissionDtoTest.php -test/Model/RefreshRequestTest.php -test/Model/ResetUserPasswordRequestTest.php -test/Model/RolePermissionsResponseTest.php -test/Model/RoleResponseTest.php -test/Model/SendTestEmailRequestTest.php -test/Model/SessionResponseTest.php -test/Model/UpdateContractRequestTest.php -test/Model/UpdateEmployeeRequestTest.php -test/Model/UpdateFacilityContactRequestTest.php -test/Model/UpdateFacilityRequestTest.php -test/Model/UpdateOrderRequestTest.php -test/Model/UpdateRolePermissionsRequestTest.php -test/Model/UpdateUserRequestTest.php -test/Model/UpdateValueListItemRequestTest.php -test/Model/UserPermissionOverrideResponseTest.php -test/Model/UserResponseTest.php -test/Model/ValueListItemResponseTest.php -test/Model/ValueListResponseTest.php -test/Model/ValueListTransitionRequestTest.php -test/Model/ValueListTransitionResponseTest.php -test/Model/ValueListUsageResponseTest.php diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/README.md b/omsorgWeb/mitarbeiter-app/api-client-php/README.md index 35379f0..3bedb46 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/README.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/README.md @@ -52,18 +52,23 @@ require_once(__DIR__ . '/vendor/autoload.php'); $config = OmsorgCoreClient\Configuration::getDefaultConfiguration()->setAccessToken('YOUR_ACCESS_TOKEN'); -$apiInstance = new OmsorgCoreClient\Api\AdminEmailApi( +$apiInstance = new OmsorgCoreClient\Api\AbsencesApi( // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. // This is optional, `GuzzleHttp\Client` will be used as default. new GuzzleHttp\Client(), $config ); +$status = 'status_example'; // string +$type = 'type_example'; // string +$employee_id = 'employee_id_example'; // string +$page = 1; // int +$page_size = 20; // int try { - $result = $apiInstance->apiAdminEmailPasswordResetTemplateGet(); + $result = $apiInstance->apiAbsencesGet($status, $type, $employee_id, $page, $page_size); print_r($result); } catch (Exception $e) { - echo 'Exception when calling AdminEmailApi->apiAdminEmailPasswordResetTemplateGet: ', $e->getMessage(), PHP_EOL; + echo 'Exception when calling AbsencesApi->apiAbsencesGet: ', $e->getMessage(), PHP_EOL; } ``` @@ -74,6 +79,12 @@ All URIs are relative to *http://localhost* Class | Method | HTTP request | Description ------------ | ------------- | ------------- | ------------- +*AbsencesApi* | [**apiAbsencesGet**](docs/Api/AbsencesApi.md#apiabsencesget) | **GET** /api/absences | +*AbsencesApi* | [**apiAbsencesIdDecisionPost**](docs/Api/AbsencesApi.md#apiabsencesiddecisionpost) | **POST** /api/absences/{id}/decision | +*AbsencesApi* | [**apiAbsencesIdDelete**](docs/Api/AbsencesApi.md#apiabsencesiddelete) | **DELETE** /api/absences/{id} | +*AbsencesApi* | [**apiAbsencesIdGet**](docs/Api/AbsencesApi.md#apiabsencesidget) | **GET** /api/absences/{id} | +*AbsencesApi* | [**apiAbsencesIdPut**](docs/Api/AbsencesApi.md#apiabsencesidput) | **PUT** /api/absences/{id} | +*AbsencesApi* | [**apiAbsencesPost**](docs/Api/AbsencesApi.md#apiabsencespost) | **POST** /api/absences | *AdminEmailApi* | [**apiAdminEmailPasswordResetTemplateGet**](docs/Api/AdminEmailApi.md#apiadminemailpasswordresettemplateget) | **GET** /api/admin/email/password-reset-template | *AdminEmailApi* | [**apiAdminEmailTestSendPost**](docs/Api/AdminEmailApi.md#apiadminemailtestsendpost) | **POST** /api/admin/email/test-send | *AdminSessionsApi* | [**apiAdminSessionsGet**](docs/Api/AdminSessionsApi.md#apiadminsessionsget) | **GET** /api/admin/sessions | @@ -90,22 +101,36 @@ Class | Method | HTTP request | Description *AuthApi* | [**apiAuthPasswordPolicyGet**](docs/Api/AuthApi.md#apiauthpasswordpolicyget) | **GET** /api/auth/password-policy | *AuthApi* | [**apiAuthRefreshPost**](docs/Api/AuthApi.md#apiauthrefreshpost) | **POST** /api/auth/refresh | *ContractsApi* | [**apiContractsGet**](docs/Api/ContractsApi.md#apicontractsget) | **GET** /api/contracts | +*ContractsApi* | [**apiContractsIdDelete**](docs/Api/ContractsApi.md#apicontractsiddelete) | **DELETE** /api/contracts/{id} | *ContractsApi* | [**apiContractsIdGet**](docs/Api/ContractsApi.md#apicontractsidget) | **GET** /api/contracts/{id} | *ContractsApi* | [**apiContractsIdPut**](docs/Api/ContractsApi.md#apicontractsidput) | **PUT** /api/contracts/{id} | *ContractsApi* | [**apiContractsPost**](docs/Api/ContractsApi.md#apicontractspost) | **POST** /api/contracts | +*DocumentsApi* | [**apiDocumentsGet**](docs/Api/DocumentsApi.md#apidocumentsget) | **GET** /api/documents | +*DocumentsApi* | [**apiDocumentsIdDelete**](docs/Api/DocumentsApi.md#apidocumentsiddelete) | **DELETE** /api/documents/{id} | +*DocumentsApi* | [**apiDocumentsIdDownloadGet**](docs/Api/DocumentsApi.md#apidocumentsiddownloadget) | **GET** /api/documents/{id}/download | +*DocumentsApi* | [**apiDocumentsIdPut**](docs/Api/DocumentsApi.md#apidocumentsidput) | **PUT** /api/documents/{id} | +*DocumentsApi* | [**apiDocumentsPost**](docs/Api/DocumentsApi.md#apidocumentspost) | **POST** /api/documents | *EmployeesApi* | [**apiEmployeesGet**](docs/Api/EmployeesApi.md#apiemployeesget) | **GET** /api/employees | +*EmployeesApi* | [**apiEmployeesIdDelete**](docs/Api/EmployeesApi.md#apiemployeesiddelete) | **DELETE** /api/employees/{id} | *EmployeesApi* | [**apiEmployeesIdGet**](docs/Api/EmployeesApi.md#apiemployeesidget) | **GET** /api/employees/{id} | *EmployeesApi* | [**apiEmployeesIdPut**](docs/Api/EmployeesApi.md#apiemployeesidput) | **PUT** /api/employees/{id} | *EmployeesApi* | [**apiEmployeesPost**](docs/Api/EmployeesApi.md#apiemployeespost) | **POST** /api/employees | *FacilitiesApi* | [**apiFacilitiesGet**](docs/Api/FacilitiesApi.md#apifacilitiesget) | **GET** /api/facilities | +*FacilitiesApi* | [**apiFacilitiesIdDelete**](docs/Api/FacilitiesApi.md#apifacilitiesiddelete) | **DELETE** /api/facilities/{id} | *FacilitiesApi* | [**apiFacilitiesIdGet**](docs/Api/FacilitiesApi.md#apifacilitiesidget) | **GET** /api/facilities/{id} | *FacilitiesApi* | [**apiFacilitiesIdPut**](docs/Api/FacilitiesApi.md#apifacilitiesidput) | **PUT** /api/facilities/{id} | *FacilitiesApi* | [**apiFacilitiesPost**](docs/Api/FacilitiesApi.md#apifacilitiespost) | **POST** /api/facilities | *FacilityContactsApi* | [**apiFacilitiesFacilityIdContactsGet**](docs/Api/FacilityContactsApi.md#apifacilitiesfacilityidcontactsget) | **GET** /api/facilities/{facilityId}/contacts | +*FacilityContactsApi* | [**apiFacilitiesFacilityIdContactsIdDelete**](docs/Api/FacilityContactsApi.md#apifacilitiesfacilityidcontactsiddelete) | **DELETE** /api/facilities/{facilityId}/contacts/{id} | *FacilityContactsApi* | [**apiFacilitiesFacilityIdContactsIdPut**](docs/Api/FacilityContactsApi.md#apifacilitiesfacilityidcontactsidput) | **PUT** /api/facilities/{facilityId}/contacts/{id} | *FacilityContactsApi* | [**apiFacilitiesFacilityIdContactsPost**](docs/Api/FacilityContactsApi.md#apifacilitiesfacilityidcontactspost) | **POST** /api/facilities/{facilityId}/contacts | +*FacilityQualificationRatesApi* | [**apiFacilitiesFacilityIdQualificationRatesGet**](docs/Api/FacilityQualificationRatesApi.md#apifacilitiesfacilityidqualificationratesget) | **GET** /api/facilities/{facilityId}/qualification-rates | +*FacilityQualificationRatesApi* | [**apiFacilitiesFacilityIdQualificationRatesIdDelete**](docs/Api/FacilityQualificationRatesApi.md#apifacilitiesfacilityidqualificationratesiddelete) | **DELETE** /api/facilities/{facilityId}/qualification-rates/{id} | +*FacilityQualificationRatesApi* | [**apiFacilitiesFacilityIdQualificationRatesIdPut**](docs/Api/FacilityQualificationRatesApi.md#apifacilitiesfacilityidqualificationratesidput) | **PUT** /api/facilities/{facilityId}/qualification-rates/{id} | +*FacilityQualificationRatesApi* | [**apiFacilitiesFacilityIdQualificationRatesPost**](docs/Api/FacilityQualificationRatesApi.md#apifacilitiesfacilityidqualificationratespost) | **POST** /api/facilities/{facilityId}/qualification-rates | *HealthApi* | [**apiHealthGet**](docs/Api/HealthApi.md#apihealthget) | **GET** /api/health | *OrdersApi* | [**apiOrdersGet**](docs/Api/OrdersApi.md#apiordersget) | **GET** /api/orders | +*OrdersApi* | [**apiOrdersIdDelete**](docs/Api/OrdersApi.md#apiordersiddelete) | **DELETE** /api/orders/{id} | *OrdersApi* | [**apiOrdersIdGet**](docs/Api/OrdersApi.md#apiordersidget) | **GET** /api/orders/{id} | *OrdersApi* | [**apiOrdersIdPut**](docs/Api/OrdersApi.md#apiordersidput) | **PUT** /api/orders/{id} | *OrdersApi* | [**apiOrdersPost**](docs/Api/OrdersApi.md#apiorderspost) | **POST** /api/orders | @@ -113,6 +138,29 @@ Class | Method | HTTP request | Description *RolesApi* | [**apiRolesIdGet**](docs/Api/RolesApi.md#apirolesidget) | **GET** /api/roles/{id} | *RolesApi* | [**apiRolesIdPermissionsPut**](docs/Api/RolesApi.md#apirolesidpermissionsput) | **PUT** /api/roles/{id}/permissions | *RolesApi* | [**apiRolesPost**](docs/Api/RolesApi.md#apirolespost) | **POST** /api/roles | +*TimeEntriesApi* | [**apiTimeEntriesGet**](docs/Api/TimeEntriesApi.md#apitimeentriesget) | **GET** /api/time-entries | +*TimeEntriesApi* | [**apiTimeEntriesIdDecisionPost**](docs/Api/TimeEntriesApi.md#apitimeentriesiddecisionpost) | **POST** /api/time-entries/{id}/decision | +*TimeEntriesApi* | [**apiTimeEntriesIdDelete**](docs/Api/TimeEntriesApi.md#apitimeentriesiddelete) | **DELETE** /api/time-entries/{id} | +*TimeEntriesApi* | [**apiTimeEntriesIdGet**](docs/Api/TimeEntriesApi.md#apitimeentriesidget) | **GET** /api/time-entries/{id} | +*TimeEntriesApi* | [**apiTimeEntriesIdPut**](docs/Api/TimeEntriesApi.md#apitimeentriesidput) | **PUT** /api/time-entries/{id} | +*TimeEntriesApi* | [**apiTimeEntriesIdSubmitPost**](docs/Api/TimeEntriesApi.md#apitimeentriesidsubmitpost) | **POST** /api/time-entries/{id}/submit | +*TimeEntriesApi* | [**apiTimeEntriesPost**](docs/Api/TimeEntriesApi.md#apitimeentriespost) | **POST** /api/time-entries | +*TrashApi* | [**apiTrashAbsencesGet**](docs/Api/TrashApi.md#apitrashabsencesget) | **GET** /api/trash/absences | +*TrashApi* | [**apiTrashAbsencesIdRestorePost**](docs/Api/TrashApi.md#apitrashabsencesidrestorepost) | **POST** /api/trash/absences/{id}/restore | +*TrashApi* | [**apiTrashContractsGet**](docs/Api/TrashApi.md#apitrashcontractsget) | **GET** /api/trash/contracts | +*TrashApi* | [**apiTrashContractsIdRestorePost**](docs/Api/TrashApi.md#apitrashcontractsidrestorepost) | **POST** /api/trash/contracts/{id}/restore | +*TrashApi* | [**apiTrashEmployeesGet**](docs/Api/TrashApi.md#apitrashemployeesget) | **GET** /api/trash/employees | +*TrashApi* | [**apiTrashEmployeesIdRestorePost**](docs/Api/TrashApi.md#apitrashemployeesidrestorepost) | **POST** /api/trash/employees/{id}/restore | +*TrashApi* | [**apiTrashFacilitiesGet**](docs/Api/TrashApi.md#apitrashfacilitiesget) | **GET** /api/trash/facilities | +*TrashApi* | [**apiTrashFacilitiesIdRestorePost**](docs/Api/TrashApi.md#apitrashfacilitiesidrestorepost) | **POST** /api/trash/facilities/{id}/restore | +*TrashApi* | [**apiTrashFacilityContactsGet**](docs/Api/TrashApi.md#apitrashfacilitycontactsget) | **GET** /api/trash/facility-contacts | +*TrashApi* | [**apiTrashFacilityContactsIdRestorePost**](docs/Api/TrashApi.md#apitrashfacilitycontactsidrestorepost) | **POST** /api/trash/facility-contacts/{id}/restore | +*TrashApi* | [**apiTrashFacilityQualificationRatesGet**](docs/Api/TrashApi.md#apitrashfacilityqualificationratesget) | **GET** /api/trash/facility-qualification-rates | +*TrashApi* | [**apiTrashFacilityQualificationRatesIdRestorePost**](docs/Api/TrashApi.md#apitrashfacilityqualificationratesidrestorepost) | **POST** /api/trash/facility-qualification-rates/{id}/restore | +*TrashApi* | [**apiTrashOrdersGet**](docs/Api/TrashApi.md#apitrashordersget) | **GET** /api/trash/orders | +*TrashApi* | [**apiTrashOrdersIdRestorePost**](docs/Api/TrashApi.md#apitrashordersidrestorepost) | **POST** /api/trash/orders/{id}/restore | +*TrashApi* | [**apiTrashTimeEntriesGet**](docs/Api/TrashApi.md#apitrashtimeentriesget) | **GET** /api/trash/time-entries | +*TrashApi* | [**apiTrashTimeEntriesIdRestorePost**](docs/Api/TrashApi.md#apitrashtimeentriesidrestorepost) | **POST** /api/trash/time-entries/{id}/restore | *UsersApi* | [**apiUsersGet**](docs/Api/UsersApi.md#apiusersget) | **GET** /api/users | *UsersApi* | [**apiUsersIdPermissionOverridesGet**](docs/Api/UsersApi.md#apiusersidpermissionoverridesget) | **GET** /api/users/{id}/permission-overrides | *UsersApi* | [**apiUsersIdPermissionOverridesOverrideIdDelete**](docs/Api/UsersApi.md#apiusersidpermissionoverridesoverrideiddelete) | **DELETE** /api/users/{id}/permission-overrides/{overrideId} | @@ -131,23 +179,32 @@ Class | Method | HTTP request | Description ## Models +- [AbsenceDecisionRequest](docs/Model/AbsenceDecisionRequest.md) +- [AbsenceResponse](docs/Model/AbsenceResponse.md) +- [AbsenceResponsePagedResponse](docs/Model/AbsenceResponsePagedResponse.md) - [AddUserPermissionOverrideRequest](docs/Model/AddUserPermissionOverrideRequest.md) +- [AuditEventCategory](docs/Model/AuditEventCategory.md) - [AuditLogEntryResponse](docs/Model/AuditLogEntryResponse.md) - [AuditLogEntryResponsePagedResponse](docs/Model/AuditLogEntryResponsePagedResponse.md) - [ChangePasswordRequest](docs/Model/ChangePasswordRequest.md) - [ContractResponse](docs/Model/ContractResponse.md) - [ContractResponsePagedResponse](docs/Model/ContractResponsePagedResponse.md) +- [CreateAbsenceRequest](docs/Model/CreateAbsenceRequest.md) - [CreateContractRequest](docs/Model/CreateContractRequest.md) - [CreateEmployeeRequest](docs/Model/CreateEmployeeRequest.md) - [CreateFacilityContactRequest](docs/Model/CreateFacilityContactRequest.md) +- [CreateFacilityQualificationRateRequest](docs/Model/CreateFacilityQualificationRateRequest.md) - [CreateFacilityRequest](docs/Model/CreateFacilityRequest.md) - [CreateOrderRequest](docs/Model/CreateOrderRequest.md) - [CreateRoleRequest](docs/Model/CreateRoleRequest.md) +- [CreateTimeEntryRequest](docs/Model/CreateTimeEntryRequest.md) - [CreateUserRequest](docs/Model/CreateUserRequest.md) - [CreateValueListItemRequest](docs/Model/CreateValueListItemRequest.md) +- [DocumentResponse](docs/Model/DocumentResponse.md) - [EmployeeResponse](docs/Model/EmployeeResponse.md) - [EmployeeResponsePagedResponse](docs/Model/EmployeeResponsePagedResponse.md) - [FacilityContactResponse](docs/Model/FacilityContactResponse.md) +- [FacilityQualificationRateResponse](docs/Model/FacilityQualificationRateResponse.md) - [FacilityResponse](docs/Model/FacilityResponse.md) - [FacilityResponsePagedResponse](docs/Model/FacilityResponsePagedResponse.md) - [ForgotPasswordRequestRequest](docs/Model/ForgotPasswordRequestRequest.md) @@ -159,23 +216,42 @@ Class | Method | HTTP request | Description - [LoginResponse](docs/Model/LoginResponse.md) - [LogoutRequest](docs/Model/LogoutRequest.md) - [MeResponse](docs/Model/MeResponse.md) +- [ModuleType](docs/Model/ModuleType.md) - [OrderResponse](docs/Model/OrderResponse.md) - [OrderResponsePagedResponse](docs/Model/OrderResponsePagedResponse.md) - [PasswordPolicyResponse](docs/Model/PasswordPolicyResponse.md) - [PasswordResetTemplateResponse](docs/Model/PasswordResetTemplateResponse.md) +- [PermissionAction](docs/Model/PermissionAction.md) - [PermissionDto](docs/Model/PermissionDto.md) +- [PermissionEffect](docs/Model/PermissionEffect.md) +- [PermissionScope](docs/Model/PermissionScope.md) - [RefreshRequest](docs/Model/RefreshRequest.md) - [ResetUserPasswordRequest](docs/Model/ResetUserPasswordRequest.md) - [RolePermissionsResponse](docs/Model/RolePermissionsResponse.md) - [RoleResponse](docs/Model/RoleResponse.md) - [SendTestEmailRequest](docs/Model/SendTestEmailRequest.md) - [SessionResponse](docs/Model/SessionResponse.md) +- [TimeEntryDecisionRequest](docs/Model/TimeEntryDecisionRequest.md) +- [TimeEntryResponse](docs/Model/TimeEntryResponse.md) +- [TimeEntryResponsePagedResponse](docs/Model/TimeEntryResponsePagedResponse.md) +- [TrashAbsenceResponse](docs/Model/TrashAbsenceResponse.md) +- [TrashContractResponse](docs/Model/TrashContractResponse.md) +- [TrashEmployeeResponse](docs/Model/TrashEmployeeResponse.md) +- [TrashFacilityContactResponse](docs/Model/TrashFacilityContactResponse.md) +- [TrashFacilityQualificationRateResponse](docs/Model/TrashFacilityQualificationRateResponse.md) +- [TrashFacilityResponse](docs/Model/TrashFacilityResponse.md) +- [TrashOrderResponse](docs/Model/TrashOrderResponse.md) +- [TrashTimeEntryResponse](docs/Model/TrashTimeEntryResponse.md) +- [UpdateAbsenceRequest](docs/Model/UpdateAbsenceRequest.md) - [UpdateContractRequest](docs/Model/UpdateContractRequest.md) +- [UpdateDocumentRequest](docs/Model/UpdateDocumentRequest.md) - [UpdateEmployeeRequest](docs/Model/UpdateEmployeeRequest.md) - [UpdateFacilityContactRequest](docs/Model/UpdateFacilityContactRequest.md) +- [UpdateFacilityQualificationRateRequest](docs/Model/UpdateFacilityQualificationRateRequest.md) - [UpdateFacilityRequest](docs/Model/UpdateFacilityRequest.md) - [UpdateOrderRequest](docs/Model/UpdateOrderRequest.md) - [UpdateRolePermissionsRequest](docs/Model/UpdateRolePermissionsRequest.md) +- [UpdateTimeEntryRequest](docs/Model/UpdateTimeEntryRequest.md) - [UpdateUserRequest](docs/Model/UpdateUserRequest.md) - [UpdateValueListItemRequest](docs/Model/UpdateValueListItemRequest.md) - [UserPermissionOverrideResponse](docs/Model/UserPermissionOverrideResponse.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/AbsencesApi.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/AbsencesApi.md new file mode 100644 index 0000000..d9ffeb1 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/AbsencesApi.md @@ -0,0 +1,372 @@ +# OmsorgCoreClient\AbsencesApi + +All URIs are relative to http://localhost, except if the operation defines another base path. + +| Method | HTTP request | Description | +| ------------- | ------------- | ------------- | +| [**apiAbsencesGet()**](AbsencesApi.md#apiAbsencesGet) | **GET** /api/absences | | +| [**apiAbsencesIdDecisionPost()**](AbsencesApi.md#apiAbsencesIdDecisionPost) | **POST** /api/absences/{id}/decision | | +| [**apiAbsencesIdDelete()**](AbsencesApi.md#apiAbsencesIdDelete) | **DELETE** /api/absences/{id} | | +| [**apiAbsencesIdGet()**](AbsencesApi.md#apiAbsencesIdGet) | **GET** /api/absences/{id} | | +| [**apiAbsencesIdPut()**](AbsencesApi.md#apiAbsencesIdPut) | **PUT** /api/absences/{id} | | +| [**apiAbsencesPost()**](AbsencesApi.md#apiAbsencesPost) | **POST** /api/absences | | + + +## `apiAbsencesGet()` + +```php +apiAbsencesGet($status, $type, $employee_id, $page, $page_size): \OmsorgCoreClient\Model\AbsenceResponsePagedResponse +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\AbsencesApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$status = 'status_example'; // string +$type = 'type_example'; // string +$employee_id = 'employee_id_example'; // string +$page = 1; // int +$page_size = 20; // int + +try { + $result = $apiInstance->apiAbsencesGet($status, $type, $employee_id, $page, $page_size); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling AbsencesApi->apiAbsencesGet: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **status** | **string**| | [optional] | +| **type** | **string**| | [optional] | +| **employee_id** | **string**| | [optional] | +| **page** | **int**| | [optional] [default to 1] | +| **page_size** | **int**| | [optional] [default to 20] | + +### Return type + +[**\OmsorgCoreClient\Model\AbsenceResponsePagedResponse**](../Model/AbsenceResponsePagedResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiAbsencesIdDecisionPost()` + +```php +apiAbsencesIdDecisionPost($id, $absence_decision_request): \OmsorgCoreClient\Model\AbsenceResponse +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\AbsencesApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string +$absence_decision_request = new \OmsorgCoreClient\Model\AbsenceDecisionRequest(); // \OmsorgCoreClient\Model\AbsenceDecisionRequest + +try { + $result = $apiInstance->apiAbsencesIdDecisionPost($id, $absence_decision_request); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling AbsencesApi->apiAbsencesIdDecisionPost: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | +| **absence_decision_request** | [**\OmsorgCoreClient\Model\AbsenceDecisionRequest**](../Model/AbsenceDecisionRequest.md)| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\AbsenceResponse**](../Model/AbsenceResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: `application/json`, `text/json`, `application/*+json` +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiAbsencesIdDelete()` + +```php +apiAbsencesIdDelete($id) +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\AbsencesApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string + +try { + $apiInstance->apiAbsencesIdDelete($id); +} catch (Exception $e) { + echo 'Exception when calling AbsencesApi->apiAbsencesIdDelete: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | + +### Return type + +void (empty response body) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiAbsencesIdGet()` + +```php +apiAbsencesIdGet($id): \OmsorgCoreClient\Model\AbsenceResponse +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\AbsencesApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string + +try { + $result = $apiInstance->apiAbsencesIdGet($id); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling AbsencesApi->apiAbsencesIdGet: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | + +### Return type + +[**\OmsorgCoreClient\Model\AbsenceResponse**](../Model/AbsenceResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiAbsencesIdPut()` + +```php +apiAbsencesIdPut($id, $update_absence_request): \OmsorgCoreClient\Model\AbsenceResponse +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\AbsencesApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string +$update_absence_request = new \OmsorgCoreClient\Model\UpdateAbsenceRequest(); // \OmsorgCoreClient\Model\UpdateAbsenceRequest + +try { + $result = $apiInstance->apiAbsencesIdPut($id, $update_absence_request); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling AbsencesApi->apiAbsencesIdPut: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | +| **update_absence_request** | [**\OmsorgCoreClient\Model\UpdateAbsenceRequest**](../Model/UpdateAbsenceRequest.md)| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\AbsenceResponse**](../Model/AbsenceResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: `application/json`, `text/json`, `application/*+json` +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiAbsencesPost()` + +```php +apiAbsencesPost($create_absence_request): \OmsorgCoreClient\Model\AbsenceResponse +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\AbsencesApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$create_absence_request = new \OmsorgCoreClient\Model\CreateAbsenceRequest(); // \OmsorgCoreClient\Model\CreateAbsenceRequest + +try { + $result = $apiInstance->apiAbsencesPost($create_absence_request); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling AbsencesApi->apiAbsencesPost: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **create_absence_request** | [**\OmsorgCoreClient\Model\CreateAbsenceRequest**](../Model/CreateAbsenceRequest.md)| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\AbsenceResponse**](../Model/AbsenceResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: `application/json`, `text/json`, `application/*+json` +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/AuditLogApi.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/AuditLogApi.md index e577a44..4bb2ba6 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/AuditLogApi.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/AuditLogApi.md @@ -10,7 +10,7 @@ All URIs are relative to http://localhost, except if the operation defines anoth ## `apiAuditLogGet()` ```php -apiAuditLogGet($entity_type, $entity_id, $actor_user_id, $from_utc, $to_utc, $page, $page_size): \OmsorgCoreClient\Model\AuditLogEntryResponsePagedResponse +apiAuditLogGet($entity_type, $entity_id, $actor_user_id, $category, $from_utc, $to_utc, $page, $page_size): \OmsorgCoreClient\Model\AuditLogEntryResponsePagedResponse ``` @@ -35,13 +35,14 @@ $apiInstance = new OmsorgCoreClient\Api\AuditLogApi( $entity_type = 'entity_type_example'; // string $entity_id = 'entity_id_example'; // string $actor_user_id = 'actor_user_id_example'; // string +$category = new \OmsorgCoreClient\Model\\OmsorgCoreClient\Model\AuditEventCategory(); // \OmsorgCoreClient\Model\AuditEventCategory $from_utc = new \DateTime('2013-10-20T19:20:30+01:00'); // \DateTime $to_utc = new \DateTime('2013-10-20T19:20:30+01:00'); // \DateTime $page = 1; // int $page_size = 50; // int try { - $result = $apiInstance->apiAuditLogGet($entity_type, $entity_id, $actor_user_id, $from_utc, $to_utc, $page, $page_size); + $result = $apiInstance->apiAuditLogGet($entity_type, $entity_id, $actor_user_id, $category, $from_utc, $to_utc, $page, $page_size); print_r($result); } catch (Exception $e) { echo 'Exception when calling AuditLogApi->apiAuditLogGet: ', $e->getMessage(), PHP_EOL; @@ -55,6 +56,7 @@ try { | **entity_type** | **string**| | [optional] | | **entity_id** | **string**| | [optional] | | **actor_user_id** | **string**| | [optional] | +| **category** | [**\OmsorgCoreClient\Model\AuditEventCategory**](../Model/.md)| | [optional] | | **from_utc** | **\DateTime**| | [optional] | | **to_utc** | **\DateTime**| | [optional] | | **page** | **int**| | [optional] [default to 1] | diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/ContractsApi.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/ContractsApi.md index 83e7574..4249852 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/ContractsApi.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/ContractsApi.md @@ -5,6 +5,7 @@ All URIs are relative to http://localhost, except if the operation defines anoth | Method | HTTP request | Description | | ------------- | ------------- | ------------- | | [**apiContractsGet()**](ContractsApi.md#apiContractsGet) | **GET** /api/contracts | | +| [**apiContractsIdDelete()**](ContractsApi.md#apiContractsIdDelete) | **DELETE** /api/contracts/{id} | | | [**apiContractsIdGet()**](ContractsApi.md#apiContractsIdGet) | **GET** /api/contracts/{id} | | | [**apiContractsIdPut()**](ContractsApi.md#apiContractsIdPut) | **PUT** /api/contracts/{id} | | | [**apiContractsPost()**](ContractsApi.md#apiContractsPost) | **POST** /api/contracts | | @@ -78,6 +79,63 @@ try { [[Back to Model list]](../../README.md#models) [[Back to README]](../../README.md) +## `apiContractsIdDelete()` + +```php +apiContractsIdDelete($id) +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\ContractsApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string + +try { + $apiInstance->apiContractsIdDelete($id); +} catch (Exception $e) { + echo 'Exception when calling ContractsApi->apiContractsIdDelete: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | + +### Return type + +void (empty response body) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + ## `apiContractsIdGet()` ```php diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/DocumentsApi.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/DocumentsApi.md new file mode 100644 index 0000000..26f2132 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/DocumentsApi.md @@ -0,0 +1,312 @@ +# OmsorgCoreClient\DocumentsApi + +All URIs are relative to http://localhost, except if the operation defines another base path. + +| Method | HTTP request | Description | +| ------------- | ------------- | ------------- | +| [**apiDocumentsGet()**](DocumentsApi.md#apiDocumentsGet) | **GET** /api/documents | | +| [**apiDocumentsIdDelete()**](DocumentsApi.md#apiDocumentsIdDelete) | **DELETE** /api/documents/{id} | | +| [**apiDocumentsIdDownloadGet()**](DocumentsApi.md#apiDocumentsIdDownloadGet) | **GET** /api/documents/{id}/download | | +| [**apiDocumentsIdPut()**](DocumentsApi.md#apiDocumentsIdPut) | **PUT** /api/documents/{id} | | +| [**apiDocumentsPost()**](DocumentsApi.md#apiDocumentsPost) | **POST** /api/documents | | + + +## `apiDocumentsGet()` + +```php +apiDocumentsGet($entity_type, $entity_id): \OmsorgCoreClient\Model\DocumentResponse[] +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\DocumentsApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$entity_type = 'entity_type_example'; // string +$entity_id = 'entity_id_example'; // string + +try { + $result = $apiInstance->apiDocumentsGet($entity_type, $entity_id); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling DocumentsApi->apiDocumentsGet: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **entity_type** | **string**| | [optional] | +| **entity_id** | **string**| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\DocumentResponse[]**](../Model/DocumentResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiDocumentsIdDelete()` + +```php +apiDocumentsIdDelete($id) +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\DocumentsApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string + +try { + $apiInstance->apiDocumentsIdDelete($id); +} catch (Exception $e) { + echo 'Exception when calling DocumentsApi->apiDocumentsIdDelete: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | + +### Return type + +void (empty response body) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiDocumentsIdDownloadGet()` + +```php +apiDocumentsIdDownloadGet($id) +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\DocumentsApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string + +try { + $apiInstance->apiDocumentsIdDownloadGet($id); +} catch (Exception $e) { + echo 'Exception when calling DocumentsApi->apiDocumentsIdDownloadGet: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | + +### Return type + +void (empty response body) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiDocumentsIdPut()` + +```php +apiDocumentsIdPut($id, $update_document_request): \OmsorgCoreClient\Model\DocumentResponse +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\DocumentsApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string +$update_document_request = new \OmsorgCoreClient\Model\UpdateDocumentRequest(); // \OmsorgCoreClient\Model\UpdateDocumentRequest + +try { + $result = $apiInstance->apiDocumentsIdPut($id, $update_document_request); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling DocumentsApi->apiDocumentsIdPut: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | +| **update_document_request** | [**\OmsorgCoreClient\Model\UpdateDocumentRequest**](../Model/UpdateDocumentRequest.md)| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\DocumentResponse**](../Model/DocumentResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: `application/json`, `text/json`, `application/*+json` +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiDocumentsPost()` + +```php +apiDocumentsPost($entity_type, $entity_id, $category, $description, $file): \OmsorgCoreClient\Model\DocumentResponse +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\DocumentsApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$entity_type = 'entity_type_example'; // string +$entity_id = 'entity_id_example'; // string +$category = 'category_example'; // string +$description = 'description_example'; // string +$file = '/path/to/file.txt'; // \SplFileObject + +try { + $result = $apiInstance->apiDocumentsPost($entity_type, $entity_id, $category, $description, $file); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling DocumentsApi->apiDocumentsPost: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **entity_type** | **string**| | [optional] | +| **entity_id** | **string**| | [optional] | +| **category** | **string**| | [optional] | +| **description** | **string**| | [optional] | +| **file** | **\SplFileObject****\SplFileObject**| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\DocumentResponse**](../Model/DocumentResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: `multipart/form-data` +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/EmployeesApi.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/EmployeesApi.md index 5496911..6980cb1 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/EmployeesApi.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/EmployeesApi.md @@ -5,6 +5,7 @@ All URIs are relative to http://localhost, except if the operation defines anoth | Method | HTTP request | Description | | ------------- | ------------- | ------------- | | [**apiEmployeesGet()**](EmployeesApi.md#apiEmployeesGet) | **GET** /api/employees | | +| [**apiEmployeesIdDelete()**](EmployeesApi.md#apiEmployeesIdDelete) | **DELETE** /api/employees/{id} | | | [**apiEmployeesIdGet()**](EmployeesApi.md#apiEmployeesIdGet) | **GET** /api/employees/{id} | | | [**apiEmployeesIdPut()**](EmployeesApi.md#apiEmployeesIdPut) | **PUT** /api/employees/{id} | | | [**apiEmployeesPost()**](EmployeesApi.md#apiEmployeesPost) | **POST** /api/employees | | @@ -76,6 +77,63 @@ try { [[Back to Model list]](../../README.md#models) [[Back to README]](../../README.md) +## `apiEmployeesIdDelete()` + +```php +apiEmployeesIdDelete($id) +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\EmployeesApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string + +try { + $apiInstance->apiEmployeesIdDelete($id); +} catch (Exception $e) { + echo 'Exception when calling EmployeesApi->apiEmployeesIdDelete: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | + +### Return type + +void (empty response body) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + ## `apiEmployeesIdGet()` ```php diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/FacilitiesApi.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/FacilitiesApi.md index b1e89b5..9364134 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/FacilitiesApi.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/FacilitiesApi.md @@ -5,6 +5,7 @@ All URIs are relative to http://localhost, except if the operation defines anoth | Method | HTTP request | Description | | ------------- | ------------- | ------------- | | [**apiFacilitiesGet()**](FacilitiesApi.md#apiFacilitiesGet) | **GET** /api/facilities | | +| [**apiFacilitiesIdDelete()**](FacilitiesApi.md#apiFacilitiesIdDelete) | **DELETE** /api/facilities/{id} | | | [**apiFacilitiesIdGet()**](FacilitiesApi.md#apiFacilitiesIdGet) | **GET** /api/facilities/{id} | | | [**apiFacilitiesIdPut()**](FacilitiesApi.md#apiFacilitiesIdPut) | **PUT** /api/facilities/{id} | | | [**apiFacilitiesPost()**](FacilitiesApi.md#apiFacilitiesPost) | **POST** /api/facilities | | @@ -13,7 +14,7 @@ All URIs are relative to http://localhost, except if the operation defines anoth ## `apiFacilitiesGet()` ```php -apiFacilitiesGet($search, $crm_status, $page, $page_size): \OmsorgCoreClient\Model\FacilityResponsePagedResponse +apiFacilitiesGet($search, $crm_status, $follow_up_due_only, $page, $page_size): \OmsorgCoreClient\Model\FacilityResponsePagedResponse ``` @@ -37,11 +38,12 @@ $apiInstance = new OmsorgCoreClient\Api\FacilitiesApi( ); $search = 'search_example'; // string $crm_status = 'crm_status_example'; // string +$follow_up_due_only = false; // bool $page = 1; // int $page_size = 20; // int try { - $result = $apiInstance->apiFacilitiesGet($search, $crm_status, $page, $page_size); + $result = $apiInstance->apiFacilitiesGet($search, $crm_status, $follow_up_due_only, $page, $page_size); print_r($result); } catch (Exception $e) { echo 'Exception when calling FacilitiesApi->apiFacilitiesGet: ', $e->getMessage(), PHP_EOL; @@ -54,6 +56,7 @@ try { | ------------- | ------------- | ------------- | ------------- | | **search** | **string**| | [optional] | | **crm_status** | **string**| | [optional] | +| **follow_up_due_only** | **bool**| | [optional] [default to false] | | **page** | **int**| | [optional] [default to 1] | | **page_size** | **int**| | [optional] [default to 20] | @@ -74,6 +77,63 @@ try { [[Back to Model list]](../../README.md#models) [[Back to README]](../../README.md) +## `apiFacilitiesIdDelete()` + +```php +apiFacilitiesIdDelete($id) +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\FacilitiesApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string + +try { + $apiInstance->apiFacilitiesIdDelete($id); +} catch (Exception $e) { + echo 'Exception when calling FacilitiesApi->apiFacilitiesIdDelete: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | + +### Return type + +void (empty response body) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + ## `apiFacilitiesIdGet()` ```php diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/FacilityContactsApi.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/FacilityContactsApi.md index 82e5afa..4120903 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/FacilityContactsApi.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/FacilityContactsApi.md @@ -5,6 +5,7 @@ All URIs are relative to http://localhost, except if the operation defines anoth | Method | HTTP request | Description | | ------------- | ------------- | ------------- | | [**apiFacilitiesFacilityIdContactsGet()**](FacilityContactsApi.md#apiFacilitiesFacilityIdContactsGet) | **GET** /api/facilities/{facilityId}/contacts | | +| [**apiFacilitiesFacilityIdContactsIdDelete()**](FacilityContactsApi.md#apiFacilitiesFacilityIdContactsIdDelete) | **DELETE** /api/facilities/{facilityId}/contacts/{id} | | | [**apiFacilitiesFacilityIdContactsIdPut()**](FacilityContactsApi.md#apiFacilitiesFacilityIdContactsIdPut) | **PUT** /api/facilities/{facilityId}/contacts/{id} | | | [**apiFacilitiesFacilityIdContactsPost()**](FacilityContactsApi.md#apiFacilitiesFacilityIdContactsPost) | **POST** /api/facilities/{facilityId}/contacts | | @@ -67,6 +68,65 @@ try { [[Back to Model list]](../../README.md#models) [[Back to README]](../../README.md) +## `apiFacilitiesFacilityIdContactsIdDelete()` + +```php +apiFacilitiesFacilityIdContactsIdDelete($facility_id, $id) +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\FacilityContactsApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$facility_id = 'facility_id_example'; // string +$id = 'id_example'; // string + +try { + $apiInstance->apiFacilitiesFacilityIdContactsIdDelete($facility_id, $id); +} catch (Exception $e) { + echo 'Exception when calling FacilityContactsApi->apiFacilitiesFacilityIdContactsIdDelete: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **facility_id** | **string**| | | +| **id** | **string**| | | + +### Return type + +void (empty response body) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + ## `apiFacilitiesFacilityIdContactsIdPut()` ```php diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/FacilityQualificationRatesApi.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/FacilityQualificationRatesApi.md new file mode 100644 index 0000000..1b75129 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/FacilityQualificationRatesApi.md @@ -0,0 +1,250 @@ +# OmsorgCoreClient\FacilityQualificationRatesApi + +All URIs are relative to http://localhost, except if the operation defines another base path. + +| Method | HTTP request | Description | +| ------------- | ------------- | ------------- | +| [**apiFacilitiesFacilityIdQualificationRatesGet()**](FacilityQualificationRatesApi.md#apiFacilitiesFacilityIdQualificationRatesGet) | **GET** /api/facilities/{facilityId}/qualification-rates | | +| [**apiFacilitiesFacilityIdQualificationRatesIdDelete()**](FacilityQualificationRatesApi.md#apiFacilitiesFacilityIdQualificationRatesIdDelete) | **DELETE** /api/facilities/{facilityId}/qualification-rates/{id} | | +| [**apiFacilitiesFacilityIdQualificationRatesIdPut()**](FacilityQualificationRatesApi.md#apiFacilitiesFacilityIdQualificationRatesIdPut) | **PUT** /api/facilities/{facilityId}/qualification-rates/{id} | | +| [**apiFacilitiesFacilityIdQualificationRatesPost()**](FacilityQualificationRatesApi.md#apiFacilitiesFacilityIdQualificationRatesPost) | **POST** /api/facilities/{facilityId}/qualification-rates | | + + +## `apiFacilitiesFacilityIdQualificationRatesGet()` + +```php +apiFacilitiesFacilityIdQualificationRatesGet($facility_id): \OmsorgCoreClient\Model\FacilityQualificationRateResponse[] +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\FacilityQualificationRatesApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$facility_id = 'facility_id_example'; // string + +try { + $result = $apiInstance->apiFacilitiesFacilityIdQualificationRatesGet($facility_id); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling FacilityQualificationRatesApi->apiFacilitiesFacilityIdQualificationRatesGet: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **facility_id** | **string**| | | + +### Return type + +[**\OmsorgCoreClient\Model\FacilityQualificationRateResponse[]**](../Model/FacilityQualificationRateResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiFacilitiesFacilityIdQualificationRatesIdDelete()` + +```php +apiFacilitiesFacilityIdQualificationRatesIdDelete($facility_id, $id) +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\FacilityQualificationRatesApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$facility_id = 'facility_id_example'; // string +$id = 'id_example'; // string + +try { + $apiInstance->apiFacilitiesFacilityIdQualificationRatesIdDelete($facility_id, $id); +} catch (Exception $e) { + echo 'Exception when calling FacilityQualificationRatesApi->apiFacilitiesFacilityIdQualificationRatesIdDelete: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **facility_id** | **string**| | | +| **id** | **string**| | | + +### Return type + +void (empty response body) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiFacilitiesFacilityIdQualificationRatesIdPut()` + +```php +apiFacilitiesFacilityIdQualificationRatesIdPut($facility_id, $id, $update_facility_qualification_rate_request): \OmsorgCoreClient\Model\FacilityQualificationRateResponse +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\FacilityQualificationRatesApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$facility_id = 'facility_id_example'; // string +$id = 'id_example'; // string +$update_facility_qualification_rate_request = new \OmsorgCoreClient\Model\UpdateFacilityQualificationRateRequest(); // \OmsorgCoreClient\Model\UpdateFacilityQualificationRateRequest + +try { + $result = $apiInstance->apiFacilitiesFacilityIdQualificationRatesIdPut($facility_id, $id, $update_facility_qualification_rate_request); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling FacilityQualificationRatesApi->apiFacilitiesFacilityIdQualificationRatesIdPut: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **facility_id** | **string**| | | +| **id** | **string**| | | +| **update_facility_qualification_rate_request** | [**\OmsorgCoreClient\Model\UpdateFacilityQualificationRateRequest**](../Model/UpdateFacilityQualificationRateRequest.md)| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\FacilityQualificationRateResponse**](../Model/FacilityQualificationRateResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: `application/json`, `text/json`, `application/*+json` +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiFacilitiesFacilityIdQualificationRatesPost()` + +```php +apiFacilitiesFacilityIdQualificationRatesPost($facility_id, $create_facility_qualification_rate_request): \OmsorgCoreClient\Model\FacilityQualificationRateResponse +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\FacilityQualificationRatesApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$facility_id = 'facility_id_example'; // string +$create_facility_qualification_rate_request = new \OmsorgCoreClient\Model\CreateFacilityQualificationRateRequest(); // \OmsorgCoreClient\Model\CreateFacilityQualificationRateRequest + +try { + $result = $apiInstance->apiFacilitiesFacilityIdQualificationRatesPost($facility_id, $create_facility_qualification_rate_request); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling FacilityQualificationRatesApi->apiFacilitiesFacilityIdQualificationRatesPost: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **facility_id** | **string**| | | +| **create_facility_qualification_rate_request** | [**\OmsorgCoreClient\Model\CreateFacilityQualificationRateRequest**](../Model/CreateFacilityQualificationRateRequest.md)| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\FacilityQualificationRateResponse**](../Model/FacilityQualificationRateResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: `application/json`, `text/json`, `application/*+json` +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/OrdersApi.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/OrdersApi.md index 386557f..2014141 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/OrdersApi.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/OrdersApi.md @@ -5,6 +5,7 @@ All URIs are relative to http://localhost, except if the operation defines anoth | Method | HTTP request | Description | | ------------- | ------------- | ------------- | | [**apiOrdersGet()**](OrdersApi.md#apiOrdersGet) | **GET** /api/orders | | +| [**apiOrdersIdDelete()**](OrdersApi.md#apiOrdersIdDelete) | **DELETE** /api/orders/{id} | | | [**apiOrdersIdGet()**](OrdersApi.md#apiOrdersIdGet) | **GET** /api/orders/{id} | | | [**apiOrdersIdPut()**](OrdersApi.md#apiOrdersIdPut) | **PUT** /api/orders/{id} | | | [**apiOrdersPost()**](OrdersApi.md#apiOrdersPost) | **POST** /api/orders | | @@ -13,7 +14,7 @@ All URIs are relative to http://localhost, except if the operation defines anoth ## `apiOrdersGet()` ```php -apiOrdersGet($search, $status_id, $facility_id, $page, $page_size): \OmsorgCoreClient\Model\OrderResponsePagedResponse +apiOrdersGet($search, $status_id, $facility_id, $priority, $required_qualification, $shift_type, $page, $page_size): \OmsorgCoreClient\Model\OrderResponsePagedResponse ``` @@ -38,11 +39,14 @@ $apiInstance = new OmsorgCoreClient\Api\OrdersApi( $search = 'search_example'; // string $status_id = 'status_id_example'; // string $facility_id = 'facility_id_example'; // string +$priority = 'priority_example'; // string +$required_qualification = 'required_qualification_example'; // string +$shift_type = 'shift_type_example'; // string $page = 1; // int $page_size = 20; // int try { - $result = $apiInstance->apiOrdersGet($search, $status_id, $facility_id, $page, $page_size); + $result = $apiInstance->apiOrdersGet($search, $status_id, $facility_id, $priority, $required_qualification, $shift_type, $page, $page_size); print_r($result); } catch (Exception $e) { echo 'Exception when calling OrdersApi->apiOrdersGet: ', $e->getMessage(), PHP_EOL; @@ -56,6 +60,9 @@ try { | **search** | **string**| | [optional] | | **status_id** | **string**| | [optional] | | **facility_id** | **string**| | [optional] | +| **priority** | **string**| | [optional] | +| **required_qualification** | **string**| | [optional] | +| **shift_type** | **string**| | [optional] | | **page** | **int**| | [optional] [default to 1] | | **page_size** | **int**| | [optional] [default to 20] | @@ -76,6 +83,63 @@ try { [[Back to Model list]](../../README.md#models) [[Back to README]](../../README.md) +## `apiOrdersIdDelete()` + +```php +apiOrdersIdDelete($id) +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\OrdersApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string + +try { + $apiInstance->apiOrdersIdDelete($id); +} catch (Exception $e) { + echo 'Exception when calling OrdersApi->apiOrdersIdDelete: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | + +### Return type + +void (empty response body) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + ## `apiOrdersIdGet()` ```php diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/TimeEntriesApi.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/TimeEntriesApi.md new file mode 100644 index 0000000..a683f65 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/TimeEntriesApi.md @@ -0,0 +1,431 @@ +# OmsorgCoreClient\TimeEntriesApi + +All URIs are relative to http://localhost, except if the operation defines another base path. + +| Method | HTTP request | Description | +| ------------- | ------------- | ------------- | +| [**apiTimeEntriesGet()**](TimeEntriesApi.md#apiTimeEntriesGet) | **GET** /api/time-entries | | +| [**apiTimeEntriesIdDecisionPost()**](TimeEntriesApi.md#apiTimeEntriesIdDecisionPost) | **POST** /api/time-entries/{id}/decision | | +| [**apiTimeEntriesIdDelete()**](TimeEntriesApi.md#apiTimeEntriesIdDelete) | **DELETE** /api/time-entries/{id} | | +| [**apiTimeEntriesIdGet()**](TimeEntriesApi.md#apiTimeEntriesIdGet) | **GET** /api/time-entries/{id} | | +| [**apiTimeEntriesIdPut()**](TimeEntriesApi.md#apiTimeEntriesIdPut) | **PUT** /api/time-entries/{id} | | +| [**apiTimeEntriesIdSubmitPost()**](TimeEntriesApi.md#apiTimeEntriesIdSubmitPost) | **POST** /api/time-entries/{id}/submit | | +| [**apiTimeEntriesPost()**](TimeEntriesApi.md#apiTimeEntriesPost) | **POST** /api/time-entries | | + + +## `apiTimeEntriesGet()` + +```php +apiTimeEntriesGet($status_id, $employee_id, $order_id, $page, $page_size): \OmsorgCoreClient\Model\TimeEntryResponsePagedResponse +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TimeEntriesApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$status_id = 'status_id_example'; // string +$employee_id = 'employee_id_example'; // string +$order_id = 'order_id_example'; // string +$page = 1; // int +$page_size = 20; // int + +try { + $result = $apiInstance->apiTimeEntriesGet($status_id, $employee_id, $order_id, $page, $page_size); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling TimeEntriesApi->apiTimeEntriesGet: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **status_id** | **string**| | [optional] | +| **employee_id** | **string**| | [optional] | +| **order_id** | **string**| | [optional] | +| **page** | **int**| | [optional] [default to 1] | +| **page_size** | **int**| | [optional] [default to 20] | + +### Return type + +[**\OmsorgCoreClient\Model\TimeEntryResponsePagedResponse**](../Model/TimeEntryResponsePagedResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTimeEntriesIdDecisionPost()` + +```php +apiTimeEntriesIdDecisionPost($id, $time_entry_decision_request): \OmsorgCoreClient\Model\TimeEntryResponse +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TimeEntriesApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string +$time_entry_decision_request = new \OmsorgCoreClient\Model\TimeEntryDecisionRequest(); // \OmsorgCoreClient\Model\TimeEntryDecisionRequest + +try { + $result = $apiInstance->apiTimeEntriesIdDecisionPost($id, $time_entry_decision_request); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling TimeEntriesApi->apiTimeEntriesIdDecisionPost: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | +| **time_entry_decision_request** | [**\OmsorgCoreClient\Model\TimeEntryDecisionRequest**](../Model/TimeEntryDecisionRequest.md)| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\TimeEntryResponse**](../Model/TimeEntryResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: `application/json`, `text/json`, `application/*+json` +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTimeEntriesIdDelete()` + +```php +apiTimeEntriesIdDelete($id) +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TimeEntriesApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string + +try { + $apiInstance->apiTimeEntriesIdDelete($id); +} catch (Exception $e) { + echo 'Exception when calling TimeEntriesApi->apiTimeEntriesIdDelete: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | + +### Return type + +void (empty response body) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTimeEntriesIdGet()` + +```php +apiTimeEntriesIdGet($id): \OmsorgCoreClient\Model\TimeEntryResponse +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TimeEntriesApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string + +try { + $result = $apiInstance->apiTimeEntriesIdGet($id); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling TimeEntriesApi->apiTimeEntriesIdGet: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | + +### Return type + +[**\OmsorgCoreClient\Model\TimeEntryResponse**](../Model/TimeEntryResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTimeEntriesIdPut()` + +```php +apiTimeEntriesIdPut($id, $update_time_entry_request): \OmsorgCoreClient\Model\TimeEntryResponse +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TimeEntriesApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string +$update_time_entry_request = new \OmsorgCoreClient\Model\UpdateTimeEntryRequest(); // \OmsorgCoreClient\Model\UpdateTimeEntryRequest + +try { + $result = $apiInstance->apiTimeEntriesIdPut($id, $update_time_entry_request); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling TimeEntriesApi->apiTimeEntriesIdPut: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | +| **update_time_entry_request** | [**\OmsorgCoreClient\Model\UpdateTimeEntryRequest**](../Model/UpdateTimeEntryRequest.md)| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\TimeEntryResponse**](../Model/TimeEntryResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: `application/json`, `text/json`, `application/*+json` +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTimeEntriesIdSubmitPost()` + +```php +apiTimeEntriesIdSubmitPost($id): \OmsorgCoreClient\Model\TimeEntryResponse +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TimeEntriesApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string + +try { + $result = $apiInstance->apiTimeEntriesIdSubmitPost($id); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling TimeEntriesApi->apiTimeEntriesIdSubmitPost: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | + +### Return type + +[**\OmsorgCoreClient\Model\TimeEntryResponse**](../Model/TimeEntryResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTimeEntriesPost()` + +```php +apiTimeEntriesPost($create_time_entry_request): \OmsorgCoreClient\Model\TimeEntryResponse +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TimeEntriesApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$create_time_entry_request = new \OmsorgCoreClient\Model\CreateTimeEntryRequest(); // \OmsorgCoreClient\Model\CreateTimeEntryRequest + +try { + $result = $apiInstance->apiTimeEntriesPost($create_time_entry_request); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling TimeEntriesApi->apiTimeEntriesPost: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **create_time_entry_request** | [**\OmsorgCoreClient\Model\CreateTimeEntryRequest**](../Model/CreateTimeEntryRequest.md)| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\TimeEntryResponse**](../Model/TimeEntryResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: `application/json`, `text/json`, `application/*+json` +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/TrashApi.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/TrashApi.md new file mode 100644 index 0000000..9dfd684 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/TrashApi.md @@ -0,0 +1,943 @@ +# OmsorgCoreClient\TrashApi + +All URIs are relative to http://localhost, except if the operation defines another base path. + +| Method | HTTP request | Description | +| ------------- | ------------- | ------------- | +| [**apiTrashAbsencesGet()**](TrashApi.md#apiTrashAbsencesGet) | **GET** /api/trash/absences | | +| [**apiTrashAbsencesIdRestorePost()**](TrashApi.md#apiTrashAbsencesIdRestorePost) | **POST** /api/trash/absences/{id}/restore | | +| [**apiTrashContractsGet()**](TrashApi.md#apiTrashContractsGet) | **GET** /api/trash/contracts | | +| [**apiTrashContractsIdRestorePost()**](TrashApi.md#apiTrashContractsIdRestorePost) | **POST** /api/trash/contracts/{id}/restore | | +| [**apiTrashEmployeesGet()**](TrashApi.md#apiTrashEmployeesGet) | **GET** /api/trash/employees | | +| [**apiTrashEmployeesIdRestorePost()**](TrashApi.md#apiTrashEmployeesIdRestorePost) | **POST** /api/trash/employees/{id}/restore | | +| [**apiTrashFacilitiesGet()**](TrashApi.md#apiTrashFacilitiesGet) | **GET** /api/trash/facilities | | +| [**apiTrashFacilitiesIdRestorePost()**](TrashApi.md#apiTrashFacilitiesIdRestorePost) | **POST** /api/trash/facilities/{id}/restore | | +| [**apiTrashFacilityContactsGet()**](TrashApi.md#apiTrashFacilityContactsGet) | **GET** /api/trash/facility-contacts | | +| [**apiTrashFacilityContactsIdRestorePost()**](TrashApi.md#apiTrashFacilityContactsIdRestorePost) | **POST** /api/trash/facility-contacts/{id}/restore | | +| [**apiTrashFacilityQualificationRatesGet()**](TrashApi.md#apiTrashFacilityQualificationRatesGet) | **GET** /api/trash/facility-qualification-rates | | +| [**apiTrashFacilityQualificationRatesIdRestorePost()**](TrashApi.md#apiTrashFacilityQualificationRatesIdRestorePost) | **POST** /api/trash/facility-qualification-rates/{id}/restore | | +| [**apiTrashOrdersGet()**](TrashApi.md#apiTrashOrdersGet) | **GET** /api/trash/orders | | +| [**apiTrashOrdersIdRestorePost()**](TrashApi.md#apiTrashOrdersIdRestorePost) | **POST** /api/trash/orders/{id}/restore | | +| [**apiTrashTimeEntriesGet()**](TrashApi.md#apiTrashTimeEntriesGet) | **GET** /api/trash/time-entries | | +| [**apiTrashTimeEntriesIdRestorePost()**](TrashApi.md#apiTrashTimeEntriesIdRestorePost) | **POST** /api/trash/time-entries/{id}/restore | | + + +## `apiTrashAbsencesGet()` + +```php +apiTrashAbsencesGet($search): \OmsorgCoreClient\Model\TrashAbsenceResponse[] +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TrashApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$search = 'search_example'; // string + +try { + $result = $apiInstance->apiTrashAbsencesGet($search); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling TrashApi->apiTrashAbsencesGet: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **search** | **string**| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\TrashAbsenceResponse[]**](../Model/TrashAbsenceResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTrashAbsencesIdRestorePost()` + +```php +apiTrashAbsencesIdRestorePost($id) +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TrashApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string + +try { + $apiInstance->apiTrashAbsencesIdRestorePost($id); +} catch (Exception $e) { + echo 'Exception when calling TrashApi->apiTrashAbsencesIdRestorePost: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | + +### Return type + +void (empty response body) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTrashContractsGet()` + +```php +apiTrashContractsGet($search): \OmsorgCoreClient\Model\TrashContractResponse[] +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TrashApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$search = 'search_example'; // string + +try { + $result = $apiInstance->apiTrashContractsGet($search); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling TrashApi->apiTrashContractsGet: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **search** | **string**| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\TrashContractResponse[]**](../Model/TrashContractResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTrashContractsIdRestorePost()` + +```php +apiTrashContractsIdRestorePost($id) +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TrashApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string + +try { + $apiInstance->apiTrashContractsIdRestorePost($id); +} catch (Exception $e) { + echo 'Exception when calling TrashApi->apiTrashContractsIdRestorePost: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | + +### Return type + +void (empty response body) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTrashEmployeesGet()` + +```php +apiTrashEmployeesGet($search): \OmsorgCoreClient\Model\TrashEmployeeResponse[] +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TrashApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$search = 'search_example'; // string + +try { + $result = $apiInstance->apiTrashEmployeesGet($search); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling TrashApi->apiTrashEmployeesGet: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **search** | **string**| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\TrashEmployeeResponse[]**](../Model/TrashEmployeeResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTrashEmployeesIdRestorePost()` + +```php +apiTrashEmployeesIdRestorePost($id) +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TrashApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string + +try { + $apiInstance->apiTrashEmployeesIdRestorePost($id); +} catch (Exception $e) { + echo 'Exception when calling TrashApi->apiTrashEmployeesIdRestorePost: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | + +### Return type + +void (empty response body) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTrashFacilitiesGet()` + +```php +apiTrashFacilitiesGet($search): \OmsorgCoreClient\Model\TrashFacilityResponse[] +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TrashApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$search = 'search_example'; // string + +try { + $result = $apiInstance->apiTrashFacilitiesGet($search); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling TrashApi->apiTrashFacilitiesGet: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **search** | **string**| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\TrashFacilityResponse[]**](../Model/TrashFacilityResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTrashFacilitiesIdRestorePost()` + +```php +apiTrashFacilitiesIdRestorePost($id) +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TrashApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string + +try { + $apiInstance->apiTrashFacilitiesIdRestorePost($id); +} catch (Exception $e) { + echo 'Exception when calling TrashApi->apiTrashFacilitiesIdRestorePost: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | + +### Return type + +void (empty response body) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTrashFacilityContactsGet()` + +```php +apiTrashFacilityContactsGet($search): \OmsorgCoreClient\Model\TrashFacilityContactResponse[] +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TrashApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$search = 'search_example'; // string + +try { + $result = $apiInstance->apiTrashFacilityContactsGet($search); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling TrashApi->apiTrashFacilityContactsGet: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **search** | **string**| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\TrashFacilityContactResponse[]**](../Model/TrashFacilityContactResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTrashFacilityContactsIdRestorePost()` + +```php +apiTrashFacilityContactsIdRestorePost($id) +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TrashApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string + +try { + $apiInstance->apiTrashFacilityContactsIdRestorePost($id); +} catch (Exception $e) { + echo 'Exception when calling TrashApi->apiTrashFacilityContactsIdRestorePost: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | + +### Return type + +void (empty response body) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTrashFacilityQualificationRatesGet()` + +```php +apiTrashFacilityQualificationRatesGet($search): \OmsorgCoreClient\Model\TrashFacilityQualificationRateResponse[] +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TrashApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$search = 'search_example'; // string + +try { + $result = $apiInstance->apiTrashFacilityQualificationRatesGet($search); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling TrashApi->apiTrashFacilityQualificationRatesGet: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **search** | **string**| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\TrashFacilityQualificationRateResponse[]**](../Model/TrashFacilityQualificationRateResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTrashFacilityQualificationRatesIdRestorePost()` + +```php +apiTrashFacilityQualificationRatesIdRestorePost($id) +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TrashApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string + +try { + $apiInstance->apiTrashFacilityQualificationRatesIdRestorePost($id); +} catch (Exception $e) { + echo 'Exception when calling TrashApi->apiTrashFacilityQualificationRatesIdRestorePost: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | + +### Return type + +void (empty response body) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTrashOrdersGet()` + +```php +apiTrashOrdersGet($search): \OmsorgCoreClient\Model\TrashOrderResponse[] +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TrashApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$search = 'search_example'; // string + +try { + $result = $apiInstance->apiTrashOrdersGet($search); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling TrashApi->apiTrashOrdersGet: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **search** | **string**| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\TrashOrderResponse[]**](../Model/TrashOrderResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTrashOrdersIdRestorePost()` + +```php +apiTrashOrdersIdRestorePost($id) +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TrashApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string + +try { + $apiInstance->apiTrashOrdersIdRestorePost($id); +} catch (Exception $e) { + echo 'Exception when calling TrashApi->apiTrashOrdersIdRestorePost: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | + +### Return type + +void (empty response body) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTrashTimeEntriesGet()` + +```php +apiTrashTimeEntriesGet($search): \OmsorgCoreClient\Model\TrashTimeEntryResponse[] +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TrashApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$search = 'search_example'; // string + +try { + $result = $apiInstance->apiTrashTimeEntriesGet($search); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling TrashApi->apiTrashTimeEntriesGet: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **search** | **string**| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\TrashTimeEntryResponse[]**](../Model/TrashTimeEntryResponse.md) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: `text/plain`, `application/json`, `text/json` + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) + +## `apiTrashTimeEntriesIdRestorePost()` + +```php +apiTrashTimeEntriesIdRestorePost($id) +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\TrashApi( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(), + $config +); +$id = 'id_example'; // string + +try { + $apiInstance->apiTrashTimeEntriesIdRestorePost($id); +} catch (Exception $e) { + echo 'Exception when calling TrashApi->apiTrashTimeEntriesIdRestorePost: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | + +### Return type + +void (empty response body) + +### Authorization + +[Bearer](../../README.md#Bearer) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../../README.md#endpoints) +[[Back to Model list]](../../README.md#models) +[[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/AbsenceDecisionRequest.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/AbsenceDecisionRequest.md new file mode 100644 index 0000000..d75a696 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/AbsenceDecisionRequest.md @@ -0,0 +1,10 @@ +# # AbsenceDecisionRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**status** | **string** | | [optional] +**admin_note** | **string** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/AbsenceResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/AbsenceResponse.md new file mode 100644 index 0000000..ce07b0a --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/AbsenceResponse.md @@ -0,0 +1,20 @@ +# # AbsenceResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **string** | | [optional] +**employee_id** | **string** | | [optional] +**employee_name** | **string** | | [optional] +**type** | **string** | | [optional] +**start_date** | **\DateTime** | | [optional] +**end_date** | **\DateTime** | | [optional] +**reason** | **string** | | [optional] +**substitute** | **string** | | [optional] +**note** | **string** | | [optional] +**status** | **string** | | [optional] +**admin_note** | **string** | | [optional] +**created_at** | **\DateTime** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/AbsenceResponsePagedResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/AbsenceResponsePagedResponse.md new file mode 100644 index 0000000..2f3a063 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/AbsenceResponsePagedResponse.md @@ -0,0 +1,12 @@ +# # AbsenceResponsePagedResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**items** | [**\OmsorgCoreClient\Model\AbsenceResponse[]**](AbsenceResponse.md) | | [optional] +**total_count** | **int** | | [optional] +**page** | **int** | | [optional] +**page_size** | **int** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/AddUserPermissionOverrideRequest.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/AddUserPermissionOverrideRequest.md index 8f451bd..0c57ae5 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/AddUserPermissionOverrideRequest.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/AddUserPermissionOverrideRequest.md @@ -4,8 +4,9 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**module** | **string** | | [optional] -**action** | **string** | | [optional] -**effect** | **string** | | [optional] +**module** | [**\OmsorgCoreClient\Model\ModuleType**](ModuleType.md) | | [optional] +**action** | [**\OmsorgCoreClient\Model\PermissionAction**](PermissionAction.md) | | [optional] +**effect** | [**\OmsorgCoreClient\Model\PermissionEffect**](PermissionEffect.md) | | [optional] +**scope** | [**\OmsorgCoreClient\Model\PermissionScope**](PermissionScope.md) | | [optional] [[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/AuditEventCategory.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/AuditEventCategory.md new file mode 100644 index 0000000..781cb2d --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/AuditEventCategory.md @@ -0,0 +1,8 @@ +# # AuditEventCategory + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateAbsenceRequest.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateAbsenceRequest.md new file mode 100644 index 0000000..f3af99d --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateAbsenceRequest.md @@ -0,0 +1,14 @@ +# # CreateAbsenceRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**type** | **string** | | [optional] +**start_date** | **\DateTime** | | [optional] +**end_date** | **\DateTime** | | [optional] +**reason** | **string** | | [optional] +**substitute** | **string** | | [optional] +**note** | **string** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateFacilityQualificationRateRequest.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateFacilityQualificationRateRequest.md new file mode 100644 index 0000000..12fde3e --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateFacilityQualificationRateRequest.md @@ -0,0 +1,10 @@ +# # CreateFacilityQualificationRateRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**qualification** | **string** | | [optional] +**rate** | **float** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateFacilityRequest.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateFacilityRequest.md index f9a0fbf..dcae53a 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateFacilityRequest.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateFacilityRequest.md @@ -6,6 +6,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **name** | **string** | | [optional] **facility_type** | **string** | | [optional] +**website** | **string** | | [optional] **street** | **string** | | [optional] **postal_code** | **string** | | [optional] **city** | **string** | | [optional] diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateTimeEntryRequest.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateTimeEntryRequest.md new file mode 100644 index 0000000..28b1915 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateTimeEntryRequest.md @@ -0,0 +1,17 @@ +# # CreateTimeEntryRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**order_id** | **string** | | [optional] +**date** | **\DateTime** | | [optional] +**start** | **string** | | [optional] +**end** | **string** | | [optional] +**break_duration** | **string** | | [optional] +**night_hours** | **float** | | [optional] +**saturday_hours** | **float** | | [optional] +**sunday_hours** | **float** | | [optional] +**holiday_hours** | **float** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateValueListItemRequest.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateValueListItemRequest.md index cf2c945..05a607a 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateValueListItemRequest.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateValueListItemRequest.md @@ -9,5 +9,6 @@ Name | Type | Description | Notes **is_default** | **bool** | | [optional] **is_initial** | **bool** | | [optional] **is_terminal** | **bool** | | [optional] +**triggers_follow_up** | **bool** | | [optional] [[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/DocumentResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/DocumentResponse.md new file mode 100644 index 0000000..4c82738 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/DocumentResponse.md @@ -0,0 +1,19 @@ +# # DocumentResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **string** | | [optional] +**entity_type** | **string** | | [optional] +**entity_id** | **string** | | [optional] +**category** | **string** | | [optional] +**file_name** | **string** | | [optional] +**content_type** | **string** | | [optional] +**size_bytes** | **int** | | [optional] +**description** | **string** | | [optional] +**uploaded_by_user_id** | **string** | | [optional] +**uploaded_by_username** | **string** | | [optional] +**created_at** | **\DateTime** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/FacilityQualificationRateResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/FacilityQualificationRateResponse.md new file mode 100644 index 0000000..e2827e7 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/FacilityQualificationRateResponse.md @@ -0,0 +1,12 @@ +# # FacilityQualificationRateResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **string** | | [optional] +**facility_id** | **string** | | [optional] +**qualification** | **string** | | [optional] +**rate** | **float** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/FacilityResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/FacilityResponse.md index 3176c54..963dfca 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/FacilityResponse.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/FacilityResponse.md @@ -8,6 +8,7 @@ Name | Type | Description | Notes **name** | **string** | | [optional] **crm_status** | **string** | | [optional] **facility_type** | **string** | | [optional] +**website** | **string** | | [optional] **street** | **string** | | [optional] **postal_code** | **string** | | [optional] **city** | **string** | | [optional] @@ -16,5 +17,17 @@ Name | Type | Description | Notes **billing_postal_code** | **string** | | [optional] **billing_city** | **string** | | [optional] **billing_country** | **string** | | [optional] +**follow_up_due_date** | **\DateTime** | | [optional] +**billing_rate** | **float** | | [optional] +**night_surcharge_percent** | **float** | | [optional] +**saturday_surcharge_percent** | **float** | | [optional] +**sunday_surcharge_percent** | **float** | | [optional] +**holiday_surcharge_percent** | **float** | | [optional] +**travel_cost_rate** | **float** | | [optional] +**minimum_hours** | **float** | | [optional] +**break_policy** | **string** | | [optional] +**billing_interval** | **string** | | [optional] +**payment_term_days** | **int** | | [optional] +**individual_agreements** | **string** | | [optional] [[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/ModuleType.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/ModuleType.md new file mode 100644 index 0000000..de97fd7 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/ModuleType.md @@ -0,0 +1,8 @@ +# # ModuleType + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/PermissionAction.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/PermissionAction.md new file mode 100644 index 0000000..b10a448 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/PermissionAction.md @@ -0,0 +1,8 @@ +# # PermissionAction + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/PermissionDto.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/PermissionDto.md index c6435bb..05568be 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/PermissionDto.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/PermissionDto.md @@ -4,7 +4,8 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**module** | **string** | | [optional] -**action** | **string** | | [optional] +**module** | [**\OmsorgCoreClient\Model\ModuleType**](ModuleType.md) | | [optional] +**action** | [**\OmsorgCoreClient\Model\PermissionAction**](PermissionAction.md) | | [optional] +**scope** | [**\OmsorgCoreClient\Model\PermissionScope**](PermissionScope.md) | | [optional] [[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/PermissionEffect.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/PermissionEffect.md new file mode 100644 index 0000000..7aae07b --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/PermissionEffect.md @@ -0,0 +1,8 @@ +# # PermissionEffect + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/PermissionScope.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/PermissionScope.md new file mode 100644 index 0000000..8e3ce85 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/PermissionScope.md @@ -0,0 +1,8 @@ +# # PermissionScope + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TimeEntryDecisionRequest.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TimeEntryDecisionRequest.md new file mode 100644 index 0000000..52b6d7b --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TimeEntryDecisionRequest.md @@ -0,0 +1,10 @@ +# # TimeEntryDecisionRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**status_id** | **string** | | [optional] +**admin_note** | **string** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TimeEntryResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TimeEntryResponse.md new file mode 100644 index 0000000..da2d752 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TimeEntryResponse.md @@ -0,0 +1,27 @@ +# # TimeEntryResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **string** | | [optional] +**employee_id** | **string** | | [optional] +**employee_name** | **string** | | [optional] +**order_id** | **string** | | [optional] +**facility_id** | **string** | | [optional] +**facility_name** | **string** | | [optional] +**date** | **\DateTime** | | [optional] +**start** | **string** | | [optional] +**end** | **string** | | [optional] +**break_duration** | **string** | | [optional] +**night_hours** | **float** | | [optional] +**saturday_hours** | **float** | | [optional] +**sunday_hours** | **float** | | [optional] +**holiday_hours** | **float** | | [optional] +**status_id** | **string** | | [optional] +**status_name** | **string** | | [optional] +**is_editable_by_owner** | **bool** | | [optional] +**admin_note** | **string** | | [optional] +**created_at** | **\DateTime** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TimeEntryResponsePagedResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TimeEntryResponsePagedResponse.md new file mode 100644 index 0000000..f3b1347 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TimeEntryResponsePagedResponse.md @@ -0,0 +1,12 @@ +# # TimeEntryResponsePagedResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**items** | [**\OmsorgCoreClient\Model\TimeEntryResponse[]**](TimeEntryResponse.md) | | [optional] +**total_count** | **int** | | [optional] +**page** | **int** | | [optional] +**page_size** | **int** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashAbsenceResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashAbsenceResponse.md new file mode 100644 index 0000000..5b61093 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashAbsenceResponse.md @@ -0,0 +1,11 @@ +# # TrashAbsenceResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **string** | | [optional] +**type** | **string** | | [optional] +**deleted_at** | **\DateTime** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashContractResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashContractResponse.md new file mode 100644 index 0000000..edbbfa1 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashContractResponse.md @@ -0,0 +1,11 @@ +# # TrashContractResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **string** | | [optional] +**contract_type** | **string** | | [optional] +**deleted_at** | **\DateTime** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashEmployeeResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashEmployeeResponse.md new file mode 100644 index 0000000..5a13793 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashEmployeeResponse.md @@ -0,0 +1,12 @@ +# # TrashEmployeeResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **string** | | [optional] +**first_name** | **string** | | [optional] +**last_name** | **string** | | [optional] +**deleted_at** | **\DateTime** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashFacilityContactResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashFacilityContactResponse.md new file mode 100644 index 0000000..be99893 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashFacilityContactResponse.md @@ -0,0 +1,12 @@ +# # TrashFacilityContactResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **string** | | [optional] +**facility_id** | **string** | | [optional] +**name** | **string** | | [optional] +**deleted_at** | **\DateTime** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashFacilityQualificationRateResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashFacilityQualificationRateResponse.md new file mode 100644 index 0000000..064ec19 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashFacilityQualificationRateResponse.md @@ -0,0 +1,12 @@ +# # TrashFacilityQualificationRateResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **string** | | [optional] +**facility_id** | **string** | | [optional] +**qualification** | **string** | | [optional] +**deleted_at** | **\DateTime** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashFacilityResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashFacilityResponse.md new file mode 100644 index 0000000..c45c1d6 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashFacilityResponse.md @@ -0,0 +1,11 @@ +# # TrashFacilityResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **string** | | [optional] +**name** | **string** | | [optional] +**deleted_at** | **\DateTime** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashOrderResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashOrderResponse.md new file mode 100644 index 0000000..cb17541 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashOrderResponse.md @@ -0,0 +1,11 @@ +# # TrashOrderResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **string** | | [optional] +**required_qualification** | **string** | | [optional] +**deleted_at** | **\DateTime** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashTimeEntryResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashTimeEntryResponse.md new file mode 100644 index 0000000..e1827b9 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashTimeEntryResponse.md @@ -0,0 +1,11 @@ +# # TrashTimeEntryResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **string** | | [optional] +**date** | **\DateTime** | | [optional] +**deleted_at** | **\DateTime** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateAbsenceRequest.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateAbsenceRequest.md new file mode 100644 index 0000000..9352585 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateAbsenceRequest.md @@ -0,0 +1,14 @@ +# # UpdateAbsenceRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**type** | **string** | | [optional] +**start_date** | **\DateTime** | | [optional] +**end_date** | **\DateTime** | | [optional] +**reason** | **string** | | [optional] +**substitute** | **string** | | [optional] +**note** | **string** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateDocumentRequest.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateDocumentRequest.md new file mode 100644 index 0000000..bdc9385 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateDocumentRequest.md @@ -0,0 +1,11 @@ +# # UpdateDocumentRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**category** | **string** | | [optional] +**description** | **string** | | [optional] +**file_name** | **string** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateFacilityQualificationRateRequest.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateFacilityQualificationRateRequest.md new file mode 100644 index 0000000..cb36853 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateFacilityQualificationRateRequest.md @@ -0,0 +1,10 @@ +# # UpdateFacilityQualificationRateRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**qualification** | **string** | | [optional] +**rate** | **float** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateFacilityRequest.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateFacilityRequest.md index 1f48abf..9bc8291 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateFacilityRequest.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateFacilityRequest.md @@ -7,6 +7,7 @@ Name | Type | Description | Notes **name** | **string** | | [optional] **crm_status** | **string** | | [optional] **facility_type** | **string** | | [optional] +**website** | **string** | | [optional] **street** | **string** | | [optional] **postal_code** | **string** | | [optional] **city** | **string** | | [optional] @@ -15,5 +16,17 @@ Name | Type | Description | Notes **billing_postal_code** | **string** | | [optional] **billing_city** | **string** | | [optional] **billing_country** | **string** | | [optional] +**follow_up_days** | **int** | | [optional] +**billing_rate** | **float** | | [optional] +**night_surcharge_percent** | **float** | | [optional] +**saturday_surcharge_percent** | **float** | | [optional] +**sunday_surcharge_percent** | **float** | | [optional] +**holiday_surcharge_percent** | **float** | | [optional] +**travel_cost_rate** | **float** | | [optional] +**minimum_hours** | **float** | | [optional] +**break_policy** | **string** | | [optional] +**billing_interval** | **string** | | [optional] +**payment_term_days** | **int** | | [optional] +**individual_agreements** | **string** | | [optional] [[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateTimeEntryRequest.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateTimeEntryRequest.md new file mode 100644 index 0000000..3854538 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateTimeEntryRequest.md @@ -0,0 +1,17 @@ +# # UpdateTimeEntryRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**order_id** | **string** | | [optional] +**date** | **\DateTime** | | [optional] +**start** | **string** | | [optional] +**end** | **string** | | [optional] +**break_duration** | **string** | | [optional] +**night_hours** | **float** | | [optional] +**saturday_hours** | **float** | | [optional] +**sunday_hours** | **float** | | [optional] +**holiday_hours** | **float** | | [optional] + +[[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateValueListItemRequest.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateValueListItemRequest.md index 58b10f4..8003d05 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateValueListItemRequest.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateValueListItemRequest.md @@ -9,5 +9,6 @@ Name | Type | Description | Notes **is_default** | **bool** | | [optional] **is_initial** | **bool** | | [optional] **is_terminal** | **bool** | | [optional] +**triggers_follow_up** | **bool** | | [optional] [[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UserPermissionOverrideResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UserPermissionOverrideResponse.md index d7b4fb3..645b3c6 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UserPermissionOverrideResponse.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UserPermissionOverrideResponse.md @@ -5,8 +5,9 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **id** | **string** | | [optional] -**module** | **string** | | [optional] -**action** | **string** | | [optional] -**effect** | **string** | | [optional] +**module** | [**\OmsorgCoreClient\Model\ModuleType**](ModuleType.md) | | [optional] +**action** | [**\OmsorgCoreClient\Model\PermissionAction**](PermissionAction.md) | | [optional] +**effect** | [**\OmsorgCoreClient\Model\PermissionEffect**](PermissionEffect.md) | | [optional] +**scope** | [**\OmsorgCoreClient\Model\PermissionScope**](PermissionScope.md) | | [optional] [[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/ValueListItemResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/ValueListItemResponse.md index 7c0cbdf..b2a56ca 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/ValueListItemResponse.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/ValueListItemResponse.md @@ -10,5 +10,6 @@ Name | Type | Description | Notes **is_default** | **bool** | | [optional] **is_initial** | **bool** | | [optional] **is_terminal** | **bool** | | [optional] +**triggers_follow_up** | **bool** | | [optional] [[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/ValueListTransitionResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/ValueListTransitionResponse.md index 79dde6c..a7dca7e 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/ValueListTransitionResponse.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/ValueListTransitionResponse.md @@ -7,5 +7,6 @@ Name | Type | Description | Notes **id** | **string** | | [optional] **from_item_id** | **string** | | [optional] **to_item_id** | **string** | | [optional] +**requires_approval** | **bool** | | [optional] [[Back to Model list]](../../README.md#models) [[Back to API list]](../../README.md#endpoints) [[Back to README]](../../README.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/AbsencesApi.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/AbsencesApi.php new file mode 100644 index 0000000..6b394ed --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/AbsencesApi.php @@ -0,0 +1,1818 @@ + [ + 'application/json', + ], + 'apiAbsencesIdDecisionPost' => [ + 'application/json', + 'text/json', + 'application/*+json', + ], + 'apiAbsencesIdDelete' => [ + 'application/json', + ], + 'apiAbsencesIdGet' => [ + 'application/json', + ], + 'apiAbsencesIdPut' => [ + 'application/json', + 'text/json', + 'application/*+json', + ], + 'apiAbsencesPost' => [ + 'application/json', + 'text/json', + 'application/*+json', + ], + ]; + + /** + * @param ClientInterface $client + * @param Configuration $config + * @param HeaderSelector $selector + * @param int $hostIndex (Optional) host index to select the list of hosts if defined in the OpenAPI spec + */ + public function __construct( + ?ClientInterface $client = null, + ?Configuration $config = null, + ?HeaderSelector $selector = null, + int $hostIndex = 0 + ) { + $this->client = $client ?: new Client(); + $this->config = $config ?: Configuration::getDefaultConfiguration(); + $this->headerSelector = $selector ?: new HeaderSelector(); + $this->hostIndex = $hostIndex; + } + + /** + * Set the host index + * + * @param int $hostIndex Host index (required) + */ + public function setHostIndex($hostIndex): void + { + $this->hostIndex = $hostIndex; + } + + /** + * Get the host index + * + * @return int Host index + */ + public function getHostIndex() + { + return $this->hostIndex; + } + + /** + * @return Configuration + */ + public function getConfig() + { + return $this->config; + } + + /** + * Operation apiAbsencesGet + * + * @param string|null $status status (optional) + * @param string|null $type type (optional) + * @param string|null $employee_id employee_id (optional) + * @param int|null $page page (optional, default to 1) + * @param int|null $page_size page_size (optional, default to 20) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\AbsenceResponsePagedResponse + */ + public function apiAbsencesGet($status = null, $type = null, $employee_id = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiAbsencesGet'][0]) + { + list($response) = $this->apiAbsencesGetWithHttpInfo($status, $type, $employee_id, $page, $page_size, $contentType); + return $response; + } + + /** + * Operation apiAbsencesGetWithHttpInfo + * + * @param string|null $status (optional) + * @param string|null $type (optional) + * @param string|null $employee_id (optional) + * @param int|null $page (optional, default to 1) + * @param int|null $page_size (optional, default to 20) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\AbsenceResponsePagedResponse, HTTP status code, HTTP response headers (array of strings) + */ + public function apiAbsencesGetWithHttpInfo($status = null, $type = null, $employee_id = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiAbsencesGet'][0]) + { + $request = $this->apiAbsencesGetRequest($status, $type, $employee_id, $page, $page_size, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\AbsenceResponsePagedResponse', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\AbsenceResponsePagedResponse', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\AbsenceResponsePagedResponse', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiAbsencesGetAsync + * + * @param string|null $status (optional) + * @param string|null $type (optional) + * @param string|null $employee_id (optional) + * @param int|null $page (optional, default to 1) + * @param int|null $page_size (optional, default to 20) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiAbsencesGetAsync($status = null, $type = null, $employee_id = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiAbsencesGet'][0]) + { + return $this->apiAbsencesGetAsyncWithHttpInfo($status, $type, $employee_id, $page, $page_size, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiAbsencesGetAsyncWithHttpInfo + * + * @param string|null $status (optional) + * @param string|null $type (optional) + * @param string|null $employee_id (optional) + * @param int|null $page (optional, default to 1) + * @param int|null $page_size (optional, default to 20) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiAbsencesGetAsyncWithHttpInfo($status = null, $type = null, $employee_id = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiAbsencesGet'][0]) + { + $returnType = '\OmsorgCoreClient\Model\AbsenceResponsePagedResponse'; + $request = $this->apiAbsencesGetRequest($status, $type, $employee_id, $page, $page_size, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiAbsencesGet' + * + * @param string|null $status (optional) + * @param string|null $type (optional) + * @param string|null $employee_id (optional) + * @param int|null $page (optional, default to 1) + * @param int|null $page_size (optional, default to 20) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiAbsencesGetRequest($status = null, $type = null, $employee_id = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiAbsencesGet'][0]) + { + + + + + + + + $resourcePath = '/api/absences'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $status, + 'status', // param base name + 'string', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $type, + 'type', // param base name + 'string', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $employee_id, + 'employeeId', // param base name + 'string', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $page, + 'page', // param base name + 'integer', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $page_size, + 'pageSize', // param base name + 'integer', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + + + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'GET', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiAbsencesIdDecisionPost + * + * @param string $id id (required) + * @param \OmsorgCoreClient\Model\AbsenceDecisionRequest|null $absence_decision_request absence_decision_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesIdDecisionPost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\AbsenceResponse + */ + public function apiAbsencesIdDecisionPost($id, $absence_decision_request = null, string $contentType = self::contentTypes['apiAbsencesIdDecisionPost'][0]) + { + list($response) = $this->apiAbsencesIdDecisionPostWithHttpInfo($id, $absence_decision_request, $contentType); + return $response; + } + + /** + * Operation apiAbsencesIdDecisionPostWithHttpInfo + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\AbsenceDecisionRequest|null $absence_decision_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesIdDecisionPost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\AbsenceResponse, HTTP status code, HTTP response headers (array of strings) + */ + public function apiAbsencesIdDecisionPostWithHttpInfo($id, $absence_decision_request = null, string $contentType = self::contentTypes['apiAbsencesIdDecisionPost'][0]) + { + $request = $this->apiAbsencesIdDecisionPostRequest($id, $absence_decision_request, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\AbsenceResponse', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\AbsenceResponse', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\AbsenceResponse', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiAbsencesIdDecisionPostAsync + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\AbsenceDecisionRequest|null $absence_decision_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesIdDecisionPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiAbsencesIdDecisionPostAsync($id, $absence_decision_request = null, string $contentType = self::contentTypes['apiAbsencesIdDecisionPost'][0]) + { + return $this->apiAbsencesIdDecisionPostAsyncWithHttpInfo($id, $absence_decision_request, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiAbsencesIdDecisionPostAsyncWithHttpInfo + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\AbsenceDecisionRequest|null $absence_decision_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesIdDecisionPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiAbsencesIdDecisionPostAsyncWithHttpInfo($id, $absence_decision_request = null, string $contentType = self::contentTypes['apiAbsencesIdDecisionPost'][0]) + { + $returnType = '\OmsorgCoreClient\Model\AbsenceResponse'; + $request = $this->apiAbsencesIdDecisionPostRequest($id, $absence_decision_request, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiAbsencesIdDecisionPost' + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\AbsenceDecisionRequest|null $absence_decision_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesIdDecisionPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiAbsencesIdDecisionPostRequest($id, $absence_decision_request = null, string $contentType = self::contentTypes['apiAbsencesIdDecisionPost'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiAbsencesIdDecisionPost' + ); + } + + + + $resourcePath = '/api/absences/{id}/decision'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (isset($absence_decision_request)) { + if (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the body + $httpBody = \GuzzleHttp\Utils::jsonEncode(ObjectSerializer::sanitizeForSerialization($absence_decision_request)); + } else { + $httpBody = $absence_decision_request; + } + } elseif (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'POST', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiAbsencesIdDelete + * + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesIdDelete'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return void + */ + public function apiAbsencesIdDelete($id, string $contentType = self::contentTypes['apiAbsencesIdDelete'][0]) + { + $this->apiAbsencesIdDeleteWithHttpInfo($id, $contentType); + } + + /** + * Operation apiAbsencesIdDeleteWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesIdDelete'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of null, HTTP status code, HTTP response headers (array of strings) + */ + public function apiAbsencesIdDeleteWithHttpInfo($id, string $contentType = self::contentTypes['apiAbsencesIdDelete'][0]) + { + $request = $this->apiAbsencesIdDeleteRequest($id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + return [null, $statusCode, $response->getHeaders()]; + } catch (ApiException $e) { + switch ($e->getCode()) { + } + + + throw $e; + } + } + + /** + * Operation apiAbsencesIdDeleteAsync + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiAbsencesIdDeleteAsync($id, string $contentType = self::contentTypes['apiAbsencesIdDelete'][0]) + { + return $this->apiAbsencesIdDeleteAsyncWithHttpInfo($id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiAbsencesIdDeleteAsyncWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiAbsencesIdDeleteAsyncWithHttpInfo($id, string $contentType = self::contentTypes['apiAbsencesIdDelete'][0]) + { + $returnType = ''; + $request = $this->apiAbsencesIdDeleteRequest($id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + return [null, $response->getStatusCode(), $response->getHeaders()]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiAbsencesIdDelete' + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiAbsencesIdDeleteRequest($id, string $contentType = self::contentTypes['apiAbsencesIdDelete'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiAbsencesIdDelete' + ); + } + + + $resourcePath = '/api/absences/{id}'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + [], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'DELETE', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiAbsencesIdGet + * + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesIdGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\AbsenceResponse + */ + public function apiAbsencesIdGet($id, string $contentType = self::contentTypes['apiAbsencesIdGet'][0]) + { + list($response) = $this->apiAbsencesIdGetWithHttpInfo($id, $contentType); + return $response; + } + + /** + * Operation apiAbsencesIdGetWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesIdGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\AbsenceResponse, HTTP status code, HTTP response headers (array of strings) + */ + public function apiAbsencesIdGetWithHttpInfo($id, string $contentType = self::contentTypes['apiAbsencesIdGet'][0]) + { + $request = $this->apiAbsencesIdGetRequest($id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\AbsenceResponse', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\AbsenceResponse', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\AbsenceResponse', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiAbsencesIdGetAsync + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesIdGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiAbsencesIdGetAsync($id, string $contentType = self::contentTypes['apiAbsencesIdGet'][0]) + { + return $this->apiAbsencesIdGetAsyncWithHttpInfo($id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiAbsencesIdGetAsyncWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesIdGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiAbsencesIdGetAsyncWithHttpInfo($id, string $contentType = self::contentTypes['apiAbsencesIdGet'][0]) + { + $returnType = '\OmsorgCoreClient\Model\AbsenceResponse'; + $request = $this->apiAbsencesIdGetRequest($id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiAbsencesIdGet' + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesIdGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiAbsencesIdGetRequest($id, string $contentType = self::contentTypes['apiAbsencesIdGet'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiAbsencesIdGet' + ); + } + + + $resourcePath = '/api/absences/{id}'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'GET', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiAbsencesIdPut + * + * @param string $id id (required) + * @param \OmsorgCoreClient\Model\UpdateAbsenceRequest|null $update_absence_request update_absence_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesIdPut'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\AbsenceResponse + */ + public function apiAbsencesIdPut($id, $update_absence_request = null, string $contentType = self::contentTypes['apiAbsencesIdPut'][0]) + { + list($response) = $this->apiAbsencesIdPutWithHttpInfo($id, $update_absence_request, $contentType); + return $response; + } + + /** + * Operation apiAbsencesIdPutWithHttpInfo + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateAbsenceRequest|null $update_absence_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesIdPut'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\AbsenceResponse, HTTP status code, HTTP response headers (array of strings) + */ + public function apiAbsencesIdPutWithHttpInfo($id, $update_absence_request = null, string $contentType = self::contentTypes['apiAbsencesIdPut'][0]) + { + $request = $this->apiAbsencesIdPutRequest($id, $update_absence_request, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\AbsenceResponse', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\AbsenceResponse', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\AbsenceResponse', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiAbsencesIdPutAsync + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateAbsenceRequest|null $update_absence_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesIdPut'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiAbsencesIdPutAsync($id, $update_absence_request = null, string $contentType = self::contentTypes['apiAbsencesIdPut'][0]) + { + return $this->apiAbsencesIdPutAsyncWithHttpInfo($id, $update_absence_request, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiAbsencesIdPutAsyncWithHttpInfo + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateAbsenceRequest|null $update_absence_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesIdPut'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiAbsencesIdPutAsyncWithHttpInfo($id, $update_absence_request = null, string $contentType = self::contentTypes['apiAbsencesIdPut'][0]) + { + $returnType = '\OmsorgCoreClient\Model\AbsenceResponse'; + $request = $this->apiAbsencesIdPutRequest($id, $update_absence_request, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiAbsencesIdPut' + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateAbsenceRequest|null $update_absence_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesIdPut'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiAbsencesIdPutRequest($id, $update_absence_request = null, string $contentType = self::contentTypes['apiAbsencesIdPut'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiAbsencesIdPut' + ); + } + + + + $resourcePath = '/api/absences/{id}'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (isset($update_absence_request)) { + if (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the body + $httpBody = \GuzzleHttp\Utils::jsonEncode(ObjectSerializer::sanitizeForSerialization($update_absence_request)); + } else { + $httpBody = $update_absence_request; + } + } elseif (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'PUT', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiAbsencesPost + * + * @param \OmsorgCoreClient\Model\CreateAbsenceRequest|null $create_absence_request create_absence_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesPost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\AbsenceResponse + */ + public function apiAbsencesPost($create_absence_request = null, string $contentType = self::contentTypes['apiAbsencesPost'][0]) + { + list($response) = $this->apiAbsencesPostWithHttpInfo($create_absence_request, $contentType); + return $response; + } + + /** + * Operation apiAbsencesPostWithHttpInfo + * + * @param \OmsorgCoreClient\Model\CreateAbsenceRequest|null $create_absence_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesPost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\AbsenceResponse, HTTP status code, HTTP response headers (array of strings) + */ + public function apiAbsencesPostWithHttpInfo($create_absence_request = null, string $contentType = self::contentTypes['apiAbsencesPost'][0]) + { + $request = $this->apiAbsencesPostRequest($create_absence_request, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\AbsenceResponse', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\AbsenceResponse', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\AbsenceResponse', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiAbsencesPostAsync + * + * @param \OmsorgCoreClient\Model\CreateAbsenceRequest|null $create_absence_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiAbsencesPostAsync($create_absence_request = null, string $contentType = self::contentTypes['apiAbsencesPost'][0]) + { + return $this->apiAbsencesPostAsyncWithHttpInfo($create_absence_request, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiAbsencesPostAsyncWithHttpInfo + * + * @param \OmsorgCoreClient\Model\CreateAbsenceRequest|null $create_absence_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiAbsencesPostAsyncWithHttpInfo($create_absence_request = null, string $contentType = self::contentTypes['apiAbsencesPost'][0]) + { + $returnType = '\OmsorgCoreClient\Model\AbsenceResponse'; + $request = $this->apiAbsencesPostRequest($create_absence_request, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiAbsencesPost' + * + * @param \OmsorgCoreClient\Model\CreateAbsenceRequest|null $create_absence_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiAbsencesPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiAbsencesPostRequest($create_absence_request = null, string $contentType = self::contentTypes['apiAbsencesPost'][0]) + { + + + + $resourcePath = '/api/absences'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (isset($create_absence_request)) { + if (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the body + $httpBody = \GuzzleHttp\Utils::jsonEncode(ObjectSerializer::sanitizeForSerialization($create_absence_request)); + } else { + $httpBody = $create_absence_request; + } + } elseif (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'POST', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Create http client option + * + * @throws \RuntimeException on file opening failure + * @return array of http client options + */ + protected function createHttpClientOption() + { + $options = []; + if ($this->config->getDebug()) { + $options[RequestOptions::DEBUG] = fopen($this->config->getDebugFile(), 'a'); + if (!$options[RequestOptions::DEBUG]) { + throw new \RuntimeException('Failed to open the debug file: ' . $this->config->getDebugFile()); + } + } + + return $options; + } + + private function handleResponseWithDataType( + string $dataType, + RequestInterface $request, + ResponseInterface $response + ): array { + if ($dataType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($dataType !== 'string') { + try { + $content = json_decode($content, false, 512, JSON_THROW_ON_ERROR); + } catch (\JsonException $exception) { + throw new ApiException( + sprintf( + 'Error JSON decoding server response (%s)', + $request->getUri() + ), + $response->getStatusCode(), + $response->getHeaders(), + $content + ); + } + } + } + + return [ + ObjectSerializer::deserialize($content, $dataType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + } + + private function responseWithinRangeCode( + string $rangeCode, + int $statusCode + ): bool { + $left = (int) ($rangeCode[0].'00'); + $right = (int) ($rangeCode[0].'99'); + + return $statusCode >= $left && $statusCode <= $right; + } +} diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/AuditLogApi.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/AuditLogApi.php index 4904b6d..0eb3a7b 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/AuditLogApi.php +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/AuditLogApi.php @@ -131,6 +131,7 @@ class AuditLogApi * @param string|null $entity_type entity_type (optional) * @param string|null $entity_id entity_id (optional) * @param string|null $actor_user_id actor_user_id (optional) + * @param \OmsorgCoreClient\Model\AuditEventCategory|null $category category (optional) * @param \DateTime|null $from_utc from_utc (optional) * @param \DateTime|null $to_utc to_utc (optional) * @param int|null $page page (optional, default to 1) @@ -141,9 +142,9 @@ class AuditLogApi * @throws \InvalidArgumentException * @return \OmsorgCoreClient\Model\AuditLogEntryResponsePagedResponse */ - public function apiAuditLogGet($entity_type = null, $entity_id = null, $actor_user_id = null, $from_utc = null, $to_utc = null, $page = 1, $page_size = 50, string $contentType = self::contentTypes['apiAuditLogGet'][0]) + public function apiAuditLogGet($entity_type = null, $entity_id = null, $actor_user_id = null, $category = null, $from_utc = null, $to_utc = null, $page = 1, $page_size = 50, string $contentType = self::contentTypes['apiAuditLogGet'][0]) { - list($response) = $this->apiAuditLogGetWithHttpInfo($entity_type, $entity_id, $actor_user_id, $from_utc, $to_utc, $page, $page_size, $contentType); + list($response) = $this->apiAuditLogGetWithHttpInfo($entity_type, $entity_id, $actor_user_id, $category, $from_utc, $to_utc, $page, $page_size, $contentType); return $response; } @@ -153,6 +154,7 @@ class AuditLogApi * @param string|null $entity_type (optional) * @param string|null $entity_id (optional) * @param string|null $actor_user_id (optional) + * @param \OmsorgCoreClient\Model\AuditEventCategory|null $category (optional) * @param \DateTime|null $from_utc (optional) * @param \DateTime|null $to_utc (optional) * @param int|null $page (optional, default to 1) @@ -163,9 +165,9 @@ class AuditLogApi * @throws \InvalidArgumentException * @return array of \OmsorgCoreClient\Model\AuditLogEntryResponsePagedResponse, HTTP status code, HTTP response headers (array of strings) */ - public function apiAuditLogGetWithHttpInfo($entity_type = null, $entity_id = null, $actor_user_id = null, $from_utc = null, $to_utc = null, $page = 1, $page_size = 50, string $contentType = self::contentTypes['apiAuditLogGet'][0]) + public function apiAuditLogGetWithHttpInfo($entity_type = null, $entity_id = null, $actor_user_id = null, $category = null, $from_utc = null, $to_utc = null, $page = 1, $page_size = 50, string $contentType = self::contentTypes['apiAuditLogGet'][0]) { - $request = $this->apiAuditLogGetRequest($entity_type, $entity_id, $actor_user_id, $from_utc, $to_utc, $page, $page_size, $contentType); + $request = $this->apiAuditLogGetRequest($entity_type, $entity_id, $actor_user_id, $category, $from_utc, $to_utc, $page, $page_size, $contentType); try { $options = $this->createHttpClientOption(); @@ -242,6 +244,7 @@ class AuditLogApi * @param string|null $entity_type (optional) * @param string|null $entity_id (optional) * @param string|null $actor_user_id (optional) + * @param \OmsorgCoreClient\Model\AuditEventCategory|null $category (optional) * @param \DateTime|null $from_utc (optional) * @param \DateTime|null $to_utc (optional) * @param int|null $page (optional, default to 1) @@ -251,9 +254,9 @@ class AuditLogApi * @throws \InvalidArgumentException * @return \GuzzleHttp\Promise\PromiseInterface */ - public function apiAuditLogGetAsync($entity_type = null, $entity_id = null, $actor_user_id = null, $from_utc = null, $to_utc = null, $page = 1, $page_size = 50, string $contentType = self::contentTypes['apiAuditLogGet'][0]) + public function apiAuditLogGetAsync($entity_type = null, $entity_id = null, $actor_user_id = null, $category = null, $from_utc = null, $to_utc = null, $page = 1, $page_size = 50, string $contentType = self::contentTypes['apiAuditLogGet'][0]) { - return $this->apiAuditLogGetAsyncWithHttpInfo($entity_type, $entity_id, $actor_user_id, $from_utc, $to_utc, $page, $page_size, $contentType) + return $this->apiAuditLogGetAsyncWithHttpInfo($entity_type, $entity_id, $actor_user_id, $category, $from_utc, $to_utc, $page, $page_size, $contentType) ->then( function ($response) { return $response[0]; @@ -267,6 +270,7 @@ class AuditLogApi * @param string|null $entity_type (optional) * @param string|null $entity_id (optional) * @param string|null $actor_user_id (optional) + * @param \OmsorgCoreClient\Model\AuditEventCategory|null $category (optional) * @param \DateTime|null $from_utc (optional) * @param \DateTime|null $to_utc (optional) * @param int|null $page (optional, default to 1) @@ -276,10 +280,10 @@ class AuditLogApi * @throws \InvalidArgumentException * @return \GuzzleHttp\Promise\PromiseInterface */ - public function apiAuditLogGetAsyncWithHttpInfo($entity_type = null, $entity_id = null, $actor_user_id = null, $from_utc = null, $to_utc = null, $page = 1, $page_size = 50, string $contentType = self::contentTypes['apiAuditLogGet'][0]) + public function apiAuditLogGetAsyncWithHttpInfo($entity_type = null, $entity_id = null, $actor_user_id = null, $category = null, $from_utc = null, $to_utc = null, $page = 1, $page_size = 50, string $contentType = self::contentTypes['apiAuditLogGet'][0]) { $returnType = '\OmsorgCoreClient\Model\AuditLogEntryResponsePagedResponse'; - $request = $this->apiAuditLogGetRequest($entity_type, $entity_id, $actor_user_id, $from_utc, $to_utc, $page, $page_size, $contentType); + $request = $this->apiAuditLogGetRequest($entity_type, $entity_id, $actor_user_id, $category, $from_utc, $to_utc, $page, $page_size, $contentType); return $this->client ->sendAsync($request, $this->createHttpClientOption()) @@ -323,6 +327,7 @@ class AuditLogApi * @param string|null $entity_type (optional) * @param string|null $entity_id (optional) * @param string|null $actor_user_id (optional) + * @param \OmsorgCoreClient\Model\AuditEventCategory|null $category (optional) * @param \DateTime|null $from_utc (optional) * @param \DateTime|null $to_utc (optional) * @param int|null $page (optional, default to 1) @@ -332,7 +337,7 @@ class AuditLogApi * @throws \InvalidArgumentException * @return \GuzzleHttp\Psr7\Request */ - public function apiAuditLogGetRequest($entity_type = null, $entity_id = null, $actor_user_id = null, $from_utc = null, $to_utc = null, $page = 1, $page_size = 50, string $contentType = self::contentTypes['apiAuditLogGet'][0]) + public function apiAuditLogGetRequest($entity_type = null, $entity_id = null, $actor_user_id = null, $category = null, $from_utc = null, $to_utc = null, $page = 1, $page_size = 50, string $contentType = self::contentTypes['apiAuditLogGet'][0]) { @@ -343,6 +348,7 @@ class AuditLogApi + $resourcePath = '/api/audit-log'; $formParams = []; $queryParams = []; @@ -378,6 +384,15 @@ class AuditLogApi false // required ) ?? []); // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $category, + 'category', // param base name + 'AuditEventCategory', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + // query params $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( $from_utc, 'fromUtc', // param base name diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/ContractsApi.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/ContractsApi.php index 8c0c432..48a8e03 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/ContractsApi.php +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/ContractsApi.php @@ -77,6 +77,9 @@ class ContractsApi 'apiContractsGet' => [ 'application/json', ], + 'apiContractsIdDelete' => [ + 'application/json', + ], 'apiContractsIdGet' => [ 'application/json', ], @@ -472,6 +475,220 @@ class ContractsApi ); } + /** + * Operation apiContractsIdDelete + * + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiContractsIdDelete'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return void + */ + public function apiContractsIdDelete($id, string $contentType = self::contentTypes['apiContractsIdDelete'][0]) + { + $this->apiContractsIdDeleteWithHttpInfo($id, $contentType); + } + + /** + * Operation apiContractsIdDeleteWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiContractsIdDelete'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of null, HTTP status code, HTTP response headers (array of strings) + */ + public function apiContractsIdDeleteWithHttpInfo($id, string $contentType = self::contentTypes['apiContractsIdDelete'][0]) + { + $request = $this->apiContractsIdDeleteRequest($id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + return [null, $statusCode, $response->getHeaders()]; + } catch (ApiException $e) { + switch ($e->getCode()) { + } + + + throw $e; + } + } + + /** + * Operation apiContractsIdDeleteAsync + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiContractsIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiContractsIdDeleteAsync($id, string $contentType = self::contentTypes['apiContractsIdDelete'][0]) + { + return $this->apiContractsIdDeleteAsyncWithHttpInfo($id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiContractsIdDeleteAsyncWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiContractsIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiContractsIdDeleteAsyncWithHttpInfo($id, string $contentType = self::contentTypes['apiContractsIdDelete'][0]) + { + $returnType = ''; + $request = $this->apiContractsIdDeleteRequest($id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + return [null, $response->getStatusCode(), $response->getHeaders()]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiContractsIdDelete' + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiContractsIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiContractsIdDeleteRequest($id, string $contentType = self::contentTypes['apiContractsIdDelete'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiContractsIdDelete' + ); + } + + + $resourcePath = '/api/contracts/{id}'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + [], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'DELETE', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + /** * Operation apiContractsIdGet * diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/DocumentsApi.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/DocumentsApi.php new file mode 100644 index 0000000..f55c9cd --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/DocumentsApi.php @@ -0,0 +1,1470 @@ + [ + 'application/json', + ], + 'apiDocumentsIdDelete' => [ + 'application/json', + ], + 'apiDocumentsIdDownloadGet' => [ + 'application/json', + ], + 'apiDocumentsIdPut' => [ + 'application/json', + 'text/json', + 'application/*+json', + ], + 'apiDocumentsPost' => [ + 'multipart/form-data', + ], + ]; + + /** + * @param ClientInterface $client + * @param Configuration $config + * @param HeaderSelector $selector + * @param int $hostIndex (Optional) host index to select the list of hosts if defined in the OpenAPI spec + */ + public function __construct( + ?ClientInterface $client = null, + ?Configuration $config = null, + ?HeaderSelector $selector = null, + int $hostIndex = 0 + ) { + $this->client = $client ?: new Client(); + $this->config = $config ?: Configuration::getDefaultConfiguration(); + $this->headerSelector = $selector ?: new HeaderSelector(); + $this->hostIndex = $hostIndex; + } + + /** + * Set the host index + * + * @param int $hostIndex Host index (required) + */ + public function setHostIndex($hostIndex): void + { + $this->hostIndex = $hostIndex; + } + + /** + * Get the host index + * + * @return int Host index + */ + public function getHostIndex() + { + return $this->hostIndex; + } + + /** + * @return Configuration + */ + public function getConfig() + { + return $this->config; + } + + /** + * Operation apiDocumentsGet + * + * @param string|null $entity_type entity_type (optional) + * @param string|null $entity_id entity_id (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\DocumentResponse[] + */ + public function apiDocumentsGet($entity_type = null, $entity_id = null, string $contentType = self::contentTypes['apiDocumentsGet'][0]) + { + list($response) = $this->apiDocumentsGetWithHttpInfo($entity_type, $entity_id, $contentType); + return $response; + } + + /** + * Operation apiDocumentsGetWithHttpInfo + * + * @param string|null $entity_type (optional) + * @param string|null $entity_id (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\DocumentResponse[], HTTP status code, HTTP response headers (array of strings) + */ + public function apiDocumentsGetWithHttpInfo($entity_type = null, $entity_id = null, string $contentType = self::contentTypes['apiDocumentsGet'][0]) + { + $request = $this->apiDocumentsGetRequest($entity_type, $entity_id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\DocumentResponse[]', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\DocumentResponse[]', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\DocumentResponse[]', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiDocumentsGetAsync + * + * @param string|null $entity_type (optional) + * @param string|null $entity_id (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiDocumentsGetAsync($entity_type = null, $entity_id = null, string $contentType = self::contentTypes['apiDocumentsGet'][0]) + { + return $this->apiDocumentsGetAsyncWithHttpInfo($entity_type, $entity_id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiDocumentsGetAsyncWithHttpInfo + * + * @param string|null $entity_type (optional) + * @param string|null $entity_id (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiDocumentsGetAsyncWithHttpInfo($entity_type = null, $entity_id = null, string $contentType = self::contentTypes['apiDocumentsGet'][0]) + { + $returnType = '\OmsorgCoreClient\Model\DocumentResponse[]'; + $request = $this->apiDocumentsGetRequest($entity_type, $entity_id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiDocumentsGet' + * + * @param string|null $entity_type (optional) + * @param string|null $entity_id (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiDocumentsGetRequest($entity_type = null, $entity_id = null, string $contentType = self::contentTypes['apiDocumentsGet'][0]) + { + + + + + $resourcePath = '/api/documents'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $entity_type, + 'entityType', // param base name + 'string', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $entity_id, + 'entityId', // param base name + 'string', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + + + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'GET', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiDocumentsIdDelete + * + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsIdDelete'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return void + */ + public function apiDocumentsIdDelete($id, string $contentType = self::contentTypes['apiDocumentsIdDelete'][0]) + { + $this->apiDocumentsIdDeleteWithHttpInfo($id, $contentType); + } + + /** + * Operation apiDocumentsIdDeleteWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsIdDelete'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of null, HTTP status code, HTTP response headers (array of strings) + */ + public function apiDocumentsIdDeleteWithHttpInfo($id, string $contentType = self::contentTypes['apiDocumentsIdDelete'][0]) + { + $request = $this->apiDocumentsIdDeleteRequest($id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + return [null, $statusCode, $response->getHeaders()]; + } catch (ApiException $e) { + switch ($e->getCode()) { + } + + + throw $e; + } + } + + /** + * Operation apiDocumentsIdDeleteAsync + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiDocumentsIdDeleteAsync($id, string $contentType = self::contentTypes['apiDocumentsIdDelete'][0]) + { + return $this->apiDocumentsIdDeleteAsyncWithHttpInfo($id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiDocumentsIdDeleteAsyncWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiDocumentsIdDeleteAsyncWithHttpInfo($id, string $contentType = self::contentTypes['apiDocumentsIdDelete'][0]) + { + $returnType = ''; + $request = $this->apiDocumentsIdDeleteRequest($id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + return [null, $response->getStatusCode(), $response->getHeaders()]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiDocumentsIdDelete' + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiDocumentsIdDeleteRequest($id, string $contentType = self::contentTypes['apiDocumentsIdDelete'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiDocumentsIdDelete' + ); + } + + + $resourcePath = '/api/documents/{id}'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + [], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'DELETE', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiDocumentsIdDownloadGet + * + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsIdDownloadGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return void + */ + public function apiDocumentsIdDownloadGet($id, string $contentType = self::contentTypes['apiDocumentsIdDownloadGet'][0]) + { + $this->apiDocumentsIdDownloadGetWithHttpInfo($id, $contentType); + } + + /** + * Operation apiDocumentsIdDownloadGetWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsIdDownloadGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of null, HTTP status code, HTTP response headers (array of strings) + */ + public function apiDocumentsIdDownloadGetWithHttpInfo($id, string $contentType = self::contentTypes['apiDocumentsIdDownloadGet'][0]) + { + $request = $this->apiDocumentsIdDownloadGetRequest($id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + return [null, $statusCode, $response->getHeaders()]; + } catch (ApiException $e) { + switch ($e->getCode()) { + } + + + throw $e; + } + } + + /** + * Operation apiDocumentsIdDownloadGetAsync + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsIdDownloadGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiDocumentsIdDownloadGetAsync($id, string $contentType = self::contentTypes['apiDocumentsIdDownloadGet'][0]) + { + return $this->apiDocumentsIdDownloadGetAsyncWithHttpInfo($id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiDocumentsIdDownloadGetAsyncWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsIdDownloadGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiDocumentsIdDownloadGetAsyncWithHttpInfo($id, string $contentType = self::contentTypes['apiDocumentsIdDownloadGet'][0]) + { + $returnType = ''; + $request = $this->apiDocumentsIdDownloadGetRequest($id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + return [null, $response->getStatusCode(), $response->getHeaders()]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiDocumentsIdDownloadGet' + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsIdDownloadGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiDocumentsIdDownloadGetRequest($id, string $contentType = self::contentTypes['apiDocumentsIdDownloadGet'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiDocumentsIdDownloadGet' + ); + } + + + $resourcePath = '/api/documents/{id}/download'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + [], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'GET', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiDocumentsIdPut + * + * @param string $id id (required) + * @param \OmsorgCoreClient\Model\UpdateDocumentRequest|null $update_document_request update_document_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsIdPut'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\DocumentResponse + */ + public function apiDocumentsIdPut($id, $update_document_request = null, string $contentType = self::contentTypes['apiDocumentsIdPut'][0]) + { + list($response) = $this->apiDocumentsIdPutWithHttpInfo($id, $update_document_request, $contentType); + return $response; + } + + /** + * Operation apiDocumentsIdPutWithHttpInfo + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateDocumentRequest|null $update_document_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsIdPut'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\DocumentResponse, HTTP status code, HTTP response headers (array of strings) + */ + public function apiDocumentsIdPutWithHttpInfo($id, $update_document_request = null, string $contentType = self::contentTypes['apiDocumentsIdPut'][0]) + { + $request = $this->apiDocumentsIdPutRequest($id, $update_document_request, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\DocumentResponse', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\DocumentResponse', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\DocumentResponse', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiDocumentsIdPutAsync + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateDocumentRequest|null $update_document_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsIdPut'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiDocumentsIdPutAsync($id, $update_document_request = null, string $contentType = self::contentTypes['apiDocumentsIdPut'][0]) + { + return $this->apiDocumentsIdPutAsyncWithHttpInfo($id, $update_document_request, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiDocumentsIdPutAsyncWithHttpInfo + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateDocumentRequest|null $update_document_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsIdPut'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiDocumentsIdPutAsyncWithHttpInfo($id, $update_document_request = null, string $contentType = self::contentTypes['apiDocumentsIdPut'][0]) + { + $returnType = '\OmsorgCoreClient\Model\DocumentResponse'; + $request = $this->apiDocumentsIdPutRequest($id, $update_document_request, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiDocumentsIdPut' + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateDocumentRequest|null $update_document_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsIdPut'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiDocumentsIdPutRequest($id, $update_document_request = null, string $contentType = self::contentTypes['apiDocumentsIdPut'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiDocumentsIdPut' + ); + } + + + + $resourcePath = '/api/documents/{id}'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (isset($update_document_request)) { + if (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the body + $httpBody = \GuzzleHttp\Utils::jsonEncode(ObjectSerializer::sanitizeForSerialization($update_document_request)); + } else { + $httpBody = $update_document_request; + } + } elseif (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'PUT', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiDocumentsPost + * + * @param string|null $entity_type entity_type (optional) + * @param string|null $entity_id entity_id (optional) + * @param string|null $category category (optional) + * @param string|null $description description (optional) + * @param \SplFileObject|null $file file (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsPost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\DocumentResponse + */ + public function apiDocumentsPost($entity_type = null, $entity_id = null, $category = null, $description = null, $file = null, string $contentType = self::contentTypes['apiDocumentsPost'][0]) + { + list($response) = $this->apiDocumentsPostWithHttpInfo($entity_type, $entity_id, $category, $description, $file, $contentType); + return $response; + } + + /** + * Operation apiDocumentsPostWithHttpInfo + * + * @param string|null $entity_type (optional) + * @param string|null $entity_id (optional) + * @param string|null $category (optional) + * @param string|null $description (optional) + * @param \SplFileObject|null $file (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsPost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\DocumentResponse, HTTP status code, HTTP response headers (array of strings) + */ + public function apiDocumentsPostWithHttpInfo($entity_type = null, $entity_id = null, $category = null, $description = null, $file = null, string $contentType = self::contentTypes['apiDocumentsPost'][0]) + { + $request = $this->apiDocumentsPostRequest($entity_type, $entity_id, $category, $description, $file, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\DocumentResponse', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\DocumentResponse', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\DocumentResponse', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiDocumentsPostAsync + * + * @param string|null $entity_type (optional) + * @param string|null $entity_id (optional) + * @param string|null $category (optional) + * @param string|null $description (optional) + * @param \SplFileObject|null $file (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiDocumentsPostAsync($entity_type = null, $entity_id = null, $category = null, $description = null, $file = null, string $contentType = self::contentTypes['apiDocumentsPost'][0]) + { + return $this->apiDocumentsPostAsyncWithHttpInfo($entity_type, $entity_id, $category, $description, $file, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiDocumentsPostAsyncWithHttpInfo + * + * @param string|null $entity_type (optional) + * @param string|null $entity_id (optional) + * @param string|null $category (optional) + * @param string|null $description (optional) + * @param \SplFileObject|null $file (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiDocumentsPostAsyncWithHttpInfo($entity_type = null, $entity_id = null, $category = null, $description = null, $file = null, string $contentType = self::contentTypes['apiDocumentsPost'][0]) + { + $returnType = '\OmsorgCoreClient\Model\DocumentResponse'; + $request = $this->apiDocumentsPostRequest($entity_type, $entity_id, $category, $description, $file, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiDocumentsPost' + * + * @param string|null $entity_type (optional) + * @param string|null $entity_id (optional) + * @param string|null $category (optional) + * @param string|null $description (optional) + * @param \SplFileObject|null $file (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiDocumentsPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiDocumentsPostRequest($entity_type = null, $entity_id = null, $category = null, $description = null, $file = null, string $contentType = self::contentTypes['apiDocumentsPost'][0]) + { + + + + + + + + $resourcePath = '/api/documents'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + + // form params + $formDataProcessor = new FormDataProcessor(); + + $formData = $formDataProcessor->prepare([ + 'entity_type' => $entity_type, + 'entity_id' => $entity_id, + 'category' => $category, + 'description' => $description, + 'file' => $file, + ]); + + $formParams = $formDataProcessor->flatten($formData); + $multipart = $formDataProcessor->has_file; + + $multipart = true; + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'POST', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Create http client option + * + * @throws \RuntimeException on file opening failure + * @return array of http client options + */ + protected function createHttpClientOption() + { + $options = []; + if ($this->config->getDebug()) { + $options[RequestOptions::DEBUG] = fopen($this->config->getDebugFile(), 'a'); + if (!$options[RequestOptions::DEBUG]) { + throw new \RuntimeException('Failed to open the debug file: ' . $this->config->getDebugFile()); + } + } + + return $options; + } + + private function handleResponseWithDataType( + string $dataType, + RequestInterface $request, + ResponseInterface $response + ): array { + if ($dataType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($dataType !== 'string') { + try { + $content = json_decode($content, false, 512, JSON_THROW_ON_ERROR); + } catch (\JsonException $exception) { + throw new ApiException( + sprintf( + 'Error JSON decoding server response (%s)', + $request->getUri() + ), + $response->getStatusCode(), + $response->getHeaders(), + $content + ); + } + } + } + + return [ + ObjectSerializer::deserialize($content, $dataType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + } + + private function responseWithinRangeCode( + string $rangeCode, + int $statusCode + ): bool { + $left = (int) ($rangeCode[0].'00'); + $right = (int) ($rangeCode[0].'99'); + + return $statusCode >= $left && $statusCode <= $right; + } +} diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/EmployeesApi.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/EmployeesApi.php index c9f4f54..b56fee5 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/EmployeesApi.php +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/EmployeesApi.php @@ -77,6 +77,9 @@ class EmployeesApi 'apiEmployeesGet' => [ 'application/json', ], + 'apiEmployeesIdDelete' => [ + 'application/json', + ], 'apiEmployeesIdGet' => [ 'application/json', ], @@ -457,6 +460,220 @@ class EmployeesApi ); } + /** + * Operation apiEmployeesIdDelete + * + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiEmployeesIdDelete'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return void + */ + public function apiEmployeesIdDelete($id, string $contentType = self::contentTypes['apiEmployeesIdDelete'][0]) + { + $this->apiEmployeesIdDeleteWithHttpInfo($id, $contentType); + } + + /** + * Operation apiEmployeesIdDeleteWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiEmployeesIdDelete'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of null, HTTP status code, HTTP response headers (array of strings) + */ + public function apiEmployeesIdDeleteWithHttpInfo($id, string $contentType = self::contentTypes['apiEmployeesIdDelete'][0]) + { + $request = $this->apiEmployeesIdDeleteRequest($id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + return [null, $statusCode, $response->getHeaders()]; + } catch (ApiException $e) { + switch ($e->getCode()) { + } + + + throw $e; + } + } + + /** + * Operation apiEmployeesIdDeleteAsync + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiEmployeesIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiEmployeesIdDeleteAsync($id, string $contentType = self::contentTypes['apiEmployeesIdDelete'][0]) + { + return $this->apiEmployeesIdDeleteAsyncWithHttpInfo($id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiEmployeesIdDeleteAsyncWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiEmployeesIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiEmployeesIdDeleteAsyncWithHttpInfo($id, string $contentType = self::contentTypes['apiEmployeesIdDelete'][0]) + { + $returnType = ''; + $request = $this->apiEmployeesIdDeleteRequest($id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + return [null, $response->getStatusCode(), $response->getHeaders()]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiEmployeesIdDelete' + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiEmployeesIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiEmployeesIdDeleteRequest($id, string $contentType = self::contentTypes['apiEmployeesIdDelete'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiEmployeesIdDelete' + ); + } + + + $resourcePath = '/api/employees/{id}'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + [], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'DELETE', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + /** * Operation apiEmployeesIdGet * diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/FacilitiesApi.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/FacilitiesApi.php index 84cf46c..43e124f 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/FacilitiesApi.php +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/FacilitiesApi.php @@ -77,6 +77,9 @@ class FacilitiesApi 'apiFacilitiesGet' => [ 'application/json', ], + 'apiFacilitiesIdDelete' => [ + 'application/json', + ], 'apiFacilitiesIdGet' => [ 'application/json', ], @@ -143,6 +146,7 @@ class FacilitiesApi * * @param string|null $search search (optional) * @param string|null $crm_status crm_status (optional) + * @param bool|null $follow_up_due_only follow_up_due_only (optional, default to false) * @param int|null $page page (optional, default to 1) * @param int|null $page_size page_size (optional, default to 20) * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesGet'] to see the possible values for this operation @@ -151,9 +155,9 @@ class FacilitiesApi * @throws \InvalidArgumentException * @return \OmsorgCoreClient\Model\FacilityResponsePagedResponse */ - public function apiFacilitiesGet($search = null, $crm_status = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiFacilitiesGet'][0]) + public function apiFacilitiesGet($search = null, $crm_status = null, $follow_up_due_only = false, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiFacilitiesGet'][0]) { - list($response) = $this->apiFacilitiesGetWithHttpInfo($search, $crm_status, $page, $page_size, $contentType); + list($response) = $this->apiFacilitiesGetWithHttpInfo($search, $crm_status, $follow_up_due_only, $page, $page_size, $contentType); return $response; } @@ -162,6 +166,7 @@ class FacilitiesApi * * @param string|null $search (optional) * @param string|null $crm_status (optional) + * @param bool|null $follow_up_due_only (optional, default to false) * @param int|null $page (optional, default to 1) * @param int|null $page_size (optional, default to 20) * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesGet'] to see the possible values for this operation @@ -170,9 +175,9 @@ class FacilitiesApi * @throws \InvalidArgumentException * @return array of \OmsorgCoreClient\Model\FacilityResponsePagedResponse, HTTP status code, HTTP response headers (array of strings) */ - public function apiFacilitiesGetWithHttpInfo($search = null, $crm_status = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiFacilitiesGet'][0]) + public function apiFacilitiesGetWithHttpInfo($search = null, $crm_status = null, $follow_up_due_only = false, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiFacilitiesGet'][0]) { - $request = $this->apiFacilitiesGetRequest($search, $crm_status, $page, $page_size, $contentType); + $request = $this->apiFacilitiesGetRequest($search, $crm_status, $follow_up_due_only, $page, $page_size, $contentType); try { $options = $this->createHttpClientOption(); @@ -248,6 +253,7 @@ class FacilitiesApi * * @param string|null $search (optional) * @param string|null $crm_status (optional) + * @param bool|null $follow_up_due_only (optional, default to false) * @param int|null $page (optional, default to 1) * @param int|null $page_size (optional, default to 20) * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesGet'] to see the possible values for this operation @@ -255,9 +261,9 @@ class FacilitiesApi * @throws \InvalidArgumentException * @return \GuzzleHttp\Promise\PromiseInterface */ - public function apiFacilitiesGetAsync($search = null, $crm_status = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiFacilitiesGet'][0]) + public function apiFacilitiesGetAsync($search = null, $crm_status = null, $follow_up_due_only = false, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiFacilitiesGet'][0]) { - return $this->apiFacilitiesGetAsyncWithHttpInfo($search, $crm_status, $page, $page_size, $contentType) + return $this->apiFacilitiesGetAsyncWithHttpInfo($search, $crm_status, $follow_up_due_only, $page, $page_size, $contentType) ->then( function ($response) { return $response[0]; @@ -270,6 +276,7 @@ class FacilitiesApi * * @param string|null $search (optional) * @param string|null $crm_status (optional) + * @param bool|null $follow_up_due_only (optional, default to false) * @param int|null $page (optional, default to 1) * @param int|null $page_size (optional, default to 20) * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesGet'] to see the possible values for this operation @@ -277,10 +284,10 @@ class FacilitiesApi * @throws \InvalidArgumentException * @return \GuzzleHttp\Promise\PromiseInterface */ - public function apiFacilitiesGetAsyncWithHttpInfo($search = null, $crm_status = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiFacilitiesGet'][0]) + public function apiFacilitiesGetAsyncWithHttpInfo($search = null, $crm_status = null, $follow_up_due_only = false, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiFacilitiesGet'][0]) { $returnType = '\OmsorgCoreClient\Model\FacilityResponsePagedResponse'; - $request = $this->apiFacilitiesGetRequest($search, $crm_status, $page, $page_size, $contentType); + $request = $this->apiFacilitiesGetRequest($search, $crm_status, $follow_up_due_only, $page, $page_size, $contentType); return $this->client ->sendAsync($request, $this->createHttpClientOption()) @@ -323,6 +330,7 @@ class FacilitiesApi * * @param string|null $search (optional) * @param string|null $crm_status (optional) + * @param bool|null $follow_up_due_only (optional, default to false) * @param int|null $page (optional, default to 1) * @param int|null $page_size (optional, default to 20) * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesGet'] to see the possible values for this operation @@ -330,7 +338,7 @@ class FacilitiesApi * @throws \InvalidArgumentException * @return \GuzzleHttp\Psr7\Request */ - public function apiFacilitiesGetRequest($search = null, $crm_status = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiFacilitiesGet'][0]) + public function apiFacilitiesGetRequest($search = null, $crm_status = null, $follow_up_due_only = false, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiFacilitiesGet'][0]) { @@ -338,6 +346,7 @@ class FacilitiesApi + $resourcePath = '/api/facilities'; $formParams = []; $queryParams = []; @@ -364,6 +373,15 @@ class FacilitiesApi false // required ) ?? []); // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $follow_up_due_only, + 'followUpDueOnly', // param base name + 'boolean', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + // query params $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( $page, 'page', // param base name @@ -442,6 +460,220 @@ class FacilitiesApi ); } + /** + * Operation apiFacilitiesIdDelete + * + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesIdDelete'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return void + */ + public function apiFacilitiesIdDelete($id, string $contentType = self::contentTypes['apiFacilitiesIdDelete'][0]) + { + $this->apiFacilitiesIdDeleteWithHttpInfo($id, $contentType); + } + + /** + * Operation apiFacilitiesIdDeleteWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesIdDelete'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of null, HTTP status code, HTTP response headers (array of strings) + */ + public function apiFacilitiesIdDeleteWithHttpInfo($id, string $contentType = self::contentTypes['apiFacilitiesIdDelete'][0]) + { + $request = $this->apiFacilitiesIdDeleteRequest($id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + return [null, $statusCode, $response->getHeaders()]; + } catch (ApiException $e) { + switch ($e->getCode()) { + } + + + throw $e; + } + } + + /** + * Operation apiFacilitiesIdDeleteAsync + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiFacilitiesIdDeleteAsync($id, string $contentType = self::contentTypes['apiFacilitiesIdDelete'][0]) + { + return $this->apiFacilitiesIdDeleteAsyncWithHttpInfo($id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiFacilitiesIdDeleteAsyncWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiFacilitiesIdDeleteAsyncWithHttpInfo($id, string $contentType = self::contentTypes['apiFacilitiesIdDelete'][0]) + { + $returnType = ''; + $request = $this->apiFacilitiesIdDeleteRequest($id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + return [null, $response->getStatusCode(), $response->getHeaders()]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiFacilitiesIdDelete' + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiFacilitiesIdDeleteRequest($id, string $contentType = self::contentTypes['apiFacilitiesIdDelete'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiFacilitiesIdDelete' + ); + } + + + $resourcePath = '/api/facilities/{id}'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + [], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'DELETE', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + /** * Operation apiFacilitiesIdGet * diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/FacilityContactsApi.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/FacilityContactsApi.php index 5bf8051..90bec9d 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/FacilityContactsApi.php +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/FacilityContactsApi.php @@ -77,6 +77,9 @@ class FacilityContactsApi 'apiFacilitiesFacilityIdContactsGet' => [ 'application/json', ], + 'apiFacilitiesFacilityIdContactsIdDelete' => [ + 'application/json', + ], 'apiFacilitiesFacilityIdContactsIdPut' => [ 'application/json', 'text/json', @@ -399,6 +402,240 @@ class FacilityContactsApi ); } + /** + * Operation apiFacilitiesFacilityIdContactsIdDelete + * + * @param string $facility_id facility_id (required) + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdContactsIdDelete'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return void + */ + public function apiFacilitiesFacilityIdContactsIdDelete($facility_id, $id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdContactsIdDelete'][0]) + { + $this->apiFacilitiesFacilityIdContactsIdDeleteWithHttpInfo($facility_id, $id, $contentType); + } + + /** + * Operation apiFacilitiesFacilityIdContactsIdDeleteWithHttpInfo + * + * @param string $facility_id (required) + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdContactsIdDelete'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of null, HTTP status code, HTTP response headers (array of strings) + */ + public function apiFacilitiesFacilityIdContactsIdDeleteWithHttpInfo($facility_id, $id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdContactsIdDelete'][0]) + { + $request = $this->apiFacilitiesFacilityIdContactsIdDeleteRequest($facility_id, $id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + return [null, $statusCode, $response->getHeaders()]; + } catch (ApiException $e) { + switch ($e->getCode()) { + } + + + throw $e; + } + } + + /** + * Operation apiFacilitiesFacilityIdContactsIdDeleteAsync + * + * @param string $facility_id (required) + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdContactsIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiFacilitiesFacilityIdContactsIdDeleteAsync($facility_id, $id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdContactsIdDelete'][0]) + { + return $this->apiFacilitiesFacilityIdContactsIdDeleteAsyncWithHttpInfo($facility_id, $id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiFacilitiesFacilityIdContactsIdDeleteAsyncWithHttpInfo + * + * @param string $facility_id (required) + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdContactsIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiFacilitiesFacilityIdContactsIdDeleteAsyncWithHttpInfo($facility_id, $id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdContactsIdDelete'][0]) + { + $returnType = ''; + $request = $this->apiFacilitiesFacilityIdContactsIdDeleteRequest($facility_id, $id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + return [null, $response->getStatusCode(), $response->getHeaders()]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiFacilitiesFacilityIdContactsIdDelete' + * + * @param string $facility_id (required) + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdContactsIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiFacilitiesFacilityIdContactsIdDeleteRequest($facility_id, $id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdContactsIdDelete'][0]) + { + + // verify the required parameter 'facility_id' is set + if ($facility_id === null || (is_array($facility_id) && count($facility_id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $facility_id when calling apiFacilitiesFacilityIdContactsIdDelete' + ); + } + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiFacilitiesFacilityIdContactsIdDelete' + ); + } + + + $resourcePath = '/api/facilities/{facilityId}/contacts/{id}'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($facility_id !== null) { + $resourcePath = str_replace( + '{' . 'facilityId' . '}', + ObjectSerializer::toPathValue($facility_id), + $resourcePath + ); + } + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + [], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'DELETE', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + /** * Operation apiFacilitiesFacilityIdContactsIdPut * diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/FacilityQualificationRatesApi.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/FacilityQualificationRatesApi.php new file mode 100644 index 0000000..d03bcc8 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/FacilityQualificationRatesApi.php @@ -0,0 +1,1274 @@ + [ + 'application/json', + ], + 'apiFacilitiesFacilityIdQualificationRatesIdDelete' => [ + 'application/json', + ], + 'apiFacilitiesFacilityIdQualificationRatesIdPut' => [ + 'application/json', + 'text/json', + 'application/*+json', + ], + 'apiFacilitiesFacilityIdQualificationRatesPost' => [ + 'application/json', + 'text/json', + 'application/*+json', + ], + ]; + + /** + * @param ClientInterface $client + * @param Configuration $config + * @param HeaderSelector $selector + * @param int $hostIndex (Optional) host index to select the list of hosts if defined in the OpenAPI spec + */ + public function __construct( + ?ClientInterface $client = null, + ?Configuration $config = null, + ?HeaderSelector $selector = null, + int $hostIndex = 0 + ) { + $this->client = $client ?: new Client(); + $this->config = $config ?: Configuration::getDefaultConfiguration(); + $this->headerSelector = $selector ?: new HeaderSelector(); + $this->hostIndex = $hostIndex; + } + + /** + * Set the host index + * + * @param int $hostIndex Host index (required) + */ + public function setHostIndex($hostIndex): void + { + $this->hostIndex = $hostIndex; + } + + /** + * Get the host index + * + * @return int Host index + */ + public function getHostIndex() + { + return $this->hostIndex; + } + + /** + * @return Configuration + */ + public function getConfig() + { + return $this->config; + } + + /** + * Operation apiFacilitiesFacilityIdQualificationRatesGet + * + * @param string $facility_id facility_id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdQualificationRatesGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\FacilityQualificationRateResponse[] + */ + public function apiFacilitiesFacilityIdQualificationRatesGet($facility_id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdQualificationRatesGet'][0]) + { + list($response) = $this->apiFacilitiesFacilityIdQualificationRatesGetWithHttpInfo($facility_id, $contentType); + return $response; + } + + /** + * Operation apiFacilitiesFacilityIdQualificationRatesGetWithHttpInfo + * + * @param string $facility_id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdQualificationRatesGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\FacilityQualificationRateResponse[], HTTP status code, HTTP response headers (array of strings) + */ + public function apiFacilitiesFacilityIdQualificationRatesGetWithHttpInfo($facility_id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdQualificationRatesGet'][0]) + { + $request = $this->apiFacilitiesFacilityIdQualificationRatesGetRequest($facility_id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\FacilityQualificationRateResponse[]', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\FacilityQualificationRateResponse[]', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\FacilityQualificationRateResponse[]', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiFacilitiesFacilityIdQualificationRatesGetAsync + * + * @param string $facility_id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdQualificationRatesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiFacilitiesFacilityIdQualificationRatesGetAsync($facility_id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdQualificationRatesGet'][0]) + { + return $this->apiFacilitiesFacilityIdQualificationRatesGetAsyncWithHttpInfo($facility_id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiFacilitiesFacilityIdQualificationRatesGetAsyncWithHttpInfo + * + * @param string $facility_id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdQualificationRatesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiFacilitiesFacilityIdQualificationRatesGetAsyncWithHttpInfo($facility_id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdQualificationRatesGet'][0]) + { + $returnType = '\OmsorgCoreClient\Model\FacilityQualificationRateResponse[]'; + $request = $this->apiFacilitiesFacilityIdQualificationRatesGetRequest($facility_id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiFacilitiesFacilityIdQualificationRatesGet' + * + * @param string $facility_id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdQualificationRatesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiFacilitiesFacilityIdQualificationRatesGetRequest($facility_id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdQualificationRatesGet'][0]) + { + + // verify the required parameter 'facility_id' is set + if ($facility_id === null || (is_array($facility_id) && count($facility_id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $facility_id when calling apiFacilitiesFacilityIdQualificationRatesGet' + ); + } + + + $resourcePath = '/api/facilities/{facilityId}/qualification-rates'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($facility_id !== null) { + $resourcePath = str_replace( + '{' . 'facilityId' . '}', + ObjectSerializer::toPathValue($facility_id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'GET', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiFacilitiesFacilityIdQualificationRatesIdDelete + * + * @param string $facility_id facility_id (required) + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdQualificationRatesIdDelete'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return void + */ + public function apiFacilitiesFacilityIdQualificationRatesIdDelete($facility_id, $id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdQualificationRatesIdDelete'][0]) + { + $this->apiFacilitiesFacilityIdQualificationRatesIdDeleteWithHttpInfo($facility_id, $id, $contentType); + } + + /** + * Operation apiFacilitiesFacilityIdQualificationRatesIdDeleteWithHttpInfo + * + * @param string $facility_id (required) + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdQualificationRatesIdDelete'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of null, HTTP status code, HTTP response headers (array of strings) + */ + public function apiFacilitiesFacilityIdQualificationRatesIdDeleteWithHttpInfo($facility_id, $id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdQualificationRatesIdDelete'][0]) + { + $request = $this->apiFacilitiesFacilityIdQualificationRatesIdDeleteRequest($facility_id, $id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + return [null, $statusCode, $response->getHeaders()]; + } catch (ApiException $e) { + switch ($e->getCode()) { + } + + + throw $e; + } + } + + /** + * Operation apiFacilitiesFacilityIdQualificationRatesIdDeleteAsync + * + * @param string $facility_id (required) + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdQualificationRatesIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiFacilitiesFacilityIdQualificationRatesIdDeleteAsync($facility_id, $id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdQualificationRatesIdDelete'][0]) + { + return $this->apiFacilitiesFacilityIdQualificationRatesIdDeleteAsyncWithHttpInfo($facility_id, $id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiFacilitiesFacilityIdQualificationRatesIdDeleteAsyncWithHttpInfo + * + * @param string $facility_id (required) + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdQualificationRatesIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiFacilitiesFacilityIdQualificationRatesIdDeleteAsyncWithHttpInfo($facility_id, $id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdQualificationRatesIdDelete'][0]) + { + $returnType = ''; + $request = $this->apiFacilitiesFacilityIdQualificationRatesIdDeleteRequest($facility_id, $id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + return [null, $response->getStatusCode(), $response->getHeaders()]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiFacilitiesFacilityIdQualificationRatesIdDelete' + * + * @param string $facility_id (required) + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdQualificationRatesIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiFacilitiesFacilityIdQualificationRatesIdDeleteRequest($facility_id, $id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdQualificationRatesIdDelete'][0]) + { + + // verify the required parameter 'facility_id' is set + if ($facility_id === null || (is_array($facility_id) && count($facility_id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $facility_id when calling apiFacilitiesFacilityIdQualificationRatesIdDelete' + ); + } + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiFacilitiesFacilityIdQualificationRatesIdDelete' + ); + } + + + $resourcePath = '/api/facilities/{facilityId}/qualification-rates/{id}'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($facility_id !== null) { + $resourcePath = str_replace( + '{' . 'facilityId' . '}', + ObjectSerializer::toPathValue($facility_id), + $resourcePath + ); + } + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + [], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'DELETE', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiFacilitiesFacilityIdQualificationRatesIdPut + * + * @param string $facility_id facility_id (required) + * @param string $id id (required) + * @param \OmsorgCoreClient\Model\UpdateFacilityQualificationRateRequest|null $update_facility_qualification_rate_request update_facility_qualification_rate_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdQualificationRatesIdPut'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\FacilityQualificationRateResponse + */ + public function apiFacilitiesFacilityIdQualificationRatesIdPut($facility_id, $id, $update_facility_qualification_rate_request = null, string $contentType = self::contentTypes['apiFacilitiesFacilityIdQualificationRatesIdPut'][0]) + { + list($response) = $this->apiFacilitiesFacilityIdQualificationRatesIdPutWithHttpInfo($facility_id, $id, $update_facility_qualification_rate_request, $contentType); + return $response; + } + + /** + * Operation apiFacilitiesFacilityIdQualificationRatesIdPutWithHttpInfo + * + * @param string $facility_id (required) + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateFacilityQualificationRateRequest|null $update_facility_qualification_rate_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdQualificationRatesIdPut'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\FacilityQualificationRateResponse, HTTP status code, HTTP response headers (array of strings) + */ + public function apiFacilitiesFacilityIdQualificationRatesIdPutWithHttpInfo($facility_id, $id, $update_facility_qualification_rate_request = null, string $contentType = self::contentTypes['apiFacilitiesFacilityIdQualificationRatesIdPut'][0]) + { + $request = $this->apiFacilitiesFacilityIdQualificationRatesIdPutRequest($facility_id, $id, $update_facility_qualification_rate_request, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\FacilityQualificationRateResponse', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\FacilityQualificationRateResponse', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\FacilityQualificationRateResponse', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiFacilitiesFacilityIdQualificationRatesIdPutAsync + * + * @param string $facility_id (required) + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateFacilityQualificationRateRequest|null $update_facility_qualification_rate_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdQualificationRatesIdPut'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiFacilitiesFacilityIdQualificationRatesIdPutAsync($facility_id, $id, $update_facility_qualification_rate_request = null, string $contentType = self::contentTypes['apiFacilitiesFacilityIdQualificationRatesIdPut'][0]) + { + return $this->apiFacilitiesFacilityIdQualificationRatesIdPutAsyncWithHttpInfo($facility_id, $id, $update_facility_qualification_rate_request, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiFacilitiesFacilityIdQualificationRatesIdPutAsyncWithHttpInfo + * + * @param string $facility_id (required) + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateFacilityQualificationRateRequest|null $update_facility_qualification_rate_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdQualificationRatesIdPut'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiFacilitiesFacilityIdQualificationRatesIdPutAsyncWithHttpInfo($facility_id, $id, $update_facility_qualification_rate_request = null, string $contentType = self::contentTypes['apiFacilitiesFacilityIdQualificationRatesIdPut'][0]) + { + $returnType = '\OmsorgCoreClient\Model\FacilityQualificationRateResponse'; + $request = $this->apiFacilitiesFacilityIdQualificationRatesIdPutRequest($facility_id, $id, $update_facility_qualification_rate_request, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiFacilitiesFacilityIdQualificationRatesIdPut' + * + * @param string $facility_id (required) + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateFacilityQualificationRateRequest|null $update_facility_qualification_rate_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdQualificationRatesIdPut'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiFacilitiesFacilityIdQualificationRatesIdPutRequest($facility_id, $id, $update_facility_qualification_rate_request = null, string $contentType = self::contentTypes['apiFacilitiesFacilityIdQualificationRatesIdPut'][0]) + { + + // verify the required parameter 'facility_id' is set + if ($facility_id === null || (is_array($facility_id) && count($facility_id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $facility_id when calling apiFacilitiesFacilityIdQualificationRatesIdPut' + ); + } + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiFacilitiesFacilityIdQualificationRatesIdPut' + ); + } + + + + $resourcePath = '/api/facilities/{facilityId}/qualification-rates/{id}'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($facility_id !== null) { + $resourcePath = str_replace( + '{' . 'facilityId' . '}', + ObjectSerializer::toPathValue($facility_id), + $resourcePath + ); + } + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (isset($update_facility_qualification_rate_request)) { + if (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the body + $httpBody = \GuzzleHttp\Utils::jsonEncode(ObjectSerializer::sanitizeForSerialization($update_facility_qualification_rate_request)); + } else { + $httpBody = $update_facility_qualification_rate_request; + } + } elseif (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'PUT', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiFacilitiesFacilityIdQualificationRatesPost + * + * @param string $facility_id facility_id (required) + * @param \OmsorgCoreClient\Model\CreateFacilityQualificationRateRequest|null $create_facility_qualification_rate_request create_facility_qualification_rate_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdQualificationRatesPost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\FacilityQualificationRateResponse + */ + public function apiFacilitiesFacilityIdQualificationRatesPost($facility_id, $create_facility_qualification_rate_request = null, string $contentType = self::contentTypes['apiFacilitiesFacilityIdQualificationRatesPost'][0]) + { + list($response) = $this->apiFacilitiesFacilityIdQualificationRatesPostWithHttpInfo($facility_id, $create_facility_qualification_rate_request, $contentType); + return $response; + } + + /** + * Operation apiFacilitiesFacilityIdQualificationRatesPostWithHttpInfo + * + * @param string $facility_id (required) + * @param \OmsorgCoreClient\Model\CreateFacilityQualificationRateRequest|null $create_facility_qualification_rate_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdQualificationRatesPost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\FacilityQualificationRateResponse, HTTP status code, HTTP response headers (array of strings) + */ + public function apiFacilitiesFacilityIdQualificationRatesPostWithHttpInfo($facility_id, $create_facility_qualification_rate_request = null, string $contentType = self::contentTypes['apiFacilitiesFacilityIdQualificationRatesPost'][0]) + { + $request = $this->apiFacilitiesFacilityIdQualificationRatesPostRequest($facility_id, $create_facility_qualification_rate_request, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\FacilityQualificationRateResponse', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\FacilityQualificationRateResponse', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\FacilityQualificationRateResponse', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiFacilitiesFacilityIdQualificationRatesPostAsync + * + * @param string $facility_id (required) + * @param \OmsorgCoreClient\Model\CreateFacilityQualificationRateRequest|null $create_facility_qualification_rate_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdQualificationRatesPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiFacilitiesFacilityIdQualificationRatesPostAsync($facility_id, $create_facility_qualification_rate_request = null, string $contentType = self::contentTypes['apiFacilitiesFacilityIdQualificationRatesPost'][0]) + { + return $this->apiFacilitiesFacilityIdQualificationRatesPostAsyncWithHttpInfo($facility_id, $create_facility_qualification_rate_request, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiFacilitiesFacilityIdQualificationRatesPostAsyncWithHttpInfo + * + * @param string $facility_id (required) + * @param \OmsorgCoreClient\Model\CreateFacilityQualificationRateRequest|null $create_facility_qualification_rate_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdQualificationRatesPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiFacilitiesFacilityIdQualificationRatesPostAsyncWithHttpInfo($facility_id, $create_facility_qualification_rate_request = null, string $contentType = self::contentTypes['apiFacilitiesFacilityIdQualificationRatesPost'][0]) + { + $returnType = '\OmsorgCoreClient\Model\FacilityQualificationRateResponse'; + $request = $this->apiFacilitiesFacilityIdQualificationRatesPostRequest($facility_id, $create_facility_qualification_rate_request, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiFacilitiesFacilityIdQualificationRatesPost' + * + * @param string $facility_id (required) + * @param \OmsorgCoreClient\Model\CreateFacilityQualificationRateRequest|null $create_facility_qualification_rate_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdQualificationRatesPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiFacilitiesFacilityIdQualificationRatesPostRequest($facility_id, $create_facility_qualification_rate_request = null, string $contentType = self::contentTypes['apiFacilitiesFacilityIdQualificationRatesPost'][0]) + { + + // verify the required parameter 'facility_id' is set + if ($facility_id === null || (is_array($facility_id) && count($facility_id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $facility_id when calling apiFacilitiesFacilityIdQualificationRatesPost' + ); + } + + + + $resourcePath = '/api/facilities/{facilityId}/qualification-rates'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($facility_id !== null) { + $resourcePath = str_replace( + '{' . 'facilityId' . '}', + ObjectSerializer::toPathValue($facility_id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (isset($create_facility_qualification_rate_request)) { + if (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the body + $httpBody = \GuzzleHttp\Utils::jsonEncode(ObjectSerializer::sanitizeForSerialization($create_facility_qualification_rate_request)); + } else { + $httpBody = $create_facility_qualification_rate_request; + } + } elseif (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'POST', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Create http client option + * + * @throws \RuntimeException on file opening failure + * @return array of http client options + */ + protected function createHttpClientOption() + { + $options = []; + if ($this->config->getDebug()) { + $options[RequestOptions::DEBUG] = fopen($this->config->getDebugFile(), 'a'); + if (!$options[RequestOptions::DEBUG]) { + throw new \RuntimeException('Failed to open the debug file: ' . $this->config->getDebugFile()); + } + } + + return $options; + } + + private function handleResponseWithDataType( + string $dataType, + RequestInterface $request, + ResponseInterface $response + ): array { + if ($dataType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($dataType !== 'string') { + try { + $content = json_decode($content, false, 512, JSON_THROW_ON_ERROR); + } catch (\JsonException $exception) { + throw new ApiException( + sprintf( + 'Error JSON decoding server response (%s)', + $request->getUri() + ), + $response->getStatusCode(), + $response->getHeaders(), + $content + ); + } + } + } + + return [ + ObjectSerializer::deserialize($content, $dataType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + } + + private function responseWithinRangeCode( + string $rangeCode, + int $statusCode + ): bool { + $left = (int) ($rangeCode[0].'00'); + $right = (int) ($rangeCode[0].'99'); + + return $statusCode >= $left && $statusCode <= $right; + } +} diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/OrdersApi.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/OrdersApi.php index 175428d..0821064 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/OrdersApi.php +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/OrdersApi.php @@ -77,6 +77,9 @@ class OrdersApi 'apiOrdersGet' => [ 'application/json', ], + 'apiOrdersIdDelete' => [ + 'application/json', + ], 'apiOrdersIdGet' => [ 'application/json', ], @@ -144,6 +147,9 @@ class OrdersApi * @param string|null $search search (optional) * @param string|null $status_id status_id (optional) * @param string|null $facility_id facility_id (optional) + * @param string|null $priority priority (optional) + * @param string|null $required_qualification required_qualification (optional) + * @param string|null $shift_type shift_type (optional) * @param int|null $page page (optional, default to 1) * @param int|null $page_size page_size (optional, default to 20) * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiOrdersGet'] to see the possible values for this operation @@ -152,9 +158,9 @@ class OrdersApi * @throws \InvalidArgumentException * @return \OmsorgCoreClient\Model\OrderResponsePagedResponse */ - public function apiOrdersGet($search = null, $status_id = null, $facility_id = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiOrdersGet'][0]) + public function apiOrdersGet($search = null, $status_id = null, $facility_id = null, $priority = null, $required_qualification = null, $shift_type = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiOrdersGet'][0]) { - list($response) = $this->apiOrdersGetWithHttpInfo($search, $status_id, $facility_id, $page, $page_size, $contentType); + list($response) = $this->apiOrdersGetWithHttpInfo($search, $status_id, $facility_id, $priority, $required_qualification, $shift_type, $page, $page_size, $contentType); return $response; } @@ -164,6 +170,9 @@ class OrdersApi * @param string|null $search (optional) * @param string|null $status_id (optional) * @param string|null $facility_id (optional) + * @param string|null $priority (optional) + * @param string|null $required_qualification (optional) + * @param string|null $shift_type (optional) * @param int|null $page (optional, default to 1) * @param int|null $page_size (optional, default to 20) * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiOrdersGet'] to see the possible values for this operation @@ -172,9 +181,9 @@ class OrdersApi * @throws \InvalidArgumentException * @return array of \OmsorgCoreClient\Model\OrderResponsePagedResponse, HTTP status code, HTTP response headers (array of strings) */ - public function apiOrdersGetWithHttpInfo($search = null, $status_id = null, $facility_id = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiOrdersGet'][0]) + public function apiOrdersGetWithHttpInfo($search = null, $status_id = null, $facility_id = null, $priority = null, $required_qualification = null, $shift_type = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiOrdersGet'][0]) { - $request = $this->apiOrdersGetRequest($search, $status_id, $facility_id, $page, $page_size, $contentType); + $request = $this->apiOrdersGetRequest($search, $status_id, $facility_id, $priority, $required_qualification, $shift_type, $page, $page_size, $contentType); try { $options = $this->createHttpClientOption(); @@ -251,6 +260,9 @@ class OrdersApi * @param string|null $search (optional) * @param string|null $status_id (optional) * @param string|null $facility_id (optional) + * @param string|null $priority (optional) + * @param string|null $required_qualification (optional) + * @param string|null $shift_type (optional) * @param int|null $page (optional, default to 1) * @param int|null $page_size (optional, default to 20) * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiOrdersGet'] to see the possible values for this operation @@ -258,9 +270,9 @@ class OrdersApi * @throws \InvalidArgumentException * @return \GuzzleHttp\Promise\PromiseInterface */ - public function apiOrdersGetAsync($search = null, $status_id = null, $facility_id = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiOrdersGet'][0]) + public function apiOrdersGetAsync($search = null, $status_id = null, $facility_id = null, $priority = null, $required_qualification = null, $shift_type = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiOrdersGet'][0]) { - return $this->apiOrdersGetAsyncWithHttpInfo($search, $status_id, $facility_id, $page, $page_size, $contentType) + return $this->apiOrdersGetAsyncWithHttpInfo($search, $status_id, $facility_id, $priority, $required_qualification, $shift_type, $page, $page_size, $contentType) ->then( function ($response) { return $response[0]; @@ -274,6 +286,9 @@ class OrdersApi * @param string|null $search (optional) * @param string|null $status_id (optional) * @param string|null $facility_id (optional) + * @param string|null $priority (optional) + * @param string|null $required_qualification (optional) + * @param string|null $shift_type (optional) * @param int|null $page (optional, default to 1) * @param int|null $page_size (optional, default to 20) * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiOrdersGet'] to see the possible values for this operation @@ -281,10 +296,10 @@ class OrdersApi * @throws \InvalidArgumentException * @return \GuzzleHttp\Promise\PromiseInterface */ - public function apiOrdersGetAsyncWithHttpInfo($search = null, $status_id = null, $facility_id = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiOrdersGet'][0]) + public function apiOrdersGetAsyncWithHttpInfo($search = null, $status_id = null, $facility_id = null, $priority = null, $required_qualification = null, $shift_type = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiOrdersGet'][0]) { $returnType = '\OmsorgCoreClient\Model\OrderResponsePagedResponse'; - $request = $this->apiOrdersGetRequest($search, $status_id, $facility_id, $page, $page_size, $contentType); + $request = $this->apiOrdersGetRequest($search, $status_id, $facility_id, $priority, $required_qualification, $shift_type, $page, $page_size, $contentType); return $this->client ->sendAsync($request, $this->createHttpClientOption()) @@ -328,6 +343,9 @@ class OrdersApi * @param string|null $search (optional) * @param string|null $status_id (optional) * @param string|null $facility_id (optional) + * @param string|null $priority (optional) + * @param string|null $required_qualification (optional) + * @param string|null $shift_type (optional) * @param int|null $page (optional, default to 1) * @param int|null $page_size (optional, default to 20) * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiOrdersGet'] to see the possible values for this operation @@ -335,7 +353,7 @@ class OrdersApi * @throws \InvalidArgumentException * @return \GuzzleHttp\Psr7\Request */ - public function apiOrdersGetRequest($search = null, $status_id = null, $facility_id = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiOrdersGet'][0]) + public function apiOrdersGetRequest($search = null, $status_id = null, $facility_id = null, $priority = null, $required_qualification = null, $shift_type = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiOrdersGet'][0]) { @@ -344,6 +362,9 @@ class OrdersApi + + + $resourcePath = '/api/orders'; $formParams = []; $queryParams = []; @@ -379,6 +400,33 @@ class OrdersApi false // required ) ?? []); // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $priority, + 'priority', // param base name + 'string', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $required_qualification, + 'requiredQualification', // param base name + 'string', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $shift_type, + 'shiftType', // param base name + 'string', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + // query params $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( $page, 'page', // param base name @@ -457,6 +505,220 @@ class OrdersApi ); } + /** + * Operation apiOrdersIdDelete + * + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiOrdersIdDelete'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return void + */ + public function apiOrdersIdDelete($id, string $contentType = self::contentTypes['apiOrdersIdDelete'][0]) + { + $this->apiOrdersIdDeleteWithHttpInfo($id, $contentType); + } + + /** + * Operation apiOrdersIdDeleteWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiOrdersIdDelete'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of null, HTTP status code, HTTP response headers (array of strings) + */ + public function apiOrdersIdDeleteWithHttpInfo($id, string $contentType = self::contentTypes['apiOrdersIdDelete'][0]) + { + $request = $this->apiOrdersIdDeleteRequest($id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + return [null, $statusCode, $response->getHeaders()]; + } catch (ApiException $e) { + switch ($e->getCode()) { + } + + + throw $e; + } + } + + /** + * Operation apiOrdersIdDeleteAsync + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiOrdersIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiOrdersIdDeleteAsync($id, string $contentType = self::contentTypes['apiOrdersIdDelete'][0]) + { + return $this->apiOrdersIdDeleteAsyncWithHttpInfo($id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiOrdersIdDeleteAsyncWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiOrdersIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiOrdersIdDeleteAsyncWithHttpInfo($id, string $contentType = self::contentTypes['apiOrdersIdDelete'][0]) + { + $returnType = ''; + $request = $this->apiOrdersIdDeleteRequest($id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + return [null, $response->getStatusCode(), $response->getHeaders()]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiOrdersIdDelete' + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiOrdersIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiOrdersIdDeleteRequest($id, string $contentType = self::contentTypes['apiOrdersIdDelete'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiOrdersIdDelete' + ); + } + + + $resourcePath = '/api/orders/{id}'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + [], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'DELETE', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + /** * Operation apiOrdersIdGet * diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/TimeEntriesApi.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/TimeEntriesApi.php new file mode 100644 index 0000000..4469200 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/TimeEntriesApi.php @@ -0,0 +1,2085 @@ + [ + 'application/json', + ], + 'apiTimeEntriesIdDecisionPost' => [ + 'application/json', + 'text/json', + 'application/*+json', + ], + 'apiTimeEntriesIdDelete' => [ + 'application/json', + ], + 'apiTimeEntriesIdGet' => [ + 'application/json', + ], + 'apiTimeEntriesIdPut' => [ + 'application/json', + 'text/json', + 'application/*+json', + ], + 'apiTimeEntriesIdSubmitPost' => [ + 'application/json', + ], + 'apiTimeEntriesPost' => [ + 'application/json', + 'text/json', + 'application/*+json', + ], + ]; + + /** + * @param ClientInterface $client + * @param Configuration $config + * @param HeaderSelector $selector + * @param int $hostIndex (Optional) host index to select the list of hosts if defined in the OpenAPI spec + */ + public function __construct( + ?ClientInterface $client = null, + ?Configuration $config = null, + ?HeaderSelector $selector = null, + int $hostIndex = 0 + ) { + $this->client = $client ?: new Client(); + $this->config = $config ?: Configuration::getDefaultConfiguration(); + $this->headerSelector = $selector ?: new HeaderSelector(); + $this->hostIndex = $hostIndex; + } + + /** + * Set the host index + * + * @param int $hostIndex Host index (required) + */ + public function setHostIndex($hostIndex): void + { + $this->hostIndex = $hostIndex; + } + + /** + * Get the host index + * + * @return int Host index + */ + public function getHostIndex() + { + return $this->hostIndex; + } + + /** + * @return Configuration + */ + public function getConfig() + { + return $this->config; + } + + /** + * Operation apiTimeEntriesGet + * + * @param string|null $status_id status_id (optional) + * @param string|null $employee_id employee_id (optional) + * @param string|null $order_id order_id (optional) + * @param int|null $page page (optional, default to 1) + * @param int|null $page_size page_size (optional, default to 20) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\TimeEntryResponsePagedResponse + */ + public function apiTimeEntriesGet($status_id = null, $employee_id = null, $order_id = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiTimeEntriesGet'][0]) + { + list($response) = $this->apiTimeEntriesGetWithHttpInfo($status_id, $employee_id, $order_id, $page, $page_size, $contentType); + return $response; + } + + /** + * Operation apiTimeEntriesGetWithHttpInfo + * + * @param string|null $status_id (optional) + * @param string|null $employee_id (optional) + * @param string|null $order_id (optional) + * @param int|null $page (optional, default to 1) + * @param int|null $page_size (optional, default to 20) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\TimeEntryResponsePagedResponse, HTTP status code, HTTP response headers (array of strings) + */ + public function apiTimeEntriesGetWithHttpInfo($status_id = null, $employee_id = null, $order_id = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiTimeEntriesGet'][0]) + { + $request = $this->apiTimeEntriesGetRequest($status_id, $employee_id, $order_id, $page, $page_size, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TimeEntryResponsePagedResponse', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TimeEntryResponsePagedResponse', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\TimeEntryResponsePagedResponse', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiTimeEntriesGetAsync + * + * @param string|null $status_id (optional) + * @param string|null $employee_id (optional) + * @param string|null $order_id (optional) + * @param int|null $page (optional, default to 1) + * @param int|null $page_size (optional, default to 20) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTimeEntriesGetAsync($status_id = null, $employee_id = null, $order_id = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiTimeEntriesGet'][0]) + { + return $this->apiTimeEntriesGetAsyncWithHttpInfo($status_id, $employee_id, $order_id, $page, $page_size, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTimeEntriesGetAsyncWithHttpInfo + * + * @param string|null $status_id (optional) + * @param string|null $employee_id (optional) + * @param string|null $order_id (optional) + * @param int|null $page (optional, default to 1) + * @param int|null $page_size (optional, default to 20) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTimeEntriesGetAsyncWithHttpInfo($status_id = null, $employee_id = null, $order_id = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiTimeEntriesGet'][0]) + { + $returnType = '\OmsorgCoreClient\Model\TimeEntryResponsePagedResponse'; + $request = $this->apiTimeEntriesGetRequest($status_id, $employee_id, $order_id, $page, $page_size, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTimeEntriesGet' + * + * @param string|null $status_id (optional) + * @param string|null $employee_id (optional) + * @param string|null $order_id (optional) + * @param int|null $page (optional, default to 1) + * @param int|null $page_size (optional, default to 20) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTimeEntriesGetRequest($status_id = null, $employee_id = null, $order_id = null, $page = 1, $page_size = 20, string $contentType = self::contentTypes['apiTimeEntriesGet'][0]) + { + + + + + + + + $resourcePath = '/api/time-entries'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $status_id, + 'statusId', // param base name + 'string', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $employee_id, + 'employeeId', // param base name + 'string', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $order_id, + 'orderId', // param base name + 'string', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $page, + 'page', // param base name + 'integer', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $page_size, + 'pageSize', // param base name + 'integer', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + + + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'GET', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTimeEntriesIdDecisionPost + * + * @param string $id id (required) + * @param \OmsorgCoreClient\Model\TimeEntryDecisionRequest|null $time_entry_decision_request time_entry_decision_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdDecisionPost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\TimeEntryResponse + */ + public function apiTimeEntriesIdDecisionPost($id, $time_entry_decision_request = null, string $contentType = self::contentTypes['apiTimeEntriesIdDecisionPost'][0]) + { + list($response) = $this->apiTimeEntriesIdDecisionPostWithHttpInfo($id, $time_entry_decision_request, $contentType); + return $response; + } + + /** + * Operation apiTimeEntriesIdDecisionPostWithHttpInfo + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\TimeEntryDecisionRequest|null $time_entry_decision_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdDecisionPost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\TimeEntryResponse, HTTP status code, HTTP response headers (array of strings) + */ + public function apiTimeEntriesIdDecisionPostWithHttpInfo($id, $time_entry_decision_request = null, string $contentType = self::contentTypes['apiTimeEntriesIdDecisionPost'][0]) + { + $request = $this->apiTimeEntriesIdDecisionPostRequest($id, $time_entry_decision_request, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TimeEntryResponse', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TimeEntryResponse', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\TimeEntryResponse', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiTimeEntriesIdDecisionPostAsync + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\TimeEntryDecisionRequest|null $time_entry_decision_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdDecisionPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTimeEntriesIdDecisionPostAsync($id, $time_entry_decision_request = null, string $contentType = self::contentTypes['apiTimeEntriesIdDecisionPost'][0]) + { + return $this->apiTimeEntriesIdDecisionPostAsyncWithHttpInfo($id, $time_entry_decision_request, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTimeEntriesIdDecisionPostAsyncWithHttpInfo + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\TimeEntryDecisionRequest|null $time_entry_decision_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdDecisionPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTimeEntriesIdDecisionPostAsyncWithHttpInfo($id, $time_entry_decision_request = null, string $contentType = self::contentTypes['apiTimeEntriesIdDecisionPost'][0]) + { + $returnType = '\OmsorgCoreClient\Model\TimeEntryResponse'; + $request = $this->apiTimeEntriesIdDecisionPostRequest($id, $time_entry_decision_request, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTimeEntriesIdDecisionPost' + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\TimeEntryDecisionRequest|null $time_entry_decision_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdDecisionPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTimeEntriesIdDecisionPostRequest($id, $time_entry_decision_request = null, string $contentType = self::contentTypes['apiTimeEntriesIdDecisionPost'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiTimeEntriesIdDecisionPost' + ); + } + + + + $resourcePath = '/api/time-entries/{id}/decision'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (isset($time_entry_decision_request)) { + if (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the body + $httpBody = \GuzzleHttp\Utils::jsonEncode(ObjectSerializer::sanitizeForSerialization($time_entry_decision_request)); + } else { + $httpBody = $time_entry_decision_request; + } + } elseif (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'POST', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTimeEntriesIdDelete + * + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdDelete'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return void + */ + public function apiTimeEntriesIdDelete($id, string $contentType = self::contentTypes['apiTimeEntriesIdDelete'][0]) + { + $this->apiTimeEntriesIdDeleteWithHttpInfo($id, $contentType); + } + + /** + * Operation apiTimeEntriesIdDeleteWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdDelete'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of null, HTTP status code, HTTP response headers (array of strings) + */ + public function apiTimeEntriesIdDeleteWithHttpInfo($id, string $contentType = self::contentTypes['apiTimeEntriesIdDelete'][0]) + { + $request = $this->apiTimeEntriesIdDeleteRequest($id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + return [null, $statusCode, $response->getHeaders()]; + } catch (ApiException $e) { + switch ($e->getCode()) { + } + + + throw $e; + } + } + + /** + * Operation apiTimeEntriesIdDeleteAsync + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTimeEntriesIdDeleteAsync($id, string $contentType = self::contentTypes['apiTimeEntriesIdDelete'][0]) + { + return $this->apiTimeEntriesIdDeleteAsyncWithHttpInfo($id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTimeEntriesIdDeleteAsyncWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTimeEntriesIdDeleteAsyncWithHttpInfo($id, string $contentType = self::contentTypes['apiTimeEntriesIdDelete'][0]) + { + $returnType = ''; + $request = $this->apiTimeEntriesIdDeleteRequest($id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + return [null, $response->getStatusCode(), $response->getHeaders()]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTimeEntriesIdDelete' + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTimeEntriesIdDeleteRequest($id, string $contentType = self::contentTypes['apiTimeEntriesIdDelete'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiTimeEntriesIdDelete' + ); + } + + + $resourcePath = '/api/time-entries/{id}'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + [], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'DELETE', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTimeEntriesIdGet + * + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\TimeEntryResponse + */ + public function apiTimeEntriesIdGet($id, string $contentType = self::contentTypes['apiTimeEntriesIdGet'][0]) + { + list($response) = $this->apiTimeEntriesIdGetWithHttpInfo($id, $contentType); + return $response; + } + + /** + * Operation apiTimeEntriesIdGetWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\TimeEntryResponse, HTTP status code, HTTP response headers (array of strings) + */ + public function apiTimeEntriesIdGetWithHttpInfo($id, string $contentType = self::contentTypes['apiTimeEntriesIdGet'][0]) + { + $request = $this->apiTimeEntriesIdGetRequest($id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TimeEntryResponse', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TimeEntryResponse', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\TimeEntryResponse', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiTimeEntriesIdGetAsync + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTimeEntriesIdGetAsync($id, string $contentType = self::contentTypes['apiTimeEntriesIdGet'][0]) + { + return $this->apiTimeEntriesIdGetAsyncWithHttpInfo($id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTimeEntriesIdGetAsyncWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTimeEntriesIdGetAsyncWithHttpInfo($id, string $contentType = self::contentTypes['apiTimeEntriesIdGet'][0]) + { + $returnType = '\OmsorgCoreClient\Model\TimeEntryResponse'; + $request = $this->apiTimeEntriesIdGetRequest($id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTimeEntriesIdGet' + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTimeEntriesIdGetRequest($id, string $contentType = self::contentTypes['apiTimeEntriesIdGet'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiTimeEntriesIdGet' + ); + } + + + $resourcePath = '/api/time-entries/{id}'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'GET', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTimeEntriesIdPut + * + * @param string $id id (required) + * @param \OmsorgCoreClient\Model\UpdateTimeEntryRequest|null $update_time_entry_request update_time_entry_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdPut'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\TimeEntryResponse + */ + public function apiTimeEntriesIdPut($id, $update_time_entry_request = null, string $contentType = self::contentTypes['apiTimeEntriesIdPut'][0]) + { + list($response) = $this->apiTimeEntriesIdPutWithHttpInfo($id, $update_time_entry_request, $contentType); + return $response; + } + + /** + * Operation apiTimeEntriesIdPutWithHttpInfo + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateTimeEntryRequest|null $update_time_entry_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdPut'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\TimeEntryResponse, HTTP status code, HTTP response headers (array of strings) + */ + public function apiTimeEntriesIdPutWithHttpInfo($id, $update_time_entry_request = null, string $contentType = self::contentTypes['apiTimeEntriesIdPut'][0]) + { + $request = $this->apiTimeEntriesIdPutRequest($id, $update_time_entry_request, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TimeEntryResponse', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TimeEntryResponse', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\TimeEntryResponse', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiTimeEntriesIdPutAsync + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateTimeEntryRequest|null $update_time_entry_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdPut'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTimeEntriesIdPutAsync($id, $update_time_entry_request = null, string $contentType = self::contentTypes['apiTimeEntriesIdPut'][0]) + { + return $this->apiTimeEntriesIdPutAsyncWithHttpInfo($id, $update_time_entry_request, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTimeEntriesIdPutAsyncWithHttpInfo + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateTimeEntryRequest|null $update_time_entry_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdPut'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTimeEntriesIdPutAsyncWithHttpInfo($id, $update_time_entry_request = null, string $contentType = self::contentTypes['apiTimeEntriesIdPut'][0]) + { + $returnType = '\OmsorgCoreClient\Model\TimeEntryResponse'; + $request = $this->apiTimeEntriesIdPutRequest($id, $update_time_entry_request, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTimeEntriesIdPut' + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateTimeEntryRequest|null $update_time_entry_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdPut'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTimeEntriesIdPutRequest($id, $update_time_entry_request = null, string $contentType = self::contentTypes['apiTimeEntriesIdPut'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiTimeEntriesIdPut' + ); + } + + + + $resourcePath = '/api/time-entries/{id}'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (isset($update_time_entry_request)) { + if (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the body + $httpBody = \GuzzleHttp\Utils::jsonEncode(ObjectSerializer::sanitizeForSerialization($update_time_entry_request)); + } else { + $httpBody = $update_time_entry_request; + } + } elseif (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'PUT', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTimeEntriesIdSubmitPost + * + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdSubmitPost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\TimeEntryResponse + */ + public function apiTimeEntriesIdSubmitPost($id, string $contentType = self::contentTypes['apiTimeEntriesIdSubmitPost'][0]) + { + list($response) = $this->apiTimeEntriesIdSubmitPostWithHttpInfo($id, $contentType); + return $response; + } + + /** + * Operation apiTimeEntriesIdSubmitPostWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdSubmitPost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\TimeEntryResponse, HTTP status code, HTTP response headers (array of strings) + */ + public function apiTimeEntriesIdSubmitPostWithHttpInfo($id, string $contentType = self::contentTypes['apiTimeEntriesIdSubmitPost'][0]) + { + $request = $this->apiTimeEntriesIdSubmitPostRequest($id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TimeEntryResponse', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TimeEntryResponse', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\TimeEntryResponse', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiTimeEntriesIdSubmitPostAsync + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdSubmitPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTimeEntriesIdSubmitPostAsync($id, string $contentType = self::contentTypes['apiTimeEntriesIdSubmitPost'][0]) + { + return $this->apiTimeEntriesIdSubmitPostAsyncWithHttpInfo($id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTimeEntriesIdSubmitPostAsyncWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdSubmitPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTimeEntriesIdSubmitPostAsyncWithHttpInfo($id, string $contentType = self::contentTypes['apiTimeEntriesIdSubmitPost'][0]) + { + $returnType = '\OmsorgCoreClient\Model\TimeEntryResponse'; + $request = $this->apiTimeEntriesIdSubmitPostRequest($id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTimeEntriesIdSubmitPost' + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesIdSubmitPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTimeEntriesIdSubmitPostRequest($id, string $contentType = self::contentTypes['apiTimeEntriesIdSubmitPost'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiTimeEntriesIdSubmitPost' + ); + } + + + $resourcePath = '/api/time-entries/{id}/submit'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'POST', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTimeEntriesPost + * + * @param \OmsorgCoreClient\Model\CreateTimeEntryRequest|null $create_time_entry_request create_time_entry_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesPost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\TimeEntryResponse + */ + public function apiTimeEntriesPost($create_time_entry_request = null, string $contentType = self::contentTypes['apiTimeEntriesPost'][0]) + { + list($response) = $this->apiTimeEntriesPostWithHttpInfo($create_time_entry_request, $contentType); + return $response; + } + + /** + * Operation apiTimeEntriesPostWithHttpInfo + * + * @param \OmsorgCoreClient\Model\CreateTimeEntryRequest|null $create_time_entry_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesPost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\TimeEntryResponse, HTTP status code, HTTP response headers (array of strings) + */ + public function apiTimeEntriesPostWithHttpInfo($create_time_entry_request = null, string $contentType = self::contentTypes['apiTimeEntriesPost'][0]) + { + $request = $this->apiTimeEntriesPostRequest($create_time_entry_request, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TimeEntryResponse', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TimeEntryResponse', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\TimeEntryResponse', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiTimeEntriesPostAsync + * + * @param \OmsorgCoreClient\Model\CreateTimeEntryRequest|null $create_time_entry_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTimeEntriesPostAsync($create_time_entry_request = null, string $contentType = self::contentTypes['apiTimeEntriesPost'][0]) + { + return $this->apiTimeEntriesPostAsyncWithHttpInfo($create_time_entry_request, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTimeEntriesPostAsyncWithHttpInfo + * + * @param \OmsorgCoreClient\Model\CreateTimeEntryRequest|null $create_time_entry_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTimeEntriesPostAsyncWithHttpInfo($create_time_entry_request = null, string $contentType = self::contentTypes['apiTimeEntriesPost'][0]) + { + $returnType = '\OmsorgCoreClient\Model\TimeEntryResponse'; + $request = $this->apiTimeEntriesPostRequest($create_time_entry_request, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTimeEntriesPost' + * + * @param \OmsorgCoreClient\Model\CreateTimeEntryRequest|null $create_time_entry_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTimeEntriesPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTimeEntriesPostRequest($create_time_entry_request = null, string $contentType = self::contentTypes['apiTimeEntriesPost'][0]) + { + + + + $resourcePath = '/api/time-entries'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (isset($create_time_entry_request)) { + if (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the body + $httpBody = \GuzzleHttp\Utils::jsonEncode(ObjectSerializer::sanitizeForSerialization($create_time_entry_request)); + } else { + $httpBody = $create_time_entry_request; + } + } elseif (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'POST', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Create http client option + * + * @throws \RuntimeException on file opening failure + * @return array of http client options + */ + protected function createHttpClientOption() + { + $options = []; + if ($this->config->getDebug()) { + $options[RequestOptions::DEBUG] = fopen($this->config->getDebugFile(), 'a'); + if (!$options[RequestOptions::DEBUG]) { + throw new \RuntimeException('Failed to open the debug file: ' . $this->config->getDebugFile()); + } + } + + return $options; + } + + private function handleResponseWithDataType( + string $dataType, + RequestInterface $request, + ResponseInterface $response + ): array { + if ($dataType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($dataType !== 'string') { + try { + $content = json_decode($content, false, 512, JSON_THROW_ON_ERROR); + } catch (\JsonException $exception) { + throw new ApiException( + sprintf( + 'Error JSON decoding server response (%s)', + $request->getUri() + ), + $response->getStatusCode(), + $response->getHeaders(), + $content + ); + } + } + } + + return [ + ObjectSerializer::deserialize($content, $dataType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + } + + private function responseWithinRangeCode( + string $rangeCode, + int $statusCode + ): bool { + $left = (int) ($rangeCode[0].'00'); + $right = (int) ($rangeCode[0].'99'); + + return $statusCode >= $left && $statusCode <= $right; + } +} diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/TrashApi.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/TrashApi.php new file mode 100644 index 0000000..ba0fd8a --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/TrashApi.php @@ -0,0 +1,4018 @@ + [ + 'application/json', + ], + 'apiTrashAbsencesIdRestorePost' => [ + 'application/json', + ], + 'apiTrashContractsGet' => [ + 'application/json', + ], + 'apiTrashContractsIdRestorePost' => [ + 'application/json', + ], + 'apiTrashEmployeesGet' => [ + 'application/json', + ], + 'apiTrashEmployeesIdRestorePost' => [ + 'application/json', + ], + 'apiTrashFacilitiesGet' => [ + 'application/json', + ], + 'apiTrashFacilitiesIdRestorePost' => [ + 'application/json', + ], + 'apiTrashFacilityContactsGet' => [ + 'application/json', + ], + 'apiTrashFacilityContactsIdRestorePost' => [ + 'application/json', + ], + 'apiTrashFacilityQualificationRatesGet' => [ + 'application/json', + ], + 'apiTrashFacilityQualificationRatesIdRestorePost' => [ + 'application/json', + ], + 'apiTrashOrdersGet' => [ + 'application/json', + ], + 'apiTrashOrdersIdRestorePost' => [ + 'application/json', + ], + 'apiTrashTimeEntriesGet' => [ + 'application/json', + ], + 'apiTrashTimeEntriesIdRestorePost' => [ + 'application/json', + ], + ]; + + /** + * @param ClientInterface $client + * @param Configuration $config + * @param HeaderSelector $selector + * @param int $hostIndex (Optional) host index to select the list of hosts if defined in the OpenAPI spec + */ + public function __construct( + ?ClientInterface $client = null, + ?Configuration $config = null, + ?HeaderSelector $selector = null, + int $hostIndex = 0 + ) { + $this->client = $client ?: new Client(); + $this->config = $config ?: Configuration::getDefaultConfiguration(); + $this->headerSelector = $selector ?: new HeaderSelector(); + $this->hostIndex = $hostIndex; + } + + /** + * Set the host index + * + * @param int $hostIndex Host index (required) + */ + public function setHostIndex($hostIndex): void + { + $this->hostIndex = $hostIndex; + } + + /** + * Get the host index + * + * @return int Host index + */ + public function getHostIndex() + { + return $this->hostIndex; + } + + /** + * @return Configuration + */ + public function getConfig() + { + return $this->config; + } + + /** + * Operation apiTrashAbsencesGet + * + * @param string|null $search search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashAbsencesGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\TrashAbsenceResponse[] + */ + public function apiTrashAbsencesGet($search = null, string $contentType = self::contentTypes['apiTrashAbsencesGet'][0]) + { + list($response) = $this->apiTrashAbsencesGetWithHttpInfo($search, $contentType); + return $response; + } + + /** + * Operation apiTrashAbsencesGetWithHttpInfo + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashAbsencesGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\TrashAbsenceResponse[], HTTP status code, HTTP response headers (array of strings) + */ + public function apiTrashAbsencesGetWithHttpInfo($search = null, string $contentType = self::contentTypes['apiTrashAbsencesGet'][0]) + { + $request = $this->apiTrashAbsencesGetRequest($search, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TrashAbsenceResponse[]', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TrashAbsenceResponse[]', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\TrashAbsenceResponse[]', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiTrashAbsencesGetAsync + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashAbsencesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashAbsencesGetAsync($search = null, string $contentType = self::contentTypes['apiTrashAbsencesGet'][0]) + { + return $this->apiTrashAbsencesGetAsyncWithHttpInfo($search, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTrashAbsencesGetAsyncWithHttpInfo + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashAbsencesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashAbsencesGetAsyncWithHttpInfo($search = null, string $contentType = self::contentTypes['apiTrashAbsencesGet'][0]) + { + $returnType = '\OmsorgCoreClient\Model\TrashAbsenceResponse[]'; + $request = $this->apiTrashAbsencesGetRequest($search, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTrashAbsencesGet' + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashAbsencesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTrashAbsencesGetRequest($search = null, string $contentType = self::contentTypes['apiTrashAbsencesGet'][0]) + { + + + + $resourcePath = '/api/trash/absences'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $search, + 'search', // param base name + 'string', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + + + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'GET', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTrashAbsencesIdRestorePost + * + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashAbsencesIdRestorePost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return void + */ + public function apiTrashAbsencesIdRestorePost($id, string $contentType = self::contentTypes['apiTrashAbsencesIdRestorePost'][0]) + { + $this->apiTrashAbsencesIdRestorePostWithHttpInfo($id, $contentType); + } + + /** + * Operation apiTrashAbsencesIdRestorePostWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashAbsencesIdRestorePost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of null, HTTP status code, HTTP response headers (array of strings) + */ + public function apiTrashAbsencesIdRestorePostWithHttpInfo($id, string $contentType = self::contentTypes['apiTrashAbsencesIdRestorePost'][0]) + { + $request = $this->apiTrashAbsencesIdRestorePostRequest($id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + return [null, $statusCode, $response->getHeaders()]; + } catch (ApiException $e) { + switch ($e->getCode()) { + } + + + throw $e; + } + } + + /** + * Operation apiTrashAbsencesIdRestorePostAsync + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashAbsencesIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashAbsencesIdRestorePostAsync($id, string $contentType = self::contentTypes['apiTrashAbsencesIdRestorePost'][0]) + { + return $this->apiTrashAbsencesIdRestorePostAsyncWithHttpInfo($id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTrashAbsencesIdRestorePostAsyncWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashAbsencesIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashAbsencesIdRestorePostAsyncWithHttpInfo($id, string $contentType = self::contentTypes['apiTrashAbsencesIdRestorePost'][0]) + { + $returnType = ''; + $request = $this->apiTrashAbsencesIdRestorePostRequest($id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + return [null, $response->getStatusCode(), $response->getHeaders()]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTrashAbsencesIdRestorePost' + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashAbsencesIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTrashAbsencesIdRestorePostRequest($id, string $contentType = self::contentTypes['apiTrashAbsencesIdRestorePost'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiTrashAbsencesIdRestorePost' + ); + } + + + $resourcePath = '/api/trash/absences/{id}/restore'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + [], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'POST', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTrashContractsGet + * + * @param string|null $search search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashContractsGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\TrashContractResponse[] + */ + public function apiTrashContractsGet($search = null, string $contentType = self::contentTypes['apiTrashContractsGet'][0]) + { + list($response) = $this->apiTrashContractsGetWithHttpInfo($search, $contentType); + return $response; + } + + /** + * Operation apiTrashContractsGetWithHttpInfo + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashContractsGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\TrashContractResponse[], HTTP status code, HTTP response headers (array of strings) + */ + public function apiTrashContractsGetWithHttpInfo($search = null, string $contentType = self::contentTypes['apiTrashContractsGet'][0]) + { + $request = $this->apiTrashContractsGetRequest($search, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TrashContractResponse[]', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TrashContractResponse[]', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\TrashContractResponse[]', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiTrashContractsGetAsync + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashContractsGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashContractsGetAsync($search = null, string $contentType = self::contentTypes['apiTrashContractsGet'][0]) + { + return $this->apiTrashContractsGetAsyncWithHttpInfo($search, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTrashContractsGetAsyncWithHttpInfo + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashContractsGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashContractsGetAsyncWithHttpInfo($search = null, string $contentType = self::contentTypes['apiTrashContractsGet'][0]) + { + $returnType = '\OmsorgCoreClient\Model\TrashContractResponse[]'; + $request = $this->apiTrashContractsGetRequest($search, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTrashContractsGet' + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashContractsGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTrashContractsGetRequest($search = null, string $contentType = self::contentTypes['apiTrashContractsGet'][0]) + { + + + + $resourcePath = '/api/trash/contracts'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $search, + 'search', // param base name + 'string', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + + + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'GET', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTrashContractsIdRestorePost + * + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashContractsIdRestorePost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return void + */ + public function apiTrashContractsIdRestorePost($id, string $contentType = self::contentTypes['apiTrashContractsIdRestorePost'][0]) + { + $this->apiTrashContractsIdRestorePostWithHttpInfo($id, $contentType); + } + + /** + * Operation apiTrashContractsIdRestorePostWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashContractsIdRestorePost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of null, HTTP status code, HTTP response headers (array of strings) + */ + public function apiTrashContractsIdRestorePostWithHttpInfo($id, string $contentType = self::contentTypes['apiTrashContractsIdRestorePost'][0]) + { + $request = $this->apiTrashContractsIdRestorePostRequest($id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + return [null, $statusCode, $response->getHeaders()]; + } catch (ApiException $e) { + switch ($e->getCode()) { + } + + + throw $e; + } + } + + /** + * Operation apiTrashContractsIdRestorePostAsync + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashContractsIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashContractsIdRestorePostAsync($id, string $contentType = self::contentTypes['apiTrashContractsIdRestorePost'][0]) + { + return $this->apiTrashContractsIdRestorePostAsyncWithHttpInfo($id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTrashContractsIdRestorePostAsyncWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashContractsIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashContractsIdRestorePostAsyncWithHttpInfo($id, string $contentType = self::contentTypes['apiTrashContractsIdRestorePost'][0]) + { + $returnType = ''; + $request = $this->apiTrashContractsIdRestorePostRequest($id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + return [null, $response->getStatusCode(), $response->getHeaders()]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTrashContractsIdRestorePost' + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashContractsIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTrashContractsIdRestorePostRequest($id, string $contentType = self::contentTypes['apiTrashContractsIdRestorePost'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiTrashContractsIdRestorePost' + ); + } + + + $resourcePath = '/api/trash/contracts/{id}/restore'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + [], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'POST', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTrashEmployeesGet + * + * @param string|null $search search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashEmployeesGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\TrashEmployeeResponse[] + */ + public function apiTrashEmployeesGet($search = null, string $contentType = self::contentTypes['apiTrashEmployeesGet'][0]) + { + list($response) = $this->apiTrashEmployeesGetWithHttpInfo($search, $contentType); + return $response; + } + + /** + * Operation apiTrashEmployeesGetWithHttpInfo + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashEmployeesGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\TrashEmployeeResponse[], HTTP status code, HTTP response headers (array of strings) + */ + public function apiTrashEmployeesGetWithHttpInfo($search = null, string $contentType = self::contentTypes['apiTrashEmployeesGet'][0]) + { + $request = $this->apiTrashEmployeesGetRequest($search, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TrashEmployeeResponse[]', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TrashEmployeeResponse[]', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\TrashEmployeeResponse[]', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiTrashEmployeesGetAsync + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashEmployeesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashEmployeesGetAsync($search = null, string $contentType = self::contentTypes['apiTrashEmployeesGet'][0]) + { + return $this->apiTrashEmployeesGetAsyncWithHttpInfo($search, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTrashEmployeesGetAsyncWithHttpInfo + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashEmployeesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashEmployeesGetAsyncWithHttpInfo($search = null, string $contentType = self::contentTypes['apiTrashEmployeesGet'][0]) + { + $returnType = '\OmsorgCoreClient\Model\TrashEmployeeResponse[]'; + $request = $this->apiTrashEmployeesGetRequest($search, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTrashEmployeesGet' + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashEmployeesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTrashEmployeesGetRequest($search = null, string $contentType = self::contentTypes['apiTrashEmployeesGet'][0]) + { + + + + $resourcePath = '/api/trash/employees'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $search, + 'search', // param base name + 'string', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + + + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'GET', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTrashEmployeesIdRestorePost + * + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashEmployeesIdRestorePost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return void + */ + public function apiTrashEmployeesIdRestorePost($id, string $contentType = self::contentTypes['apiTrashEmployeesIdRestorePost'][0]) + { + $this->apiTrashEmployeesIdRestorePostWithHttpInfo($id, $contentType); + } + + /** + * Operation apiTrashEmployeesIdRestorePostWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashEmployeesIdRestorePost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of null, HTTP status code, HTTP response headers (array of strings) + */ + public function apiTrashEmployeesIdRestorePostWithHttpInfo($id, string $contentType = self::contentTypes['apiTrashEmployeesIdRestorePost'][0]) + { + $request = $this->apiTrashEmployeesIdRestorePostRequest($id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + return [null, $statusCode, $response->getHeaders()]; + } catch (ApiException $e) { + switch ($e->getCode()) { + } + + + throw $e; + } + } + + /** + * Operation apiTrashEmployeesIdRestorePostAsync + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashEmployeesIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashEmployeesIdRestorePostAsync($id, string $contentType = self::contentTypes['apiTrashEmployeesIdRestorePost'][0]) + { + return $this->apiTrashEmployeesIdRestorePostAsyncWithHttpInfo($id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTrashEmployeesIdRestorePostAsyncWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashEmployeesIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashEmployeesIdRestorePostAsyncWithHttpInfo($id, string $contentType = self::contentTypes['apiTrashEmployeesIdRestorePost'][0]) + { + $returnType = ''; + $request = $this->apiTrashEmployeesIdRestorePostRequest($id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + return [null, $response->getStatusCode(), $response->getHeaders()]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTrashEmployeesIdRestorePost' + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashEmployeesIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTrashEmployeesIdRestorePostRequest($id, string $contentType = self::contentTypes['apiTrashEmployeesIdRestorePost'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiTrashEmployeesIdRestorePost' + ); + } + + + $resourcePath = '/api/trash/employees/{id}/restore'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + [], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'POST', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTrashFacilitiesGet + * + * @param string|null $search search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilitiesGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\TrashFacilityResponse[] + */ + public function apiTrashFacilitiesGet($search = null, string $contentType = self::contentTypes['apiTrashFacilitiesGet'][0]) + { + list($response) = $this->apiTrashFacilitiesGetWithHttpInfo($search, $contentType); + return $response; + } + + /** + * Operation apiTrashFacilitiesGetWithHttpInfo + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilitiesGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\TrashFacilityResponse[], HTTP status code, HTTP response headers (array of strings) + */ + public function apiTrashFacilitiesGetWithHttpInfo($search = null, string $contentType = self::contentTypes['apiTrashFacilitiesGet'][0]) + { + $request = $this->apiTrashFacilitiesGetRequest($search, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TrashFacilityResponse[]', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TrashFacilityResponse[]', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\TrashFacilityResponse[]', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiTrashFacilitiesGetAsync + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilitiesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashFacilitiesGetAsync($search = null, string $contentType = self::contentTypes['apiTrashFacilitiesGet'][0]) + { + return $this->apiTrashFacilitiesGetAsyncWithHttpInfo($search, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTrashFacilitiesGetAsyncWithHttpInfo + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilitiesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashFacilitiesGetAsyncWithHttpInfo($search = null, string $contentType = self::contentTypes['apiTrashFacilitiesGet'][0]) + { + $returnType = '\OmsorgCoreClient\Model\TrashFacilityResponse[]'; + $request = $this->apiTrashFacilitiesGetRequest($search, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTrashFacilitiesGet' + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilitiesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTrashFacilitiesGetRequest($search = null, string $contentType = self::contentTypes['apiTrashFacilitiesGet'][0]) + { + + + + $resourcePath = '/api/trash/facilities'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $search, + 'search', // param base name + 'string', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + + + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'GET', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTrashFacilitiesIdRestorePost + * + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilitiesIdRestorePost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return void + */ + public function apiTrashFacilitiesIdRestorePost($id, string $contentType = self::contentTypes['apiTrashFacilitiesIdRestorePost'][0]) + { + $this->apiTrashFacilitiesIdRestorePostWithHttpInfo($id, $contentType); + } + + /** + * Operation apiTrashFacilitiesIdRestorePostWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilitiesIdRestorePost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of null, HTTP status code, HTTP response headers (array of strings) + */ + public function apiTrashFacilitiesIdRestorePostWithHttpInfo($id, string $contentType = self::contentTypes['apiTrashFacilitiesIdRestorePost'][0]) + { + $request = $this->apiTrashFacilitiesIdRestorePostRequest($id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + return [null, $statusCode, $response->getHeaders()]; + } catch (ApiException $e) { + switch ($e->getCode()) { + } + + + throw $e; + } + } + + /** + * Operation apiTrashFacilitiesIdRestorePostAsync + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilitiesIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashFacilitiesIdRestorePostAsync($id, string $contentType = self::contentTypes['apiTrashFacilitiesIdRestorePost'][0]) + { + return $this->apiTrashFacilitiesIdRestorePostAsyncWithHttpInfo($id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTrashFacilitiesIdRestorePostAsyncWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilitiesIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashFacilitiesIdRestorePostAsyncWithHttpInfo($id, string $contentType = self::contentTypes['apiTrashFacilitiesIdRestorePost'][0]) + { + $returnType = ''; + $request = $this->apiTrashFacilitiesIdRestorePostRequest($id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + return [null, $response->getStatusCode(), $response->getHeaders()]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTrashFacilitiesIdRestorePost' + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilitiesIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTrashFacilitiesIdRestorePostRequest($id, string $contentType = self::contentTypes['apiTrashFacilitiesIdRestorePost'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiTrashFacilitiesIdRestorePost' + ); + } + + + $resourcePath = '/api/trash/facilities/{id}/restore'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + [], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'POST', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTrashFacilityContactsGet + * + * @param string|null $search search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilityContactsGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\TrashFacilityContactResponse[] + */ + public function apiTrashFacilityContactsGet($search = null, string $contentType = self::contentTypes['apiTrashFacilityContactsGet'][0]) + { + list($response) = $this->apiTrashFacilityContactsGetWithHttpInfo($search, $contentType); + return $response; + } + + /** + * Operation apiTrashFacilityContactsGetWithHttpInfo + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilityContactsGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\TrashFacilityContactResponse[], HTTP status code, HTTP response headers (array of strings) + */ + public function apiTrashFacilityContactsGetWithHttpInfo($search = null, string $contentType = self::contentTypes['apiTrashFacilityContactsGet'][0]) + { + $request = $this->apiTrashFacilityContactsGetRequest($search, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TrashFacilityContactResponse[]', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TrashFacilityContactResponse[]', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\TrashFacilityContactResponse[]', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiTrashFacilityContactsGetAsync + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilityContactsGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashFacilityContactsGetAsync($search = null, string $contentType = self::contentTypes['apiTrashFacilityContactsGet'][0]) + { + return $this->apiTrashFacilityContactsGetAsyncWithHttpInfo($search, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTrashFacilityContactsGetAsyncWithHttpInfo + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilityContactsGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashFacilityContactsGetAsyncWithHttpInfo($search = null, string $contentType = self::contentTypes['apiTrashFacilityContactsGet'][0]) + { + $returnType = '\OmsorgCoreClient\Model\TrashFacilityContactResponse[]'; + $request = $this->apiTrashFacilityContactsGetRequest($search, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTrashFacilityContactsGet' + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilityContactsGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTrashFacilityContactsGetRequest($search = null, string $contentType = self::contentTypes['apiTrashFacilityContactsGet'][0]) + { + + + + $resourcePath = '/api/trash/facility-contacts'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $search, + 'search', // param base name + 'string', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + + + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'GET', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTrashFacilityContactsIdRestorePost + * + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilityContactsIdRestorePost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return void + */ + public function apiTrashFacilityContactsIdRestorePost($id, string $contentType = self::contentTypes['apiTrashFacilityContactsIdRestorePost'][0]) + { + $this->apiTrashFacilityContactsIdRestorePostWithHttpInfo($id, $contentType); + } + + /** + * Operation apiTrashFacilityContactsIdRestorePostWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilityContactsIdRestorePost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of null, HTTP status code, HTTP response headers (array of strings) + */ + public function apiTrashFacilityContactsIdRestorePostWithHttpInfo($id, string $contentType = self::contentTypes['apiTrashFacilityContactsIdRestorePost'][0]) + { + $request = $this->apiTrashFacilityContactsIdRestorePostRequest($id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + return [null, $statusCode, $response->getHeaders()]; + } catch (ApiException $e) { + switch ($e->getCode()) { + } + + + throw $e; + } + } + + /** + * Operation apiTrashFacilityContactsIdRestorePostAsync + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilityContactsIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashFacilityContactsIdRestorePostAsync($id, string $contentType = self::contentTypes['apiTrashFacilityContactsIdRestorePost'][0]) + { + return $this->apiTrashFacilityContactsIdRestorePostAsyncWithHttpInfo($id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTrashFacilityContactsIdRestorePostAsyncWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilityContactsIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashFacilityContactsIdRestorePostAsyncWithHttpInfo($id, string $contentType = self::contentTypes['apiTrashFacilityContactsIdRestorePost'][0]) + { + $returnType = ''; + $request = $this->apiTrashFacilityContactsIdRestorePostRequest($id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + return [null, $response->getStatusCode(), $response->getHeaders()]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTrashFacilityContactsIdRestorePost' + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilityContactsIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTrashFacilityContactsIdRestorePostRequest($id, string $contentType = self::contentTypes['apiTrashFacilityContactsIdRestorePost'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiTrashFacilityContactsIdRestorePost' + ); + } + + + $resourcePath = '/api/trash/facility-contacts/{id}/restore'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + [], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'POST', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTrashFacilityQualificationRatesGet + * + * @param string|null $search search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilityQualificationRatesGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\TrashFacilityQualificationRateResponse[] + */ + public function apiTrashFacilityQualificationRatesGet($search = null, string $contentType = self::contentTypes['apiTrashFacilityQualificationRatesGet'][0]) + { + list($response) = $this->apiTrashFacilityQualificationRatesGetWithHttpInfo($search, $contentType); + return $response; + } + + /** + * Operation apiTrashFacilityQualificationRatesGetWithHttpInfo + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilityQualificationRatesGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\TrashFacilityQualificationRateResponse[], HTTP status code, HTTP response headers (array of strings) + */ + public function apiTrashFacilityQualificationRatesGetWithHttpInfo($search = null, string $contentType = self::contentTypes['apiTrashFacilityQualificationRatesGet'][0]) + { + $request = $this->apiTrashFacilityQualificationRatesGetRequest($search, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TrashFacilityQualificationRateResponse[]', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TrashFacilityQualificationRateResponse[]', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\TrashFacilityQualificationRateResponse[]', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiTrashFacilityQualificationRatesGetAsync + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilityQualificationRatesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashFacilityQualificationRatesGetAsync($search = null, string $contentType = self::contentTypes['apiTrashFacilityQualificationRatesGet'][0]) + { + return $this->apiTrashFacilityQualificationRatesGetAsyncWithHttpInfo($search, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTrashFacilityQualificationRatesGetAsyncWithHttpInfo + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilityQualificationRatesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashFacilityQualificationRatesGetAsyncWithHttpInfo($search = null, string $contentType = self::contentTypes['apiTrashFacilityQualificationRatesGet'][0]) + { + $returnType = '\OmsorgCoreClient\Model\TrashFacilityQualificationRateResponse[]'; + $request = $this->apiTrashFacilityQualificationRatesGetRequest($search, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTrashFacilityQualificationRatesGet' + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilityQualificationRatesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTrashFacilityQualificationRatesGetRequest($search = null, string $contentType = self::contentTypes['apiTrashFacilityQualificationRatesGet'][0]) + { + + + + $resourcePath = '/api/trash/facility-qualification-rates'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $search, + 'search', // param base name + 'string', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + + + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'GET', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTrashFacilityQualificationRatesIdRestorePost + * + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilityQualificationRatesIdRestorePost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return void + */ + public function apiTrashFacilityQualificationRatesIdRestorePost($id, string $contentType = self::contentTypes['apiTrashFacilityQualificationRatesIdRestorePost'][0]) + { + $this->apiTrashFacilityQualificationRatesIdRestorePostWithHttpInfo($id, $contentType); + } + + /** + * Operation apiTrashFacilityQualificationRatesIdRestorePostWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilityQualificationRatesIdRestorePost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of null, HTTP status code, HTTP response headers (array of strings) + */ + public function apiTrashFacilityQualificationRatesIdRestorePostWithHttpInfo($id, string $contentType = self::contentTypes['apiTrashFacilityQualificationRatesIdRestorePost'][0]) + { + $request = $this->apiTrashFacilityQualificationRatesIdRestorePostRequest($id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + return [null, $statusCode, $response->getHeaders()]; + } catch (ApiException $e) { + switch ($e->getCode()) { + } + + + throw $e; + } + } + + /** + * Operation apiTrashFacilityQualificationRatesIdRestorePostAsync + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilityQualificationRatesIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashFacilityQualificationRatesIdRestorePostAsync($id, string $contentType = self::contentTypes['apiTrashFacilityQualificationRatesIdRestorePost'][0]) + { + return $this->apiTrashFacilityQualificationRatesIdRestorePostAsyncWithHttpInfo($id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTrashFacilityQualificationRatesIdRestorePostAsyncWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilityQualificationRatesIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashFacilityQualificationRatesIdRestorePostAsyncWithHttpInfo($id, string $contentType = self::contentTypes['apiTrashFacilityQualificationRatesIdRestorePost'][0]) + { + $returnType = ''; + $request = $this->apiTrashFacilityQualificationRatesIdRestorePostRequest($id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + return [null, $response->getStatusCode(), $response->getHeaders()]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTrashFacilityQualificationRatesIdRestorePost' + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashFacilityQualificationRatesIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTrashFacilityQualificationRatesIdRestorePostRequest($id, string $contentType = self::contentTypes['apiTrashFacilityQualificationRatesIdRestorePost'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiTrashFacilityQualificationRatesIdRestorePost' + ); + } + + + $resourcePath = '/api/trash/facility-qualification-rates/{id}/restore'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + [], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'POST', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTrashOrdersGet + * + * @param string|null $search search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashOrdersGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\TrashOrderResponse[] + */ + public function apiTrashOrdersGet($search = null, string $contentType = self::contentTypes['apiTrashOrdersGet'][0]) + { + list($response) = $this->apiTrashOrdersGetWithHttpInfo($search, $contentType); + return $response; + } + + /** + * Operation apiTrashOrdersGetWithHttpInfo + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashOrdersGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\TrashOrderResponse[], HTTP status code, HTTP response headers (array of strings) + */ + public function apiTrashOrdersGetWithHttpInfo($search = null, string $contentType = self::contentTypes['apiTrashOrdersGet'][0]) + { + $request = $this->apiTrashOrdersGetRequest($search, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TrashOrderResponse[]', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TrashOrderResponse[]', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\TrashOrderResponse[]', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiTrashOrdersGetAsync + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashOrdersGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashOrdersGetAsync($search = null, string $contentType = self::contentTypes['apiTrashOrdersGet'][0]) + { + return $this->apiTrashOrdersGetAsyncWithHttpInfo($search, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTrashOrdersGetAsyncWithHttpInfo + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashOrdersGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashOrdersGetAsyncWithHttpInfo($search = null, string $contentType = self::contentTypes['apiTrashOrdersGet'][0]) + { + $returnType = '\OmsorgCoreClient\Model\TrashOrderResponse[]'; + $request = $this->apiTrashOrdersGetRequest($search, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTrashOrdersGet' + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashOrdersGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTrashOrdersGetRequest($search = null, string $contentType = self::contentTypes['apiTrashOrdersGet'][0]) + { + + + + $resourcePath = '/api/trash/orders'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $search, + 'search', // param base name + 'string', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + + + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'GET', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTrashOrdersIdRestorePost + * + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashOrdersIdRestorePost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return void + */ + public function apiTrashOrdersIdRestorePost($id, string $contentType = self::contentTypes['apiTrashOrdersIdRestorePost'][0]) + { + $this->apiTrashOrdersIdRestorePostWithHttpInfo($id, $contentType); + } + + /** + * Operation apiTrashOrdersIdRestorePostWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashOrdersIdRestorePost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of null, HTTP status code, HTTP response headers (array of strings) + */ + public function apiTrashOrdersIdRestorePostWithHttpInfo($id, string $contentType = self::contentTypes['apiTrashOrdersIdRestorePost'][0]) + { + $request = $this->apiTrashOrdersIdRestorePostRequest($id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + return [null, $statusCode, $response->getHeaders()]; + } catch (ApiException $e) { + switch ($e->getCode()) { + } + + + throw $e; + } + } + + /** + * Operation apiTrashOrdersIdRestorePostAsync + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashOrdersIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashOrdersIdRestorePostAsync($id, string $contentType = self::contentTypes['apiTrashOrdersIdRestorePost'][0]) + { + return $this->apiTrashOrdersIdRestorePostAsyncWithHttpInfo($id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTrashOrdersIdRestorePostAsyncWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashOrdersIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashOrdersIdRestorePostAsyncWithHttpInfo($id, string $contentType = self::contentTypes['apiTrashOrdersIdRestorePost'][0]) + { + $returnType = ''; + $request = $this->apiTrashOrdersIdRestorePostRequest($id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + return [null, $response->getStatusCode(), $response->getHeaders()]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTrashOrdersIdRestorePost' + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashOrdersIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTrashOrdersIdRestorePostRequest($id, string $contentType = self::contentTypes['apiTrashOrdersIdRestorePost'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiTrashOrdersIdRestorePost' + ); + } + + + $resourcePath = '/api/trash/orders/{id}/restore'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + [], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'POST', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTrashTimeEntriesGet + * + * @param string|null $search search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashTimeEntriesGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return \OmsorgCoreClient\Model\TrashTimeEntryResponse[] + */ + public function apiTrashTimeEntriesGet($search = null, string $contentType = self::contentTypes['apiTrashTimeEntriesGet'][0]) + { + list($response) = $this->apiTrashTimeEntriesGetWithHttpInfo($search, $contentType); + return $response; + } + + /** + * Operation apiTrashTimeEntriesGetWithHttpInfo + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashTimeEntriesGet'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of \OmsorgCoreClient\Model\TrashTimeEntryResponse[], HTTP status code, HTTP response headers (array of strings) + */ + public function apiTrashTimeEntriesGetWithHttpInfo($search = null, string $contentType = self::contentTypes['apiTrashTimeEntriesGet'][0]) + { + $request = $this->apiTrashTimeEntriesGetRequest($search, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + switch($statusCode) { + case 200: + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TrashTimeEntryResponse[]', + $request, + $response, + ); + } + + + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + (string) $request->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + + return $this->handleResponseWithDataType( + '\OmsorgCoreClient\Model\TrashTimeEntryResponse[]', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\TrashTimeEntryResponse[]', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiTrashTimeEntriesGetAsync + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashTimeEntriesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashTimeEntriesGetAsync($search = null, string $contentType = self::contentTypes['apiTrashTimeEntriesGet'][0]) + { + return $this->apiTrashTimeEntriesGetAsyncWithHttpInfo($search, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTrashTimeEntriesGetAsyncWithHttpInfo + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashTimeEntriesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashTimeEntriesGetAsyncWithHttpInfo($search = null, string $contentType = self::contentTypes['apiTrashTimeEntriesGet'][0]) + { + $returnType = '\OmsorgCoreClient\Model\TrashTimeEntryResponse[]'; + $request = $this->apiTrashTimeEntriesGetRequest($search, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + if ($returnType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTrashTimeEntriesGet' + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashTimeEntriesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTrashTimeEntriesGetRequest($search = null, string $contentType = self::contentTypes['apiTrashTimeEntriesGet'][0]) + { + + + + $resourcePath = '/api/trash/time-entries'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + // query params + $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue( + $search, + 'search', // param base name + 'string', // openApiType + 'form', // style + true, // explode + false // required + ) ?? []); + + + + + $headers = $this->headerSelector->selectHeaders( + ['text/plain', 'application/json', 'text/json', ], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'GET', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Operation apiTrashTimeEntriesIdRestorePost + * + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashTimeEntriesIdRestorePost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return void + */ + public function apiTrashTimeEntriesIdRestorePost($id, string $contentType = self::contentTypes['apiTrashTimeEntriesIdRestorePost'][0]) + { + $this->apiTrashTimeEntriesIdRestorePostWithHttpInfo($id, $contentType); + } + + /** + * Operation apiTrashTimeEntriesIdRestorePostWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashTimeEntriesIdRestorePost'] to see the possible values for this operation + * + * @throws \OmsorgCoreClient\ApiException on non-2xx response or if the response body is not in the expected format + * @throws \InvalidArgumentException + * @return array of null, HTTP status code, HTTP response headers (array of strings) + */ + public function apiTrashTimeEntriesIdRestorePostWithHttpInfo($id, string $contentType = self::contentTypes['apiTrashTimeEntriesIdRestorePost'][0]) + { + $request = $this->apiTrashTimeEntriesIdRestorePostRequest($id, $contentType); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse() ? (string) $e->getResponse()->getBody() : null + ); + } catch (ConnectException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + (int) $e->getCode(), + null, + null + ); + } + + $statusCode = $response->getStatusCode(); + + + return [null, $statusCode, $response->getHeaders()]; + } catch (ApiException $e) { + switch ($e->getCode()) { + } + + + throw $e; + } + } + + /** + * Operation apiTrashTimeEntriesIdRestorePostAsync + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashTimeEntriesIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashTimeEntriesIdRestorePostAsync($id, string $contentType = self::contentTypes['apiTrashTimeEntriesIdRestorePost'][0]) + { + return $this->apiTrashTimeEntriesIdRestorePostAsyncWithHttpInfo($id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTrashTimeEntriesIdRestorePostAsyncWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashTimeEntriesIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashTimeEntriesIdRestorePostAsyncWithHttpInfo($id, string $contentType = self::contentTypes['apiTrashTimeEntriesIdRestorePost'][0]) + { + $returnType = ''; + $request = $this->apiTrashTimeEntriesIdRestorePostRequest($id, $contentType); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + return [null, $response->getStatusCode(), $response->getHeaders()]; + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + (string) $response->getBody() + ); + } + ); + } + + /** + * Create request for operation 'apiTrashTimeEntriesIdRestorePost' + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashTimeEntriesIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTrashTimeEntriesIdRestorePostRequest($id, string $contentType = self::contentTypes['apiTrashTimeEntriesIdRestorePost'][0]) + { + + // verify the required parameter 'id' is set + if ($id === null || (is_array($id) && count($id) === 0)) { + throw new \InvalidArgumentException( + 'Missing the required parameter $id when calling apiTrashTimeEntriesIdRestorePost' + ); + } + + + $resourcePath = '/api/trash/time-entries/{id}/restore'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + // path params + if ($id !== null) { + $resourcePath = str_replace( + '{' . 'id' . '}', + ObjectSerializer::toPathValue($id), + $resourcePath + ); + } + + + $headers = $this->headerSelector->selectHeaders( + [], + $contentType, + $multipart + ); + + // for model (json/xml) + if (count($formParams) > 0) { + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue]; + foreach ($formParamValueItems as $formParamValueItem) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValueItem + ]; + } + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif (stripos($headers['Content-Type'], 'application/json') !== false) { + # if Content-Type contains "application/json", json_encode the form parameters + $httpBody = \GuzzleHttp\Utils::jsonEncode($formParams); + } else { + // for HTTP post (form) + $httpBody = ObjectSerializer::buildQuery($formParams); + } + } + + // this endpoint requires Bearer (JWT) authentication (access token) + if (!empty($this->config->getAccessToken())) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); + } + + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); + } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $operationHost = $this->config->getHost(); + $query = ObjectSerializer::buildQuery($queryParams); + return new Request( + 'POST', + $operationHost . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); + } + + /** + * Create http client option + * + * @throws \RuntimeException on file opening failure + * @return array of http client options + */ + protected function createHttpClientOption() + { + $options = []; + if ($this->config->getDebug()) { + $options[RequestOptions::DEBUG] = fopen($this->config->getDebugFile(), 'a'); + if (!$options[RequestOptions::DEBUG]) { + throw new \RuntimeException('Failed to open the debug file: ' . $this->config->getDebugFile()); + } + } + + return $options; + } + + private function handleResponseWithDataType( + string $dataType, + RequestInterface $request, + ResponseInterface $response + ): array { + if ($dataType === '\SplFileObject') { + $content = $response->getBody(); //stream goes to serializer + } else { + $content = (string) $response->getBody(); + if ($dataType !== 'string') { + try { + $content = json_decode($content, false, 512, JSON_THROW_ON_ERROR); + } catch (\JsonException $exception) { + throw new ApiException( + sprintf( + 'Error JSON decoding server response (%s)', + $request->getUri() + ), + $response->getStatusCode(), + $response->getHeaders(), + $content + ); + } + } + } + + return [ + ObjectSerializer::deserialize($content, $dataType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + } + + private function responseWithinRangeCode( + string $rangeCode, + int $statusCode + ): bool { + $left = (int) ($rangeCode[0].'00'); + $right = (int) ($rangeCode[0].'99'); + + return $statusCode >= $left && $statusCode <= $right; + } +} diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/AbsenceDecisionRequest.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/AbsenceDecisionRequest.php new file mode 100644 index 0000000..9f40223 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/AbsenceDecisionRequest.php @@ -0,0 +1,457 @@ + + */ +class AbsenceDecisionRequest implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'AbsenceDecisionRequest'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'status' => 'string', + 'admin_note' => 'string' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'status' => null, + 'admin_note' => null + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'status' => true, + 'admin_note' => true + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'status' => 'status', + 'admin_note' => 'adminNote' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'status' => 'setStatus', + 'admin_note' => 'setAdminNote' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'status' => 'getStatus', + 'admin_note' => 'getAdminNote' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('status', $data ?? [], null); + $this->setIfExists('admin_note', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets status + * + * @return string|null + */ + public function getStatus() + { + return $this->container['status']; + } + + /** + * Sets status + * + * @param string|null $status status + * + * @return self + */ + public function setStatus($status) + { + if (is_null($status)) { + array_push($this->openAPINullablesSetToNull, 'status'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('status', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['status'] = $status; + + return $this; + } + + /** + * Gets admin_note + * + * @return string|null + */ + public function getAdminNote() + { + return $this->container['admin_note']; + } + + /** + * Sets admin_note + * + * @param string|null $admin_note admin_note + * + * @return self + */ + public function setAdminNote($admin_note) + { + if (is_null($admin_note)) { + array_push($this->openAPINullablesSetToNull, 'admin_note'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('admin_note', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['admin_note'] = $admin_note; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/AbsenceResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/AbsenceResponse.php new file mode 100644 index 0000000..7d62d0d --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/AbsenceResponse.php @@ -0,0 +1,832 @@ + + */ +class AbsenceResponse implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'AbsenceResponse'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'id' => 'string', + 'employee_id' => 'string', + 'employee_name' => 'string', + 'type' => 'string', + 'start_date' => '\DateTime', + 'end_date' => '\DateTime', + 'reason' => 'string', + 'substitute' => 'string', + 'note' => 'string', + 'status' => 'string', + 'admin_note' => 'string', + 'created_at' => '\DateTime' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'id' => 'uuid', + 'employee_id' => 'uuid', + 'employee_name' => null, + 'type' => null, + 'start_date' => 'date', + 'end_date' => 'date', + 'reason' => null, + 'substitute' => null, + 'note' => null, + 'status' => null, + 'admin_note' => null, + 'created_at' => 'date-time' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'id' => false, + 'employee_id' => false, + 'employee_name' => true, + 'type' => true, + 'start_date' => false, + 'end_date' => false, + 'reason' => true, + 'substitute' => true, + 'note' => true, + 'status' => true, + 'admin_note' => true, + 'created_at' => false + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'id' => 'id', + 'employee_id' => 'employeeId', + 'employee_name' => 'employeeName', + 'type' => 'type', + 'start_date' => 'startDate', + 'end_date' => 'endDate', + 'reason' => 'reason', + 'substitute' => 'substitute', + 'note' => 'note', + 'status' => 'status', + 'admin_note' => 'adminNote', + 'created_at' => 'createdAt' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'id' => 'setId', + 'employee_id' => 'setEmployeeId', + 'employee_name' => 'setEmployeeName', + 'type' => 'setType', + 'start_date' => 'setStartDate', + 'end_date' => 'setEndDate', + 'reason' => 'setReason', + 'substitute' => 'setSubstitute', + 'note' => 'setNote', + 'status' => 'setStatus', + 'admin_note' => 'setAdminNote', + 'created_at' => 'setCreatedAt' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'id' => 'getId', + 'employee_id' => 'getEmployeeId', + 'employee_name' => 'getEmployeeName', + 'type' => 'getType', + 'start_date' => 'getStartDate', + 'end_date' => 'getEndDate', + 'reason' => 'getReason', + 'substitute' => 'getSubstitute', + 'note' => 'getNote', + 'status' => 'getStatus', + 'admin_note' => 'getAdminNote', + 'created_at' => 'getCreatedAt' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('id', $data ?? [], null); + $this->setIfExists('employee_id', $data ?? [], null); + $this->setIfExists('employee_name', $data ?? [], null); + $this->setIfExists('type', $data ?? [], null); + $this->setIfExists('start_date', $data ?? [], null); + $this->setIfExists('end_date', $data ?? [], null); + $this->setIfExists('reason', $data ?? [], null); + $this->setIfExists('substitute', $data ?? [], null); + $this->setIfExists('note', $data ?? [], null); + $this->setIfExists('status', $data ?? [], null); + $this->setIfExists('admin_note', $data ?? [], null); + $this->setIfExists('created_at', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets id + * + * @return string|null + */ + public function getId() + { + return $this->container['id']; + } + + /** + * Sets id + * + * @param string|null $id id + * + * @return self + */ + public function setId($id) + { + if (is_null($id)) { + throw new \InvalidArgumentException('non-nullable id cannot be null'); + } + $this->container['id'] = $id; + + return $this; + } + + /** + * Gets employee_id + * + * @return string|null + */ + public function getEmployeeId() + { + return $this->container['employee_id']; + } + + /** + * Sets employee_id + * + * @param string|null $employee_id employee_id + * + * @return self + */ + public function setEmployeeId($employee_id) + { + if (is_null($employee_id)) { + throw new \InvalidArgumentException('non-nullable employee_id cannot be null'); + } + $this->container['employee_id'] = $employee_id; + + return $this; + } + + /** + * Gets employee_name + * + * @return string|null + */ + public function getEmployeeName() + { + return $this->container['employee_name']; + } + + /** + * Sets employee_name + * + * @param string|null $employee_name employee_name + * + * @return self + */ + public function setEmployeeName($employee_name) + { + if (is_null($employee_name)) { + array_push($this->openAPINullablesSetToNull, 'employee_name'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('employee_name', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['employee_name'] = $employee_name; + + return $this; + } + + /** + * Gets type + * + * @return string|null + */ + public function getType() + { + return $this->container['type']; + } + + /** + * Sets type + * + * @param string|null $type type + * + * @return self + */ + public function setType($type) + { + if (is_null($type)) { + array_push($this->openAPINullablesSetToNull, 'type'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('type', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['type'] = $type; + + return $this; + } + + /** + * Gets start_date + * + * @return \DateTime|null + */ + public function getStartDate() + { + return $this->container['start_date']; + } + + /** + * Sets start_date + * + * @param \DateTime|null $start_date start_date + * + * @return self + */ + public function setStartDate($start_date) + { + if (is_null($start_date)) { + throw new \InvalidArgumentException('non-nullable start_date cannot be null'); + } + $this->container['start_date'] = $start_date; + + return $this; + } + + /** + * Gets end_date + * + * @return \DateTime|null + */ + public function getEndDate() + { + return $this->container['end_date']; + } + + /** + * Sets end_date + * + * @param \DateTime|null $end_date end_date + * + * @return self + */ + public function setEndDate($end_date) + { + if (is_null($end_date)) { + throw new \InvalidArgumentException('non-nullable end_date cannot be null'); + } + $this->container['end_date'] = $end_date; + + return $this; + } + + /** + * Gets reason + * + * @return string|null + */ + public function getReason() + { + return $this->container['reason']; + } + + /** + * Sets reason + * + * @param string|null $reason reason + * + * @return self + */ + public function setReason($reason) + { + if (is_null($reason)) { + array_push($this->openAPINullablesSetToNull, 'reason'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('reason', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['reason'] = $reason; + + return $this; + } + + /** + * Gets substitute + * + * @return string|null + */ + public function getSubstitute() + { + return $this->container['substitute']; + } + + /** + * Sets substitute + * + * @param string|null $substitute substitute + * + * @return self + */ + public function setSubstitute($substitute) + { + if (is_null($substitute)) { + array_push($this->openAPINullablesSetToNull, 'substitute'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('substitute', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['substitute'] = $substitute; + + return $this; + } + + /** + * Gets note + * + * @return string|null + */ + public function getNote() + { + return $this->container['note']; + } + + /** + * Sets note + * + * @param string|null $note note + * + * @return self + */ + public function setNote($note) + { + if (is_null($note)) { + array_push($this->openAPINullablesSetToNull, 'note'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('note', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['note'] = $note; + + return $this; + } + + /** + * Gets status + * + * @return string|null + */ + public function getStatus() + { + return $this->container['status']; + } + + /** + * Sets status + * + * @param string|null $status status + * + * @return self + */ + public function setStatus($status) + { + if (is_null($status)) { + array_push($this->openAPINullablesSetToNull, 'status'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('status', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['status'] = $status; + + return $this; + } + + /** + * Gets admin_note + * + * @return string|null + */ + public function getAdminNote() + { + return $this->container['admin_note']; + } + + /** + * Sets admin_note + * + * @param string|null $admin_note admin_note + * + * @return self + */ + public function setAdminNote($admin_note) + { + if (is_null($admin_note)) { + array_push($this->openAPINullablesSetToNull, 'admin_note'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('admin_note', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['admin_note'] = $admin_note; + + return $this; + } + + /** + * Gets created_at + * + * @return \DateTime|null + */ + public function getCreatedAt() + { + return $this->container['created_at']; + } + + /** + * Sets created_at + * + * @param \DateTime|null $created_at created_at + * + * @return self + */ + public function setCreatedAt($created_at) + { + if (is_null($created_at)) { + throw new \InvalidArgumentException('non-nullable created_at cannot be null'); + } + $this->container['created_at'] = $created_at; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/AbsenceResponsePagedResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/AbsenceResponsePagedResponse.php new file mode 100644 index 0000000..90972a3 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/AbsenceResponsePagedResponse.php @@ -0,0 +1,518 @@ + + */ +class AbsenceResponsePagedResponse implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'AbsenceResponsePagedResponse'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'items' => '\OmsorgCoreClient\Model\AbsenceResponse[]', + 'total_count' => 'int', + 'page' => 'int', + 'page_size' => 'int' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'items' => null, + 'total_count' => 'int32', + 'page' => 'int32', + 'page_size' => 'int32' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'items' => true, + 'total_count' => false, + 'page' => false, + 'page_size' => false + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'items' => 'items', + 'total_count' => 'totalCount', + 'page' => 'page', + 'page_size' => 'pageSize' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'items' => 'setItems', + 'total_count' => 'setTotalCount', + 'page' => 'setPage', + 'page_size' => 'setPageSize' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'items' => 'getItems', + 'total_count' => 'getTotalCount', + 'page' => 'getPage', + 'page_size' => 'getPageSize' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('items', $data ?? [], null); + $this->setIfExists('total_count', $data ?? [], null); + $this->setIfExists('page', $data ?? [], null); + $this->setIfExists('page_size', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets items + * + * @return \OmsorgCoreClient\Model\AbsenceResponse[]|null + */ + public function getItems() + { + return $this->container['items']; + } + + /** + * Sets items + * + * @param \OmsorgCoreClient\Model\AbsenceResponse[]|null $items items + * + * @return self + */ + public function setItems($items) + { + if (is_null($items)) { + array_push($this->openAPINullablesSetToNull, 'items'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('items', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['items'] = $items; + + return $this; + } + + /** + * Gets total_count + * + * @return int|null + */ + public function getTotalCount() + { + return $this->container['total_count']; + } + + /** + * Sets total_count + * + * @param int|null $total_count total_count + * + * @return self + */ + public function setTotalCount($total_count) + { + if (is_null($total_count)) { + throw new \InvalidArgumentException('non-nullable total_count cannot be null'); + } + $this->container['total_count'] = $total_count; + + return $this; + } + + /** + * Gets page + * + * @return int|null + */ + public function getPage() + { + return $this->container['page']; + } + + /** + * Sets page + * + * @param int|null $page page + * + * @return self + */ + public function setPage($page) + { + if (is_null($page)) { + throw new \InvalidArgumentException('non-nullable page cannot be null'); + } + $this->container['page'] = $page; + + return $this; + } + + /** + * Gets page_size + * + * @return int|null + */ + public function getPageSize() + { + return $this->container['page_size']; + } + + /** + * Sets page_size + * + * @param int|null $page_size page_size + * + * @return self + */ + public function setPageSize($page_size) + { + if (is_null($page_size)) { + throw new \InvalidArgumentException('non-nullable page_size cannot be null'); + } + $this->container['page_size'] = $page_size; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/AddUserPermissionOverrideRequest.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/AddUserPermissionOverrideRequest.php index 7b375db..81c204d 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/AddUserPermissionOverrideRequest.php +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/AddUserPermissionOverrideRequest.php @@ -57,9 +57,10 @@ class AddUserPermissionOverrideRequest implements ModelInterface, ArrayAccess, \ * @var string[] */ protected static $openAPITypes = [ - 'module' => 'string', - 'action' => 'string', - 'effect' => 'string' + 'module' => '\OmsorgCoreClient\Model\ModuleType', + 'action' => '\OmsorgCoreClient\Model\PermissionAction', + 'effect' => '\OmsorgCoreClient\Model\PermissionEffect', + 'scope' => '\OmsorgCoreClient\Model\PermissionScope' ]; /** @@ -72,7 +73,8 @@ class AddUserPermissionOverrideRequest implements ModelInterface, ArrayAccess, \ protected static $openAPIFormats = [ 'module' => null, 'action' => null, - 'effect' => null + 'effect' => null, + 'scope' => null ]; /** @@ -81,9 +83,10 @@ class AddUserPermissionOverrideRequest implements ModelInterface, ArrayAccess, \ * @var boolean[] */ protected static array $openAPINullables = [ - 'module' => true, - 'action' => true, - 'effect' => true + 'module' => false, + 'action' => false, + 'effect' => false, + 'scope' => false ]; /** @@ -174,7 +177,8 @@ class AddUserPermissionOverrideRequest implements ModelInterface, ArrayAccess, \ protected static $attributeMap = [ 'module' => 'module', 'action' => 'action', - 'effect' => 'effect' + 'effect' => 'effect', + 'scope' => 'scope' ]; /** @@ -185,7 +189,8 @@ class AddUserPermissionOverrideRequest implements ModelInterface, ArrayAccess, \ protected static $setters = [ 'module' => 'setModule', 'action' => 'setAction', - 'effect' => 'setEffect' + 'effect' => 'setEffect', + 'scope' => 'setScope' ]; /** @@ -196,7 +201,8 @@ class AddUserPermissionOverrideRequest implements ModelInterface, ArrayAccess, \ protected static $getters = [ 'module' => 'getModule', 'action' => 'getAction', - 'effect' => 'getEffect' + 'effect' => 'getEffect', + 'scope' => 'getScope' ]; /** @@ -259,6 +265,7 @@ class AddUserPermissionOverrideRequest implements ModelInterface, ArrayAccess, \ $this->setIfExists('module', $data ?? [], null); $this->setIfExists('action', $data ?? [], null); $this->setIfExists('effect', $data ?? [], null); + $this->setIfExists('scope', $data ?? [], null); } /** @@ -306,7 +313,7 @@ class AddUserPermissionOverrideRequest implements ModelInterface, ArrayAccess, \ /** * Gets module * - * @return string|null + * @return \OmsorgCoreClient\Model\ModuleType|null */ public function getModule() { @@ -316,21 +323,14 @@ class AddUserPermissionOverrideRequest implements ModelInterface, ArrayAccess, \ /** * Sets module * - * @param string|null $module module + * @param \OmsorgCoreClient\Model\ModuleType|null $module module * * @return self */ public function setModule($module) { if (is_null($module)) { - array_push($this->openAPINullablesSetToNull, 'module'); - } else { - $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); - $index = array_search('module', $nullablesSetToNull); - if ($index !== FALSE) { - unset($nullablesSetToNull[$index]); - $this->setOpenAPINullablesSetToNull($nullablesSetToNull); - } + throw new \InvalidArgumentException('non-nullable module cannot be null'); } $this->container['module'] = $module; @@ -340,7 +340,7 @@ class AddUserPermissionOverrideRequest implements ModelInterface, ArrayAccess, \ /** * Gets action * - * @return string|null + * @return \OmsorgCoreClient\Model\PermissionAction|null */ public function getAction() { @@ -350,21 +350,14 @@ class AddUserPermissionOverrideRequest implements ModelInterface, ArrayAccess, \ /** * Sets action * - * @param string|null $action action + * @param \OmsorgCoreClient\Model\PermissionAction|null $action action * * @return self */ public function setAction($action) { if (is_null($action)) { - array_push($this->openAPINullablesSetToNull, 'action'); - } else { - $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); - $index = array_search('action', $nullablesSetToNull); - if ($index !== FALSE) { - unset($nullablesSetToNull[$index]); - $this->setOpenAPINullablesSetToNull($nullablesSetToNull); - } + throw new \InvalidArgumentException('non-nullable action cannot be null'); } $this->container['action'] = $action; @@ -374,7 +367,7 @@ class AddUserPermissionOverrideRequest implements ModelInterface, ArrayAccess, \ /** * Gets effect * - * @return string|null + * @return \OmsorgCoreClient\Model\PermissionEffect|null */ public function getEffect() { @@ -384,26 +377,46 @@ class AddUserPermissionOverrideRequest implements ModelInterface, ArrayAccess, \ /** * Sets effect * - * @param string|null $effect effect + * @param \OmsorgCoreClient\Model\PermissionEffect|null $effect effect * * @return self */ public function setEffect($effect) { if (is_null($effect)) { - array_push($this->openAPINullablesSetToNull, 'effect'); - } else { - $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); - $index = array_search('effect', $nullablesSetToNull); - if ($index !== FALSE) { - unset($nullablesSetToNull[$index]); - $this->setOpenAPINullablesSetToNull($nullablesSetToNull); - } + throw new \InvalidArgumentException('non-nullable effect cannot be null'); } $this->container['effect'] = $effect; return $this; } + + /** + * Gets scope + * + * @return \OmsorgCoreClient\Model\PermissionScope|null + */ + public function getScope() + { + return $this->container['scope']; + } + + /** + * Sets scope + * + * @param \OmsorgCoreClient\Model\PermissionScope|null $scope scope + * + * @return self + */ + public function setScope($scope) + { + if (is_null($scope)) { + throw new \InvalidArgumentException('non-nullable scope cannot be null'); + } + $this->container['scope'] = $scope; + + return $this; + } /** * Returns true if offset exists. False otherwise. * diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/AuditEventCategory.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/AuditEventCategory.php new file mode 100644 index 0000000..da08aec --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/AuditEventCategory.php @@ -0,0 +1,62 @@ + + */ +class CreateAbsenceRequest implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'CreateAbsenceRequest'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'type' => 'string', + 'start_date' => '\DateTime', + 'end_date' => '\DateTime', + 'reason' => 'string', + 'substitute' => 'string', + 'note' => 'string' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'type' => null, + 'start_date' => 'date', + 'end_date' => 'date', + 'reason' => null, + 'substitute' => null, + 'note' => null + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'type' => true, + 'start_date' => false, + 'end_date' => false, + 'reason' => true, + 'substitute' => true, + 'note' => true + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'type' => 'type', + 'start_date' => 'startDate', + 'end_date' => 'endDate', + 'reason' => 'reason', + 'substitute' => 'substitute', + 'note' => 'note' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'type' => 'setType', + 'start_date' => 'setStartDate', + 'end_date' => 'setEndDate', + 'reason' => 'setReason', + 'substitute' => 'setSubstitute', + 'note' => 'setNote' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'type' => 'getType', + 'start_date' => 'getStartDate', + 'end_date' => 'getEndDate', + 'reason' => 'getReason', + 'substitute' => 'getSubstitute', + 'note' => 'getNote' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('type', $data ?? [], null); + $this->setIfExists('start_date', $data ?? [], null); + $this->setIfExists('end_date', $data ?? [], null); + $this->setIfExists('reason', $data ?? [], null); + $this->setIfExists('substitute', $data ?? [], null); + $this->setIfExists('note', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets type + * + * @return string|null + */ + public function getType() + { + return $this->container['type']; + } + + /** + * Sets type + * + * @param string|null $type type + * + * @return self + */ + public function setType($type) + { + if (is_null($type)) { + array_push($this->openAPINullablesSetToNull, 'type'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('type', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['type'] = $type; + + return $this; + } + + /** + * Gets start_date + * + * @return \DateTime|null + */ + public function getStartDate() + { + return $this->container['start_date']; + } + + /** + * Sets start_date + * + * @param \DateTime|null $start_date start_date + * + * @return self + */ + public function setStartDate($start_date) + { + if (is_null($start_date)) { + throw new \InvalidArgumentException('non-nullable start_date cannot be null'); + } + $this->container['start_date'] = $start_date; + + return $this; + } + + /** + * Gets end_date + * + * @return \DateTime|null + */ + public function getEndDate() + { + return $this->container['end_date']; + } + + /** + * Sets end_date + * + * @param \DateTime|null $end_date end_date + * + * @return self + */ + public function setEndDate($end_date) + { + if (is_null($end_date)) { + throw new \InvalidArgumentException('non-nullable end_date cannot be null'); + } + $this->container['end_date'] = $end_date; + + return $this; + } + + /** + * Gets reason + * + * @return string|null + */ + public function getReason() + { + return $this->container['reason']; + } + + /** + * Sets reason + * + * @param string|null $reason reason + * + * @return self + */ + public function setReason($reason) + { + if (is_null($reason)) { + array_push($this->openAPINullablesSetToNull, 'reason'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('reason', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['reason'] = $reason; + + return $this; + } + + /** + * Gets substitute + * + * @return string|null + */ + public function getSubstitute() + { + return $this->container['substitute']; + } + + /** + * Sets substitute + * + * @param string|null $substitute substitute + * + * @return self + */ + public function setSubstitute($substitute) + { + if (is_null($substitute)) { + array_push($this->openAPINullablesSetToNull, 'substitute'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('substitute', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['substitute'] = $substitute; + + return $this; + } + + /** + * Gets note + * + * @return string|null + */ + public function getNote() + { + return $this->container['note']; + } + + /** + * Sets note + * + * @param string|null $note note + * + * @return self + */ + public function setNote($note) + { + if (is_null($note)) { + array_push($this->openAPINullablesSetToNull, 'note'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('note', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['note'] = $note; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/CreateFacilityQualificationRateRequest.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/CreateFacilityQualificationRateRequest.php new file mode 100644 index 0000000..c7eb377 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/CreateFacilityQualificationRateRequest.php @@ -0,0 +1,450 @@ + + */ +class CreateFacilityQualificationRateRequest implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'CreateFacilityQualificationRateRequest'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'qualification' => 'string', + 'rate' => 'float' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'qualification' => null, + 'rate' => 'double' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'qualification' => true, + 'rate' => false + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'qualification' => 'qualification', + 'rate' => 'rate' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'qualification' => 'setQualification', + 'rate' => 'setRate' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'qualification' => 'getQualification', + 'rate' => 'getRate' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('qualification', $data ?? [], null); + $this->setIfExists('rate', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets qualification + * + * @return string|null + */ + public function getQualification() + { + return $this->container['qualification']; + } + + /** + * Sets qualification + * + * @param string|null $qualification qualification + * + * @return self + */ + public function setQualification($qualification) + { + if (is_null($qualification)) { + array_push($this->openAPINullablesSetToNull, 'qualification'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('qualification', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['qualification'] = $qualification; + + return $this; + } + + /** + * Gets rate + * + * @return float|null + */ + public function getRate() + { + return $this->container['rate']; + } + + /** + * Sets rate + * + * @param float|null $rate rate + * + * @return self + */ + public function setRate($rate) + { + if (is_null($rate)) { + throw new \InvalidArgumentException('non-nullable rate cannot be null'); + } + $this->container['rate'] = $rate; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/CreateFacilityRequest.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/CreateFacilityRequest.php index 2e36ade..2502889 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/CreateFacilityRequest.php +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/CreateFacilityRequest.php @@ -59,6 +59,7 @@ class CreateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali protected static $openAPITypes = [ 'name' => 'string', 'facility_type' => 'string', + 'website' => 'string', 'street' => 'string', 'postal_code' => 'string', 'city' => 'string', @@ -79,6 +80,7 @@ class CreateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali protected static $openAPIFormats = [ 'name' => null, 'facility_type' => null, + 'website' => null, 'street' => null, 'postal_code' => null, 'city' => null, @@ -97,6 +99,7 @@ class CreateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali protected static array $openAPINullables = [ 'name' => true, 'facility_type' => true, + 'website' => true, 'street' => true, 'postal_code' => true, 'city' => true, @@ -195,6 +198,7 @@ class CreateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali protected static $attributeMap = [ 'name' => 'name', 'facility_type' => 'facilityType', + 'website' => 'website', 'street' => 'street', 'postal_code' => 'postalCode', 'city' => 'city', @@ -213,6 +217,7 @@ class CreateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali protected static $setters = [ 'name' => 'setName', 'facility_type' => 'setFacilityType', + 'website' => 'setWebsite', 'street' => 'setStreet', 'postal_code' => 'setPostalCode', 'city' => 'setCity', @@ -231,6 +236,7 @@ class CreateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali protected static $getters = [ 'name' => 'getName', 'facility_type' => 'getFacilityType', + 'website' => 'getWebsite', 'street' => 'getStreet', 'postal_code' => 'getPostalCode', 'city' => 'getCity', @@ -300,6 +306,7 @@ class CreateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali { $this->setIfExists('name', $data ?? [], null); $this->setIfExists('facility_type', $data ?? [], null); + $this->setIfExists('website', $data ?? [], null); $this->setIfExists('street', $data ?? [], null); $this->setIfExists('postal_code', $data ?? [], null); $this->setIfExists('city', $data ?? [], null); @@ -420,6 +427,40 @@ class CreateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali return $this; } + /** + * Gets website + * + * @return string|null + */ + public function getWebsite() + { + return $this->container['website']; + } + + /** + * Sets website + * + * @param string|null $website website + * + * @return self + */ + public function setWebsite($website) + { + if (is_null($website)) { + array_push($this->openAPINullablesSetToNull, 'website'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('website', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['website'] = $website; + + return $this; + } + /** * Gets street * diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/CreateTimeEntryRequest.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/CreateTimeEntryRequest.php new file mode 100644 index 0000000..a289f90 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/CreateTimeEntryRequest.php @@ -0,0 +1,681 @@ + + */ +class CreateTimeEntryRequest implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'CreateTimeEntryRequest'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'order_id' => 'string', + 'date' => '\DateTime', + 'start' => 'string', + 'end' => 'string', + 'break_duration' => 'string', + 'night_hours' => 'float', + 'saturday_hours' => 'float', + 'sunday_hours' => 'float', + 'holiday_hours' => 'float' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'order_id' => 'uuid', + 'date' => 'date', + 'start' => 'time', + 'end' => 'time', + 'break_duration' => 'date-span', + 'night_hours' => 'double', + 'saturday_hours' => 'double', + 'sunday_hours' => 'double', + 'holiday_hours' => 'double' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'order_id' => false, + 'date' => false, + 'start' => false, + 'end' => false, + 'break_duration' => false, + 'night_hours' => false, + 'saturday_hours' => false, + 'sunday_hours' => false, + 'holiday_hours' => false + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'order_id' => 'orderId', + 'date' => 'date', + 'start' => 'start', + 'end' => 'end', + 'break_duration' => 'breakDuration', + 'night_hours' => 'nightHours', + 'saturday_hours' => 'saturdayHours', + 'sunday_hours' => 'sundayHours', + 'holiday_hours' => 'holidayHours' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'order_id' => 'setOrderId', + 'date' => 'setDate', + 'start' => 'setStart', + 'end' => 'setEnd', + 'break_duration' => 'setBreakDuration', + 'night_hours' => 'setNightHours', + 'saturday_hours' => 'setSaturdayHours', + 'sunday_hours' => 'setSundayHours', + 'holiday_hours' => 'setHolidayHours' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'order_id' => 'getOrderId', + 'date' => 'getDate', + 'start' => 'getStart', + 'end' => 'getEnd', + 'break_duration' => 'getBreakDuration', + 'night_hours' => 'getNightHours', + 'saturday_hours' => 'getSaturdayHours', + 'sunday_hours' => 'getSundayHours', + 'holiday_hours' => 'getHolidayHours' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('order_id', $data ?? [], null); + $this->setIfExists('date', $data ?? [], null); + $this->setIfExists('start', $data ?? [], null); + $this->setIfExists('end', $data ?? [], null); + $this->setIfExists('break_duration', $data ?? [], null); + $this->setIfExists('night_hours', $data ?? [], null); + $this->setIfExists('saturday_hours', $data ?? [], null); + $this->setIfExists('sunday_hours', $data ?? [], null); + $this->setIfExists('holiday_hours', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets order_id + * + * @return string|null + */ + public function getOrderId() + { + return $this->container['order_id']; + } + + /** + * Sets order_id + * + * @param string|null $order_id order_id + * + * @return self + */ + public function setOrderId($order_id) + { + if (is_null($order_id)) { + throw new \InvalidArgumentException('non-nullable order_id cannot be null'); + } + $this->container['order_id'] = $order_id; + + return $this; + } + + /** + * Gets date + * + * @return \DateTime|null + */ + public function getDate() + { + return $this->container['date']; + } + + /** + * Sets date + * + * @param \DateTime|null $date date + * + * @return self + */ + public function setDate($date) + { + if (is_null($date)) { + throw new \InvalidArgumentException('non-nullable date cannot be null'); + } + $this->container['date'] = $date; + + return $this; + } + + /** + * Gets start + * + * @return string|null + */ + public function getStart() + { + return $this->container['start']; + } + + /** + * Sets start + * + * @param string|null $start start + * + * @return self + */ + public function setStart($start) + { + if (is_null($start)) { + throw new \InvalidArgumentException('non-nullable start cannot be null'); + } + $this->container['start'] = $start; + + return $this; + } + + /** + * Gets end + * + * @return string|null + */ + public function getEnd() + { + return $this->container['end']; + } + + /** + * Sets end + * + * @param string|null $end end + * + * @return self + */ + public function setEnd($end) + { + if (is_null($end)) { + throw new \InvalidArgumentException('non-nullable end cannot be null'); + } + $this->container['end'] = $end; + + return $this; + } + + /** + * Gets break_duration + * + * @return string|null + */ + public function getBreakDuration() + { + return $this->container['break_duration']; + } + + /** + * Sets break_duration + * + * @param string|null $break_duration break_duration + * + * @return self + */ + public function setBreakDuration($break_duration) + { + if (is_null($break_duration)) { + throw new \InvalidArgumentException('non-nullable break_duration cannot be null'); + } + $this->container['break_duration'] = $break_duration; + + return $this; + } + + /** + * Gets night_hours + * + * @return float|null + */ + public function getNightHours() + { + return $this->container['night_hours']; + } + + /** + * Sets night_hours + * + * @param float|null $night_hours night_hours + * + * @return self + */ + public function setNightHours($night_hours) + { + if (is_null($night_hours)) { + throw new \InvalidArgumentException('non-nullable night_hours cannot be null'); + } + $this->container['night_hours'] = $night_hours; + + return $this; + } + + /** + * Gets saturday_hours + * + * @return float|null + */ + public function getSaturdayHours() + { + return $this->container['saturday_hours']; + } + + /** + * Sets saturday_hours + * + * @param float|null $saturday_hours saturday_hours + * + * @return self + */ + public function setSaturdayHours($saturday_hours) + { + if (is_null($saturday_hours)) { + throw new \InvalidArgumentException('non-nullable saturday_hours cannot be null'); + } + $this->container['saturday_hours'] = $saturday_hours; + + return $this; + } + + /** + * Gets sunday_hours + * + * @return float|null + */ + public function getSundayHours() + { + return $this->container['sunday_hours']; + } + + /** + * Sets sunday_hours + * + * @param float|null $sunday_hours sunday_hours + * + * @return self + */ + public function setSundayHours($sunday_hours) + { + if (is_null($sunday_hours)) { + throw new \InvalidArgumentException('non-nullable sunday_hours cannot be null'); + } + $this->container['sunday_hours'] = $sunday_hours; + + return $this; + } + + /** + * Gets holiday_hours + * + * @return float|null + */ + public function getHolidayHours() + { + return $this->container['holiday_hours']; + } + + /** + * Sets holiday_hours + * + * @param float|null $holiday_hours holiday_hours + * + * @return self + */ + public function setHolidayHours($holiday_hours) + { + if (is_null($holiday_hours)) { + throw new \InvalidArgumentException('non-nullable holiday_hours cannot be null'); + } + $this->container['holiday_hours'] = $holiday_hours; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/CreateValueListItemRequest.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/CreateValueListItemRequest.php index 4f3abd0..3eee2eb 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/CreateValueListItemRequest.php +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/CreateValueListItemRequest.php @@ -61,7 +61,8 @@ class CreateValueListItemRequest implements ModelInterface, ArrayAccess, \JsonSe 'sort_order' => 'int', 'is_default' => 'bool', 'is_initial' => 'bool', - 'is_terminal' => 'bool' + 'is_terminal' => 'bool', + 'triggers_follow_up' => 'bool' ]; /** @@ -76,7 +77,8 @@ class CreateValueListItemRequest implements ModelInterface, ArrayAccess, \JsonSe 'sort_order' => 'int32', 'is_default' => null, 'is_initial' => null, - 'is_terminal' => null + 'is_terminal' => null, + 'triggers_follow_up' => null ]; /** @@ -89,7 +91,8 @@ class CreateValueListItemRequest implements ModelInterface, ArrayAccess, \JsonSe 'sort_order' => false, 'is_default' => false, 'is_initial' => false, - 'is_terminal' => false + 'is_terminal' => false, + 'triggers_follow_up' => false ]; /** @@ -182,7 +185,8 @@ class CreateValueListItemRequest implements ModelInterface, ArrayAccess, \JsonSe 'sort_order' => 'sortOrder', 'is_default' => 'isDefault', 'is_initial' => 'isInitial', - 'is_terminal' => 'isTerminal' + 'is_terminal' => 'isTerminal', + 'triggers_follow_up' => 'triggersFollowUp' ]; /** @@ -195,7 +199,8 @@ class CreateValueListItemRequest implements ModelInterface, ArrayAccess, \JsonSe 'sort_order' => 'setSortOrder', 'is_default' => 'setIsDefault', 'is_initial' => 'setIsInitial', - 'is_terminal' => 'setIsTerminal' + 'is_terminal' => 'setIsTerminal', + 'triggers_follow_up' => 'setTriggersFollowUp' ]; /** @@ -208,7 +213,8 @@ class CreateValueListItemRequest implements ModelInterface, ArrayAccess, \JsonSe 'sort_order' => 'getSortOrder', 'is_default' => 'getIsDefault', 'is_initial' => 'getIsInitial', - 'is_terminal' => 'getIsTerminal' + 'is_terminal' => 'getIsTerminal', + 'triggers_follow_up' => 'getTriggersFollowUp' ]; /** @@ -273,6 +279,7 @@ class CreateValueListItemRequest implements ModelInterface, ArrayAccess, \JsonSe $this->setIfExists('is_default', $data ?? [], null); $this->setIfExists('is_initial', $data ?? [], null); $this->setIfExists('is_terminal', $data ?? [], null); + $this->setIfExists('triggers_follow_up', $data ?? [], null); } /** @@ -458,6 +465,33 @@ class CreateValueListItemRequest implements ModelInterface, ArrayAccess, \JsonSe return $this; } + + /** + * Gets triggers_follow_up + * + * @return bool|null + */ + public function getTriggersFollowUp() + { + return $this->container['triggers_follow_up']; + } + + /** + * Sets triggers_follow_up + * + * @param bool|null $triggers_follow_up triggers_follow_up + * + * @return self + */ + public function setTriggersFollowUp($triggers_follow_up) + { + if (is_null($triggers_follow_up)) { + throw new \InvalidArgumentException('non-nullable triggers_follow_up cannot be null'); + } + $this->container['triggers_follow_up'] = $triggers_follow_up; + + return $this; + } /** * Returns true if offset exists. False otherwise. * diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/DocumentResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/DocumentResponse.php new file mode 100644 index 0000000..a8754f9 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/DocumentResponse.php @@ -0,0 +1,791 @@ + + */ +class DocumentResponse implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'DocumentResponse'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'id' => 'string', + 'entity_type' => 'string', + 'entity_id' => 'string', + 'category' => 'string', + 'file_name' => 'string', + 'content_type' => 'string', + 'size_bytes' => 'int', + 'description' => 'string', + 'uploaded_by_user_id' => 'string', + 'uploaded_by_username' => 'string', + 'created_at' => '\DateTime' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'id' => 'uuid', + 'entity_type' => null, + 'entity_id' => 'uuid', + 'category' => null, + 'file_name' => null, + 'content_type' => null, + 'size_bytes' => 'int64', + 'description' => null, + 'uploaded_by_user_id' => 'uuid', + 'uploaded_by_username' => null, + 'created_at' => 'date-time' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'id' => false, + 'entity_type' => true, + 'entity_id' => false, + 'category' => true, + 'file_name' => true, + 'content_type' => true, + 'size_bytes' => false, + 'description' => true, + 'uploaded_by_user_id' => false, + 'uploaded_by_username' => true, + 'created_at' => false + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'id' => 'id', + 'entity_type' => 'entityType', + 'entity_id' => 'entityId', + 'category' => 'category', + 'file_name' => 'fileName', + 'content_type' => 'contentType', + 'size_bytes' => 'sizeBytes', + 'description' => 'description', + 'uploaded_by_user_id' => 'uploadedByUserId', + 'uploaded_by_username' => 'uploadedByUsername', + 'created_at' => 'createdAt' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'id' => 'setId', + 'entity_type' => 'setEntityType', + 'entity_id' => 'setEntityId', + 'category' => 'setCategory', + 'file_name' => 'setFileName', + 'content_type' => 'setContentType', + 'size_bytes' => 'setSizeBytes', + 'description' => 'setDescription', + 'uploaded_by_user_id' => 'setUploadedByUserId', + 'uploaded_by_username' => 'setUploadedByUsername', + 'created_at' => 'setCreatedAt' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'id' => 'getId', + 'entity_type' => 'getEntityType', + 'entity_id' => 'getEntityId', + 'category' => 'getCategory', + 'file_name' => 'getFileName', + 'content_type' => 'getContentType', + 'size_bytes' => 'getSizeBytes', + 'description' => 'getDescription', + 'uploaded_by_user_id' => 'getUploadedByUserId', + 'uploaded_by_username' => 'getUploadedByUsername', + 'created_at' => 'getCreatedAt' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('id', $data ?? [], null); + $this->setIfExists('entity_type', $data ?? [], null); + $this->setIfExists('entity_id', $data ?? [], null); + $this->setIfExists('category', $data ?? [], null); + $this->setIfExists('file_name', $data ?? [], null); + $this->setIfExists('content_type', $data ?? [], null); + $this->setIfExists('size_bytes', $data ?? [], null); + $this->setIfExists('description', $data ?? [], null); + $this->setIfExists('uploaded_by_user_id', $data ?? [], null); + $this->setIfExists('uploaded_by_username', $data ?? [], null); + $this->setIfExists('created_at', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets id + * + * @return string|null + */ + public function getId() + { + return $this->container['id']; + } + + /** + * Sets id + * + * @param string|null $id id + * + * @return self + */ + public function setId($id) + { + if (is_null($id)) { + throw new \InvalidArgumentException('non-nullable id cannot be null'); + } + $this->container['id'] = $id; + + return $this; + } + + /** + * Gets entity_type + * + * @return string|null + */ + public function getEntityType() + { + return $this->container['entity_type']; + } + + /** + * Sets entity_type + * + * @param string|null $entity_type entity_type + * + * @return self + */ + public function setEntityType($entity_type) + { + if (is_null($entity_type)) { + array_push($this->openAPINullablesSetToNull, 'entity_type'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('entity_type', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['entity_type'] = $entity_type; + + return $this; + } + + /** + * Gets entity_id + * + * @return string|null + */ + public function getEntityId() + { + return $this->container['entity_id']; + } + + /** + * Sets entity_id + * + * @param string|null $entity_id entity_id + * + * @return self + */ + public function setEntityId($entity_id) + { + if (is_null($entity_id)) { + throw new \InvalidArgumentException('non-nullable entity_id cannot be null'); + } + $this->container['entity_id'] = $entity_id; + + return $this; + } + + /** + * Gets category + * + * @return string|null + */ + public function getCategory() + { + return $this->container['category']; + } + + /** + * Sets category + * + * @param string|null $category category + * + * @return self + */ + public function setCategory($category) + { + if (is_null($category)) { + array_push($this->openAPINullablesSetToNull, 'category'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('category', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['category'] = $category; + + return $this; + } + + /** + * Gets file_name + * + * @return string|null + */ + public function getFileName() + { + return $this->container['file_name']; + } + + /** + * Sets file_name + * + * @param string|null $file_name file_name + * + * @return self + */ + public function setFileName($file_name) + { + if (is_null($file_name)) { + array_push($this->openAPINullablesSetToNull, 'file_name'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('file_name', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['file_name'] = $file_name; + + return $this; + } + + /** + * Gets content_type + * + * @return string|null + */ + public function getContentType() + { + return $this->container['content_type']; + } + + /** + * Sets content_type + * + * @param string|null $content_type content_type + * + * @return self + */ + public function setContentType($content_type) + { + if (is_null($content_type)) { + array_push($this->openAPINullablesSetToNull, 'content_type'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('content_type', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['content_type'] = $content_type; + + return $this; + } + + /** + * Gets size_bytes + * + * @return int|null + */ + public function getSizeBytes() + { + return $this->container['size_bytes']; + } + + /** + * Sets size_bytes + * + * @param int|null $size_bytes size_bytes + * + * @return self + */ + public function setSizeBytes($size_bytes) + { + if (is_null($size_bytes)) { + throw new \InvalidArgumentException('non-nullable size_bytes cannot be null'); + } + $this->container['size_bytes'] = $size_bytes; + + return $this; + } + + /** + * Gets description + * + * @return string|null + */ + public function getDescription() + { + return $this->container['description']; + } + + /** + * Sets description + * + * @param string|null $description description + * + * @return self + */ + public function setDescription($description) + { + if (is_null($description)) { + array_push($this->openAPINullablesSetToNull, 'description'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('description', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['description'] = $description; + + return $this; + } + + /** + * Gets uploaded_by_user_id + * + * @return string|null + */ + public function getUploadedByUserId() + { + return $this->container['uploaded_by_user_id']; + } + + /** + * Sets uploaded_by_user_id + * + * @param string|null $uploaded_by_user_id uploaded_by_user_id + * + * @return self + */ + public function setUploadedByUserId($uploaded_by_user_id) + { + if (is_null($uploaded_by_user_id)) { + throw new \InvalidArgumentException('non-nullable uploaded_by_user_id cannot be null'); + } + $this->container['uploaded_by_user_id'] = $uploaded_by_user_id; + + return $this; + } + + /** + * Gets uploaded_by_username + * + * @return string|null + */ + public function getUploadedByUsername() + { + return $this->container['uploaded_by_username']; + } + + /** + * Sets uploaded_by_username + * + * @param string|null $uploaded_by_username uploaded_by_username + * + * @return self + */ + public function setUploadedByUsername($uploaded_by_username) + { + if (is_null($uploaded_by_username)) { + array_push($this->openAPINullablesSetToNull, 'uploaded_by_username'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('uploaded_by_username', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['uploaded_by_username'] = $uploaded_by_username; + + return $this; + } + + /** + * Gets created_at + * + * @return \DateTime|null + */ + public function getCreatedAt() + { + return $this->container['created_at']; + } + + /** + * Sets created_at + * + * @param \DateTime|null $created_at created_at + * + * @return self + */ + public function setCreatedAt($created_at) + { + if (is_null($created_at)) { + throw new \InvalidArgumentException('non-nullable created_at cannot be null'); + } + $this->container['created_at'] = $created_at; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/FacilityQualificationRateResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/FacilityQualificationRateResponse.php new file mode 100644 index 0000000..fae5a85 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/FacilityQualificationRateResponse.php @@ -0,0 +1,518 @@ + + */ +class FacilityQualificationRateResponse implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'FacilityQualificationRateResponse'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'id' => 'string', + 'facility_id' => 'string', + 'qualification' => 'string', + 'rate' => 'float' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'id' => 'uuid', + 'facility_id' => 'uuid', + 'qualification' => null, + 'rate' => 'double' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'id' => false, + 'facility_id' => false, + 'qualification' => true, + 'rate' => false + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'id' => 'id', + 'facility_id' => 'facilityId', + 'qualification' => 'qualification', + 'rate' => 'rate' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'id' => 'setId', + 'facility_id' => 'setFacilityId', + 'qualification' => 'setQualification', + 'rate' => 'setRate' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'id' => 'getId', + 'facility_id' => 'getFacilityId', + 'qualification' => 'getQualification', + 'rate' => 'getRate' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('id', $data ?? [], null); + $this->setIfExists('facility_id', $data ?? [], null); + $this->setIfExists('qualification', $data ?? [], null); + $this->setIfExists('rate', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets id + * + * @return string|null + */ + public function getId() + { + return $this->container['id']; + } + + /** + * Sets id + * + * @param string|null $id id + * + * @return self + */ + public function setId($id) + { + if (is_null($id)) { + throw new \InvalidArgumentException('non-nullable id cannot be null'); + } + $this->container['id'] = $id; + + return $this; + } + + /** + * Gets facility_id + * + * @return string|null + */ + public function getFacilityId() + { + return $this->container['facility_id']; + } + + /** + * Sets facility_id + * + * @param string|null $facility_id facility_id + * + * @return self + */ + public function setFacilityId($facility_id) + { + if (is_null($facility_id)) { + throw new \InvalidArgumentException('non-nullable facility_id cannot be null'); + } + $this->container['facility_id'] = $facility_id; + + return $this; + } + + /** + * Gets qualification + * + * @return string|null + */ + public function getQualification() + { + return $this->container['qualification']; + } + + /** + * Sets qualification + * + * @param string|null $qualification qualification + * + * @return self + */ + public function setQualification($qualification) + { + if (is_null($qualification)) { + array_push($this->openAPINullablesSetToNull, 'qualification'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('qualification', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['qualification'] = $qualification; + + return $this; + } + + /** + * Gets rate + * + * @return float|null + */ + public function getRate() + { + return $this->container['rate']; + } + + /** + * Sets rate + * + * @param float|null $rate rate + * + * @return self + */ + public function setRate($rate) + { + if (is_null($rate)) { + throw new \InvalidArgumentException('non-nullable rate cannot be null'); + } + $this->container['rate'] = $rate; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/FacilityResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/FacilityResponse.php index 2ce62db..0cbad7a 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/FacilityResponse.php +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/FacilityResponse.php @@ -61,6 +61,7 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable 'name' => 'string', 'crm_status' => 'string', 'facility_type' => 'string', + 'website' => 'string', 'street' => 'string', 'postal_code' => 'string', 'city' => 'string', @@ -68,7 +69,19 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable 'billing_street' => 'string', 'billing_postal_code' => 'string', 'billing_city' => 'string', - 'billing_country' => 'string' + 'billing_country' => 'string', + 'follow_up_due_date' => '\DateTime', + 'billing_rate' => 'float', + 'night_surcharge_percent' => 'float', + 'saturday_surcharge_percent' => 'float', + 'sunday_surcharge_percent' => 'float', + 'holiday_surcharge_percent' => 'float', + 'travel_cost_rate' => 'float', + 'minimum_hours' => 'float', + 'break_policy' => 'string', + 'billing_interval' => 'string', + 'payment_term_days' => 'int', + 'individual_agreements' => 'string' ]; /** @@ -83,6 +96,7 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable 'name' => null, 'crm_status' => null, 'facility_type' => null, + 'website' => null, 'street' => null, 'postal_code' => null, 'city' => null, @@ -90,7 +104,19 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable 'billing_street' => null, 'billing_postal_code' => null, 'billing_city' => null, - 'billing_country' => null + 'billing_country' => null, + 'follow_up_due_date' => 'date-time', + 'billing_rate' => 'double', + 'night_surcharge_percent' => 'double', + 'saturday_surcharge_percent' => 'double', + 'sunday_surcharge_percent' => 'double', + 'holiday_surcharge_percent' => 'double', + 'travel_cost_rate' => 'double', + 'minimum_hours' => 'double', + 'break_policy' => null, + 'billing_interval' => null, + 'payment_term_days' => 'int32', + 'individual_agreements' => null ]; /** @@ -103,6 +129,7 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable 'name' => true, 'crm_status' => true, 'facility_type' => true, + 'website' => true, 'street' => true, 'postal_code' => true, 'city' => true, @@ -110,7 +137,19 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable 'billing_street' => true, 'billing_postal_code' => true, 'billing_city' => true, - 'billing_country' => true + 'billing_country' => true, + 'follow_up_due_date' => true, + 'billing_rate' => true, + 'night_surcharge_percent' => true, + 'saturday_surcharge_percent' => true, + 'sunday_surcharge_percent' => true, + 'holiday_surcharge_percent' => true, + 'travel_cost_rate' => true, + 'minimum_hours' => true, + 'break_policy' => true, + 'billing_interval' => true, + 'payment_term_days' => true, + 'individual_agreements' => true ]; /** @@ -203,6 +242,7 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable 'name' => 'name', 'crm_status' => 'crmStatus', 'facility_type' => 'facilityType', + 'website' => 'website', 'street' => 'street', 'postal_code' => 'postalCode', 'city' => 'city', @@ -210,7 +250,19 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable 'billing_street' => 'billingStreet', 'billing_postal_code' => 'billingPostalCode', 'billing_city' => 'billingCity', - 'billing_country' => 'billingCountry' + 'billing_country' => 'billingCountry', + 'follow_up_due_date' => 'followUpDueDate', + 'billing_rate' => 'billingRate', + 'night_surcharge_percent' => 'nightSurchargePercent', + 'saturday_surcharge_percent' => 'saturdaySurchargePercent', + 'sunday_surcharge_percent' => 'sundaySurchargePercent', + 'holiday_surcharge_percent' => 'holidaySurchargePercent', + 'travel_cost_rate' => 'travelCostRate', + 'minimum_hours' => 'minimumHours', + 'break_policy' => 'breakPolicy', + 'billing_interval' => 'billingInterval', + 'payment_term_days' => 'paymentTermDays', + 'individual_agreements' => 'individualAgreements' ]; /** @@ -223,6 +275,7 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable 'name' => 'setName', 'crm_status' => 'setCrmStatus', 'facility_type' => 'setFacilityType', + 'website' => 'setWebsite', 'street' => 'setStreet', 'postal_code' => 'setPostalCode', 'city' => 'setCity', @@ -230,7 +283,19 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable 'billing_street' => 'setBillingStreet', 'billing_postal_code' => 'setBillingPostalCode', 'billing_city' => 'setBillingCity', - 'billing_country' => 'setBillingCountry' + 'billing_country' => 'setBillingCountry', + 'follow_up_due_date' => 'setFollowUpDueDate', + 'billing_rate' => 'setBillingRate', + 'night_surcharge_percent' => 'setNightSurchargePercent', + 'saturday_surcharge_percent' => 'setSaturdaySurchargePercent', + 'sunday_surcharge_percent' => 'setSundaySurchargePercent', + 'holiday_surcharge_percent' => 'setHolidaySurchargePercent', + 'travel_cost_rate' => 'setTravelCostRate', + 'minimum_hours' => 'setMinimumHours', + 'break_policy' => 'setBreakPolicy', + 'billing_interval' => 'setBillingInterval', + 'payment_term_days' => 'setPaymentTermDays', + 'individual_agreements' => 'setIndividualAgreements' ]; /** @@ -243,6 +308,7 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable 'name' => 'getName', 'crm_status' => 'getCrmStatus', 'facility_type' => 'getFacilityType', + 'website' => 'getWebsite', 'street' => 'getStreet', 'postal_code' => 'getPostalCode', 'city' => 'getCity', @@ -250,7 +316,19 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable 'billing_street' => 'getBillingStreet', 'billing_postal_code' => 'getBillingPostalCode', 'billing_city' => 'getBillingCity', - 'billing_country' => 'getBillingCountry' + 'billing_country' => 'getBillingCountry', + 'follow_up_due_date' => 'getFollowUpDueDate', + 'billing_rate' => 'getBillingRate', + 'night_surcharge_percent' => 'getNightSurchargePercent', + 'saturday_surcharge_percent' => 'getSaturdaySurchargePercent', + 'sunday_surcharge_percent' => 'getSundaySurchargePercent', + 'holiday_surcharge_percent' => 'getHolidaySurchargePercent', + 'travel_cost_rate' => 'getTravelCostRate', + 'minimum_hours' => 'getMinimumHours', + 'break_policy' => 'getBreakPolicy', + 'billing_interval' => 'getBillingInterval', + 'payment_term_days' => 'getPaymentTermDays', + 'individual_agreements' => 'getIndividualAgreements' ]; /** @@ -314,6 +392,7 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable $this->setIfExists('name', $data ?? [], null); $this->setIfExists('crm_status', $data ?? [], null); $this->setIfExists('facility_type', $data ?? [], null); + $this->setIfExists('website', $data ?? [], null); $this->setIfExists('street', $data ?? [], null); $this->setIfExists('postal_code', $data ?? [], null); $this->setIfExists('city', $data ?? [], null); @@ -322,6 +401,18 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable $this->setIfExists('billing_postal_code', $data ?? [], null); $this->setIfExists('billing_city', $data ?? [], null); $this->setIfExists('billing_country', $data ?? [], null); + $this->setIfExists('follow_up_due_date', $data ?? [], null); + $this->setIfExists('billing_rate', $data ?? [], null); + $this->setIfExists('night_surcharge_percent', $data ?? [], null); + $this->setIfExists('saturday_surcharge_percent', $data ?? [], null); + $this->setIfExists('sunday_surcharge_percent', $data ?? [], null); + $this->setIfExists('holiday_surcharge_percent', $data ?? [], null); + $this->setIfExists('travel_cost_rate', $data ?? [], null); + $this->setIfExists('minimum_hours', $data ?? [], null); + $this->setIfExists('break_policy', $data ?? [], null); + $this->setIfExists('billing_interval', $data ?? [], null); + $this->setIfExists('payment_term_days', $data ?? [], null); + $this->setIfExists('individual_agreements', $data ?? [], null); } /** @@ -495,6 +586,40 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable return $this; } + /** + * Gets website + * + * @return string|null + */ + public function getWebsite() + { + return $this->container['website']; + } + + /** + * Sets website + * + * @param string|null $website website + * + * @return self + */ + public function setWebsite($website) + { + if (is_null($website)) { + array_push($this->openAPINullablesSetToNull, 'website'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('website', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['website'] = $website; + + return $this; + } + /** * Gets street * @@ -766,6 +891,414 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable return $this; } + + /** + * Gets follow_up_due_date + * + * @return \DateTime|null + */ + public function getFollowUpDueDate() + { + return $this->container['follow_up_due_date']; + } + + /** + * Sets follow_up_due_date + * + * @param \DateTime|null $follow_up_due_date follow_up_due_date + * + * @return self + */ + public function setFollowUpDueDate($follow_up_due_date) + { + if (is_null($follow_up_due_date)) { + array_push($this->openAPINullablesSetToNull, 'follow_up_due_date'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('follow_up_due_date', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['follow_up_due_date'] = $follow_up_due_date; + + return $this; + } + + /** + * Gets billing_rate + * + * @return float|null + */ + public function getBillingRate() + { + return $this->container['billing_rate']; + } + + /** + * Sets billing_rate + * + * @param float|null $billing_rate billing_rate + * + * @return self + */ + public function setBillingRate($billing_rate) + { + if (is_null($billing_rate)) { + array_push($this->openAPINullablesSetToNull, 'billing_rate'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('billing_rate', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['billing_rate'] = $billing_rate; + + return $this; + } + + /** + * Gets night_surcharge_percent + * + * @return float|null + */ + public function getNightSurchargePercent() + { + return $this->container['night_surcharge_percent']; + } + + /** + * Sets night_surcharge_percent + * + * @param float|null $night_surcharge_percent night_surcharge_percent + * + * @return self + */ + public function setNightSurchargePercent($night_surcharge_percent) + { + if (is_null($night_surcharge_percent)) { + array_push($this->openAPINullablesSetToNull, 'night_surcharge_percent'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('night_surcharge_percent', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['night_surcharge_percent'] = $night_surcharge_percent; + + return $this; + } + + /** + * Gets saturday_surcharge_percent + * + * @return float|null + */ + public function getSaturdaySurchargePercent() + { + return $this->container['saturday_surcharge_percent']; + } + + /** + * Sets saturday_surcharge_percent + * + * @param float|null $saturday_surcharge_percent saturday_surcharge_percent + * + * @return self + */ + public function setSaturdaySurchargePercent($saturday_surcharge_percent) + { + if (is_null($saturday_surcharge_percent)) { + array_push($this->openAPINullablesSetToNull, 'saturday_surcharge_percent'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('saturday_surcharge_percent', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['saturday_surcharge_percent'] = $saturday_surcharge_percent; + + return $this; + } + + /** + * Gets sunday_surcharge_percent + * + * @return float|null + */ + public function getSundaySurchargePercent() + { + return $this->container['sunday_surcharge_percent']; + } + + /** + * Sets sunday_surcharge_percent + * + * @param float|null $sunday_surcharge_percent sunday_surcharge_percent + * + * @return self + */ + public function setSundaySurchargePercent($sunday_surcharge_percent) + { + if (is_null($sunday_surcharge_percent)) { + array_push($this->openAPINullablesSetToNull, 'sunday_surcharge_percent'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('sunday_surcharge_percent', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['sunday_surcharge_percent'] = $sunday_surcharge_percent; + + return $this; + } + + /** + * Gets holiday_surcharge_percent + * + * @return float|null + */ + public function getHolidaySurchargePercent() + { + return $this->container['holiday_surcharge_percent']; + } + + /** + * Sets holiday_surcharge_percent + * + * @param float|null $holiday_surcharge_percent holiday_surcharge_percent + * + * @return self + */ + public function setHolidaySurchargePercent($holiday_surcharge_percent) + { + if (is_null($holiday_surcharge_percent)) { + array_push($this->openAPINullablesSetToNull, 'holiday_surcharge_percent'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('holiday_surcharge_percent', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['holiday_surcharge_percent'] = $holiday_surcharge_percent; + + return $this; + } + + /** + * Gets travel_cost_rate + * + * @return float|null + */ + public function getTravelCostRate() + { + return $this->container['travel_cost_rate']; + } + + /** + * Sets travel_cost_rate + * + * @param float|null $travel_cost_rate travel_cost_rate + * + * @return self + */ + public function setTravelCostRate($travel_cost_rate) + { + if (is_null($travel_cost_rate)) { + array_push($this->openAPINullablesSetToNull, 'travel_cost_rate'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('travel_cost_rate', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['travel_cost_rate'] = $travel_cost_rate; + + return $this; + } + + /** + * Gets minimum_hours + * + * @return float|null + */ + public function getMinimumHours() + { + return $this->container['minimum_hours']; + } + + /** + * Sets minimum_hours + * + * @param float|null $minimum_hours minimum_hours + * + * @return self + */ + public function setMinimumHours($minimum_hours) + { + if (is_null($minimum_hours)) { + array_push($this->openAPINullablesSetToNull, 'minimum_hours'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('minimum_hours', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['minimum_hours'] = $minimum_hours; + + return $this; + } + + /** + * Gets break_policy + * + * @return string|null + */ + public function getBreakPolicy() + { + return $this->container['break_policy']; + } + + /** + * Sets break_policy + * + * @param string|null $break_policy break_policy + * + * @return self + */ + public function setBreakPolicy($break_policy) + { + if (is_null($break_policy)) { + array_push($this->openAPINullablesSetToNull, 'break_policy'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('break_policy', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['break_policy'] = $break_policy; + + return $this; + } + + /** + * Gets billing_interval + * + * @return string|null + */ + public function getBillingInterval() + { + return $this->container['billing_interval']; + } + + /** + * Sets billing_interval + * + * @param string|null $billing_interval billing_interval + * + * @return self + */ + public function setBillingInterval($billing_interval) + { + if (is_null($billing_interval)) { + array_push($this->openAPINullablesSetToNull, 'billing_interval'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('billing_interval', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['billing_interval'] = $billing_interval; + + return $this; + } + + /** + * Gets payment_term_days + * + * @return int|null + */ + public function getPaymentTermDays() + { + return $this->container['payment_term_days']; + } + + /** + * Sets payment_term_days + * + * @param int|null $payment_term_days payment_term_days + * + * @return self + */ + public function setPaymentTermDays($payment_term_days) + { + if (is_null($payment_term_days)) { + array_push($this->openAPINullablesSetToNull, 'payment_term_days'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('payment_term_days', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['payment_term_days'] = $payment_term_days; + + return $this; + } + + /** + * Gets individual_agreements + * + * @return string|null + */ + public function getIndividualAgreements() + { + return $this->container['individual_agreements']; + } + + /** + * Sets individual_agreements + * + * @param string|null $individual_agreements individual_agreements + * + * @return self + */ + public function setIndividualAgreements($individual_agreements) + { + if (is_null($individual_agreements)) { + array_push($this->openAPINullablesSetToNull, 'individual_agreements'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('individual_agreements', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['individual_agreements'] = $individual_agreements; + + return $this; + } /** * Returns true if offset exists. False otherwise. * diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/ModuleType.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/ModuleType.php new file mode 100644 index 0000000..e484e43 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/ModuleType.php @@ -0,0 +1,98 @@ + 'string', - 'action' => 'string' + 'module' => '\OmsorgCoreClient\Model\ModuleType', + 'action' => '\OmsorgCoreClient\Model\PermissionAction', + 'scope' => '\OmsorgCoreClient\Model\PermissionScope' ]; /** @@ -70,7 +71,8 @@ class PermissionDto implements ModelInterface, ArrayAccess, \JsonSerializable */ protected static $openAPIFormats = [ 'module' => null, - 'action' => null + 'action' => null, + 'scope' => null ]; /** @@ -79,8 +81,9 @@ class PermissionDto implements ModelInterface, ArrayAccess, \JsonSerializable * @var boolean[] */ protected static array $openAPINullables = [ - 'module' => true, - 'action' => true + 'module' => false, + 'action' => false, + 'scope' => false ]; /** @@ -170,7 +173,8 @@ class PermissionDto implements ModelInterface, ArrayAccess, \JsonSerializable */ protected static $attributeMap = [ 'module' => 'module', - 'action' => 'action' + 'action' => 'action', + 'scope' => 'scope' ]; /** @@ -180,7 +184,8 @@ class PermissionDto implements ModelInterface, ArrayAccess, \JsonSerializable */ protected static $setters = [ 'module' => 'setModule', - 'action' => 'setAction' + 'action' => 'setAction', + 'scope' => 'setScope' ]; /** @@ -190,7 +195,8 @@ class PermissionDto implements ModelInterface, ArrayAccess, \JsonSerializable */ protected static $getters = [ 'module' => 'getModule', - 'action' => 'getAction' + 'action' => 'getAction', + 'scope' => 'getScope' ]; /** @@ -252,6 +258,7 @@ class PermissionDto implements ModelInterface, ArrayAccess, \JsonSerializable { $this->setIfExists('module', $data ?? [], null); $this->setIfExists('action', $data ?? [], null); + $this->setIfExists('scope', $data ?? [], null); } /** @@ -299,7 +306,7 @@ class PermissionDto implements ModelInterface, ArrayAccess, \JsonSerializable /** * Gets module * - * @return string|null + * @return \OmsorgCoreClient\Model\ModuleType|null */ public function getModule() { @@ -309,21 +316,14 @@ class PermissionDto implements ModelInterface, ArrayAccess, \JsonSerializable /** * Sets module * - * @param string|null $module module + * @param \OmsorgCoreClient\Model\ModuleType|null $module module * * @return self */ public function setModule($module) { if (is_null($module)) { - array_push($this->openAPINullablesSetToNull, 'module'); - } else { - $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); - $index = array_search('module', $nullablesSetToNull); - if ($index !== FALSE) { - unset($nullablesSetToNull[$index]); - $this->setOpenAPINullablesSetToNull($nullablesSetToNull); - } + throw new \InvalidArgumentException('non-nullable module cannot be null'); } $this->container['module'] = $module; @@ -333,7 +333,7 @@ class PermissionDto implements ModelInterface, ArrayAccess, \JsonSerializable /** * Gets action * - * @return string|null + * @return \OmsorgCoreClient\Model\PermissionAction|null */ public function getAction() { @@ -343,26 +343,46 @@ class PermissionDto implements ModelInterface, ArrayAccess, \JsonSerializable /** * Sets action * - * @param string|null $action action + * @param \OmsorgCoreClient\Model\PermissionAction|null $action action * * @return self */ public function setAction($action) { if (is_null($action)) { - array_push($this->openAPINullablesSetToNull, 'action'); - } else { - $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); - $index = array_search('action', $nullablesSetToNull); - if ($index !== FALSE) { - unset($nullablesSetToNull[$index]); - $this->setOpenAPINullablesSetToNull($nullablesSetToNull); - } + throw new \InvalidArgumentException('non-nullable action cannot be null'); } $this->container['action'] = $action; return $this; } + + /** + * Gets scope + * + * @return \OmsorgCoreClient\Model\PermissionScope|null + */ + public function getScope() + { + return $this->container['scope']; + } + + /** + * Sets scope + * + * @param \OmsorgCoreClient\Model\PermissionScope|null $scope scope + * + * @return self + */ + public function setScope($scope) + { + if (is_null($scope)) { + throw new \InvalidArgumentException('non-nullable scope cannot be null'); + } + $this->container['scope'] = $scope; + + return $this; + } /** * Returns true if offset exists. False otherwise. * diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/PermissionEffect.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/PermissionEffect.php new file mode 100644 index 0000000..062dedc --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/PermissionEffect.php @@ -0,0 +1,62 @@ + + */ +class TimeEntryDecisionRequest implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'TimeEntryDecisionRequest'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'status_id' => 'string', + 'admin_note' => 'string' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'status_id' => 'uuid', + 'admin_note' => null + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'status_id' => false, + 'admin_note' => true + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'status_id' => 'statusId', + 'admin_note' => 'adminNote' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'status_id' => 'setStatusId', + 'admin_note' => 'setAdminNote' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'status_id' => 'getStatusId', + 'admin_note' => 'getAdminNote' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('status_id', $data ?? [], null); + $this->setIfExists('admin_note', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets status_id + * + * @return string|null + */ + public function getStatusId() + { + return $this->container['status_id']; + } + + /** + * Sets status_id + * + * @param string|null $status_id status_id + * + * @return self + */ + public function setStatusId($status_id) + { + if (is_null($status_id)) { + throw new \InvalidArgumentException('non-nullable status_id cannot be null'); + } + $this->container['status_id'] = $status_id; + + return $this; + } + + /** + * Gets admin_note + * + * @return string|null + */ + public function getAdminNote() + { + return $this->container['admin_note']; + } + + /** + * Sets admin_note + * + * @param string|null $admin_note admin_note + * + * @return self + */ + public function setAdminNote($admin_note) + { + if (is_null($admin_note)) { + array_push($this->openAPINullablesSetToNull, 'admin_note'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('admin_note', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['admin_note'] = $admin_note; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TimeEntryResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TimeEntryResponse.php new file mode 100644 index 0000000..8115252 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TimeEntryResponse.php @@ -0,0 +1,1049 @@ + + */ +class TimeEntryResponse implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'TimeEntryResponse'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'id' => 'string', + 'employee_id' => 'string', + 'employee_name' => 'string', + 'order_id' => 'string', + 'facility_id' => 'string', + 'facility_name' => 'string', + 'date' => '\DateTime', + 'start' => 'string', + 'end' => 'string', + 'break_duration' => 'string', + 'night_hours' => 'float', + 'saturday_hours' => 'float', + 'sunday_hours' => 'float', + 'holiday_hours' => 'float', + 'status_id' => 'string', + 'status_name' => 'string', + 'is_editable_by_owner' => 'bool', + 'admin_note' => 'string', + 'created_at' => '\DateTime' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'id' => 'uuid', + 'employee_id' => 'uuid', + 'employee_name' => null, + 'order_id' => 'uuid', + 'facility_id' => 'uuid', + 'facility_name' => null, + 'date' => 'date', + 'start' => 'time', + 'end' => 'time', + 'break_duration' => 'date-span', + 'night_hours' => 'double', + 'saturday_hours' => 'double', + 'sunday_hours' => 'double', + 'holiday_hours' => 'double', + 'status_id' => 'uuid', + 'status_name' => null, + 'is_editable_by_owner' => null, + 'admin_note' => null, + 'created_at' => 'date-time' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'id' => false, + 'employee_id' => false, + 'employee_name' => true, + 'order_id' => false, + 'facility_id' => false, + 'facility_name' => true, + 'date' => false, + 'start' => false, + 'end' => false, + 'break_duration' => false, + 'night_hours' => false, + 'saturday_hours' => false, + 'sunday_hours' => false, + 'holiday_hours' => false, + 'status_id' => false, + 'status_name' => true, + 'is_editable_by_owner' => false, + 'admin_note' => true, + 'created_at' => false + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'id' => 'id', + 'employee_id' => 'employeeId', + 'employee_name' => 'employeeName', + 'order_id' => 'orderId', + 'facility_id' => 'facilityId', + 'facility_name' => 'facilityName', + 'date' => 'date', + 'start' => 'start', + 'end' => 'end', + 'break_duration' => 'breakDuration', + 'night_hours' => 'nightHours', + 'saturday_hours' => 'saturdayHours', + 'sunday_hours' => 'sundayHours', + 'holiday_hours' => 'holidayHours', + 'status_id' => 'statusId', + 'status_name' => 'statusName', + 'is_editable_by_owner' => 'isEditableByOwner', + 'admin_note' => 'adminNote', + 'created_at' => 'createdAt' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'id' => 'setId', + 'employee_id' => 'setEmployeeId', + 'employee_name' => 'setEmployeeName', + 'order_id' => 'setOrderId', + 'facility_id' => 'setFacilityId', + 'facility_name' => 'setFacilityName', + 'date' => 'setDate', + 'start' => 'setStart', + 'end' => 'setEnd', + 'break_duration' => 'setBreakDuration', + 'night_hours' => 'setNightHours', + 'saturday_hours' => 'setSaturdayHours', + 'sunday_hours' => 'setSundayHours', + 'holiday_hours' => 'setHolidayHours', + 'status_id' => 'setStatusId', + 'status_name' => 'setStatusName', + 'is_editable_by_owner' => 'setIsEditableByOwner', + 'admin_note' => 'setAdminNote', + 'created_at' => 'setCreatedAt' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'id' => 'getId', + 'employee_id' => 'getEmployeeId', + 'employee_name' => 'getEmployeeName', + 'order_id' => 'getOrderId', + 'facility_id' => 'getFacilityId', + 'facility_name' => 'getFacilityName', + 'date' => 'getDate', + 'start' => 'getStart', + 'end' => 'getEnd', + 'break_duration' => 'getBreakDuration', + 'night_hours' => 'getNightHours', + 'saturday_hours' => 'getSaturdayHours', + 'sunday_hours' => 'getSundayHours', + 'holiday_hours' => 'getHolidayHours', + 'status_id' => 'getStatusId', + 'status_name' => 'getStatusName', + 'is_editable_by_owner' => 'getIsEditableByOwner', + 'admin_note' => 'getAdminNote', + 'created_at' => 'getCreatedAt' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('id', $data ?? [], null); + $this->setIfExists('employee_id', $data ?? [], null); + $this->setIfExists('employee_name', $data ?? [], null); + $this->setIfExists('order_id', $data ?? [], null); + $this->setIfExists('facility_id', $data ?? [], null); + $this->setIfExists('facility_name', $data ?? [], null); + $this->setIfExists('date', $data ?? [], null); + $this->setIfExists('start', $data ?? [], null); + $this->setIfExists('end', $data ?? [], null); + $this->setIfExists('break_duration', $data ?? [], null); + $this->setIfExists('night_hours', $data ?? [], null); + $this->setIfExists('saturday_hours', $data ?? [], null); + $this->setIfExists('sunday_hours', $data ?? [], null); + $this->setIfExists('holiday_hours', $data ?? [], null); + $this->setIfExists('status_id', $data ?? [], null); + $this->setIfExists('status_name', $data ?? [], null); + $this->setIfExists('is_editable_by_owner', $data ?? [], null); + $this->setIfExists('admin_note', $data ?? [], null); + $this->setIfExists('created_at', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets id + * + * @return string|null + */ + public function getId() + { + return $this->container['id']; + } + + /** + * Sets id + * + * @param string|null $id id + * + * @return self + */ + public function setId($id) + { + if (is_null($id)) { + throw new \InvalidArgumentException('non-nullable id cannot be null'); + } + $this->container['id'] = $id; + + return $this; + } + + /** + * Gets employee_id + * + * @return string|null + */ + public function getEmployeeId() + { + return $this->container['employee_id']; + } + + /** + * Sets employee_id + * + * @param string|null $employee_id employee_id + * + * @return self + */ + public function setEmployeeId($employee_id) + { + if (is_null($employee_id)) { + throw new \InvalidArgumentException('non-nullable employee_id cannot be null'); + } + $this->container['employee_id'] = $employee_id; + + return $this; + } + + /** + * Gets employee_name + * + * @return string|null + */ + public function getEmployeeName() + { + return $this->container['employee_name']; + } + + /** + * Sets employee_name + * + * @param string|null $employee_name employee_name + * + * @return self + */ + public function setEmployeeName($employee_name) + { + if (is_null($employee_name)) { + array_push($this->openAPINullablesSetToNull, 'employee_name'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('employee_name', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['employee_name'] = $employee_name; + + return $this; + } + + /** + * Gets order_id + * + * @return string|null + */ + public function getOrderId() + { + return $this->container['order_id']; + } + + /** + * Sets order_id + * + * @param string|null $order_id order_id + * + * @return self + */ + public function setOrderId($order_id) + { + if (is_null($order_id)) { + throw new \InvalidArgumentException('non-nullable order_id cannot be null'); + } + $this->container['order_id'] = $order_id; + + return $this; + } + + /** + * Gets facility_id + * + * @return string|null + */ + public function getFacilityId() + { + return $this->container['facility_id']; + } + + /** + * Sets facility_id + * + * @param string|null $facility_id facility_id + * + * @return self + */ + public function setFacilityId($facility_id) + { + if (is_null($facility_id)) { + throw new \InvalidArgumentException('non-nullable facility_id cannot be null'); + } + $this->container['facility_id'] = $facility_id; + + return $this; + } + + /** + * Gets facility_name + * + * @return string|null + */ + public function getFacilityName() + { + return $this->container['facility_name']; + } + + /** + * Sets facility_name + * + * @param string|null $facility_name facility_name + * + * @return self + */ + public function setFacilityName($facility_name) + { + if (is_null($facility_name)) { + array_push($this->openAPINullablesSetToNull, 'facility_name'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('facility_name', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['facility_name'] = $facility_name; + + return $this; + } + + /** + * Gets date + * + * @return \DateTime|null + */ + public function getDate() + { + return $this->container['date']; + } + + /** + * Sets date + * + * @param \DateTime|null $date date + * + * @return self + */ + public function setDate($date) + { + if (is_null($date)) { + throw new \InvalidArgumentException('non-nullable date cannot be null'); + } + $this->container['date'] = $date; + + return $this; + } + + /** + * Gets start + * + * @return string|null + */ + public function getStart() + { + return $this->container['start']; + } + + /** + * Sets start + * + * @param string|null $start start + * + * @return self + */ + public function setStart($start) + { + if (is_null($start)) { + throw new \InvalidArgumentException('non-nullable start cannot be null'); + } + $this->container['start'] = $start; + + return $this; + } + + /** + * Gets end + * + * @return string|null + */ + public function getEnd() + { + return $this->container['end']; + } + + /** + * Sets end + * + * @param string|null $end end + * + * @return self + */ + public function setEnd($end) + { + if (is_null($end)) { + throw new \InvalidArgumentException('non-nullable end cannot be null'); + } + $this->container['end'] = $end; + + return $this; + } + + /** + * Gets break_duration + * + * @return string|null + */ + public function getBreakDuration() + { + return $this->container['break_duration']; + } + + /** + * Sets break_duration + * + * @param string|null $break_duration break_duration + * + * @return self + */ + public function setBreakDuration($break_duration) + { + if (is_null($break_duration)) { + throw new \InvalidArgumentException('non-nullable break_duration cannot be null'); + } + $this->container['break_duration'] = $break_duration; + + return $this; + } + + /** + * Gets night_hours + * + * @return float|null + */ + public function getNightHours() + { + return $this->container['night_hours']; + } + + /** + * Sets night_hours + * + * @param float|null $night_hours night_hours + * + * @return self + */ + public function setNightHours($night_hours) + { + if (is_null($night_hours)) { + throw new \InvalidArgumentException('non-nullable night_hours cannot be null'); + } + $this->container['night_hours'] = $night_hours; + + return $this; + } + + /** + * Gets saturday_hours + * + * @return float|null + */ + public function getSaturdayHours() + { + return $this->container['saturday_hours']; + } + + /** + * Sets saturday_hours + * + * @param float|null $saturday_hours saturday_hours + * + * @return self + */ + public function setSaturdayHours($saturday_hours) + { + if (is_null($saturday_hours)) { + throw new \InvalidArgumentException('non-nullable saturday_hours cannot be null'); + } + $this->container['saturday_hours'] = $saturday_hours; + + return $this; + } + + /** + * Gets sunday_hours + * + * @return float|null + */ + public function getSundayHours() + { + return $this->container['sunday_hours']; + } + + /** + * Sets sunday_hours + * + * @param float|null $sunday_hours sunday_hours + * + * @return self + */ + public function setSundayHours($sunday_hours) + { + if (is_null($sunday_hours)) { + throw new \InvalidArgumentException('non-nullable sunday_hours cannot be null'); + } + $this->container['sunday_hours'] = $sunday_hours; + + return $this; + } + + /** + * Gets holiday_hours + * + * @return float|null + */ + public function getHolidayHours() + { + return $this->container['holiday_hours']; + } + + /** + * Sets holiday_hours + * + * @param float|null $holiday_hours holiday_hours + * + * @return self + */ + public function setHolidayHours($holiday_hours) + { + if (is_null($holiday_hours)) { + throw new \InvalidArgumentException('non-nullable holiday_hours cannot be null'); + } + $this->container['holiday_hours'] = $holiday_hours; + + return $this; + } + + /** + * Gets status_id + * + * @return string|null + */ + public function getStatusId() + { + return $this->container['status_id']; + } + + /** + * Sets status_id + * + * @param string|null $status_id status_id + * + * @return self + */ + public function setStatusId($status_id) + { + if (is_null($status_id)) { + throw new \InvalidArgumentException('non-nullable status_id cannot be null'); + } + $this->container['status_id'] = $status_id; + + return $this; + } + + /** + * Gets status_name + * + * @return string|null + */ + public function getStatusName() + { + return $this->container['status_name']; + } + + /** + * Sets status_name + * + * @param string|null $status_name status_name + * + * @return self + */ + public function setStatusName($status_name) + { + if (is_null($status_name)) { + array_push($this->openAPINullablesSetToNull, 'status_name'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('status_name', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['status_name'] = $status_name; + + return $this; + } + + /** + * Gets is_editable_by_owner + * + * @return bool|null + */ + public function getIsEditableByOwner() + { + return $this->container['is_editable_by_owner']; + } + + /** + * Sets is_editable_by_owner + * + * @param bool|null $is_editable_by_owner is_editable_by_owner + * + * @return self + */ + public function setIsEditableByOwner($is_editable_by_owner) + { + if (is_null($is_editable_by_owner)) { + throw new \InvalidArgumentException('non-nullable is_editable_by_owner cannot be null'); + } + $this->container['is_editable_by_owner'] = $is_editable_by_owner; + + return $this; + } + + /** + * Gets admin_note + * + * @return string|null + */ + public function getAdminNote() + { + return $this->container['admin_note']; + } + + /** + * Sets admin_note + * + * @param string|null $admin_note admin_note + * + * @return self + */ + public function setAdminNote($admin_note) + { + if (is_null($admin_note)) { + array_push($this->openAPINullablesSetToNull, 'admin_note'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('admin_note', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['admin_note'] = $admin_note; + + return $this; + } + + /** + * Gets created_at + * + * @return \DateTime|null + */ + public function getCreatedAt() + { + return $this->container['created_at']; + } + + /** + * Sets created_at + * + * @param \DateTime|null $created_at created_at + * + * @return self + */ + public function setCreatedAt($created_at) + { + if (is_null($created_at)) { + throw new \InvalidArgumentException('non-nullable created_at cannot be null'); + } + $this->container['created_at'] = $created_at; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TimeEntryResponsePagedResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TimeEntryResponsePagedResponse.php new file mode 100644 index 0000000..7d9ef77 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TimeEntryResponsePagedResponse.php @@ -0,0 +1,518 @@ + + */ +class TimeEntryResponsePagedResponse implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'TimeEntryResponsePagedResponse'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'items' => '\OmsorgCoreClient\Model\TimeEntryResponse[]', + 'total_count' => 'int', + 'page' => 'int', + 'page_size' => 'int' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'items' => null, + 'total_count' => 'int32', + 'page' => 'int32', + 'page_size' => 'int32' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'items' => true, + 'total_count' => false, + 'page' => false, + 'page_size' => false + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'items' => 'items', + 'total_count' => 'totalCount', + 'page' => 'page', + 'page_size' => 'pageSize' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'items' => 'setItems', + 'total_count' => 'setTotalCount', + 'page' => 'setPage', + 'page_size' => 'setPageSize' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'items' => 'getItems', + 'total_count' => 'getTotalCount', + 'page' => 'getPage', + 'page_size' => 'getPageSize' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('items', $data ?? [], null); + $this->setIfExists('total_count', $data ?? [], null); + $this->setIfExists('page', $data ?? [], null); + $this->setIfExists('page_size', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets items + * + * @return \OmsorgCoreClient\Model\TimeEntryResponse[]|null + */ + public function getItems() + { + return $this->container['items']; + } + + /** + * Sets items + * + * @param \OmsorgCoreClient\Model\TimeEntryResponse[]|null $items items + * + * @return self + */ + public function setItems($items) + { + if (is_null($items)) { + array_push($this->openAPINullablesSetToNull, 'items'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('items', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['items'] = $items; + + return $this; + } + + /** + * Gets total_count + * + * @return int|null + */ + public function getTotalCount() + { + return $this->container['total_count']; + } + + /** + * Sets total_count + * + * @param int|null $total_count total_count + * + * @return self + */ + public function setTotalCount($total_count) + { + if (is_null($total_count)) { + throw new \InvalidArgumentException('non-nullable total_count cannot be null'); + } + $this->container['total_count'] = $total_count; + + return $this; + } + + /** + * Gets page + * + * @return int|null + */ + public function getPage() + { + return $this->container['page']; + } + + /** + * Sets page + * + * @param int|null $page page + * + * @return self + */ + public function setPage($page) + { + if (is_null($page)) { + throw new \InvalidArgumentException('non-nullable page cannot be null'); + } + $this->container['page'] = $page; + + return $this; + } + + /** + * Gets page_size + * + * @return int|null + */ + public function getPageSize() + { + return $this->container['page_size']; + } + + /** + * Sets page_size + * + * @param int|null $page_size page_size + * + * @return self + */ + public function setPageSize($page_size) + { + if (is_null($page_size)) { + throw new \InvalidArgumentException('non-nullable page_size cannot be null'); + } + $this->container['page_size'] = $page_size; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashAbsenceResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashAbsenceResponse.php new file mode 100644 index 0000000..9123547 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashAbsenceResponse.php @@ -0,0 +1,491 @@ + + */ +class TrashAbsenceResponse implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'TrashAbsenceResponse'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'id' => 'string', + 'type' => 'string', + 'deleted_at' => '\DateTime' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'id' => 'uuid', + 'type' => null, + 'deleted_at' => 'date-time' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'id' => false, + 'type' => true, + 'deleted_at' => true + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'id' => 'id', + 'type' => 'type', + 'deleted_at' => 'deletedAt' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'id' => 'setId', + 'type' => 'setType', + 'deleted_at' => 'setDeletedAt' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'id' => 'getId', + 'type' => 'getType', + 'deleted_at' => 'getDeletedAt' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('id', $data ?? [], null); + $this->setIfExists('type', $data ?? [], null); + $this->setIfExists('deleted_at', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets id + * + * @return string|null + */ + public function getId() + { + return $this->container['id']; + } + + /** + * Sets id + * + * @param string|null $id id + * + * @return self + */ + public function setId($id) + { + if (is_null($id)) { + throw new \InvalidArgumentException('non-nullable id cannot be null'); + } + $this->container['id'] = $id; + + return $this; + } + + /** + * Gets type + * + * @return string|null + */ + public function getType() + { + return $this->container['type']; + } + + /** + * Sets type + * + * @param string|null $type type + * + * @return self + */ + public function setType($type) + { + if (is_null($type)) { + array_push($this->openAPINullablesSetToNull, 'type'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('type', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['type'] = $type; + + return $this; + } + + /** + * Gets deleted_at + * + * @return \DateTime|null + */ + public function getDeletedAt() + { + return $this->container['deleted_at']; + } + + /** + * Sets deleted_at + * + * @param \DateTime|null $deleted_at deleted_at + * + * @return self + */ + public function setDeletedAt($deleted_at) + { + if (is_null($deleted_at)) { + array_push($this->openAPINullablesSetToNull, 'deleted_at'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('deleted_at', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['deleted_at'] = $deleted_at; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashContractResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashContractResponse.php new file mode 100644 index 0000000..2b8534a --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashContractResponse.php @@ -0,0 +1,491 @@ + + */ +class TrashContractResponse implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'TrashContractResponse'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'id' => 'string', + 'contract_type' => 'string', + 'deleted_at' => '\DateTime' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'id' => 'uuid', + 'contract_type' => null, + 'deleted_at' => 'date-time' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'id' => false, + 'contract_type' => true, + 'deleted_at' => true + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'id' => 'id', + 'contract_type' => 'contractType', + 'deleted_at' => 'deletedAt' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'id' => 'setId', + 'contract_type' => 'setContractType', + 'deleted_at' => 'setDeletedAt' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'id' => 'getId', + 'contract_type' => 'getContractType', + 'deleted_at' => 'getDeletedAt' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('id', $data ?? [], null); + $this->setIfExists('contract_type', $data ?? [], null); + $this->setIfExists('deleted_at', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets id + * + * @return string|null + */ + public function getId() + { + return $this->container['id']; + } + + /** + * Sets id + * + * @param string|null $id id + * + * @return self + */ + public function setId($id) + { + if (is_null($id)) { + throw new \InvalidArgumentException('non-nullable id cannot be null'); + } + $this->container['id'] = $id; + + return $this; + } + + /** + * Gets contract_type + * + * @return string|null + */ + public function getContractType() + { + return $this->container['contract_type']; + } + + /** + * Sets contract_type + * + * @param string|null $contract_type contract_type + * + * @return self + */ + public function setContractType($contract_type) + { + if (is_null($contract_type)) { + array_push($this->openAPINullablesSetToNull, 'contract_type'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('contract_type', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['contract_type'] = $contract_type; + + return $this; + } + + /** + * Gets deleted_at + * + * @return \DateTime|null + */ + public function getDeletedAt() + { + return $this->container['deleted_at']; + } + + /** + * Sets deleted_at + * + * @param \DateTime|null $deleted_at deleted_at + * + * @return self + */ + public function setDeletedAt($deleted_at) + { + if (is_null($deleted_at)) { + array_push($this->openAPINullablesSetToNull, 'deleted_at'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('deleted_at', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['deleted_at'] = $deleted_at; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashEmployeeResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashEmployeeResponse.php new file mode 100644 index 0000000..375edba --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashEmployeeResponse.php @@ -0,0 +1,532 @@ + + */ +class TrashEmployeeResponse implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'TrashEmployeeResponse'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'id' => 'string', + 'first_name' => 'string', + 'last_name' => 'string', + 'deleted_at' => '\DateTime' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'id' => 'uuid', + 'first_name' => null, + 'last_name' => null, + 'deleted_at' => 'date-time' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'id' => false, + 'first_name' => true, + 'last_name' => true, + 'deleted_at' => true + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'id' => 'id', + 'first_name' => 'firstName', + 'last_name' => 'lastName', + 'deleted_at' => 'deletedAt' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'id' => 'setId', + 'first_name' => 'setFirstName', + 'last_name' => 'setLastName', + 'deleted_at' => 'setDeletedAt' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'id' => 'getId', + 'first_name' => 'getFirstName', + 'last_name' => 'getLastName', + 'deleted_at' => 'getDeletedAt' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('id', $data ?? [], null); + $this->setIfExists('first_name', $data ?? [], null); + $this->setIfExists('last_name', $data ?? [], null); + $this->setIfExists('deleted_at', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets id + * + * @return string|null + */ + public function getId() + { + return $this->container['id']; + } + + /** + * Sets id + * + * @param string|null $id id + * + * @return self + */ + public function setId($id) + { + if (is_null($id)) { + throw new \InvalidArgumentException('non-nullable id cannot be null'); + } + $this->container['id'] = $id; + + return $this; + } + + /** + * Gets first_name + * + * @return string|null + */ + public function getFirstName() + { + return $this->container['first_name']; + } + + /** + * Sets first_name + * + * @param string|null $first_name first_name + * + * @return self + */ + public function setFirstName($first_name) + { + if (is_null($first_name)) { + array_push($this->openAPINullablesSetToNull, 'first_name'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('first_name', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['first_name'] = $first_name; + + return $this; + } + + /** + * Gets last_name + * + * @return string|null + */ + public function getLastName() + { + return $this->container['last_name']; + } + + /** + * Sets last_name + * + * @param string|null $last_name last_name + * + * @return self + */ + public function setLastName($last_name) + { + if (is_null($last_name)) { + array_push($this->openAPINullablesSetToNull, 'last_name'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('last_name', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['last_name'] = $last_name; + + return $this; + } + + /** + * Gets deleted_at + * + * @return \DateTime|null + */ + public function getDeletedAt() + { + return $this->container['deleted_at']; + } + + /** + * Sets deleted_at + * + * @param \DateTime|null $deleted_at deleted_at + * + * @return self + */ + public function setDeletedAt($deleted_at) + { + if (is_null($deleted_at)) { + array_push($this->openAPINullablesSetToNull, 'deleted_at'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('deleted_at', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['deleted_at'] = $deleted_at; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashFacilityContactResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashFacilityContactResponse.php new file mode 100644 index 0000000..c7519c6 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashFacilityContactResponse.php @@ -0,0 +1,525 @@ + + */ +class TrashFacilityContactResponse implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'TrashFacilityContactResponse'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'id' => 'string', + 'facility_id' => 'string', + 'name' => 'string', + 'deleted_at' => '\DateTime' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'id' => 'uuid', + 'facility_id' => 'uuid', + 'name' => null, + 'deleted_at' => 'date-time' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'id' => false, + 'facility_id' => false, + 'name' => true, + 'deleted_at' => true + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'id' => 'id', + 'facility_id' => 'facilityId', + 'name' => 'name', + 'deleted_at' => 'deletedAt' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'id' => 'setId', + 'facility_id' => 'setFacilityId', + 'name' => 'setName', + 'deleted_at' => 'setDeletedAt' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'id' => 'getId', + 'facility_id' => 'getFacilityId', + 'name' => 'getName', + 'deleted_at' => 'getDeletedAt' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('id', $data ?? [], null); + $this->setIfExists('facility_id', $data ?? [], null); + $this->setIfExists('name', $data ?? [], null); + $this->setIfExists('deleted_at', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets id + * + * @return string|null + */ + public function getId() + { + return $this->container['id']; + } + + /** + * Sets id + * + * @param string|null $id id + * + * @return self + */ + public function setId($id) + { + if (is_null($id)) { + throw new \InvalidArgumentException('non-nullable id cannot be null'); + } + $this->container['id'] = $id; + + return $this; + } + + /** + * Gets facility_id + * + * @return string|null + */ + public function getFacilityId() + { + return $this->container['facility_id']; + } + + /** + * Sets facility_id + * + * @param string|null $facility_id facility_id + * + * @return self + */ + public function setFacilityId($facility_id) + { + if (is_null($facility_id)) { + throw new \InvalidArgumentException('non-nullable facility_id cannot be null'); + } + $this->container['facility_id'] = $facility_id; + + return $this; + } + + /** + * Gets name + * + * @return string|null + */ + public function getName() + { + return $this->container['name']; + } + + /** + * Sets name + * + * @param string|null $name name + * + * @return self + */ + public function setName($name) + { + if (is_null($name)) { + array_push($this->openAPINullablesSetToNull, 'name'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('name', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['name'] = $name; + + return $this; + } + + /** + * Gets deleted_at + * + * @return \DateTime|null + */ + public function getDeletedAt() + { + return $this->container['deleted_at']; + } + + /** + * Sets deleted_at + * + * @param \DateTime|null $deleted_at deleted_at + * + * @return self + */ + public function setDeletedAt($deleted_at) + { + if (is_null($deleted_at)) { + array_push($this->openAPINullablesSetToNull, 'deleted_at'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('deleted_at', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['deleted_at'] = $deleted_at; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashFacilityQualificationRateResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashFacilityQualificationRateResponse.php new file mode 100644 index 0000000..d0600e9 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashFacilityQualificationRateResponse.php @@ -0,0 +1,525 @@ + + */ +class TrashFacilityQualificationRateResponse implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'TrashFacilityQualificationRateResponse'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'id' => 'string', + 'facility_id' => 'string', + 'qualification' => 'string', + 'deleted_at' => '\DateTime' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'id' => 'uuid', + 'facility_id' => 'uuid', + 'qualification' => null, + 'deleted_at' => 'date-time' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'id' => false, + 'facility_id' => false, + 'qualification' => true, + 'deleted_at' => true + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'id' => 'id', + 'facility_id' => 'facilityId', + 'qualification' => 'qualification', + 'deleted_at' => 'deletedAt' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'id' => 'setId', + 'facility_id' => 'setFacilityId', + 'qualification' => 'setQualification', + 'deleted_at' => 'setDeletedAt' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'id' => 'getId', + 'facility_id' => 'getFacilityId', + 'qualification' => 'getQualification', + 'deleted_at' => 'getDeletedAt' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('id', $data ?? [], null); + $this->setIfExists('facility_id', $data ?? [], null); + $this->setIfExists('qualification', $data ?? [], null); + $this->setIfExists('deleted_at', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets id + * + * @return string|null + */ + public function getId() + { + return $this->container['id']; + } + + /** + * Sets id + * + * @param string|null $id id + * + * @return self + */ + public function setId($id) + { + if (is_null($id)) { + throw new \InvalidArgumentException('non-nullable id cannot be null'); + } + $this->container['id'] = $id; + + return $this; + } + + /** + * Gets facility_id + * + * @return string|null + */ + public function getFacilityId() + { + return $this->container['facility_id']; + } + + /** + * Sets facility_id + * + * @param string|null $facility_id facility_id + * + * @return self + */ + public function setFacilityId($facility_id) + { + if (is_null($facility_id)) { + throw new \InvalidArgumentException('non-nullable facility_id cannot be null'); + } + $this->container['facility_id'] = $facility_id; + + return $this; + } + + /** + * Gets qualification + * + * @return string|null + */ + public function getQualification() + { + return $this->container['qualification']; + } + + /** + * Sets qualification + * + * @param string|null $qualification qualification + * + * @return self + */ + public function setQualification($qualification) + { + if (is_null($qualification)) { + array_push($this->openAPINullablesSetToNull, 'qualification'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('qualification', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['qualification'] = $qualification; + + return $this; + } + + /** + * Gets deleted_at + * + * @return \DateTime|null + */ + public function getDeletedAt() + { + return $this->container['deleted_at']; + } + + /** + * Sets deleted_at + * + * @param \DateTime|null $deleted_at deleted_at + * + * @return self + */ + public function setDeletedAt($deleted_at) + { + if (is_null($deleted_at)) { + array_push($this->openAPINullablesSetToNull, 'deleted_at'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('deleted_at', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['deleted_at'] = $deleted_at; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashFacilityResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashFacilityResponse.php new file mode 100644 index 0000000..1e1a60b --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashFacilityResponse.php @@ -0,0 +1,491 @@ + + */ +class TrashFacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'TrashFacilityResponse'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'id' => 'string', + 'name' => 'string', + 'deleted_at' => '\DateTime' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'id' => 'uuid', + 'name' => null, + 'deleted_at' => 'date-time' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'id' => false, + 'name' => true, + 'deleted_at' => true + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'id' => 'id', + 'name' => 'name', + 'deleted_at' => 'deletedAt' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'id' => 'setId', + 'name' => 'setName', + 'deleted_at' => 'setDeletedAt' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'id' => 'getId', + 'name' => 'getName', + 'deleted_at' => 'getDeletedAt' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('id', $data ?? [], null); + $this->setIfExists('name', $data ?? [], null); + $this->setIfExists('deleted_at', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets id + * + * @return string|null + */ + public function getId() + { + return $this->container['id']; + } + + /** + * Sets id + * + * @param string|null $id id + * + * @return self + */ + public function setId($id) + { + if (is_null($id)) { + throw new \InvalidArgumentException('non-nullable id cannot be null'); + } + $this->container['id'] = $id; + + return $this; + } + + /** + * Gets name + * + * @return string|null + */ + public function getName() + { + return $this->container['name']; + } + + /** + * Sets name + * + * @param string|null $name name + * + * @return self + */ + public function setName($name) + { + if (is_null($name)) { + array_push($this->openAPINullablesSetToNull, 'name'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('name', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['name'] = $name; + + return $this; + } + + /** + * Gets deleted_at + * + * @return \DateTime|null + */ + public function getDeletedAt() + { + return $this->container['deleted_at']; + } + + /** + * Sets deleted_at + * + * @param \DateTime|null $deleted_at deleted_at + * + * @return self + */ + public function setDeletedAt($deleted_at) + { + if (is_null($deleted_at)) { + array_push($this->openAPINullablesSetToNull, 'deleted_at'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('deleted_at', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['deleted_at'] = $deleted_at; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashOrderResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashOrderResponse.php new file mode 100644 index 0000000..dadb438 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashOrderResponse.php @@ -0,0 +1,491 @@ + + */ +class TrashOrderResponse implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'TrashOrderResponse'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'id' => 'string', + 'required_qualification' => 'string', + 'deleted_at' => '\DateTime' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'id' => 'uuid', + 'required_qualification' => null, + 'deleted_at' => 'date-time' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'id' => false, + 'required_qualification' => true, + 'deleted_at' => true + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'id' => 'id', + 'required_qualification' => 'requiredQualification', + 'deleted_at' => 'deletedAt' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'id' => 'setId', + 'required_qualification' => 'setRequiredQualification', + 'deleted_at' => 'setDeletedAt' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'id' => 'getId', + 'required_qualification' => 'getRequiredQualification', + 'deleted_at' => 'getDeletedAt' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('id', $data ?? [], null); + $this->setIfExists('required_qualification', $data ?? [], null); + $this->setIfExists('deleted_at', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets id + * + * @return string|null + */ + public function getId() + { + return $this->container['id']; + } + + /** + * Sets id + * + * @param string|null $id id + * + * @return self + */ + public function setId($id) + { + if (is_null($id)) { + throw new \InvalidArgumentException('non-nullable id cannot be null'); + } + $this->container['id'] = $id; + + return $this; + } + + /** + * Gets required_qualification + * + * @return string|null + */ + public function getRequiredQualification() + { + return $this->container['required_qualification']; + } + + /** + * Sets required_qualification + * + * @param string|null $required_qualification required_qualification + * + * @return self + */ + public function setRequiredQualification($required_qualification) + { + if (is_null($required_qualification)) { + array_push($this->openAPINullablesSetToNull, 'required_qualification'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('required_qualification', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['required_qualification'] = $required_qualification; + + return $this; + } + + /** + * Gets deleted_at + * + * @return \DateTime|null + */ + public function getDeletedAt() + { + return $this->container['deleted_at']; + } + + /** + * Sets deleted_at + * + * @param \DateTime|null $deleted_at deleted_at + * + * @return self + */ + public function setDeletedAt($deleted_at) + { + if (is_null($deleted_at)) { + array_push($this->openAPINullablesSetToNull, 'deleted_at'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('deleted_at', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['deleted_at'] = $deleted_at; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashTimeEntryResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashTimeEntryResponse.php new file mode 100644 index 0000000..213d634 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashTimeEntryResponse.php @@ -0,0 +1,484 @@ + + */ +class TrashTimeEntryResponse implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'TrashTimeEntryResponse'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'id' => 'string', + 'date' => '\DateTime', + 'deleted_at' => '\DateTime' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'id' => 'uuid', + 'date' => 'date', + 'deleted_at' => 'date-time' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'id' => false, + 'date' => false, + 'deleted_at' => true + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'id' => 'id', + 'date' => 'date', + 'deleted_at' => 'deletedAt' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'id' => 'setId', + 'date' => 'setDate', + 'deleted_at' => 'setDeletedAt' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'id' => 'getId', + 'date' => 'getDate', + 'deleted_at' => 'getDeletedAt' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('id', $data ?? [], null); + $this->setIfExists('date', $data ?? [], null); + $this->setIfExists('deleted_at', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets id + * + * @return string|null + */ + public function getId() + { + return $this->container['id']; + } + + /** + * Sets id + * + * @param string|null $id id + * + * @return self + */ + public function setId($id) + { + if (is_null($id)) { + throw new \InvalidArgumentException('non-nullable id cannot be null'); + } + $this->container['id'] = $id; + + return $this; + } + + /** + * Gets date + * + * @return \DateTime|null + */ + public function getDate() + { + return $this->container['date']; + } + + /** + * Sets date + * + * @param \DateTime|null $date date + * + * @return self + */ + public function setDate($date) + { + if (is_null($date)) { + throw new \InvalidArgumentException('non-nullable date cannot be null'); + } + $this->container['date'] = $date; + + return $this; + } + + /** + * Gets deleted_at + * + * @return \DateTime|null + */ + public function getDeletedAt() + { + return $this->container['deleted_at']; + } + + /** + * Sets deleted_at + * + * @param \DateTime|null $deleted_at deleted_at + * + * @return self + */ + public function setDeletedAt($deleted_at) + { + if (is_null($deleted_at)) { + array_push($this->openAPINullablesSetToNull, 'deleted_at'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('deleted_at', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['deleted_at'] = $deleted_at; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateAbsenceRequest.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateAbsenceRequest.php new file mode 100644 index 0000000..3013b66 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateAbsenceRequest.php @@ -0,0 +1,607 @@ + + */ +class UpdateAbsenceRequest implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'UpdateAbsenceRequest'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'type' => 'string', + 'start_date' => '\DateTime', + 'end_date' => '\DateTime', + 'reason' => 'string', + 'substitute' => 'string', + 'note' => 'string' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'type' => null, + 'start_date' => 'date', + 'end_date' => 'date', + 'reason' => null, + 'substitute' => null, + 'note' => null + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'type' => true, + 'start_date' => false, + 'end_date' => false, + 'reason' => true, + 'substitute' => true, + 'note' => true + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'type' => 'type', + 'start_date' => 'startDate', + 'end_date' => 'endDate', + 'reason' => 'reason', + 'substitute' => 'substitute', + 'note' => 'note' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'type' => 'setType', + 'start_date' => 'setStartDate', + 'end_date' => 'setEndDate', + 'reason' => 'setReason', + 'substitute' => 'setSubstitute', + 'note' => 'setNote' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'type' => 'getType', + 'start_date' => 'getStartDate', + 'end_date' => 'getEndDate', + 'reason' => 'getReason', + 'substitute' => 'getSubstitute', + 'note' => 'getNote' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('type', $data ?? [], null); + $this->setIfExists('start_date', $data ?? [], null); + $this->setIfExists('end_date', $data ?? [], null); + $this->setIfExists('reason', $data ?? [], null); + $this->setIfExists('substitute', $data ?? [], null); + $this->setIfExists('note', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets type + * + * @return string|null + */ + public function getType() + { + return $this->container['type']; + } + + /** + * Sets type + * + * @param string|null $type type + * + * @return self + */ + public function setType($type) + { + if (is_null($type)) { + array_push($this->openAPINullablesSetToNull, 'type'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('type', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['type'] = $type; + + return $this; + } + + /** + * Gets start_date + * + * @return \DateTime|null + */ + public function getStartDate() + { + return $this->container['start_date']; + } + + /** + * Sets start_date + * + * @param \DateTime|null $start_date start_date + * + * @return self + */ + public function setStartDate($start_date) + { + if (is_null($start_date)) { + throw new \InvalidArgumentException('non-nullable start_date cannot be null'); + } + $this->container['start_date'] = $start_date; + + return $this; + } + + /** + * Gets end_date + * + * @return \DateTime|null + */ + public function getEndDate() + { + return $this->container['end_date']; + } + + /** + * Sets end_date + * + * @param \DateTime|null $end_date end_date + * + * @return self + */ + public function setEndDate($end_date) + { + if (is_null($end_date)) { + throw new \InvalidArgumentException('non-nullable end_date cannot be null'); + } + $this->container['end_date'] = $end_date; + + return $this; + } + + /** + * Gets reason + * + * @return string|null + */ + public function getReason() + { + return $this->container['reason']; + } + + /** + * Sets reason + * + * @param string|null $reason reason + * + * @return self + */ + public function setReason($reason) + { + if (is_null($reason)) { + array_push($this->openAPINullablesSetToNull, 'reason'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('reason', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['reason'] = $reason; + + return $this; + } + + /** + * Gets substitute + * + * @return string|null + */ + public function getSubstitute() + { + return $this->container['substitute']; + } + + /** + * Sets substitute + * + * @param string|null $substitute substitute + * + * @return self + */ + public function setSubstitute($substitute) + { + if (is_null($substitute)) { + array_push($this->openAPINullablesSetToNull, 'substitute'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('substitute', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['substitute'] = $substitute; + + return $this; + } + + /** + * Gets note + * + * @return string|null + */ + public function getNote() + { + return $this->container['note']; + } + + /** + * Sets note + * + * @param string|null $note note + * + * @return self + */ + public function setNote($note) + { + if (is_null($note)) { + array_push($this->openAPINullablesSetToNull, 'note'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('note', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['note'] = $note; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateDocumentRequest.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateDocumentRequest.php new file mode 100644 index 0000000..00be9c5 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateDocumentRequest.php @@ -0,0 +1,498 @@ + + */ +class UpdateDocumentRequest implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'UpdateDocumentRequest'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'category' => 'string', + 'description' => 'string', + 'file_name' => 'string' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'category' => null, + 'description' => null, + 'file_name' => null + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'category' => true, + 'description' => true, + 'file_name' => true + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'category' => 'category', + 'description' => 'description', + 'file_name' => 'fileName' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'category' => 'setCategory', + 'description' => 'setDescription', + 'file_name' => 'setFileName' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'category' => 'getCategory', + 'description' => 'getDescription', + 'file_name' => 'getFileName' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('category', $data ?? [], null); + $this->setIfExists('description', $data ?? [], null); + $this->setIfExists('file_name', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets category + * + * @return string|null + */ + public function getCategory() + { + return $this->container['category']; + } + + /** + * Sets category + * + * @param string|null $category category + * + * @return self + */ + public function setCategory($category) + { + if (is_null($category)) { + array_push($this->openAPINullablesSetToNull, 'category'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('category', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['category'] = $category; + + return $this; + } + + /** + * Gets description + * + * @return string|null + */ + public function getDescription() + { + return $this->container['description']; + } + + /** + * Sets description + * + * @param string|null $description description + * + * @return self + */ + public function setDescription($description) + { + if (is_null($description)) { + array_push($this->openAPINullablesSetToNull, 'description'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('description', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['description'] = $description; + + return $this; + } + + /** + * Gets file_name + * + * @return string|null + */ + public function getFileName() + { + return $this->container['file_name']; + } + + /** + * Sets file_name + * + * @param string|null $file_name file_name + * + * @return self + */ + public function setFileName($file_name) + { + if (is_null($file_name)) { + array_push($this->openAPINullablesSetToNull, 'file_name'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('file_name', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['file_name'] = $file_name; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateFacilityQualificationRateRequest.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateFacilityQualificationRateRequest.php new file mode 100644 index 0000000..ac8cf52 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateFacilityQualificationRateRequest.php @@ -0,0 +1,450 @@ + + */ +class UpdateFacilityQualificationRateRequest implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'UpdateFacilityQualificationRateRequest'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'qualification' => 'string', + 'rate' => 'float' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'qualification' => null, + 'rate' => 'double' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'qualification' => true, + 'rate' => false + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'qualification' => 'qualification', + 'rate' => 'rate' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'qualification' => 'setQualification', + 'rate' => 'setRate' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'qualification' => 'getQualification', + 'rate' => 'getRate' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('qualification', $data ?? [], null); + $this->setIfExists('rate', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets qualification + * + * @return string|null + */ + public function getQualification() + { + return $this->container['qualification']; + } + + /** + * Sets qualification + * + * @param string|null $qualification qualification + * + * @return self + */ + public function setQualification($qualification) + { + if (is_null($qualification)) { + array_push($this->openAPINullablesSetToNull, 'qualification'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('qualification', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['qualification'] = $qualification; + + return $this; + } + + /** + * Gets rate + * + * @return float|null + */ + public function getRate() + { + return $this->container['rate']; + } + + /** + * Sets rate + * + * @param float|null $rate rate + * + * @return self + */ + public function setRate($rate) + { + if (is_null($rate)) { + throw new \InvalidArgumentException('non-nullable rate cannot be null'); + } + $this->container['rate'] = $rate; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateFacilityRequest.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateFacilityRequest.php index 4a90817..d9c2c64 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateFacilityRequest.php +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateFacilityRequest.php @@ -60,6 +60,7 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali 'name' => 'string', 'crm_status' => 'string', 'facility_type' => 'string', + 'website' => 'string', 'street' => 'string', 'postal_code' => 'string', 'city' => 'string', @@ -67,7 +68,19 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali 'billing_street' => 'string', 'billing_postal_code' => 'string', 'billing_city' => 'string', - 'billing_country' => 'string' + 'billing_country' => 'string', + 'follow_up_days' => 'int', + 'billing_rate' => 'float', + 'night_surcharge_percent' => 'float', + 'saturday_surcharge_percent' => 'float', + 'sunday_surcharge_percent' => 'float', + 'holiday_surcharge_percent' => 'float', + 'travel_cost_rate' => 'float', + 'minimum_hours' => 'float', + 'break_policy' => 'string', + 'billing_interval' => 'string', + 'payment_term_days' => 'int', + 'individual_agreements' => 'string' ]; /** @@ -81,6 +94,7 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali 'name' => null, 'crm_status' => null, 'facility_type' => null, + 'website' => null, 'street' => null, 'postal_code' => null, 'city' => null, @@ -88,7 +102,19 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali 'billing_street' => null, 'billing_postal_code' => null, 'billing_city' => null, - 'billing_country' => null + 'billing_country' => null, + 'follow_up_days' => 'int32', + 'billing_rate' => 'double', + 'night_surcharge_percent' => 'double', + 'saturday_surcharge_percent' => 'double', + 'sunday_surcharge_percent' => 'double', + 'holiday_surcharge_percent' => 'double', + 'travel_cost_rate' => 'double', + 'minimum_hours' => 'double', + 'break_policy' => null, + 'billing_interval' => null, + 'payment_term_days' => 'int32', + 'individual_agreements' => null ]; /** @@ -100,6 +126,7 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali 'name' => true, 'crm_status' => true, 'facility_type' => true, + 'website' => true, 'street' => true, 'postal_code' => true, 'city' => true, @@ -107,7 +134,19 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali 'billing_street' => true, 'billing_postal_code' => true, 'billing_city' => true, - 'billing_country' => true + 'billing_country' => true, + 'follow_up_days' => true, + 'billing_rate' => true, + 'night_surcharge_percent' => true, + 'saturday_surcharge_percent' => true, + 'sunday_surcharge_percent' => true, + 'holiday_surcharge_percent' => true, + 'travel_cost_rate' => true, + 'minimum_hours' => true, + 'break_policy' => true, + 'billing_interval' => true, + 'payment_term_days' => true, + 'individual_agreements' => true ]; /** @@ -199,6 +238,7 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali 'name' => 'name', 'crm_status' => 'crmStatus', 'facility_type' => 'facilityType', + 'website' => 'website', 'street' => 'street', 'postal_code' => 'postalCode', 'city' => 'city', @@ -206,7 +246,19 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali 'billing_street' => 'billingStreet', 'billing_postal_code' => 'billingPostalCode', 'billing_city' => 'billingCity', - 'billing_country' => 'billingCountry' + 'billing_country' => 'billingCountry', + 'follow_up_days' => 'followUpDays', + 'billing_rate' => 'billingRate', + 'night_surcharge_percent' => 'nightSurchargePercent', + 'saturday_surcharge_percent' => 'saturdaySurchargePercent', + 'sunday_surcharge_percent' => 'sundaySurchargePercent', + 'holiday_surcharge_percent' => 'holidaySurchargePercent', + 'travel_cost_rate' => 'travelCostRate', + 'minimum_hours' => 'minimumHours', + 'break_policy' => 'breakPolicy', + 'billing_interval' => 'billingInterval', + 'payment_term_days' => 'paymentTermDays', + 'individual_agreements' => 'individualAgreements' ]; /** @@ -218,6 +270,7 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali 'name' => 'setName', 'crm_status' => 'setCrmStatus', 'facility_type' => 'setFacilityType', + 'website' => 'setWebsite', 'street' => 'setStreet', 'postal_code' => 'setPostalCode', 'city' => 'setCity', @@ -225,7 +278,19 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali 'billing_street' => 'setBillingStreet', 'billing_postal_code' => 'setBillingPostalCode', 'billing_city' => 'setBillingCity', - 'billing_country' => 'setBillingCountry' + 'billing_country' => 'setBillingCountry', + 'follow_up_days' => 'setFollowUpDays', + 'billing_rate' => 'setBillingRate', + 'night_surcharge_percent' => 'setNightSurchargePercent', + 'saturday_surcharge_percent' => 'setSaturdaySurchargePercent', + 'sunday_surcharge_percent' => 'setSundaySurchargePercent', + 'holiday_surcharge_percent' => 'setHolidaySurchargePercent', + 'travel_cost_rate' => 'setTravelCostRate', + 'minimum_hours' => 'setMinimumHours', + 'break_policy' => 'setBreakPolicy', + 'billing_interval' => 'setBillingInterval', + 'payment_term_days' => 'setPaymentTermDays', + 'individual_agreements' => 'setIndividualAgreements' ]; /** @@ -237,6 +302,7 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali 'name' => 'getName', 'crm_status' => 'getCrmStatus', 'facility_type' => 'getFacilityType', + 'website' => 'getWebsite', 'street' => 'getStreet', 'postal_code' => 'getPostalCode', 'city' => 'getCity', @@ -244,7 +310,19 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali 'billing_street' => 'getBillingStreet', 'billing_postal_code' => 'getBillingPostalCode', 'billing_city' => 'getBillingCity', - 'billing_country' => 'getBillingCountry' + 'billing_country' => 'getBillingCountry', + 'follow_up_days' => 'getFollowUpDays', + 'billing_rate' => 'getBillingRate', + 'night_surcharge_percent' => 'getNightSurchargePercent', + 'saturday_surcharge_percent' => 'getSaturdaySurchargePercent', + 'sunday_surcharge_percent' => 'getSundaySurchargePercent', + 'holiday_surcharge_percent' => 'getHolidaySurchargePercent', + 'travel_cost_rate' => 'getTravelCostRate', + 'minimum_hours' => 'getMinimumHours', + 'break_policy' => 'getBreakPolicy', + 'billing_interval' => 'getBillingInterval', + 'payment_term_days' => 'getPaymentTermDays', + 'individual_agreements' => 'getIndividualAgreements' ]; /** @@ -307,6 +385,7 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali $this->setIfExists('name', $data ?? [], null); $this->setIfExists('crm_status', $data ?? [], null); $this->setIfExists('facility_type', $data ?? [], null); + $this->setIfExists('website', $data ?? [], null); $this->setIfExists('street', $data ?? [], null); $this->setIfExists('postal_code', $data ?? [], null); $this->setIfExists('city', $data ?? [], null); @@ -315,6 +394,18 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali $this->setIfExists('billing_postal_code', $data ?? [], null); $this->setIfExists('billing_city', $data ?? [], null); $this->setIfExists('billing_country', $data ?? [], null); + $this->setIfExists('follow_up_days', $data ?? [], null); + $this->setIfExists('billing_rate', $data ?? [], null); + $this->setIfExists('night_surcharge_percent', $data ?? [], null); + $this->setIfExists('saturday_surcharge_percent', $data ?? [], null); + $this->setIfExists('sunday_surcharge_percent', $data ?? [], null); + $this->setIfExists('holiday_surcharge_percent', $data ?? [], null); + $this->setIfExists('travel_cost_rate', $data ?? [], null); + $this->setIfExists('minimum_hours', $data ?? [], null); + $this->setIfExists('break_policy', $data ?? [], null); + $this->setIfExists('billing_interval', $data ?? [], null); + $this->setIfExists('payment_term_days', $data ?? [], null); + $this->setIfExists('individual_agreements', $data ?? [], null); } /** @@ -461,6 +552,40 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali return $this; } + /** + * Gets website + * + * @return string|null + */ + public function getWebsite() + { + return $this->container['website']; + } + + /** + * Sets website + * + * @param string|null $website website + * + * @return self + */ + public function setWebsite($website) + { + if (is_null($website)) { + array_push($this->openAPINullablesSetToNull, 'website'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('website', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['website'] = $website; + + return $this; + } + /** * Gets street * @@ -732,6 +857,414 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali return $this; } + + /** + * Gets follow_up_days + * + * @return int|null + */ + public function getFollowUpDays() + { + return $this->container['follow_up_days']; + } + + /** + * Sets follow_up_days + * + * @param int|null $follow_up_days follow_up_days + * + * @return self + */ + public function setFollowUpDays($follow_up_days) + { + if (is_null($follow_up_days)) { + array_push($this->openAPINullablesSetToNull, 'follow_up_days'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('follow_up_days', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['follow_up_days'] = $follow_up_days; + + return $this; + } + + /** + * Gets billing_rate + * + * @return float|null + */ + public function getBillingRate() + { + return $this->container['billing_rate']; + } + + /** + * Sets billing_rate + * + * @param float|null $billing_rate billing_rate + * + * @return self + */ + public function setBillingRate($billing_rate) + { + if (is_null($billing_rate)) { + array_push($this->openAPINullablesSetToNull, 'billing_rate'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('billing_rate', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['billing_rate'] = $billing_rate; + + return $this; + } + + /** + * Gets night_surcharge_percent + * + * @return float|null + */ + public function getNightSurchargePercent() + { + return $this->container['night_surcharge_percent']; + } + + /** + * Sets night_surcharge_percent + * + * @param float|null $night_surcharge_percent night_surcharge_percent + * + * @return self + */ + public function setNightSurchargePercent($night_surcharge_percent) + { + if (is_null($night_surcharge_percent)) { + array_push($this->openAPINullablesSetToNull, 'night_surcharge_percent'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('night_surcharge_percent', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['night_surcharge_percent'] = $night_surcharge_percent; + + return $this; + } + + /** + * Gets saturday_surcharge_percent + * + * @return float|null + */ + public function getSaturdaySurchargePercent() + { + return $this->container['saturday_surcharge_percent']; + } + + /** + * Sets saturday_surcharge_percent + * + * @param float|null $saturday_surcharge_percent saturday_surcharge_percent + * + * @return self + */ + public function setSaturdaySurchargePercent($saturday_surcharge_percent) + { + if (is_null($saturday_surcharge_percent)) { + array_push($this->openAPINullablesSetToNull, 'saturday_surcharge_percent'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('saturday_surcharge_percent', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['saturday_surcharge_percent'] = $saturday_surcharge_percent; + + return $this; + } + + /** + * Gets sunday_surcharge_percent + * + * @return float|null + */ + public function getSundaySurchargePercent() + { + return $this->container['sunday_surcharge_percent']; + } + + /** + * Sets sunday_surcharge_percent + * + * @param float|null $sunday_surcharge_percent sunday_surcharge_percent + * + * @return self + */ + public function setSundaySurchargePercent($sunday_surcharge_percent) + { + if (is_null($sunday_surcharge_percent)) { + array_push($this->openAPINullablesSetToNull, 'sunday_surcharge_percent'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('sunday_surcharge_percent', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['sunday_surcharge_percent'] = $sunday_surcharge_percent; + + return $this; + } + + /** + * Gets holiday_surcharge_percent + * + * @return float|null + */ + public function getHolidaySurchargePercent() + { + return $this->container['holiday_surcharge_percent']; + } + + /** + * Sets holiday_surcharge_percent + * + * @param float|null $holiday_surcharge_percent holiday_surcharge_percent + * + * @return self + */ + public function setHolidaySurchargePercent($holiday_surcharge_percent) + { + if (is_null($holiday_surcharge_percent)) { + array_push($this->openAPINullablesSetToNull, 'holiday_surcharge_percent'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('holiday_surcharge_percent', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['holiday_surcharge_percent'] = $holiday_surcharge_percent; + + return $this; + } + + /** + * Gets travel_cost_rate + * + * @return float|null + */ + public function getTravelCostRate() + { + return $this->container['travel_cost_rate']; + } + + /** + * Sets travel_cost_rate + * + * @param float|null $travel_cost_rate travel_cost_rate + * + * @return self + */ + public function setTravelCostRate($travel_cost_rate) + { + if (is_null($travel_cost_rate)) { + array_push($this->openAPINullablesSetToNull, 'travel_cost_rate'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('travel_cost_rate', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['travel_cost_rate'] = $travel_cost_rate; + + return $this; + } + + /** + * Gets minimum_hours + * + * @return float|null + */ + public function getMinimumHours() + { + return $this->container['minimum_hours']; + } + + /** + * Sets minimum_hours + * + * @param float|null $minimum_hours minimum_hours + * + * @return self + */ + public function setMinimumHours($minimum_hours) + { + if (is_null($minimum_hours)) { + array_push($this->openAPINullablesSetToNull, 'minimum_hours'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('minimum_hours', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['minimum_hours'] = $minimum_hours; + + return $this; + } + + /** + * Gets break_policy + * + * @return string|null + */ + public function getBreakPolicy() + { + return $this->container['break_policy']; + } + + /** + * Sets break_policy + * + * @param string|null $break_policy break_policy + * + * @return self + */ + public function setBreakPolicy($break_policy) + { + if (is_null($break_policy)) { + array_push($this->openAPINullablesSetToNull, 'break_policy'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('break_policy', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['break_policy'] = $break_policy; + + return $this; + } + + /** + * Gets billing_interval + * + * @return string|null + */ + public function getBillingInterval() + { + return $this->container['billing_interval']; + } + + /** + * Sets billing_interval + * + * @param string|null $billing_interval billing_interval + * + * @return self + */ + public function setBillingInterval($billing_interval) + { + if (is_null($billing_interval)) { + array_push($this->openAPINullablesSetToNull, 'billing_interval'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('billing_interval', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['billing_interval'] = $billing_interval; + + return $this; + } + + /** + * Gets payment_term_days + * + * @return int|null + */ + public function getPaymentTermDays() + { + return $this->container['payment_term_days']; + } + + /** + * Sets payment_term_days + * + * @param int|null $payment_term_days payment_term_days + * + * @return self + */ + public function setPaymentTermDays($payment_term_days) + { + if (is_null($payment_term_days)) { + array_push($this->openAPINullablesSetToNull, 'payment_term_days'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('payment_term_days', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['payment_term_days'] = $payment_term_days; + + return $this; + } + + /** + * Gets individual_agreements + * + * @return string|null + */ + public function getIndividualAgreements() + { + return $this->container['individual_agreements']; + } + + /** + * Sets individual_agreements + * + * @param string|null $individual_agreements individual_agreements + * + * @return self + */ + public function setIndividualAgreements($individual_agreements) + { + if (is_null($individual_agreements)) { + array_push($this->openAPINullablesSetToNull, 'individual_agreements'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('individual_agreements', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['individual_agreements'] = $individual_agreements; + + return $this; + } /** * Returns true if offset exists. False otherwise. * diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateTimeEntryRequest.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateTimeEntryRequest.php new file mode 100644 index 0000000..9243aad --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateTimeEntryRequest.php @@ -0,0 +1,681 @@ + + */ +class UpdateTimeEntryRequest implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'UpdateTimeEntryRequest'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'order_id' => 'string', + 'date' => '\DateTime', + 'start' => 'string', + 'end' => 'string', + 'break_duration' => 'string', + 'night_hours' => 'float', + 'saturday_hours' => 'float', + 'sunday_hours' => 'float', + 'holiday_hours' => 'float' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'order_id' => 'uuid', + 'date' => 'date', + 'start' => 'time', + 'end' => 'time', + 'break_duration' => 'date-span', + 'night_hours' => 'double', + 'saturday_hours' => 'double', + 'sunday_hours' => 'double', + 'holiday_hours' => 'double' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'order_id' => false, + 'date' => false, + 'start' => false, + 'end' => false, + 'break_duration' => false, + 'night_hours' => false, + 'saturday_hours' => false, + 'sunday_hours' => false, + 'holiday_hours' => false + ]; + + /** + * If a nullable field gets set to null, insert it here + * + * @var boolean[] + */ + protected array $openAPINullablesSetToNull = []; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPITypes() + { + return self::$openAPITypes; + } + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function openAPIFormats() + { + return self::$openAPIFormats; + } + + /** + * Array of nullable properties + * + * @return array + */ + protected static function openAPINullables(): array + { + return self::$openAPINullables; + } + + /** + * Array of nullable field names deliberately set to null + * + * @return boolean[] + */ + private function getOpenAPINullablesSetToNull(): array + { + return $this->openAPINullablesSetToNull; + } + + /** + * Setter - Array of nullable field names deliberately set to null + * + * @param boolean[] $openAPINullablesSetToNull + */ + private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void + { + $this->openAPINullablesSetToNull = $openAPINullablesSetToNull; + } + + /** + * Checks if a property is nullable + * + * @param string $property + * @return bool + */ + public static function isNullable(string $property): bool + { + return self::openAPINullables()[$property] ?? false; + } + + /** + * Checks if a nullable property is set to null. + * + * @param string $property + * @return bool + */ + public function isNullableSetToNull(string $property): bool + { + return in_array($property, $this->getOpenAPINullablesSetToNull(), true); + } + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @var string[] + */ + protected static $attributeMap = [ + 'order_id' => 'orderId', + 'date' => 'date', + 'start' => 'start', + 'end' => 'end', + 'break_duration' => 'breakDuration', + 'night_hours' => 'nightHours', + 'saturday_hours' => 'saturdayHours', + 'sunday_hours' => 'sundayHours', + 'holiday_hours' => 'holidayHours' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'order_id' => 'setOrderId', + 'date' => 'setDate', + 'start' => 'setStart', + 'end' => 'setEnd', + 'break_duration' => 'setBreakDuration', + 'night_hours' => 'setNightHours', + 'saturday_hours' => 'setSaturdayHours', + 'sunday_hours' => 'setSundayHours', + 'holiday_hours' => 'setHolidayHours' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'order_id' => 'getOrderId', + 'date' => 'getDate', + 'start' => 'getStart', + 'end' => 'getEnd', + 'break_duration' => 'getBreakDuration', + 'night_hours' => 'getNightHours', + 'saturday_hours' => 'getSaturdayHours', + 'sunday_hours' => 'getSundayHours', + 'holiday_hours' => 'getHolidayHours' + ]; + + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ + public static function attributeMap() + { + return self::$attributeMap; + } + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters() + { + return self::$setters; + } + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters() + { + return self::$getters; + } + + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$openAPIModelName; + } + + + /** + * Associative array for storing property values + * + * @var mixed[] + */ + protected $container = []; + + /** + * Constructor + * + * @param mixed[]|null $data Associated array of property values + * initializing the model + */ + public function __construct(?array $data = null) + { + $this->setIfExists('order_id', $data ?? [], null); + $this->setIfExists('date', $data ?? [], null); + $this->setIfExists('start', $data ?? [], null); + $this->setIfExists('end', $data ?? [], null); + $this->setIfExists('break_duration', $data ?? [], null); + $this->setIfExists('night_hours', $data ?? [], null); + $this->setIfExists('saturday_hours', $data ?? [], null); + $this->setIfExists('sunday_hours', $data ?? [], null); + $this->setIfExists('holiday_hours', $data ?? [], null); + } + + /** + * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName + * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the + * $this->openAPINullablesSetToNull array + * + * @param string $variableName + * @param array $fields + * @param mixed $defaultValue + */ + private function setIfExists(string $variableName, array $fields, $defaultValue): void + { + if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) { + $this->openAPINullablesSetToNull[] = $variableName; + } + + $this->container[$variableName] = $fields[$variableName] ?? $defaultValue; + } + + /** + * Show all the invalid properties with reasons. + * + * @return array invalid properties with reasons + */ + public function listInvalidProperties() + { + $invalidProperties = []; + + return $invalidProperties; + } + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool True if all properties are valid + */ + public function valid() + { + return count($this->listInvalidProperties()) === 0; + } + + + /** + * Gets order_id + * + * @return string|null + */ + public function getOrderId() + { + return $this->container['order_id']; + } + + /** + * Sets order_id + * + * @param string|null $order_id order_id + * + * @return self + */ + public function setOrderId($order_id) + { + if (is_null($order_id)) { + throw new \InvalidArgumentException('non-nullable order_id cannot be null'); + } + $this->container['order_id'] = $order_id; + + return $this; + } + + /** + * Gets date + * + * @return \DateTime|null + */ + public function getDate() + { + return $this->container['date']; + } + + /** + * Sets date + * + * @param \DateTime|null $date date + * + * @return self + */ + public function setDate($date) + { + if (is_null($date)) { + throw new \InvalidArgumentException('non-nullable date cannot be null'); + } + $this->container['date'] = $date; + + return $this; + } + + /** + * Gets start + * + * @return string|null + */ + public function getStart() + { + return $this->container['start']; + } + + /** + * Sets start + * + * @param string|null $start start + * + * @return self + */ + public function setStart($start) + { + if (is_null($start)) { + throw new \InvalidArgumentException('non-nullable start cannot be null'); + } + $this->container['start'] = $start; + + return $this; + } + + /** + * Gets end + * + * @return string|null + */ + public function getEnd() + { + return $this->container['end']; + } + + /** + * Sets end + * + * @param string|null $end end + * + * @return self + */ + public function setEnd($end) + { + if (is_null($end)) { + throw new \InvalidArgumentException('non-nullable end cannot be null'); + } + $this->container['end'] = $end; + + return $this; + } + + /** + * Gets break_duration + * + * @return string|null + */ + public function getBreakDuration() + { + return $this->container['break_duration']; + } + + /** + * Sets break_duration + * + * @param string|null $break_duration break_duration + * + * @return self + */ + public function setBreakDuration($break_duration) + { + if (is_null($break_duration)) { + throw new \InvalidArgumentException('non-nullable break_duration cannot be null'); + } + $this->container['break_duration'] = $break_duration; + + return $this; + } + + /** + * Gets night_hours + * + * @return float|null + */ + public function getNightHours() + { + return $this->container['night_hours']; + } + + /** + * Sets night_hours + * + * @param float|null $night_hours night_hours + * + * @return self + */ + public function setNightHours($night_hours) + { + if (is_null($night_hours)) { + throw new \InvalidArgumentException('non-nullable night_hours cannot be null'); + } + $this->container['night_hours'] = $night_hours; + + return $this; + } + + /** + * Gets saturday_hours + * + * @return float|null + */ + public function getSaturdayHours() + { + return $this->container['saturday_hours']; + } + + /** + * Sets saturday_hours + * + * @param float|null $saturday_hours saturday_hours + * + * @return self + */ + public function setSaturdayHours($saturday_hours) + { + if (is_null($saturday_hours)) { + throw new \InvalidArgumentException('non-nullable saturday_hours cannot be null'); + } + $this->container['saturday_hours'] = $saturday_hours; + + return $this; + } + + /** + * Gets sunday_hours + * + * @return float|null + */ + public function getSundayHours() + { + return $this->container['sunday_hours']; + } + + /** + * Sets sunday_hours + * + * @param float|null $sunday_hours sunday_hours + * + * @return self + */ + public function setSundayHours($sunday_hours) + { + if (is_null($sunday_hours)) { + throw new \InvalidArgumentException('non-nullable sunday_hours cannot be null'); + } + $this->container['sunday_hours'] = $sunday_hours; + + return $this; + } + + /** + * Gets holiday_hours + * + * @return float|null + */ + public function getHolidayHours() + { + return $this->container['holiday_hours']; + } + + /** + * Sets holiday_hours + * + * @param float|null $holiday_hours holiday_hours + * + * @return self + */ + public function setHolidayHours($holiday_hours) + { + if (is_null($holiday_hours)) { + throw new \InvalidArgumentException('non-nullable holiday_hours cannot be null'); + } + $this->container['holiday_hours'] = $holiday_hours; + + return $this; + } + /** + * Returns true if offset exists. False otherwise. + * + * @param integer $offset Offset + * + * @return boolean + */ + public function offsetExists($offset): bool + { + return isset($this->container[$offset]); + } + + /** + * Gets offset. + * + * @param integer $offset Offset + * + * @return mixed|null + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->container[$offset] ?? null; + } + + /** + * Sets value based on offset. + * + * @param int|null $offset Offset + * @param mixed $value Value to be set + * + * @return void + */ + public function offsetSet($offset, $value): void + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + /** + * Unsets offset. + * + * @param integer $offset Offset + * + * @return void + */ + public function offsetUnset($offset): void + { + unset($this->container[$offset]); + } + + /** + * Serializes the object to a value that can be serialized natively by json_encode(). + * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed Returns data which can be serialized by json_encode(), which is a value + * of any type other than a resource. + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ObjectSerializer::sanitizeForSerialization($this); + } + + /** + * Gets the string presentation of the object + * + * @return string + */ + public function __toString() + { + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); + } + + /** + * Gets a header-safe presentation of the object + * + * @return string + */ + public function toHeaderValue() + { + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); + } +} + + diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateValueListItemRequest.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateValueListItemRequest.php index e064052..343d5fa 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateValueListItemRequest.php +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateValueListItemRequest.php @@ -61,7 +61,8 @@ class UpdateValueListItemRequest implements ModelInterface, ArrayAccess, \JsonSe 'sort_order' => 'int', 'is_default' => 'bool', 'is_initial' => 'bool', - 'is_terminal' => 'bool' + 'is_terminal' => 'bool', + 'triggers_follow_up' => 'bool' ]; /** @@ -76,7 +77,8 @@ class UpdateValueListItemRequest implements ModelInterface, ArrayAccess, \JsonSe 'sort_order' => 'int32', 'is_default' => null, 'is_initial' => null, - 'is_terminal' => null + 'is_terminal' => null, + 'triggers_follow_up' => null ]; /** @@ -89,7 +91,8 @@ class UpdateValueListItemRequest implements ModelInterface, ArrayAccess, \JsonSe 'sort_order' => false, 'is_default' => false, 'is_initial' => false, - 'is_terminal' => false + 'is_terminal' => false, + 'triggers_follow_up' => false ]; /** @@ -182,7 +185,8 @@ class UpdateValueListItemRequest implements ModelInterface, ArrayAccess, \JsonSe 'sort_order' => 'sortOrder', 'is_default' => 'isDefault', 'is_initial' => 'isInitial', - 'is_terminal' => 'isTerminal' + 'is_terminal' => 'isTerminal', + 'triggers_follow_up' => 'triggersFollowUp' ]; /** @@ -195,7 +199,8 @@ class UpdateValueListItemRequest implements ModelInterface, ArrayAccess, \JsonSe 'sort_order' => 'setSortOrder', 'is_default' => 'setIsDefault', 'is_initial' => 'setIsInitial', - 'is_terminal' => 'setIsTerminal' + 'is_terminal' => 'setIsTerminal', + 'triggers_follow_up' => 'setTriggersFollowUp' ]; /** @@ -208,7 +213,8 @@ class UpdateValueListItemRequest implements ModelInterface, ArrayAccess, \JsonSe 'sort_order' => 'getSortOrder', 'is_default' => 'getIsDefault', 'is_initial' => 'getIsInitial', - 'is_terminal' => 'getIsTerminal' + 'is_terminal' => 'getIsTerminal', + 'triggers_follow_up' => 'getTriggersFollowUp' ]; /** @@ -273,6 +279,7 @@ class UpdateValueListItemRequest implements ModelInterface, ArrayAccess, \JsonSe $this->setIfExists('is_default', $data ?? [], null); $this->setIfExists('is_initial', $data ?? [], null); $this->setIfExists('is_terminal', $data ?? [], null); + $this->setIfExists('triggers_follow_up', $data ?? [], null); } /** @@ -458,6 +465,33 @@ class UpdateValueListItemRequest implements ModelInterface, ArrayAccess, \JsonSe return $this; } + + /** + * Gets triggers_follow_up + * + * @return bool|null + */ + public function getTriggersFollowUp() + { + return $this->container['triggers_follow_up']; + } + + /** + * Sets triggers_follow_up + * + * @param bool|null $triggers_follow_up triggers_follow_up + * + * @return self + */ + public function setTriggersFollowUp($triggers_follow_up) + { + if (is_null($triggers_follow_up)) { + throw new \InvalidArgumentException('non-nullable triggers_follow_up cannot be null'); + } + $this->container['triggers_follow_up'] = $triggers_follow_up; + + return $this; + } /** * Returns true if offset exists. False otherwise. * diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UserPermissionOverrideResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UserPermissionOverrideResponse.php index 47ba17b..5f99228 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UserPermissionOverrideResponse.php +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UserPermissionOverrideResponse.php @@ -58,9 +58,10 @@ class UserPermissionOverrideResponse implements ModelInterface, ArrayAccess, \Js */ protected static $openAPITypes = [ 'id' => 'string', - 'module' => 'string', - 'action' => 'string', - 'effect' => 'string' + 'module' => '\OmsorgCoreClient\Model\ModuleType', + 'action' => '\OmsorgCoreClient\Model\PermissionAction', + 'effect' => '\OmsorgCoreClient\Model\PermissionEffect', + 'scope' => '\OmsorgCoreClient\Model\PermissionScope' ]; /** @@ -74,7 +75,8 @@ class UserPermissionOverrideResponse implements ModelInterface, ArrayAccess, \Js 'id' => 'uuid', 'module' => null, 'action' => null, - 'effect' => null + 'effect' => null, + 'scope' => null ]; /** @@ -84,9 +86,10 @@ class UserPermissionOverrideResponse implements ModelInterface, ArrayAccess, \Js */ protected static array $openAPINullables = [ 'id' => false, - 'module' => true, - 'action' => true, - 'effect' => true + 'module' => false, + 'action' => false, + 'effect' => false, + 'scope' => false ]; /** @@ -178,7 +181,8 @@ class UserPermissionOverrideResponse implements ModelInterface, ArrayAccess, \Js 'id' => 'id', 'module' => 'module', 'action' => 'action', - 'effect' => 'effect' + 'effect' => 'effect', + 'scope' => 'scope' ]; /** @@ -190,7 +194,8 @@ class UserPermissionOverrideResponse implements ModelInterface, ArrayAccess, \Js 'id' => 'setId', 'module' => 'setModule', 'action' => 'setAction', - 'effect' => 'setEffect' + 'effect' => 'setEffect', + 'scope' => 'setScope' ]; /** @@ -202,7 +207,8 @@ class UserPermissionOverrideResponse implements ModelInterface, ArrayAccess, \Js 'id' => 'getId', 'module' => 'getModule', 'action' => 'getAction', - 'effect' => 'getEffect' + 'effect' => 'getEffect', + 'scope' => 'getScope' ]; /** @@ -266,6 +272,7 @@ class UserPermissionOverrideResponse implements ModelInterface, ArrayAccess, \Js $this->setIfExists('module', $data ?? [], null); $this->setIfExists('action', $data ?? [], null); $this->setIfExists('effect', $data ?? [], null); + $this->setIfExists('scope', $data ?? [], null); } /** @@ -340,7 +347,7 @@ class UserPermissionOverrideResponse implements ModelInterface, ArrayAccess, \Js /** * Gets module * - * @return string|null + * @return \OmsorgCoreClient\Model\ModuleType|null */ public function getModule() { @@ -350,21 +357,14 @@ class UserPermissionOverrideResponse implements ModelInterface, ArrayAccess, \Js /** * Sets module * - * @param string|null $module module + * @param \OmsorgCoreClient\Model\ModuleType|null $module module * * @return self */ public function setModule($module) { if (is_null($module)) { - array_push($this->openAPINullablesSetToNull, 'module'); - } else { - $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); - $index = array_search('module', $nullablesSetToNull); - if ($index !== FALSE) { - unset($nullablesSetToNull[$index]); - $this->setOpenAPINullablesSetToNull($nullablesSetToNull); - } + throw new \InvalidArgumentException('non-nullable module cannot be null'); } $this->container['module'] = $module; @@ -374,7 +374,7 @@ class UserPermissionOverrideResponse implements ModelInterface, ArrayAccess, \Js /** * Gets action * - * @return string|null + * @return \OmsorgCoreClient\Model\PermissionAction|null */ public function getAction() { @@ -384,21 +384,14 @@ class UserPermissionOverrideResponse implements ModelInterface, ArrayAccess, \Js /** * Sets action * - * @param string|null $action action + * @param \OmsorgCoreClient\Model\PermissionAction|null $action action * * @return self */ public function setAction($action) { if (is_null($action)) { - array_push($this->openAPINullablesSetToNull, 'action'); - } else { - $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); - $index = array_search('action', $nullablesSetToNull); - if ($index !== FALSE) { - unset($nullablesSetToNull[$index]); - $this->setOpenAPINullablesSetToNull($nullablesSetToNull); - } + throw new \InvalidArgumentException('non-nullable action cannot be null'); } $this->container['action'] = $action; @@ -408,7 +401,7 @@ class UserPermissionOverrideResponse implements ModelInterface, ArrayAccess, \Js /** * Gets effect * - * @return string|null + * @return \OmsorgCoreClient\Model\PermissionEffect|null */ public function getEffect() { @@ -418,26 +411,46 @@ class UserPermissionOverrideResponse implements ModelInterface, ArrayAccess, \Js /** * Sets effect * - * @param string|null $effect effect + * @param \OmsorgCoreClient\Model\PermissionEffect|null $effect effect * * @return self */ public function setEffect($effect) { if (is_null($effect)) { - array_push($this->openAPINullablesSetToNull, 'effect'); - } else { - $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); - $index = array_search('effect', $nullablesSetToNull); - if ($index !== FALSE) { - unset($nullablesSetToNull[$index]); - $this->setOpenAPINullablesSetToNull($nullablesSetToNull); - } + throw new \InvalidArgumentException('non-nullable effect cannot be null'); } $this->container['effect'] = $effect; return $this; } + + /** + * Gets scope + * + * @return \OmsorgCoreClient\Model\PermissionScope|null + */ + public function getScope() + { + return $this->container['scope']; + } + + /** + * Sets scope + * + * @param \OmsorgCoreClient\Model\PermissionScope|null $scope scope + * + * @return self + */ + public function setScope($scope) + { + if (is_null($scope)) { + throw new \InvalidArgumentException('non-nullable scope cannot be null'); + } + $this->container['scope'] = $scope; + + return $this; + } /** * Returns true if offset exists. False otherwise. * diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/ValueListItemResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/ValueListItemResponse.php index 970336e..3e2304c 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/ValueListItemResponse.php +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/ValueListItemResponse.php @@ -62,7 +62,8 @@ class ValueListItemResponse implements ModelInterface, ArrayAccess, \JsonSeriali 'sort_order' => 'int', 'is_default' => 'bool', 'is_initial' => 'bool', - 'is_terminal' => 'bool' + 'is_terminal' => 'bool', + 'triggers_follow_up' => 'bool' ]; /** @@ -78,7 +79,8 @@ class ValueListItemResponse implements ModelInterface, ArrayAccess, \JsonSeriali 'sort_order' => 'int32', 'is_default' => null, 'is_initial' => null, - 'is_terminal' => null + 'is_terminal' => null, + 'triggers_follow_up' => null ]; /** @@ -92,7 +94,8 @@ class ValueListItemResponse implements ModelInterface, ArrayAccess, \JsonSeriali 'sort_order' => false, 'is_default' => false, 'is_initial' => false, - 'is_terminal' => false + 'is_terminal' => false, + 'triggers_follow_up' => false ]; /** @@ -186,7 +189,8 @@ class ValueListItemResponse implements ModelInterface, ArrayAccess, \JsonSeriali 'sort_order' => 'sortOrder', 'is_default' => 'isDefault', 'is_initial' => 'isInitial', - 'is_terminal' => 'isTerminal' + 'is_terminal' => 'isTerminal', + 'triggers_follow_up' => 'triggersFollowUp' ]; /** @@ -200,7 +204,8 @@ class ValueListItemResponse implements ModelInterface, ArrayAccess, \JsonSeriali 'sort_order' => 'setSortOrder', 'is_default' => 'setIsDefault', 'is_initial' => 'setIsInitial', - 'is_terminal' => 'setIsTerminal' + 'is_terminal' => 'setIsTerminal', + 'triggers_follow_up' => 'setTriggersFollowUp' ]; /** @@ -214,7 +219,8 @@ class ValueListItemResponse implements ModelInterface, ArrayAccess, \JsonSeriali 'sort_order' => 'getSortOrder', 'is_default' => 'getIsDefault', 'is_initial' => 'getIsInitial', - 'is_terminal' => 'getIsTerminal' + 'is_terminal' => 'getIsTerminal', + 'triggers_follow_up' => 'getTriggersFollowUp' ]; /** @@ -280,6 +286,7 @@ class ValueListItemResponse implements ModelInterface, ArrayAccess, \JsonSeriali $this->setIfExists('is_default', $data ?? [], null); $this->setIfExists('is_initial', $data ?? [], null); $this->setIfExists('is_terminal', $data ?? [], null); + $this->setIfExists('triggers_follow_up', $data ?? [], null); } /** @@ -492,6 +499,33 @@ class ValueListItemResponse implements ModelInterface, ArrayAccess, \JsonSeriali return $this; } + + /** + * Gets triggers_follow_up + * + * @return bool|null + */ + public function getTriggersFollowUp() + { + return $this->container['triggers_follow_up']; + } + + /** + * Sets triggers_follow_up + * + * @param bool|null $triggers_follow_up triggers_follow_up + * + * @return self + */ + public function setTriggersFollowUp($triggers_follow_up) + { + if (is_null($triggers_follow_up)) { + throw new \InvalidArgumentException('non-nullable triggers_follow_up cannot be null'); + } + $this->container['triggers_follow_up'] = $triggers_follow_up; + + return $this; + } /** * Returns true if offset exists. False otherwise. * diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/ValueListTransitionResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/ValueListTransitionResponse.php index 221e94c..3e55120 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/ValueListTransitionResponse.php +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/ValueListTransitionResponse.php @@ -59,7 +59,8 @@ class ValueListTransitionResponse implements ModelInterface, ArrayAccess, \JsonS protected static $openAPITypes = [ 'id' => 'string', 'from_item_id' => 'string', - 'to_item_id' => 'string' + 'to_item_id' => 'string', + 'requires_approval' => 'bool' ]; /** @@ -72,7 +73,8 @@ class ValueListTransitionResponse implements ModelInterface, ArrayAccess, \JsonS protected static $openAPIFormats = [ 'id' => 'uuid', 'from_item_id' => 'uuid', - 'to_item_id' => 'uuid' + 'to_item_id' => 'uuid', + 'requires_approval' => null ]; /** @@ -83,7 +85,8 @@ class ValueListTransitionResponse implements ModelInterface, ArrayAccess, \JsonS protected static array $openAPINullables = [ 'id' => false, 'from_item_id' => false, - 'to_item_id' => false + 'to_item_id' => false, + 'requires_approval' => false ]; /** @@ -174,7 +177,8 @@ class ValueListTransitionResponse implements ModelInterface, ArrayAccess, \JsonS protected static $attributeMap = [ 'id' => 'id', 'from_item_id' => 'fromItemId', - 'to_item_id' => 'toItemId' + 'to_item_id' => 'toItemId', + 'requires_approval' => 'requiresApproval' ]; /** @@ -185,7 +189,8 @@ class ValueListTransitionResponse implements ModelInterface, ArrayAccess, \JsonS protected static $setters = [ 'id' => 'setId', 'from_item_id' => 'setFromItemId', - 'to_item_id' => 'setToItemId' + 'to_item_id' => 'setToItemId', + 'requires_approval' => 'setRequiresApproval' ]; /** @@ -196,7 +201,8 @@ class ValueListTransitionResponse implements ModelInterface, ArrayAccess, \JsonS protected static $getters = [ 'id' => 'getId', 'from_item_id' => 'getFromItemId', - 'to_item_id' => 'getToItemId' + 'to_item_id' => 'getToItemId', + 'requires_approval' => 'getRequiresApproval' ]; /** @@ -259,6 +265,7 @@ class ValueListTransitionResponse implements ModelInterface, ArrayAccess, \JsonS $this->setIfExists('id', $data ?? [], null); $this->setIfExists('from_item_id', $data ?? [], null); $this->setIfExists('to_item_id', $data ?? [], null); + $this->setIfExists('requires_approval', $data ?? [], null); } /** @@ -383,6 +390,33 @@ class ValueListTransitionResponse implements ModelInterface, ArrayAccess, \JsonS return $this; } + + /** + * Gets requires_approval + * + * @return bool|null + */ + public function getRequiresApproval() + { + return $this->container['requires_approval']; + } + + /** + * Sets requires_approval + * + * @param bool|null $requires_approval requires_approval + * + * @return self + */ + public function setRequiresApproval($requires_approval) + { + if (is_null($requires_approval)) { + throw new \InvalidArgumentException('non-nullable requires_approval cannot be null'); + } + $this->container['requires_approval'] = $requires_approval; + + return $this; + } /** * Returns true if offset exists. False otherwise. * diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/test/Api/AbsencesApiTest.php b/omsorgWeb/mitarbeiter-app/api-client-php/test/Api/AbsencesApiTest.php new file mode 100644 index 0000000..b85a1ed --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/test/Api/AbsencesApiTest.php @@ -0,0 +1,133 @@ +"> Dashboard + + + 🗓 Urlaub & Abwesenheit + + + + + Zeiterfassung + + Einstellungen diff --git a/omsorgWeb/mitarbeiter-app/lib/omsorgCoreClient.php b/omsorgWeb/mitarbeiter-app/lib/omsorgCoreClient.php index b707079..ccabfef 100644 --- a/omsorgWeb/mitarbeiter-app/lib/omsorgCoreClient.php +++ b/omsorgWeb/mitarbeiter-app/lib/omsorgCoreClient.php @@ -3,8 +3,9 @@ // analog zu httpClient.cjs/authClient.cjs in omsorgapp. Alle Aufrufer bekommen ein einheitliches // Ergebnis-Array: ['ok' => bool, 'status' => int, 'data' => array|null]. // -// Nur Auth-Endpunkte - Connect verwaltet keine anderen Nutzer/Rollen/Mitarbeiter (das bleibt -// OMSORG Desktop vorbehalten), deshalb fehlen hier bewusst omsorgcore_users_*/roles_*/employees_*. +// Auth-Endpunkte plus die für den Außendienst freigegebenen Absences-/ValueLists-Endpunkte - +// Connect verwaltet weiterhin keine anderen Nutzer/Rollen/Mitarbeiter (das bleibt OMSORG Desktop +// vorbehalten), deshalb fehlen hier bewusst omsorgcore_users_*/roles_*/employees_*. // // Transport läuft über den generierten Client in ../api-client-php/ (openapi-generator, siehe // ../api-client-php/ANLEITUNG.md) statt über rohes cURL. @@ -110,3 +111,150 @@ function omsorgcore_password_policy(array $config): array return omsorgcore_call($config, fn($cfg) => (new \OmsorgCoreClient\Api\AuthApi(new \GuzzleHttp\Client(), $cfg)) ->apiAuthPasswordPolicyGetWithHttpInfo()); } + +// --- ValueLists (nur lesend - Dropdown-Optionen, z. B. "AbsenceType") --- + +function omsorgcore_value_list_items(array $config, string $accessToken, string $key): array +{ + return omsorgcore_call($config, function ($cfg) use ($accessToken, $key) { + $cfg->setAccessToken($accessToken); + return (new \OmsorgCoreClient\Api\ValueListsApi(new \GuzzleHttp\Client(), $cfg)) + ->apiValueListsKeyItemsGetWithHttpInfo($key); + }); +} + +// --- Absences (Urlaub/Krankmeldung/Sonstige, FR-CON-1) --- +// Own-Scope: die EmployeeId wird serverseitig aus dem JWT aufgelöst (siehe AbsenceService in +// omsorgCore), Connect schickt hier bewusst nie eine employeeId mit. + +function omsorgcore_absences_list(array $config, string $accessToken, ?string $status = null): array +{ + return omsorgcore_call($config, function ($cfg) use ($accessToken, $status) { + $cfg->setAccessToken($accessToken); + return (new \OmsorgCoreClient\Api\AbsencesApi(new \GuzzleHttp\Client(), $cfg)) + ->apiAbsencesGetWithHttpInfo($status); + }); +} + +function omsorgcore_absences_create(array $config, string $accessToken, array $payload): array +{ + $createAbsenceRequest = new \OmsorgCoreClient\Model\CreateAbsenceRequest([ + 'type' => $payload['type'], + 'start_date' => new \DateTime($payload['startDate']), + 'end_date' => new \DateTime($payload['endDate']), + 'reason' => $payload['reason'] !== '' ? $payload['reason'] : null, + 'substitute' => $payload['substitute'] !== '' ? $payload['substitute'] : null, + 'note' => $payload['note'] !== '' ? $payload['note'] : null, + ]); + return omsorgcore_call($config, function ($cfg) use ($accessToken, $createAbsenceRequest) { + $cfg->setAccessToken($accessToken); + return (new \OmsorgCoreClient\Api\AbsencesApi(new \GuzzleHttp\Client(), $cfg)) + ->apiAbsencesPostWithHttpInfo($createAbsenceRequest); + }); +} + +// Nur solange der Antrag noch "Eingereicht" ist (serverseitig erzwungen, siehe +// AbsenceService.UpdateAsync in omsorgCore) - Genehmigt/Abgelehnt sind über diesen Endpoint +// bewusst nicht mehr änderbar. +function omsorgcore_absences_update(array $config, string $accessToken, string $id, array $payload): array +{ + $updateAbsenceRequest = new \OmsorgCoreClient\Model\UpdateAbsenceRequest([ + 'type' => $payload['type'], + 'start_date' => new \DateTime($payload['startDate']), + 'end_date' => new \DateTime($payload['endDate']), + 'reason' => $payload['reason'] !== '' ? $payload['reason'] : null, + 'substitute' => $payload['substitute'] !== '' ? $payload['substitute'] : null, + 'note' => $payload['note'] !== '' ? $payload['note'] : null, + ]); + return omsorgcore_call($config, function ($cfg) use ($accessToken, $id, $updateAbsenceRequest) { + $cfg->setAccessToken($accessToken); + return (new \OmsorgCoreClient\Api\AbsencesApi(new \GuzzleHttp\Client(), $cfg)) + ->apiAbsencesIdPutWithHttpInfo($id, $updateAbsenceRequest); + }); +} + +// --- Orders (nur lesend - Auftrags-Dropdown fürs Zeiterfassungsformular) --- +// Order hat noch keine Mitarbeiter-Zuweisung (FR-EM-3 offen), daher zeigt das Dropdown alle +// aktiven Aufträge - siehe omsorgCore/CLAUDE.md, Abschnitt "Zeiterfassung", dokumentierte Einschränkung. + +function omsorgcore_orders_list(array $config, string $accessToken): array +{ + return omsorgcore_call($config, function ($cfg) use ($accessToken) { + $cfg->setAccessToken($accessToken); + return (new \OmsorgCoreClient\Api\OrdersApi(new \GuzzleHttp\Client(), $cfg)) + ->apiOrdersGetWithHttpInfo(null, null, null, null, null, null, 1, 200); + }); +} + +// --- TimeEntries (Zeiterfassung pro Schicht, FR-ZE-1/FR-ZE-2) --- +// Own-Scope: die EmployeeId wird serverseitig aus dem JWT aufgelöst (siehe TimeEntryService in +// omsorgCore), Connect schickt hier bewusst nie eine employeeId mit. + +function omsorgcore_time_entries_list(array $config, string $accessToken, ?string $statusId = null): array +{ + return omsorgcore_call($config, function ($cfg) use ($accessToken, $statusId) { + $cfg->setAccessToken($accessToken); + return (new \OmsorgCoreClient\Api\TimeEntriesApi(new \GuzzleHttp\Client(), $cfg)) + ->apiTimeEntriesGetWithHttpInfo($statusId); + }); +} + +function omsorgcore_time_entries_create(array $config, string $accessToken, array $payload): array +{ + $createTimeEntryRequest = new \OmsorgCoreClient\Model\CreateTimeEntryRequest([ + 'order_id' => $payload['orderId'], + 'date' => new \DateTime($payload['date']), + 'start' => $payload['start'], + 'end' => $payload['end'], + 'break_duration' => $payload['breakDuration'], + 'night_hours' => $payload['nightHours'], + 'saturday_hours' => $payload['saturdayHours'], + 'sunday_hours' => $payload['sundayHours'], + 'holiday_hours' => $payload['holidayHours'], + ]); + return omsorgcore_call($config, function ($cfg) use ($accessToken, $createTimeEntryRequest) { + $cfg->setAccessToken($accessToken); + return (new \OmsorgCoreClient\Api\TimeEntriesApi(new \GuzzleHttp\Client(), $cfg)) + ->apiTimeEntriesPostWithHttpInfo($createTimeEntryRequest); + }); +} + +// Nur solange status.isEditableByOwner (serverseitig erzwungen, siehe TimeEntryService.UpdateAsync +// in omsorgCore) - kein statusId-Feld hier, Statuswechsel laufen ausschließlich über submit/decision. +function omsorgcore_time_entries_update(array $config, string $accessToken, string $id, array $payload): array +{ + $updateTimeEntryRequest = new \OmsorgCoreClient\Model\UpdateTimeEntryRequest([ + 'order_id' => $payload['orderId'], + 'date' => new \DateTime($payload['date']), + 'start' => $payload['start'], + 'end' => $payload['end'], + 'break_duration' => $payload['breakDuration'], + 'night_hours' => $payload['nightHours'], + 'saturday_hours' => $payload['saturdayHours'], + 'sunday_hours' => $payload['sundayHours'], + 'holiday_hours' => $payload['holidayHours'], + ]); + return omsorgcore_call($config, function ($cfg) use ($accessToken, $id, $updateTimeEntryRequest) { + $cfg->setAccessToken($accessToken); + return (new \OmsorgCoreClient\Api\TimeEntriesApi(new \GuzzleHttp\Client(), $cfg)) + ->apiTimeEntriesIdPutWithHttpInfo($id, $updateTimeEntryRequest); + }); +} + +function omsorgcore_value_list_transitions(array $config, string $accessToken, string $key): array +{ + return omsorgcore_call($config, function ($cfg) use ($accessToken, $key) { + $cfg->setAccessToken($accessToken); + return (new \OmsorgCoreClient\Api\ValueListsApi(new \GuzzleHttp\Client(), $cfg)) + ->apiValueListsKeyTransitionsGetWithHttpInfo($key); + }); +} + +function omsorgcore_time_entries_submit(array $config, string $accessToken, string $id): array +{ + return omsorgcore_call($config, function ($cfg) use ($accessToken, $id) { + $cfg->setAccessToken($accessToken); + return (new \OmsorgCoreClient\Api\TimeEntriesApi(new \GuzzleHttp\Client(), $cfg)) + ->apiTimeEntriesIdSubmitPostWithHttpInfo($id); + }); +} diff --git a/omsorgWeb/mitarbeiter-app/pages/forgot-password.php b/omsorgWeb/mitarbeiter-app/pages/forgot-password.php index 6fcf51c..502fafd 100644 --- a/omsorgWeb/mitarbeiter-app/pages/forgot-password.php +++ b/omsorgWeb/mitarbeiter-app/pages/forgot-password.php @@ -20,10 +20,21 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($username === '') { $error = 'Bitte Benutzername eingeben.'; } else { - omsorgcore_forgot_password_request(_omsorgcore_config(), $username); - // Bewusst immer weiter zu Schritt 2, unabhängig vom Ergebnis - kein Rückschluss darauf, - // ob der Username existiert (siehe PasswordResetService.RequestResetAsync in omsorgCore). - $step = 'verify'; + $result = omsorgcore_forgot_password_request(_omsorgcore_config(), $username); + $status = $result['ok'] ? ($result['data']['status'] ?? null) : null; + + if ($status === 'email_unavailable') { + // Einzige Ausnahme vom "immer weiter"-Prinzip unten: der Code wurde zwar angelegt, + // aber die E-Mail kam nachweislich nicht raus (z.B. SMTP down) - kein Sinn, den Nutzer + // auf eine Code-Eingabe warten zu lassen, die nie ankommt. Kein zusätzliches + // Enumeration-Risiko: "sent" vs. "cannot_reset" unterscheidet bereits, ob der Username + // existiert (siehe AuthController.ForgotPasswordRequest in omsorgCore). + $error = 'Der E-Mail-Versand ist gerade nicht verfügbar. Bitte später erneut versuchen oder einen Administrator kontaktieren.'; + } else { + // Bewusst immer weiter zu Schritt 2, unabhängig vom Ergebnis - kein Rückschluss darauf, + // ob der Username existiert (siehe PasswordResetService.RequestResetAsync in omsorgCore). + $step = 'verify'; + } } } elseif ($step === 'verify') { $username = trim($_POST['username'] ?? ''); diff --git a/omsorgWeb/mitarbeiter-app/pages/stundenerfassung.php b/omsorgWeb/mitarbeiter-app/pages/stundenerfassung.php new file mode 100644 index 0000000..ee9e2a7 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/pages/stundenerfassung.php @@ -0,0 +1,292 @@ + $_POST['order_id'] ?? '', + 'date' => $_POST['date'] ?? '', + 'start' => ($_POST['start'] ?? '') !== '' ? $_POST['start'] . ':00' : '', + 'end' => ($_POST['end'] ?? '') !== '' ? $_POST['end'] . ':00' : '', + 'breakDuration' => sprintf('%02d:%02d:00', intdiv((int)($_POST['break_minutes'] ?? 0), 60), (int)($_POST['break_minutes'] ?? 0) % 60), + 'nightHours' => (float)($_POST['night_hours'] ?? 0), + 'saturdayHours' => (float)($_POST['saturday_hours'] ?? 0), + 'sundayHours' => (float)($_POST['sunday_hours'] ?? 0), + 'holidayHours' => (float)($_POST['holiday_hours'] ?? 0), + ]; + + if ($payload['orderId'] === '' || $payload['date'] === '' || $payload['start'] === '' || $payload['end'] === '') { + $error = 'Auftrag, Datum, Beginn und Ende sind erforderlich.'; + $editId = $mode === 'edit' ? ($_POST['time_entry_id'] ?? null) : null; + } elseif ($mode === 'edit') { + if (!$canEdit) { + // Server lehnt das ohnehin über [RequirePermission(TimeEntries, Edit)] ab - dieser Zweig + // greift nur, wenn jemand das Formular manuell postet, obwohl es clientseitig gar nicht + // angezeigt wurde. + $error = 'Keine Berechtigung, diese Zeiterfassung zu bearbeiten.'; + } else { + $timeEntryId = $_POST['time_entry_id'] ?? ''; + $result = omsorgcore_time_entries_update(_omsorgcore_config(), $_SESSION['omsorgcore_access_token'], $timeEntryId, $payload); + + if ($result['ok']) { + header('Location: stundenerfassung.php?updated=1'); + exit; + } + + $editId = $timeEntryId; + $error = $result['status'] === 403 + ? 'Keine Berechtigung, diese Zeiterfassung zu bearbeiten.' + : (is_string($result['data'] ?? null) ? $result['data'] : 'Zeiterfassung konnte nicht gespeichert werden.'); + } + } elseif (!$canCreate) { + $error = 'Keine Berechtigung, eine Zeiterfassung anzulegen.'; + } else { + $result = omsorgcore_time_entries_create(_omsorgcore_config(), $_SESSION['omsorgcore_access_token'], $payload); + + if ($result['ok']) { + $success = true; + } elseif ($result['status'] === 403) { + $error = 'Keine Berechtigung, eine Zeiterfassung anzulegen.'; + } else { + $error = is_string($result['data'] ?? null) ? $result['data'] : 'Zeiterfassung konnte nicht gespeichert werden.'; + } + } + } +} elseif (isset($_GET['edit'])) { + $editId = $_GET['edit']; +} + +$listResult = omsorgcore_time_entries_list(_omsorgcore_config(), $_SESSION['omsorgcore_access_token']); +$timeEntries = $listResult['ok'] ? ($listResult['data']['items'] ?? []) : []; + +// Statuswerte mit einer Selbst-Einreichungs-Kante (requiresApproval=false, siehe +// TimeEntryService.GetSelfServiceTransitionAsync in omsorgCore) - nur für diese Einträge zeigt die +// Liste den "Einreichen"-Button, damit er nicht auch bei bereits eingereichten Einträgen (die zwar +// noch isEditableByOwner sind, aber keine ausgehende Selbst-Einreichungs-Kante mehr haben) erscheint. +$transitionsResult = omsorgcore_value_list_transitions(_omsorgcore_config(), $_SESSION['omsorgcore_access_token'], 'TimeEntryStatus'); +$transitions = $transitionsResult['ok'] ? ($transitionsResult['data'] ?? []) : []; +$submittableStatusIds = array_column(array_filter($transitions, fn($t) => empty($t['requiresApproval'])), 'fromItemId'); + +$editTimeEntry = null; +if ($editId !== null) { + foreach ($timeEntries as $timeEntry) { + if ($timeEntry['id'] === $editId) { + $editTimeEntry = $timeEntry; + break; + } + } + // Fremder/ungültiger/nicht mehr bearbeitbarer Eintrag - Own-Scope-Filterung passiert serverseitig + // in omsorgcore_time_entries_list, hier nur zusätzlich: nicht (mehr) bearbeitbar zurück zu "Neuer Eintrag". + if ($editTimeEntry === null || empty($editTimeEntry['isEditableByOwner'])) { + $editId = null; + $editTimeEntry = null; + } +} + +function order_label(array $order): string +{ + return substr($order['id'], 0, 8) . ' (' . ($order['requiredQualification'] ?? 'Auftrag') . ')'; +} + +layout_start('Zeiterfassung – Mitarbeiter-App', 'stundenerfassung'); +?> +

Zeiterfassung

+ +
+
+

Meine Einträge

+ + +

Noch keine Zeiterfassung angelegt.

+ + +
+ +
+ Uhr, +
+ +
Kommentar:
+ +
+ + Bearbeiten + + +
+ + + + +
+ +
+
+ + +
+ +
+

+ +

+ + +
Eintrag wurde angelegt.
+ + + +
Eintrag wurde aktualisiert.
+ + + +
Eintrag wurde eingereicht.
+ + + +
+ + + +
+ + + + + + + + + + + + + +
+

+ Abbrechen +

+ +
+ + + + + + + + + + + + +
+ +

Keine Berechtigung, eine Zeiterfassung anzulegen.

+ +
+
+ $_POST['type'] ?? '', + 'startDate' => $_POST['start_date'] ?? '', + 'endDate' => $_POST['end_date'] ?? '', + 'reason' => trim($_POST['reason'] ?? ''), + 'substitute' => trim($_POST['substitute'] ?? ''), + 'note' => trim($_POST['note'] ?? ''), + ]; + + if ($payload['type'] === '' || $payload['startDate'] === '' || $payload['endDate'] === '') { + $error = 'Art, Beginn und Ende sind erforderlich.'; + $editId = $mode === 'edit' ? ($_POST['absence_id'] ?? null) : null; + } elseif ($payload['endDate'] < $payload['startDate']) { + $error = 'Das Ende darf nicht vor dem Beginn liegen.'; + $editId = $mode === 'edit' ? ($_POST['absence_id'] ?? null) : null; + } elseif ($mode === 'edit') { + if (!$canEdit) { + // Server lehnt das ohnehin über [RequirePermission(Absences, Edit)] ab - dieser Zweig + // greift nur, wenn jemand das Formular manuell postet, obwohl es clientseitig gar nicht + // angezeigt wurde. + $error = 'Keine Berechtigung, diesen Antrag zu bearbeiten.'; + } else { + $absenceId = $_POST['absence_id'] ?? ''; + $result = omsorgcore_absences_update(_omsorgcore_config(), $_SESSION['omsorgcore_access_token'], $absenceId, $payload); + + if ($result['ok']) { + header('Location: urlaubsantrag.php?updated=1'); + exit; + } + + $editId = $absenceId; + $error = $result['status'] === 403 + ? 'Keine Berechtigung, diesen Antrag zu bearbeiten.' + : (is_string($result['data'] ?? null) ? $result['data'] : 'Antrag konnte nicht gespeichert werden.'); + } + } elseif (!$canCreate) { + $error = 'Keine Berechtigung, einen Antrag zu stellen.'; + } else { + $result = omsorgcore_absences_create(_omsorgcore_config(), $_SESSION['omsorgcore_access_token'], $payload); + + if ($result['ok']) { + $success = true; + } elseif ($result['status'] === 403) { + $error = 'Keine Berechtigung, einen Antrag zu stellen.'; + } else { + $error = is_string($result['data'] ?? null) ? $result['data'] : 'Antrag konnte nicht gespeichert werden.'; + } + } +} elseif (isset($_GET['edit'])) { + $editId = $_GET['edit']; +} + +$listResult = omsorgcore_absences_list(_omsorgcore_config(), $_SESSION['omsorgcore_access_token']); +$absences = $listResult['ok'] ? ($listResult['data']['items'] ?? []) : []; + +$editAbsence = null; +if ($editId !== null) { + foreach ($absences as $absence) { + if ($absence['id'] === $editId) { + $editAbsence = $absence; + break; + } + } + // Fremder/ungültiger/schon entschiedener Antrag - Own-Scope-Filterung passiert serverseitig + // in omsorgcore_absences_list, hier nur zusätzlich: nicht (mehr) bearbeitbar zurück zu "Neuer Antrag". + if ($editAbsence === null || $editAbsence['status'] !== $initialStatus) { + $editId = null; + $editAbsence = null; + } +} + +layout_start('Urlaub & Abwesenheit – Mitarbeiter-App', 'urlaubsantrag'); +?> +

Urlaub & Abwesenheit

+ +
+
+

Meine Anträge

+ + +

Noch keine Anträge eingereicht.

+ + +
+ +
+ +
+ +
Kommentar:
+ + + + +
+ + +
+ +
+

+ +

+ + +
Antrag wurde eingereicht.
+ + + +
Antrag wurde aktualisiert.
+ + + +
+ + + +
+ + + + + + + + + + +
+

+ Abbrechen +

+ +
+ + + + + + + + + +
+ +

Keine Berechtigung, einen Antrag zu stellen.

+ +
+
+Client.cjs`-Datei, analog zu den Controllern in `omsorgCore` — siehe `main.cjs`s `api:get`/`api:post`-Proxy für Ressourcen ohne eigene Client-Datei. -- **Login + Session:** `main.cjs` hält den Access-Token nur im Speicher, verschlüsselt den Refresh-Token via `safeStorage` und speichert ihn unter `app.getPath('userData')/session.enc`. Silent Refresh läuft per Timer (kurz vor Ablauf) und reaktiv bei 401 (z. B. nach Laptop-Standby). IPC-Handler: `auth:login`, `auth:logout`, `auth:getSession`, `api:get`/`api:post` (authentifizierter Proxy). Kein Token verlässt je den Main-Prozess in Richtung Renderer. -- `electron/preload.cjs` — exponiert `window.omsorg` (contextBridge) mit `auth.{login,logout,getSession,onSessionChanged}`, `api.{get,post}` und den ressourcenspezifischen Namespaces (`employees`, `facilities`, `facilityContacts`, `users`, `roles`, `auditLog`, `valueLists`). Renderer darf nie direkt auf Node/`fs` zugreifen — immer über diese Brücke. -- `src/main.jsx` — Renderer-Einstiegspunkt, wrappt `App` in `AuthProvider` (`src/app/AuthContext.jsx`) und rendert in `#root`. +- **React 18** + **Vite**, reine Browser-SPA (kein Electron, kein Node-Zugriff aus dem Renderer), reines JSX (kein TS). +- `src/api/` — Adapterschicht zu `omsorgCore` (HTTP), ersetzt das frühere `electron/backend/*.cjs` + `electron/main.cjs`/`preload.cjs`-IPC 1:1 als reines Browser-JS: `config.js` (einzige Stelle mit der Backend-URL, Default `http://localhost:5245`, überschreibbar per Vite-Env `VITE_OMSORG_CORE_URL`), `apiClientHelpers.js` (`configFor`/`callApi`-Wrapper um den generierten `omsorgcore-client-ts`-Client, setzt `credentials:"include"` für die HttpOnly-Refresh-Cookie), `session.js` (Access-Token nur als Modul-Variable im Speicher, Silent-Refresh-Timer, `withAuthRetry`, `onSessionChanged`-Listener — Browser-Ersatz für den früheren Session-State im Electron-Hauptprozess, siehe "Login + Session" unten), `authApi.js` (`login`/`refresh`/`logout`/`me`/... gegen `/api/auth/*`), `genericApi.js` (Ersatz für den früheren `api:get`/`api:post`-IPC-Proxy, für Ressourcen ohne eigene `Api.js`-Datei, aktuell `/api/admin/sessions*`/`/api/admin/email/*`), je eine `Api.js`-Datei pro Ressource (`employeesApi.js`, `facilitiesApi.js`, `facilityContactsApi.js`, `facilityQualificationRatesApi.js`, `ordersApi.js`, `absencesApi.js`, `timeEntriesApi.js`, `usersApi.js`, `rolesApi.js`, `auditLogApi.js`, `valueListsApi.js`, `trashApi.js`, `contractsApi.js`, `documentsApi.js`, analog zu den Controllern in `omsorgCore`), `index.js` (`buildOmsorgApi()` — baut das komplette `window.omsorg`-Objekt zusammen, wrapped jede Ressourcenfunktion mit `session.withAuthRetry`). +- **Login + Session:** Der Refresh-Token liegt als **HttpOnly-Secure-Cookie** (von `omsorgCore`s `AuthController` gesetzt, `Path=/api/auth`) beim Server — nie per JS lesbar, kein Electron-`safeStorage` mehr nötig, dafür braucht `omsorgCore` CORS mit `AllowCredentials()` (siehe `omsorgCore/CLAUDE.md`, Abschnitt "Auth-Flow"). Der Access-Token lebt nur als Modul-Variable in `src/api/session.js` (verschwindet bei Tab-Reload/-Schließen) — `session.bootstrapSession()` läuft beim App-Start (`src/main.jsx`) und stellt die Session über die noch gültige Cookie per `POST /api/auth/refresh` (ohne Body, die Cookie geht automatisch mit) wieder her, damit ein Reload nicht zum erneuten Login zwingt. Silent Refresh läuft zusätzlich per Timer (kurz vor Ablauf) und reaktiv bei 401. `window.omsorg.auth` (`auth.login`, `auth.logout`, `auth.getSession`, `auth.onSessionChanged`) bildet dieselbe Oberfläche wie früher über IPC ab, nur als direkte Funktionsaufrufe. +- `src/main.jsx` — Renderer-Einstiegspunkt, setzt `window.omsorg = buildOmsorgApi()` (aus `src/api/index.js`, ersetzt das frühere `contextBridge.exposeInMainWorld`), wrappt `App` in `AuthProvider` (`src/app/AuthContext.jsx`) und rendert in `#root`. - `src/app/AuthContext.jsx` — React-Context um `window.omsorg.auth`, stellt `useAuth()` mit `{ isAuthenticated, user, isLoading, login, logout }` bereit. - `src/app/app.jsx` — gated zuerst auf Auth (`isLoading` → Ladehinweis, `!isAuthenticated` → `src/modules/auth/LoginPage.jsx`), danach Top-Level-Router: hält `activePage` als lokalen State (kein Routing-Framework), switcht zwischen Modulen. Noch nicht implementierte Module rendern `PlaceholderPage`. - `src/layouts/AppLayout.jsx` — Grundlayout: `Sidebar` + `Header` + `main.page-content`. - `src/components/` — geteilte UI: `Sidebar`, `Header`, `OmsorgCard`, und `components/ui/` (`OmsorgButton`, `OmsorgBadge`, `OmsorgStatCard`). -- `src/modules//` — ein Ordner pro Fachmodul, z. B. `modules/home/` (HomePage, HomeStats, ContractWidget), `modules/employees/` (EmployeesPage, EmployeeDetailPanel, EmployeeTabs), `modules/facilities/` (FacilitiesPage, FacilityDetailPanel, FacilityForm, FacilityContactsList/FacilityContactForm für die Ansprechpartner-Unterliste (FR-EIN-2) — Referenz war 1:1 `modules/employees/`, ohne Tabs, da noch keine weiteren Unterobjekte wie Dokumente/Verträge existieren), `modules/debug/` (`DebugSessionsPage.jsx`), `modules/settings/` (`SettingsPage.jsx`, `RolesPanel.jsx`, `RolePermissionMatrix.jsx`, `UserOverridesPanel.jsx`, `permissionOptions.js`). Neue Module folgen diesem Muster: eigener Ordner unter `src/modules/`, Einstiegskomponente `Page.jsx`. -- **Rollen- & Rechteverwaltung (`modules/settings/`):** Admin-UI unter dem Sidebar-Tab "Einstellungen" (`ModuleType.UserManagement`). `SettingsPage.jsx` schaltet zwischen drei Tabs: `RolesPanel.jsx` (Rollen anlegen via `window.omsorg.roles.create`, Auswahl öffnet `RolePermissionMatrix.jsx` — Checkbox-Matrix `ModuleType`×`PermissionAction`, speichert über `window.omsorg.roles.updatePermissions`), `UserOverridesPanel.jsx` (Nutzerauswahl über `window.omsorg.users.list`, individuelle `UserPermissionOverride`-Ausnahmen je Nutzer via `window.omsorg.users.{listPermissionOverrides,addPermissionOverride,deletePermissionOverride}`) und `StatusManagementPanel.jsx` ("Status-Verwaltung" — Mitarbeiterstatus, Beschäftigungsart, CRM-Status, Einrichtungstyp, Vertragstyp/-status, Auftragsstatus als admin-editierbare Auswahllisten über `window.omsorg.valueLists.*`; Löschen eines Werts wird serverseitig verweigert, solange er noch irgendwo gesetzt ist — Details: `omsorgCore/CLAUDE.md`, Abschnitt "Konfigurierbare Auswahllisten"). `ModuleType`/`PermissionAction`/`PermissionEffect`-Werte + deutsche Labels sind in `permissionOptions.js` hart codiert (kein gemeinsames Enum-Modul zwischen Backend und Frontend, analog `navPermissions.js`). Backend-Details: `omsorgCore/CLAUDE.md`, Abschnitt "Rechtesystem". -- **Audit-Log (`modules/auditLog/AuditLogPage.jsx`):** rein lesende, paginierte Liste (`OmsorgPagination`, `PAGE_SIZE = 50`) über `window.omsorg.auditLog.list({ page, pageSize })` → `electron/backend/auditLogClient.cjs` → `GET /api/audit-log` in `omsorgCore`. Keine Bearbeiten-/Löschen-Aktionen (Audit-Einträge sind unveränderlich, es gibt serverseitig keine entsprechenden Endpoints). Im `Sidebar.jsx`-Menü nur sichtbar, wenn `hasPermission("AuditLog", "View")` (`navPermissions.js`, `NAV_MODULES["Audit-Log"] = "AuditLog"`) — per Default nur die Rolle Geschäftsführung (Backend-Details: `omsorgCore/CLAUDE.md`, Abschnitt "Audit-Log"). `ModuleType.AuditLog` ist zusätzlich in `modules/settings/permissionOptions.js`s `MODULE_OPTIONS` eingetragen, damit Geschäftsführung die Berechtigung über die Rollen-Rechte-Matrix auch anderen Rollen zuweisen kann. -- **Debug-Sicht (`modules/debug/DebugSessionsPage.jsx`):** listet aktive Sessions (`GET /api/admin/sessions` über den generischen `api:get`-Proxy), erlaubt Einzel- und Gesamt-Widerruf (`POST /api/admin/sessions/{id}/revoke`, `POST /api/admin/sessions/revoke-all`) — invalidiert serverseitig sofort alle betroffenen Access-Tokens (Details: `omsorgCore/CLAUDE.md` Abschnitt "Session-Killswitch"). Im `Sidebar.jsx`-Menü nur sichtbar, wenn `useAuth().hasPermission("UserManagement", "View")` — dieselbe granulare Rechteprüfung, die serverseitig über `RequirePermission(ModuleType.UserManagement, ...)` auf `AdminSessionsController` erzwungen wird (kein Rollennamen-Vergleich mehr, siehe "Rechtesystem im Client" unten). -- **Rechtesystem im Client:** Rechte kommen nicht aus dem JWT (das trägt nur `sub`/`name`/`role`/`sst`). `main.cjs` lädt nach jedem Login/Refresh zusätzlich `GET /api/auth/me` (`electron/backend/authClient.cjs#me`) und ersetzt `session.user` durch `{ username, role, permissions }` — `permissions` ist die vom Backend aufgelöste Liste aus `PermissionService.GetGrantedPermissionsAsync` (Rollen-Default + Overrides, je `{ module, action }` als String). `AuthContext.jsx` stellt darauf `hasPermission(module, action)` bereit; UI-Komponenten prüfen darüber, nie über `user.role` direkt. +- `src/modules//` — ein Ordner pro Fachmodul, z. B. `modules/home/` (HomePage, HomeStats, ContractWidget), `modules/employees/` (EmployeesPage, EmployeeDetailPanel, EmployeeTabs), `modules/facilities/` (FacilitiesPage, FacilityDetailPanel, FacilityForm, FacilityContactsList/FacilityContactForm für die Ansprechpartner-Unterliste (FR-EIN-2), FacilityQualificationRatesList/FacilityQualificationRateForm für die qualifikationsabhängigen Preise (FR-EIN-4) — Referenz war 1:1 `modules/employees/`, ohne Tabs, da noch keine weiteren Unterobjekte wie Dokumente/Verträge existieren), `modules/debug/` (`DebugSessionsPage.jsx`), `modules/settings/` (`SettingsPage.jsx`, `UsersPanel.jsx`, `ResetUserPasswordDialog.jsx`, `RolesPanel.jsx`, `RolePermissionMatrix.jsx`, `UserOverridesPanel.jsx`, `StatusManagementPanel.jsx`, `permissionOptions.js`). Neue Module folgen diesem Muster: eigener Ordner unter `src/modules/`, Einstiegskomponente `Page.jsx`. +- **Benutzer-, Rollen- & Rechteverwaltung (`modules/settings/`):** Admin-UI unter dem Sidebar-Tab "Einstellungen" — sichtbar, sobald `View` auf mindestens einem von `Users`/`UserManagement`/`Configuration` vorliegt (`navPermissions.js`, `SETTINGS_MODULES`). `SettingsPage.jsx` filtert vier mögliche Tabs jeweils einzeln nach ihrem Modul (`hasPermission(tab.module, "View")`), ein Nutzer sieht also nur die Tabs, für die er tatsächlich berechtigt ist — kein pauschales Alles-oder-nichts mehr (Hintergrund/Historie: `omsorgCore/CLAUDE.md`, Abschnitt "Rechtesystem", "Drei getrennte Admin-Rechte"): + - `UsersPanel.jsx` (Modul `Users`) — Benutzerkonten sehen, Rolle ändern, aktivieren/deaktivieren (`window.omsorg.users.{list,update}`), Passwort zurücksetzen über `ResetUserPasswordDialog.jsx` (`window.omsorg.users.resetPassword`, gleiches Invite/Direct-Formular wie beim Account-Anlegen in `modules/employees/CreateUserAccountDialog.jsx`). + - `RolesPanel.jsx`/`RolePermissionMatrix.jsx` (Modul `UserManagement`) — Rollen anlegen via `window.omsorg.roles.create`, Auswahl öffnet die Checkbox-Matrix `ModuleType`×`PermissionAction`, speichert über `window.omsorg.roles.updatePermissions`. + - `UserOverridesPanel.jsx` (Modul `UserManagement`) — Nutzerauswahl über `window.omsorg.users.list` (braucht dafür zusätzlich `Users`/`View`, siehe `omsorgCore/CLAUDE.md`), individuelle `UserPermissionOverride`-Ausnahmen je Nutzer via `window.omsorg.users.{listPermissionOverrides,addPermissionOverride,deletePermissionOverride}`. + - `StatusManagementPanel.jsx` (Modul `Configuration`) — Mitarbeiterstatus, Beschäftigungsart, CRM-Status, Einrichtungstyp, Vertragstyp/-status, Auftragsstatus als admin-editierbare Auswahllisten über `window.omsorg.valueLists.*`; Löschen eines Werts wird serverseitig verweigert, solange er noch irgendwo gesetzt ist — Details: `omsorgCore/CLAUDE.md`, Abschnitt "Konfigurierbare Auswahllisten". + + `ModuleType`/`PermissionAction`/`PermissionEffect`-Werte + deutsche Labels sind in `permissionOptions.js` hart codiert (kein gemeinsames Enum-Modul zwischen Backend und Frontend, analog `navPermissions.js`). Backend-Details: `omsorgCore/CLAUDE.md`, Abschnitt "Rechtesystem". +- **Audit-Log (`modules/auditLog/AuditLogPage.jsx`):** rein lesende, paginierte Liste (`OmsorgPagination`, `PAGE_SIZE = 50`) über `window.omsorg.auditLog.list({ page, pageSize })` → `src/api/auditLogApi.js` → `GET /api/audit-log` in `omsorgCore`. Keine Bearbeiten-/Löschen-Aktionen (Audit-Einträge sind unveränderlich, es gibt serverseitig keine entsprechenden Endpoints). Im `Sidebar.jsx`-Menü nur sichtbar, wenn `hasPermission("AuditLog", "View")` (`navPermissions.js`, `NAV_MODULES["Audit-Log"] = "AuditLog"`) — per Default nur die Rolle Geschäftsführung (Backend-Details: `omsorgCore/CLAUDE.md`, Abschnitt "Audit-Log"). `ModuleType.AuditLog` ist zusätzlich in `modules/settings/permissionOptions.js`s `MODULE_OPTIONS` eingetragen, damit Geschäftsführung die Berechtigung über die Rollen-Rechte-Matrix auch anderen Rollen zuweisen kann. +- **Debug-Sicht (`modules/debug/DebugSessionsPage.jsx`):** listet aktive Sessions (`GET /api/admin/sessions` über den generischen `api:get`-Proxy, `src/api/genericApi.js`), erlaubt Einzel- und Gesamt-Widerruf (`POST /api/admin/sessions/{id}/revoke`, `POST /api/admin/sessions/revoke-all`) — invalidiert serverseitig sofort alle betroffenen Access-Tokens (Details: `omsorgCore/CLAUDE.md` Abschnitt "Session-Killswitch"). Im `Sidebar.jsx`-Menü nur sichtbar, wenn `useAuth().hasPermission("UserManagement", "View")` — dieselbe granulare Rechteprüfung, die serverseitig über `RequirePermission(ModuleType.UserManagement, ...)` auf `AdminSessionsController` erzwungen wird (kein Rollennamen-Vergleich mehr, siehe "Rechtesystem im Client" unten). +- **Rechtesystem im Client:** Rechte kommen nicht aus dem JWT (das trägt nur `sub`/`name`/`role`/`sst`). `src/api/session.js` lädt nach jedem Login/Refresh zusätzlich `GET /api/auth/me` (`src/api/authApi.js#me`) und ersetzt `session.user` durch `{ username, role, permissions }` — `permissions` ist die vom Backend aufgelöste Liste aus `PermissionService.GetGrantedPermissionsAsync` (Rollen-Default + Overrides, je `{ module, action, scope }`). `AuthContext.jsx` stellt darauf `hasPermission(module, action)` bereit; UI-Komponenten prüfen darüber, nie über `user.role` direkt. Zusätzlich `getScope(module, action)` (liefert `"All"`/`"Own"`/`null`) für rein kosmetische UI-Anpassungen bei Own-Scope (z. B. Suchfeld ausblenden) — die eigentliche "nur eigene Daten"-Durchsetzung passiert serverseitig (`omsorgCore/CLAUDE.md`, Abschnitt "Datenebenen-Scope"), aktuell für die Module Mitarbeiter/Verträge/Abwesenheiten relevant. Admin-Verwaltung dieser dritten Matrix-Dimension: `modules/settings/RolePermissionMatrix.jsx` (3-Zustands-Auswahl je Zelle für `Employees`/`Contracts`/`Absences`, sonst weiterhin Checkbox) und `UserOverridesPanel.jsx` (Scope-Auswahl im Override-Formular), Optionen/Labels in `permissionOptions.js` (`SCOPE_OPTIONS`, `SCOPE_CAPABLE_MODULES`). - **Verbindliche Regel — UI folgt den Rechten, für jedes Modul:** Jeder Sidebar-Tab und jede Aktion (Anlegen/Bearbeiten/Löschen/...) muss über `hasPermission(module, action)` gegated werden — das ist kein Sonderfall für Mitarbeiter, sondern das Standardmuster für jedes neue Modul (mindestens `View` fürs Sichtbarsein des Tabs, `Create`/`Edit`/... für einzelne Aktionen darin). Referenzimplementierung: `src/modules/employees/EmployeesPage.jsx` (`canCreate = hasPermission("Employees", "Create")`) und `src/modules/employees/EmployeeDetailPanel.jsx` (`canEdit = hasPermission("Employees", "Edit")`). - - Die Zuordnung Sidebar-Tab → `ModuleType` steht zentral in `src/app/navPermissions.js` (`NAV_MODULES`) und wird sowohl von `Sidebar.jsx` (blendet nicht erlaubte Tabs aus) als auch von `app.jsx` (fällt auf `"Home"` zurück, falls der aktive Tab durch eine Rechteänderung nicht mehr erlaubt ist) genutzt — neue Zuordnungen nur dort eintragen, nicht duplizieren. - - Aktuelles Mapping: Mitarbeiter→`Employees`, Kunden→`Facilities`, Disposition→`Orders`, Rechnungen→`Invoices`, Controlling→`Controlling`, Einstellungen→`UserManagement`, Debug→`UserManagement`, Audit-Log→`AuditLog`. Home hat kein Modul und ist immer sichtbar. Kalkulation und Fahrzeuge sind reine `PlaceholderPage`-Stubs ohne Fachlogik und haben (noch) kein passendes `ModuleType` — bewusst ungegated, bis ein echtes Modul dahintersteht; dann Eintrag in `navPermissions.js` ergänzen (ggf. mit neuem `ModuleType`-Wert in `omsorgCore`, additiv, keine Migration nötig). + - Die Zuordnung Sidebar-Tab → `ModuleType` steht zentral in `src/app/navPermissions.js` (`NAV_MODULES`) und wird sowohl von `Sidebar.jsx` (blendet nicht erlaubte Tabs aus) als auch von `app.jsx` (fällt auf `"Home"` zurück, falls der aktive Tab durch eine Rechteänderung nicht mehr erlaubt ist) genutzt — neue Zuordnungen nur dort eintragen, nicht duplizieren. Zwei Tabs hängen nicht an einem einzelnen Modul, sondern an einer Liste (`isNavItemVisible` prüft dafür `Array.some(...)` statt eines einzelnen Lookups): "Papierkorb" (`TRASH_MODULES`, sichtbar bei `Recover` auf irgendeinem Objekt mit Soft-Delete) und "Einstellungen" (`SETTINGS_MODULES = [Users, UserManagement, Configuration]`, sichtbar bei `View` auf irgendeinem der drei — welcher Tab innerhalb der Seite dann tatsächlich erscheint, entscheidet `SettingsPage.jsx` separat pro Tab, siehe oben). + - Aktuelles Mapping: Mitarbeiter→`Employees`, Kunden→`Facilities`, Disposition→`Orders`, Abwesenheiten→`Absences`, Zeiterfassung→`TimeEntries`, Rechnungen→`Invoices`, Controlling→`Controlling`, Debug→`UserManagement`, Audit-Log→`AuditLog`, Einstellungen→ siehe `SETTINGS_MODULES` oben. Home hat kein Modul und ist immer sichtbar. Kalkulation und Fahrzeuge sind reine `PlaceholderPage`-Stubs ohne Fachlogik und haben (noch) kein passendes `ModuleType` — bewusst ungegated, bis ein echtes Modul dahintersteht; dann Eintrag in `navPermissions.js` ergänzen (ggf. mit neuem `ModuleType`-Wert in `omsorgCore`, additiv, keine Migration nötig). - `src/style.css` — einziges Stylesheet, kein CSS-Framework. ## Wichtige Startbefehle ```bash npm install -npm run dev # Vite-Devserver + Electron parallel (concurrently/wait-on) -npm run web # nur Vite, im Browser statt Electron -npm run start # nur Electron (erwartet gebauten dist/) +npm run dev # Vite-Devserver, http://127.0.0.1:5173 im Browser öffnen ``` +`omsorgCore` muss dafür separat laufen (siehe `omsorgCore/CLAUDE.md`) und dessen `Cors:AllowedOrigins` `http://127.0.0.1:5173` enthalten (per Default in `appsettings.Development.json` gesetzt). + ## Aktueller Ist-Stand (siehe auch `REQUIREMENTS.md`) -- Vollständig: `HomePage`, `EmployeesPage` (Grundgerüst; Daten kommen über `electron/backend/employeesClient.cjs` echt aus `omsorgCore`, keine hartcodierten Beispieldaten mehr für dieses Modul), `FacilitiesPage` (Sidebar-Tab "Kunden", `ModuleType.Facilities`; Grundgerüst analog `EmployeesPage`, Daten über `electron/backend/facilitiesClient.cjs` echt aus `omsorgCore`; deckt Stammdaten **und** eine Ansprechpartner-Liste je Einrichtung ab (`FacilityContactsList` im `FacilityDetailPanel`, über `electron/backend/facilityContactsClient.cjs` gegen `/api/facilities/{id}/contacts`, Anlegen/Bearbeiten, kein Löschen) — CRM-Pipeline-Automatik/Konditionen aus `REQUIREMENTS.md` FR-EIN-3..5 sind noch offen), Login-Screen + persistente Session gegen `omsorgCore`, `SettingsPage` (Rollen-Rechte-Matrix + User-Permission-Overrides, siehe "Rollen- & Rechteverwaltung" oben), `AuditLogPage` (siehe "Audit-Log" oben). -- Alle anderen Menüpunkte (Disposition, Kalkulation, Fahrzeuge, Rechnungen, Controlling) sind reine `PlaceholderPage`-Platzhalter in `app.jsx`. -- **Auth, Mitarbeiter und Kunden/Einrichtungen sprechen bereits gegen `omsorgCore`** (siehe `employeesClient.cjs`/`facilitiesClient.cjs`, IPC-Handler `employees:list/create/update` und `facilities:list/create/update` in `main.cjs`). Die übrigen Fachmodule (Disposition, Kalkulation, Fahrzeuge, Rechnungen, Controlling) haben noch **gar keine** Datenhaltung — weder lokal noch über `omsorgCore` — sondern sind reine `PlaceholderPage`-Stubs. Nächster Schritt: weitere Fachmodule nach und nach nach demselben Muster (`electron/backend/Client.cjs` + `omsorgCore`-Endpunkte) umsetzen, analog zum Mitarbeiter-/Kunden-Vorbild. -- Keine Verbindung zu `omsorgWeb`/MySQL — Mitarbeiterdaten hier sind komplett getrennt von denen in OMSORG Connect. Nicht durch neue Kopplungen/Workarounds "beheben"; die eigentliche Lösung ist die gemeinsame Datenbasis in `omsorgCore`. +- Vollständig: `HomePage`, `EmployeesPage` (Grundgerüst; Daten kommen über `src/api/employeesApi.js` echt aus `omsorgCore`, keine hartcodierten Beispieldaten mehr für dieses Modul), `FacilitiesPage` (Sidebar-Tab "Kunden", `ModuleType.Facilities`; Grundgerüst analog `EmployeesPage`, Daten über `src/api/facilitiesApi.js` echt aus `omsorgCore`; deckt Stammdaten **und** eine Ansprechpartner-Liste je Einrichtung ab (`FacilityContactsList` im `FacilityDetailPanel`, über `src/api/facilityContactsApi.js` gegen `/api/facilities/{id}/contacts`, Anlegen/Bearbeiten, kein Löschen); CRM-Pipeline (FR-EIN-3) und Konditionen (FR-EIN-4, neues "Konditionen"-Fieldset in `FacilityForm.jsx` + `FacilityQualificationRatesList` im `FacilityDetailPanel`, über `src/api/facilityQualificationRatesApi.js` gegen `/api/facilities/{id}/qualification-rates`) sind jetzt ebenfalls umgesetzt — FR-EIN-5 (konsolidierte Historie) bleibt offen), Login-Screen + persistente Session gegen `omsorgCore`, `SettingsPage` (Benutzerübersicht + Rollen-Rechte-Matrix + User-Permission-Overrides + Status-Verwaltung, siehe "Benutzer-, Rollen- & Rechteverwaltung" oben), `AuditLogPage` (siehe "Audit-Log" oben), `OrdersPage` (Sidebar-Tab "Disposition", `ModuleType.Orders`; FR-EM-1, Grundgerüst 1:1 analog `FacilitiesPage`, Daten über `src/api/ordersApi.js` echt aus `omsorgCore`, volles CRUD (`OrdersPage`/`OrderDetailPanel`/`Create-`/`EditOrderDialog`/`OrderForm.jsx`); Pflichtfelder Einrichtung/Ansprechpartner/Zeitraum/Qualifikation/Schichtart/Anzahl Mitarbeiter/Konditionen/Priorität abgedeckt — Ansprechpartner-Dropdown lädt beim Wechsel der Einrichtung deren Kontakte per `window.omsorg.facilityContacts.list(facilityId)` nach; Qualifikation/Schichtart/Priorität/Status als admin-editierbare `ValueList`s über `useValueListItems`; Statusfeld ist nur im Bearbeiten-Formular sichtbar und zeigt dabei nur die laut `/api/value-lists/OrderStatus/transitions` erlaubten Zielstatus, analog zum CRM-Status-Dropdown bei Facilities). FR-EM-2 (Dashboard-Sichtbarkeit der Statuspipeline): `modules/home/OrderStatusWidget.jsx` in `HomePage.jsx`, listet die aktuelle Auftragsanzahl je `OrderStatus`-Wert (client-seitig aus `window.omsorg.orders.list({ pageSize: 200 })` aggregiert, kein eigener Stats-Endpoint), rechtegegated über `hasPermission("Orders","View")`, UI-Muster 1:1 von `FollowUpWidget.jsx` übernommen. `AbsencesPage` (Sidebar-Tab "Abwesenheiten", `ModuleType.Absences`; Datenbasis für FR-CON-1/FR-EM-3, Backend-Details `omsorgCore/CLAUDE.md` "Abwesenheits-/Urlaubs-/Krankmeldungsanträge"): Liste+Filter (Status/Art) + `AbsenceDetailPanel` mit Genehmigen/Ablehnen-Aktionen (`hasPermission("Absences","Approve")`, `window.omsorg.absences.decide(id, { status, adminNote })`) und Bearbeiten (`hasPermission("Absences","Edit")`, `EditAbsenceDialog.jsx`/`AbsenceForm.jsx` nach dem `OrderForm.jsx`-Muster, `window.omsorg.absences.update(id, payload)`) — Bearbeiten-Button nur sichtbar, solange der Status noch der initiale ist (`statusItems.find(i => i.isInitial)?.value` aus `useValueListItems("AbsenceStatus")` — nicht der Literal `"Eingereicht"`, umbenennbar über die Status-Verwaltung, ohne dass diese Komponente angefasst werden muss; serverseitig ohnehin erzwungen, siehe `omsorgCore/CLAUDE.md`). **Genehmigen/Ablehnen ist bewusst jederzeit möglich, nicht nur solange der Antrag noch im initialen Status ist** — der `Decide`-Endpoint hat serverseitig keine Statusprüfung (anders als `Update`), damit eine versehentliche Entscheidung korrigierbar bleibt; die UI zeigt bei bereits entschiedenen Anträgen zusätzlich einen Hinweistext ("Bereits entschieden (...) — hier lässt sich die Entscheidung bei Bedarf noch ändern"). Bewusst **kein** Anlegen-Dialog hier, Anträge stellt ausschließlich der Außendienst über `omsorgWeb/mitarbeiter-app` (`pages/urlaubsantrag.php`), `omsorgapp` prüft/bearbeitet/entscheidet nur. `TimeEntriesPage` (Sidebar-Tab "Zeiterfassung", `ModuleType.TimeEntries`; FR-ZE-1/FR-ZE-2, Backend-Details `omsorgCore/CLAUDE.md` "Zeiterfassung"): Liste+Statusfilter + `TimeEntryDetailPanel` mit dynamischen Büro-Entscheidungs-Buttons (`hasPermission("TimeEntries","Approve")`, aus `window.omsorg.valueLists.listTransitions("TimeEntryStatus")` gefiltert auf `requiresApproval===true`-Kanten ab dem aktuellen Status, `window.omsorg.timeEntries.decide(id, { statusId, adminNote })` — Muster 1:1 von `OrderForm.jsx`s Statuswechsel-Filterung übernommen, nicht von `AbsenceDetailPanel`, da hier eine echte Mehrstufen-Pipeline statt einer binären Entscheidung vorliegt) und Bearbeiten (`hasPermission("TimeEntries","Edit")`, `EditTimeEntryDialog.jsx`/`TimeEntryForm.jsx`, `window.omsorg.timeEntries.update(id, payload)`) — Bearbeiten-Button nur sichtbar, solange `timeEntry.isEditableByOwner` (direkt aus der `TimeEntryResponse`, nicht per Statuswert-Vergleich). Bewusst **kein** Anlegen-Dialog und **kein** `statusId`-Feld im Bearbeiten-Formular — Erfassung passiert in `omsorgWeb/mitarbeiter-app` (`pages/stundenerfassung.php`), Statuswechsel laufen ausschließlich über `submit` (dort) bzw. `decide` (hier). +- **Papierkorb (`modules/trash/TrashPage.jsx`):** tab-basierte Liste, ein Eintrag in `TABS` pro Objekt mit Soft-Delete (`window.omsorg.trash.{list,restore}`), inzwischen `employees`/`facilities`/`contracts`/`orders`/`facilityContacts`/`facilityQualificationRates`/`absences`/`timeEntries` — jeder Tab nur sichtbar mit `hasPermission(tab.module, "Recover")`. Neue Objekte mit Soft-Delete: Eintrag hier UND in `navPermissions.js`s `TRASH_MODULES` ergänzen (beide nötig, siehe oben). +- **Dokumente (FR-MA-3, `modules/employees/DocumentsList.jsx`/`UploadDocumentDialog.jsx`/`EditDocumentDialog.jsx`):** eigener Tab in der Personalakte (`EmployeeDetailPanel.jsx`), nur sichtbar mit `hasPermission("Documents","View")` (`EmployeeTabs.jsx` bekommt dafür einen `hiddenTabIds`-Prop). Liste gruppiert nach Kategorie (admin-editierbare `DocumentCategory`-Auswahlliste, siehe `omsorgCore/CLAUDE.md`), Buttons Hochladen/Bearbeiten/Herunterladen/Löschen je nach `Documents`-Rechten. Datei-Upload läuft über einen normalen ``; die Bytes gehen als `ArrayBuffer` (aus `file.arrayBuffer()`) direkt an `src/api/documentsApi.js#uploadDocument`, das daraus ein `Blob` für den generierten `DocumentsApi`-Multipart-Aufruf baut. **Download läuft bewusst NICHT über den generierten Client** (`apiDocumentsIdDownloadGetRaw` ist als `VoidApiResponse` generiert, verwirft den Response-Body) — `documentsApi.downloadDocument` macht dafür einen direkten `fetch` mit `Authorization`-Header und liefert einen `Blob` zurück; `src/api/index.js`s `documents.download` erzeugt daraus `URL.createObjectURL(blob)` + einen unsichtbaren ``-Klick (Browser-natives Herunterladen statt Electrons `dialog.showSaveDialog`), `documents.view` (für `DocumentViewerDialog.jsx`) reicht denselben Blob als `