diff --git a/REQUIREMENTS.md b/REQUIREMENTS.md index 5acd463..b1076b7 100644 --- a/REQUIREMENTS.md +++ b/REQUIREMENTS.md @@ -125,8 +125,10 @@ Umfasst alle drei Plattform-Ebenen: OMSORG Desktop, OMSORG Connect, OMSORG Backe |---|---|---|---|---| | FR-REC-1 | Bewerberpipeline: Interessent → Bewerbung → Erstkontakt → Vorstellung → Unterlagen → Angebot → Einstellung → geplanter Eintritt [Blueprint 3.2] | Sascha | Bewerberdatensatz mit Statuswechsel | ⬜ | | FR-REC-2 | Werbung/Empfehlung: Kanäle Indeed, Facebook, Instagram, Mitarbeiterempfehlung erfassbar [omsorg.md] | Sascha | Herkunft je Bewerber taggable, auswertbar | 🔶 (`pages/werben.php`/`actions/submit-werben.php` erfasst Mitarbeiterempfehlungen bereits; Kanaltracking für externe Werbung fehlt) | -| FR-REC-3 | Einrichtungsakquise: Lead anlegen → Ansprechpartner erfassen → Kontakt → Gesprächsnotiz → Ergebnis → automatische Wiedervorlage nach 14 Tagen [Blueprint 3.5] | Sascha | Wiedervorlage wird ohne manuellen Trigger vom System erzeugt | ⬜ | +| FR-REC-3 | Einrichtungsakquise: Lead anlegen → Ansprechpartner erfassen → Kontakt → Gesprächsnotiz → Ergebnis → automatische Wiedervorlage nach gestaffeltem Akquise-Rhythmus [Blueprint 3.5]. Empfohlener Rhythmus (fachliche Vorgabe, nicht Blueprint-Wortlaut): Tag 1 Erstanruf, Tag 1 E-Mail mit Kurzvorstellung, nach 10–14 Tagen zweiter Anruf, nach 4–6 Wochen erneuter Kontakt falls kein Bedarf bestand, danach regelmäßige Wiedervorlagen zum freundlichen In-Erinnerung-Bleiben | Sascha | Wiedervorlage wird ohne manuellen Trigger vom System erzeugt, mit dem zur jeweiligen Akquise-Stufe passenden Fristvorschlag (10–14 Tage nach Erstkontakt, 4–6 Wochen nach zweitem Kontakt ohne Bedarf, danach wiederkehrend) statt einer einzigen festen Frist | ⬜ | | FR-REC-4 | Sascha hat keinen Zugriff auf Rechnungen, Zahlungen, Gewinn, Margen, Controlling [Blueprint 6.2] | System | Rollenprüfung blockiert Zugriff serverseitig | ⬜ | +| FR-REC-5 | Kaltakquise vollständig im CRM abgebildet statt einer losen Telefonnummernliste: Lead anlegen, Ansprechpartner speichern, Telefonat dokumentieren (Gesprächsprotokoll je Kontaktversuch), automatische Wiedervorlage je Akquise-Stufe (siehe FR-REC-3), E-Mail-Versand aus dem CRM heraus (z. B. Kurzvorstellung am Erstkontakttag), Angebotsstatus je Lead | Sascha | Jeder Akquise-Schritt (Anruf, E-Mail, Gesprächsnotiz, Status-/Angebotswechsel) ist am Lead-Datensatz nachvollziehbar, ohne Werkzeug außerhalb der Software | ⬜ | +| FR-REC-6 | Akquise-Erfolgsmessung: Abschlussquote je Mitarbeiter, Dashboard mit täglichen Anrufen, Terminen und gewonnenen Kunden [omsorg.md] | Sascha, Sabina, Malik | Kennzahlen werden aus den dokumentierten Akquise-Aktivitäten (FR-REC-5) berechnet, nicht manuell gepflegt | ⬜ | ### 4.8 Controlling @@ -258,7 +260,7 @@ Für zukünftige Büromitarbeiter: Rolle als Vorlage, zusätzlich granular je Mo | Phase 2 – Stammdaten | FR-MA-1..5, FR-EIN-1..5 | Blockiert durch Phase 1 | | Phase 3 – Operatives Geschäft | FR-EM-1..5 | Blockiert durch Phase 1/2 | | Phase 4 – Zeit und Abrechnung | FR-ZE-1..4, FR-RE-1..4 | Teilbasis vorhanden (Connect-Upload), aber ohne Core nicht rechnungsfähig | -| Phase 5 – Recruiting und CRM | FR-REC-1..4 | Teilbasis vorhanden (Werben), CRM-Pipeline fehlt | +| Phase 5 – Recruiting und CRM | FR-REC-1..6 | Teilbasis vorhanden (Werben), CRM-Pipeline fehlt | | Phase 6 – OMSORG Connect | FR-CON-1..4 | 🔶 größtenteils vorhanden, Fahrtenbuch + Sync offen | | Phase 7 – Dashboard und Controlling | FR-DASH-1..4, FR-CTL-1..3, FR-OUT-1 | Teilbasis (`omsorgapp` Dashboard-Ansätze) vorhanden | diff --git a/omsorgCore/CLAUDE.md b/omsorgCore/CLAUDE.md index 0ff6791..c364fd1 100644 --- a/omsorgCore/CLAUDE.md +++ b/omsorgCore/CLAUDE.md @@ -68,6 +68,8 @@ 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. +**`ModuleType.EmployeeFacilityDistances`** (additiv hinzugefügt 2026-08-10) gated ausschließlich `MyFacilityDistancesController` (Selbstbedienung, siehe "Konditionen einer Einrichtung" unten) — bewusst getrennt von `Facilities`, damit der Außendienst seine eigene Fahrtstrecke pflegen kann, ohne Konditionen/CRM-Daten von Einrichtungen zu sehen. Nur `Außendienst` bekommt es im Basis-Seed (`Create`/`View`/`Edit`, Scope `Own`, siehe `DbSeeder.SeedBaseRolesAsync`); Büro-Rollen verwalten Distanzen weiterhin über `EmployeeFacilityDistancesController`/`Facilities`-Recht. + 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: @@ -87,6 +89,8 @@ Der Basis-Rollen-Seed (`DbSeeder.SeedBaseRolesAsync`, siehe unten) wurde bei die **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`). +**Rolle löschen (seit 2026-08-10):** `DELETE /api/roles/{id}` (`[RequirePermission(ModuleType.UserManagement, PermissionAction.Delete)]`) — Löschschutz analog zu `ValueListService.DeleteItemAsync`, aber ohne die generische `IValueListUsageChecker`-Abstraktion, da eine Rolle nur über genau einen Ownership-Anker referenziert wird (`User.RoleId`, echte FK statt Freitext): `RoleService.DeleteAsync` prüft über `IUserRepository.GetUserIdsByRoleAsync` (bereits für die Cache-Invalidierung bei Rechte-Änderungen vorhanden), ob noch Benutzer dieser Rolle zugewiesen sind, und lehnt mit `409` + Anzahl im Klartext ab (`DeleteRoleResult`/`DeleteRoleFailureReason`), statt zu löschen. Hard-Delete (kein Soft-Delete/Papierkorb) — `Role` ist `Entity`, nicht `AuditableEntity`, kein eigenständiges Core-Objekt mit Löschprotokoll-Anforderung. **Wichtig:** `DbSeeder.SeedBaseRolesAsync` legt eine der vier Basis-Rollen (Geschäftsführung/Disposition/Buchhaltung/Recruiting/Außendienst) bei fehlendem Namen bei jedem Start erneut an (idempotent pro Rollenname, siehe dort) — eine gelöschte Basis-Rolle taucht nach einem Neustart automatisch wieder auf, das ist bestehendes, dokumentiertes Verhalten, keine Neuerung dieses Schritts. Frontend: `RolesPanel.jsx` zeigt einen Löschen-Button je Rolle (nur mit `UserManagement`/`Delete`), zeigt die `409`-Fehlermeldung des Servers an statt sie zu verschlucken. + **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. @@ -160,7 +164,7 @@ Vollständig implementiert: `PasswordResetCode`-Entity + `PasswordResetService` - 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). `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. +- 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. `AddTravelCostModeAndEmployeeFacilityDistances` (`Facility.TravelCostMode`/`TravelCostPerKm` + Tabelle `employee_facility_distances`, siehe "Konditionen einer Einrichtung") ist erzeugt (`dotnet build`/`dotnet test` grün), aber noch nicht gegen eine echte Instanz verifiziert. ## Build- und Run-Befehle @@ -190,7 +194,7 @@ 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). **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). +- `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`), seit den Absences-/TimeEntries-/EmployeeFacilityDistances-Erweiterungen zusätzlich `Absences.{Create,View,Edit}`, `TimeEntries.{Create,View,Edit}` und `EmployeeFacilityDistances.{Create,View,Edit}` (alle Scope `Own`) — die Zahl "0" für Außendienst ist damit veraltet (jetzt 11 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 @@ -231,7 +235,10 @@ Dropdown-Werte, die früher als hartcodierte Arrays im `omsorgapp`-Frontend lebt 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). +- `BillingRate` (Verrechnungssatz, EUR/Std.), `NightSurchargePercent`/`SaturdaySurchargePercent`/`SundaySurchargePercent`/`HolidaySurchargePercent` (Zuschläge als **Prozent** auf den Verrechnungssatz, nicht als EUR-Betrag), `MinimumHours` (Mindeststunden je Einsatz), `BreakPolicy` (Pausenregelung, Freitext), `PaymentTermDays` (Zahlungsziel in Tagen), `IndividualAgreements` (Freitext). +- **Fahrtkosten (seit 2026-08-10 zwei Modi statt nur Pauschale):** `TravelCostMode` (`"Pauschale"` oder `"ProKilometer"`, Default `"Pauschale"`, im Controller gegen ein festes Literal-Array geprüft — **bewusst keine `ValueList`**, da die Rechnungserstellung später hart zwischen genau diesen zwei Fällen unterscheiden muss, ein dritter admin-hinzufügbarer Wert würde die künftige `FR-RE-1`-Berechnungslogik lautlos brechen, analog zur Begründung bei `AbsenceStatus.IsInitial`) entscheidet, welches Feld gilt: `TravelCostRate` (Pauschale je Einsatz, EUR) oder `TravelCostPerKm` (EUR/km) × `EmployeeFacilityDistance.DistanceKm`. Eine feste Facility-Distanz reicht nicht, weil jeder Mitarbeiter von einem anderen Wohnort anfährt — daher `EmployeeFacilityDistance` (`Facility`×`Employee` → `DistanceKm`, `AuditableEntity`) als **zweite** 1:n-Unterressource von Facility, exakt nach dem `FacilityQualificationRate`-Muster: `GET/POST/PUT/DELETE /api/facilities/{facilityId}/employee-distances[/...]` (`EmployeeFacilityDistancesController`), gegated über dieselben `Facilities`-Rechte, kein eigener `ModuleType`. `Create` validiert zusätzlich, dass `EmployeeId` auf einen existierenden Mitarbeiter zeigt (`IEmployeeService.GetByIdAsync`) und dass für das Facility/Employee-Paar noch keine (nicht gelöschte) Distanz existiert. Soft-Delete, über `TrashController` (`api/trash/employee-facility-distances/...`) wiederherstellbar. +- **Selbstbedienung durch den Außendienst (seit 2026-08-10, `MyFacilityDistancesController`, Route `api/me/facility-distances`):** Mitarbeiter sollen ihre eigene Fahrtstrecke selbst über `omsorgWeb/mitarbeiter-app` pflegen können, statt dass das Büro jede Kilometerangabe manuell einträgt. Dafür bewusst **kein** `Facilities`-Recht (das würde Konditionen/CRM-Daten offenlegen, die dem Außendienst laut Rechtematrix nicht zustehen), sondern ein neuer, eigenständiger `ModuleType.EmployeeFacilityDistances` (siehe "Rechtesystem" oben) — `GET /api/me/facility-distances` (eigene Distanzen inkl. `FacilityName`), `GET /api/me/facility-distances/facilities` (minimale Einrichtungsauswahl, nur `Id`/`Name` über `FacilityOptionResponse`, für das Formular-Dropdown), `POST`/`PUT` (Anlegen/Bearbeiten). `EmployeeId` kommt bei jeder Aktion ausschließlich aus `ICurrentUserService.EmployeeId` (JWT-Claim), nie vom Client — bei fehlender Verknüpfung `400` statt eines FK-Fehlers, analog zum `AbsenceService.CreateAsync`-Fallback. Bewusst **kein** `DELETE` hier (Löschen bleibt Büro-Aufgabe über den Papierkorb). Nutzt intern denselben `IEmployeeFacilityDistanceService`/dieselbe Tabelle wie `EmployeeFacilityDistancesController` — zwei Controller auf demselben Application-Service, unterschiedliche Zugriffsrechte, kein Datenmodell-Unterschied. +- **Zugleich behobener Bestandsfehler:** `FacilityService.UpdateAsync` kopierte die elf Konditionsfelder bislang gar nicht auf die getrackte Entität — der Controller validierte sie korrekt, aber `PUT /api/facilities/{id}` verwarf sie stillschweigend (kein Fehler, kein Log, das Feld blieb einfach `null`/unverändert). Betraf `BillingRate`/alle vier Zuschläge/`TravelCostRate`/`MinimumHours`/`BreakPolicy`/`BillingInterval`/`PaymentTermDays`/`IndividualAgreements` seit deren Einführung. Jetzt behoben, Regressionstest: `FacilityServiceTests.UpdateAsync_PersistsKonditionenFields`. - `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. @@ -273,7 +280,7 @@ Drei Endpoints statt zwei (Absence hat Edit+Decide, TimeEntry hat Edit+Submit+De ## Offene Punkte -- `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. +- `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), `FacilityQualificationRate` (FR-EIN-4, qualifikationsabhängige Preise) und `EmployeeFacilityDistance` (FR-EIN-4, kilometerbasierte Fahrtkosten) als 1:n-Unterressourcen unter `GET/POST/PUT/DELETE /api/facilities/{facilityId}/contacts[/...]`, `.../qualification-rates[/...]` bzw. `.../employee-distances[/...]` (`FacilityContactsController`/`FacilityQualificationRatesController`/`EmployeeFacilityDistancesController`, siehe "Konditionen einer Einrichtung" oben) — alle drei bewusst kein eigener `ModuleType`, sondern über dieselben `Facilities`-Rechte gegated, da keines ein eigenständiges Core-Objekt ist. Löschen (`Employee`/`Facility`/`Contract`/`Order`/`FacilityContact`/`FacilityQualificationRate`/`EmployeeFacilityDistance`) 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). @@ -285,7 +292,7 @@ Drei Endpoints statt zwei (Absence hat Edit+Decide, TimeEntry hat Edit+Submit+De 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 — 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`. +**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`, `employeeFacilityDistancesApi.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` 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). @@ -310,7 +317,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. 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. +5. Kein Hard-Delete für sensible/geschäftsrelevante Daten — Soft-Delete/Archivierung. Umgesetzt für `Employee`/`Facility`/`Contract`/`Order`/`FacilityContact`/`FacilityQualificationRate`/`EmployeeFacilityDistance`/`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/src/OmsorgCore.Api/Contracts/CreateEmployeeFacilityDistanceRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/CreateEmployeeFacilityDistanceRequest.cs new file mode 100644 index 0000000..bd08384 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/CreateEmployeeFacilityDistanceRequest.cs @@ -0,0 +1,5 @@ +namespace OmsorgCore.Api.Contracts; + +public record CreateEmployeeFacilityDistanceRequest( + Guid EmployeeId, + decimal DistanceKm); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/CreateMyFacilityDistanceRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/CreateMyFacilityDistanceRequest.cs new file mode 100644 index 0000000..40f759d --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/CreateMyFacilityDistanceRequest.cs @@ -0,0 +1,5 @@ +namespace OmsorgCore.Api.Contracts; + +public record CreateMyFacilityDistanceRequest( + Guid FacilityId, + decimal DistanceKm); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/EmployeeFacilityDistanceResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/EmployeeFacilityDistanceResponse.cs new file mode 100644 index 0000000..3dc340f --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/EmployeeFacilityDistanceResponse.cs @@ -0,0 +1,9 @@ +namespace OmsorgCore.Api.Contracts; + +public record EmployeeFacilityDistanceResponse( + Guid Id, + Guid FacilityId, + Guid EmployeeId, + string EmployeeFirstName, + string EmployeeLastName, + decimal DistanceKm); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/FacilityOptionResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/FacilityOptionResponse.cs new file mode 100644 index 0000000..95e0100 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/FacilityOptionResponse.cs @@ -0,0 +1,9 @@ +namespace OmsorgCore.Api.Contracts; + +/// +/// Minimale Einrichtungsauswahl für Selbstbedienungs-Formulare ohne Facilities-Recht (z. B. +/// MyFacilityDistancesController) — bewusst nur Id/Name, keine Konditionen/CRM-Daten. +/// +public record FacilityOptionResponse( + Guid Id, + string Name); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/FacilityResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/FacilityResponse.cs index 070c150..e06e2cc 100644 --- a/omsorgCore/src/OmsorgCore.Api/Contracts/FacilityResponse.cs +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/FacilityResponse.cs @@ -21,6 +21,8 @@ public record FacilityResponse( decimal? SundaySurchargePercent, decimal? HolidaySurchargePercent, decimal? TravelCostRate, + string TravelCostMode, + decimal? TravelCostPerKm, decimal? MinimumHours, string? BreakPolicy, string? BillingInterval, diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/MyFacilityDistanceResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/MyFacilityDistanceResponse.cs new file mode 100644 index 0000000..e928764 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/MyFacilityDistanceResponse.cs @@ -0,0 +1,7 @@ +namespace OmsorgCore.Api.Contracts; + +public record MyFacilityDistanceResponse( + Guid Id, + Guid FacilityId, + string FacilityName, + decimal DistanceKm); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/TrashEmployeeFacilityDistanceResponse.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/TrashEmployeeFacilityDistanceResponse.cs new file mode 100644 index 0000000..a7f9800 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/TrashEmployeeFacilityDistanceResponse.cs @@ -0,0 +1,8 @@ +namespace OmsorgCore.Api.Contracts; + +public record TrashEmployeeFacilityDistanceResponse( + Guid Id, + Guid FacilityId, + Guid EmployeeId, + decimal DistanceKm, + DateTime? DeletedAt); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateEmployeeFacilityDistanceRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateEmployeeFacilityDistanceRequest.cs new file mode 100644 index 0000000..40061bf --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateEmployeeFacilityDistanceRequest.cs @@ -0,0 +1,4 @@ +namespace OmsorgCore.Api.Contracts; + +public record UpdateEmployeeFacilityDistanceRequest( + decimal DistanceKm); diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateFacilityRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateFacilityRequest.cs index dfec2a8..3918e9e 100644 --- a/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateFacilityRequest.cs +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateFacilityRequest.cs @@ -20,6 +20,8 @@ public record UpdateFacilityRequest( decimal? SundaySurchargePercent, decimal? HolidaySurchargePercent, decimal? TravelCostRate, + string TravelCostMode, + decimal? TravelCostPerKm, decimal? MinimumHours, string? BreakPolicy, string? BillingInterval, diff --git a/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateMyFacilityDistanceRequest.cs b/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateMyFacilityDistanceRequest.cs new file mode 100644 index 0000000..6361095 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Contracts/UpdateMyFacilityDistanceRequest.cs @@ -0,0 +1,4 @@ +namespace OmsorgCore.Api.Contracts; + +public record UpdateMyFacilityDistanceRequest( + decimal DistanceKm); diff --git a/omsorgCore/src/OmsorgCore.Api/Controllers/EmployeeFacilityDistancesController.cs b/omsorgCore/src/OmsorgCore.Api/Controllers/EmployeeFacilityDistancesController.cs new file mode 100644 index 0000000..659b1ba --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Controllers/EmployeeFacilityDistancesController.cs @@ -0,0 +1,137 @@ +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; + +/// +/// Mitarbeiter-Einrichtung-Entfernungen (km) sind eine 1:n-Unterressource von Facility (FR-EIN-4, +/// Grundlage für Facility.TravelCostMode == "ProKilometer") — kein eigenständiges Core-Objekt, daher +/// unter /api/facilities/{facilityId}/employee-distances und mit den gleichen Facilities-Rechten +/// gegated statt einem eigenen ModuleType, analog zu FacilityQualificationRate. +/// +[ApiController] +[Authorize] +[Route("api/facilities/{facilityId:guid}/employee-distances")] +public class EmployeeFacilityDistancesController : ControllerBase +{ + private readonly IFacilityService _facilityService; + private readonly IEmployeeService _employeeService; + private readonly IEmployeeFacilityDistanceService _employeeFacilityDistanceService; + + public EmployeeFacilityDistancesController( + IFacilityService facilityService, + IEmployeeService employeeService, + IEmployeeFacilityDistanceService employeeFacilityDistanceService) + { + _facilityService = facilityService; + _employeeService = employeeService; + _employeeFacilityDistanceService = employeeFacilityDistanceService; + } + + [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 distances = await _employeeFacilityDistanceService.GetByFacilityIdAsync(facilityId, cancellationToken); + return Ok(distances.Select(d => ToResponse(d, d.Employee)).ToList()); + } + + [HttpPost] + [RequirePermission(ModuleType.Facilities, PermissionAction.Create)] + public async Task> Create(Guid facilityId, CreateEmployeeFacilityDistanceRequest request, CancellationToken cancellationToken) + { + if (await _facilityService.GetByIdAsync(facilityId, cancellationToken) is null) + { + return NotFound(); + } + + var employee = await _employeeService.GetByIdAsync(request.EmployeeId, cancellationToken); + if (employee is null) + { + return BadRequest("EmployeeId verweist auf keinen existierenden Mitarbeiter."); + } + + if (request.DistanceKm < 0) + { + return BadRequest("DistanceKm darf nicht negativ sein."); + } + + if (await _employeeFacilityDistanceService.GetByFacilityAndEmployeeAsync(facilityId, request.EmployeeId, cancellationToken) is not null) + { + return BadRequest("Für diesen Mitarbeiter existiert bereits eine Entfernung zu dieser Einrichtung."); + } + + var distance = new EmployeeFacilityDistance + { + FacilityId = facilityId, + EmployeeId = request.EmployeeId, + DistanceKm = request.DistanceKm + }; + + var created = await _employeeFacilityDistanceService.CreateAsync(distance, cancellationToken); + return CreatedAtAction(nameof(GetAll), new { facilityId }, ToResponse(created, employee)); + } + + [HttpPut("{id:guid}")] + [RequirePermission(ModuleType.Facilities, PermissionAction.Edit)] + public async Task> Update(Guid facilityId, Guid id, UpdateEmployeeFacilityDistanceRequest request, CancellationToken cancellationToken) + { + var existing = await _employeeFacilityDistanceService.GetByIdAsync(id, cancellationToken); + if (existing is null || existing.FacilityId != facilityId) + { + return NotFound(); + } + + if (request.DistanceKm < 0) + { + return BadRequest("DistanceKm darf nicht negativ sein."); + } + + var updates = new EmployeeFacilityDistance + { + DistanceKm = request.DistanceKm + }; + + var updated = await _employeeFacilityDistanceService.UpdateAsync(id, updates, cancellationToken); + if (updated is null) + { + return NotFound(); + } + + var employee = await _employeeService.GetByIdAsync(updated.EmployeeId, cancellationToken); + return Ok(ToResponse(updated, employee!)); + } + + [HttpDelete("{id:guid}")] + [RequirePermission(ModuleType.Facilities, PermissionAction.Delete)] + public async Task Delete(Guid facilityId, Guid id, CancellationToken cancellationToken) + { + var existing = await _employeeFacilityDistanceService.GetByIdAsync(id, cancellationToken); + if (existing is null || existing.FacilityId != facilityId) + { + return NotFound(); + } + + var deleted = await _employeeFacilityDistanceService.DeleteAsync(id, cancellationToken); + return deleted ? NoContent() : NotFound(); + } + + private static EmployeeFacilityDistanceResponse ToResponse(EmployeeFacilityDistance distance, Employee employee) + => new( + distance.Id, + distance.FacilityId, + distance.EmployeeId, + employee.FirstName, + employee.LastName, + distance.DistanceKm); +} diff --git a/omsorgCore/src/OmsorgCore.Api/Controllers/FacilitiesController.cs b/omsorgCore/src/OmsorgCore.Api/Controllers/FacilitiesController.cs index 85c264c..e6aeb22 100644 --- a/omsorgCore/src/OmsorgCore.Api/Controllers/FacilitiesController.cs +++ b/omsorgCore/src/OmsorgCore.Api/Controllers/FacilitiesController.cs @@ -19,6 +19,7 @@ public class FacilitiesController : ControllerBase private const string FacilityTypeListKey = "FacilityType"; private const string FollowUpPeriodsListKey = "FollowUpPeriods"; private const string BillingIntervalListKey = "BillingInterval"; + private static readonly string[] TravelCostModes = { "Pauschale", "ProKilometer" }; private readonly IFacilityService _facilityService; private readonly IValueListRepository _valueListRepository; @@ -222,6 +223,7 @@ public class FacilitiesController : ControllerBase if (request.BillingRate is < 0 || request.TravelCostRate is < 0 + || request.TravelCostPerKm is < 0 || request.MinimumHours is < 0 || request.NightSurchargePercent is < 0 || request.SaturdaySurchargePercent is < 0 @@ -232,6 +234,11 @@ public class FacilitiesController : ControllerBase return BadRequest("Konditionswerte dürfen nicht negativ sein."); } + if (!TravelCostModes.Contains(request.TravelCostMode)) + { + return BadRequest($"TravelCostMode muss einer der folgenden Werte sein: {string.Join(", ", TravelCostModes)}."); + } + var updates = new Facility { Name = request.Name, @@ -243,6 +250,8 @@ public class FacilitiesController : ControllerBase SundaySurchargePercent = request.SundaySurchargePercent, HolidaySurchargePercent = request.HolidaySurchargePercent, TravelCostRate = request.TravelCostRate, + TravelCostMode = request.TravelCostMode, + TravelCostPerKm = request.TravelCostPerKm, MinimumHours = request.MinimumHours, BreakPolicy = request.BreakPolicy, BillingInterval = request.BillingInterval, @@ -319,6 +328,8 @@ public class FacilitiesController : ControllerBase facility.SundaySurchargePercent, facility.HolidaySurchargePercent, facility.TravelCostRate, + facility.TravelCostMode, + facility.TravelCostPerKm, facility.MinimumHours, facility.BreakPolicy, facility.BillingInterval, diff --git a/omsorgCore/src/OmsorgCore.Api/Controllers/MyFacilityDistancesController.cs b/omsorgCore/src/OmsorgCore.Api/Controllers/MyFacilityDistancesController.cs new file mode 100644 index 0000000..0790d32 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Api/Controllers/MyFacilityDistancesController.cs @@ -0,0 +1,143 @@ +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; + +/// +/// Selbstbedienungs-Endpoint für Außendienst-Mitarbeiter (OMSORG Connect, +/// `omsorgWeb/mitarbeiter-app`): eigene Fahrtstrecke (km) je Einrichtung pflegen, Grundlage für die +/// kilometerbasierte Fahrtkostenabrechnung (Facility.TravelCostMode == "ProKilometer", FR-EIN-4). +/// Bewusst ein eigener ModuleType (EmployeeFacilityDistances) statt Facilities-Rechten — der +/// Außendienst darf hier ausschließlich seine eigenen Datensätze sehen/anlegen/bearbeiten, nicht die +/// Konditionen/CRM-Daten einer Einrichtung. EmployeeId kommt IMMER serverseitig aus dem JWT-Claim, +/// nie vom Client (analog AbsencesController/TimeEntriesController). +/// +[ApiController] +[Authorize] +[Route("api/me/facility-distances")] +public class MyFacilityDistancesController : ControllerBase +{ + private readonly IEmployeeFacilityDistanceService _employeeFacilityDistanceService; + private readonly IFacilityService _facilityService; + private readonly ICurrentUserService _currentUserService; + + public MyFacilityDistancesController( + IEmployeeFacilityDistanceService employeeFacilityDistanceService, + IFacilityService facilityService, + ICurrentUserService currentUserService) + { + _employeeFacilityDistanceService = employeeFacilityDistanceService; + _facilityService = facilityService; + _currentUserService = currentUserService; + } + + [HttpGet] + [RequirePermission(ModuleType.EmployeeFacilityDistances, PermissionAction.View)] + public async Task>> GetAll(CancellationToken cancellationToken) + { + var employeeId = RequireOwnEmployeeId(); + if (employeeId is null) + { + return BadRequest("Dieser Benutzer ist mit keinem Mitarbeiter verknüpft."); + } + + var facilities = (await _facilityService.GetAllAsync(cancellationToken)).ToDictionary(f => f.Id); + var all = new List(); + + foreach (var facility in facilities.Values) + { + var distance = await _employeeFacilityDistanceService.GetByFacilityAndEmployeeAsync(facility.Id, employeeId.Value, cancellationToken); + if (distance is not null) + { + all.Add(new MyFacilityDistanceResponse(distance.Id, facility.Id, facility.Name, distance.DistanceKm)); + } + } + + return Ok(all); + } + + [HttpGet("facilities")] + [RequirePermission(ModuleType.EmployeeFacilityDistances, PermissionAction.View)] + public async Task>> GetFacilityOptions(CancellationToken cancellationToken) + { + var facilities = await _facilityService.GetAllAsync(cancellationToken); + return Ok(facilities.OrderBy(f => f.Name).Select(f => new FacilityOptionResponse(f.Id, f.Name)).ToList()); + } + + [HttpPost] + [RequirePermission(ModuleType.EmployeeFacilityDistances, PermissionAction.Create)] + public async Task> Create(CreateMyFacilityDistanceRequest request, CancellationToken cancellationToken) + { + var employeeId = RequireOwnEmployeeId(); + if (employeeId is null) + { + return BadRequest("Dieser Benutzer ist mit keinem Mitarbeiter verknüpft."); + } + + var facility = await _facilityService.GetByIdAsync(request.FacilityId, cancellationToken); + if (facility is null) + { + return BadRequest("FacilityId verweist auf keine existierende Einrichtung."); + } + + if (request.DistanceKm < 0) + { + return BadRequest("DistanceKm darf nicht negativ sein."); + } + + if (await _employeeFacilityDistanceService.GetByFacilityAndEmployeeAsync(request.FacilityId, employeeId.Value, cancellationToken) is not null) + { + return BadRequest("Für diese Einrichtung existiert bereits eine eigene Entfernung."); + } + + var distance = new EmployeeFacilityDistance + { + FacilityId = request.FacilityId, + EmployeeId = employeeId.Value, + DistanceKm = request.DistanceKm + }; + + var created = await _employeeFacilityDistanceService.CreateAsync(distance, cancellationToken); + return CreatedAtAction(nameof(GetAll), new MyFacilityDistanceResponse(created.Id, facility.Id, facility.Name, created.DistanceKm)); + } + + [HttpPut("{id:guid}")] + [RequirePermission(ModuleType.EmployeeFacilityDistances, PermissionAction.Edit)] + public async Task> Update(Guid id, UpdateMyFacilityDistanceRequest request, CancellationToken cancellationToken) + { + var employeeId = RequireOwnEmployeeId(); + if (employeeId is null) + { + return BadRequest("Dieser Benutzer ist mit keinem Mitarbeiter verknüpft."); + } + + var existing = await _employeeFacilityDistanceService.GetByIdAsync(id, cancellationToken); + if (existing is null || existing.EmployeeId != employeeId.Value) + { + return NotFound(); + } + + if (request.DistanceKm < 0) + { + return BadRequest("DistanceKm darf nicht negativ sein."); + } + + var updated = await _employeeFacilityDistanceService.UpdateAsync(id, new EmployeeFacilityDistance { DistanceKm = request.DistanceKm }, cancellationToken); + if (updated is null) + { + return NotFound(); + } + + var facility = await _facilityService.GetByIdAsync(updated.FacilityId, cancellationToken); + return Ok(new MyFacilityDistanceResponse(updated.Id, updated.FacilityId, facility?.Name ?? "", updated.DistanceKm)); + } + + private Guid? RequireOwnEmployeeId() + => _currentUserService.EmployeeId is { } id && id != Guid.Empty ? id : null; +} diff --git a/omsorgCore/src/OmsorgCore.Api/Controllers/RolesController.cs b/omsorgCore/src/OmsorgCore.Api/Controllers/RolesController.cs index f7f38e3..fbacfaf 100644 --- a/omsorgCore/src/OmsorgCore.Api/Controllers/RolesController.cs +++ b/omsorgCore/src/OmsorgCore.Api/Controllers/RolesController.cs @@ -84,4 +84,22 @@ public class RolesController : ControllerBase return NoContent(); } + + [HttpDelete("{id:guid}")] + [RequirePermission(ModuleType.UserManagement, PermissionAction.Delete)] + public async Task Delete(Guid id, CancellationToken cancellationToken) + { + var result = await _roleService.DeleteAsync(id, cancellationToken); + if (!result.Success) + { + return result.FailureReason switch + { + DeleteRoleFailureReason.RoleNotFound => NotFound("Rolle nicht gefunden."), + DeleteRoleFailureReason.InUse => Conflict($"Diese Rolle ist noch {result.AssignedUserCount} Benutzer(n) zugewiesen und kann nicht gelöscht werden."), + _ => BadRequest() + }; + } + + return NoContent(); + } } diff --git a/omsorgCore/src/OmsorgCore.Api/Controllers/TrashController.cs b/omsorgCore/src/OmsorgCore.Api/Controllers/TrashController.cs index 4386ce8..37899fd 100644 --- a/omsorgCore/src/OmsorgCore.Api/Controllers/TrashController.cs +++ b/omsorgCore/src/OmsorgCore.Api/Controllers/TrashController.cs @@ -26,6 +26,7 @@ public class TrashController : ControllerBase private readonly IOrderService _orderService; private readonly IFacilityContactService _facilityContactService; private readonly IFacilityQualificationRateService _facilityQualificationRateService; + private readonly IEmployeeFacilityDistanceService _employeeFacilityDistanceService; private readonly IAbsenceService _absenceService; private readonly ITimeEntryService _timeEntryService; @@ -36,6 +37,7 @@ public class TrashController : ControllerBase IOrderService orderService, IFacilityContactService facilityContactService, IFacilityQualificationRateService facilityQualificationRateService, + IEmployeeFacilityDistanceService employeeFacilityDistanceService, IAbsenceService absenceService, ITimeEntryService timeEntryService) { @@ -45,6 +47,7 @@ public class TrashController : ControllerBase _orderService = orderService; _facilityContactService = facilityContactService; _facilityQualificationRateService = facilityQualificationRateService; + _employeeFacilityDistanceService = employeeFacilityDistanceService; _absenceService = absenceService; _timeEntryService = timeEntryService; } @@ -145,6 +148,22 @@ public class TrashController : ControllerBase return restored ? NoContent() : NotFound(); } + [HttpGet("employee-facility-distances")] + [RequirePermission(ModuleType.Facilities, PermissionAction.Recover)] + public async Task>> GetDeletedEmployeeFacilityDistances([FromQuery] string? search, CancellationToken cancellationToken) + { + var distances = await _employeeFacilityDistanceService.GetDeletedAsync(search, cancellationToken); + return Ok(distances.Select(d => new TrashEmployeeFacilityDistanceResponse(d.Id, d.FacilityId, d.EmployeeId, d.DistanceKm, d.DeletedAt)).ToList()); + } + + [HttpPost("employee-facility-distances/{id:guid}/restore")] + [RequirePermission(ModuleType.Facilities, PermissionAction.Recover)] + public async Task RestoreEmployeeFacilityDistance(Guid id, CancellationToken cancellationToken) + { + var restored = await _employeeFacilityDistanceService.RestoreAsync(id, cancellationToken); + return restored ? NoContent() : NotFound(); + } + [HttpGet("absences")] [RequirePermission(ModuleType.Absences, PermissionAction.Recover)] public async Task>> GetDeletedAbsences([FromQuery] string? search, CancellationToken cancellationToken) diff --git a/omsorgCore/src/OmsorgCore.Application/Abstractions/IEmployeeFacilityDistanceRepository.cs b/omsorgCore/src/OmsorgCore.Application/Abstractions/IEmployeeFacilityDistanceRepository.cs new file mode 100644 index 0000000..ec90ee3 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Abstractions/IEmployeeFacilityDistanceRepository.cs @@ -0,0 +1,16 @@ +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Application.Abstractions; + +public interface IEmployeeFacilityDistanceRepository +{ + Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetByFacilityIdAsync(Guid facilityId, CancellationToken cancellationToken = default); + Task GetByFacilityAndEmployeeAsync(Guid facilityId, Guid employeeId, CancellationToken cancellationToken = default); + Task AddAsync(EmployeeFacilityDistance distance, CancellationToken cancellationToken = default); + Task UpdateAsync(EmployeeFacilityDistance distance, 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/IRoleRepository.cs b/omsorgCore/src/OmsorgCore.Application/Abstractions/IRoleRepository.cs index dc76caa..8b4e290 100644 --- a/omsorgCore/src/OmsorgCore.Application/Abstractions/IRoleRepository.cs +++ b/omsorgCore/src/OmsorgCore.Application/Abstractions/IRoleRepository.cs @@ -18,5 +18,6 @@ public interface IRoleRepository Task AddPermissionRangeAsync(IEnumerable permissions, CancellationToken cancellationToken = default); Task ExistsByNameAsync(string name, CancellationToken cancellationToken = default); Task AddAsync(Role role, CancellationToken cancellationToken = default); + Task RemoveAsync(Role role, 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 5b8904c..92857cc 100644 --- a/omsorgCore/src/OmsorgCore.Application/DependencyInjection.cs +++ b/omsorgCore/src/OmsorgCore.Application/DependencyInjection.cs @@ -14,6 +14,7 @@ public static class DependencyInjection services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/omsorgCore/src/OmsorgCore.Application/Services/DeleteRoleResult.cs b/omsorgCore/src/OmsorgCore.Application/Services/DeleteRoleResult.cs new file mode 100644 index 0000000..5963d51 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Services/DeleteRoleResult.cs @@ -0,0 +1,28 @@ +namespace OmsorgCore.Application.Services; + +public enum DeleteRoleFailureReason +{ + RoleNotFound, + InUse +} + +public class DeleteRoleResult +{ + public bool Success { get; init; } + public DeleteRoleFailureReason? FailureReason { get; init; } + + /// Nur gesetzt bei - Anzahl der Benutzer, die dieser Rolle noch zugewiesen sind. + public int AssignedUserCount { get; init; } + + public static DeleteRoleResult Fail(DeleteRoleFailureReason reason, int assignedUserCount = 0) => new() + { + Success = false, + FailureReason = reason, + AssignedUserCount = assignedUserCount + }; + + public static DeleteRoleResult Ok() => new() + { + Success = true + }; +} diff --git a/omsorgCore/src/OmsorgCore.Application/Services/EmployeeFacilityDistanceService.cs b/omsorgCore/src/OmsorgCore.Application/Services/EmployeeFacilityDistanceService.cs new file mode 100644 index 0000000..5d56693 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Services/EmployeeFacilityDistanceService.cs @@ -0,0 +1,71 @@ +using OmsorgCore.Application.Abstractions; +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Application.Services; + +public class EmployeeFacilityDistanceService : IEmployeeFacilityDistanceService +{ + private readonly IEmployeeFacilityDistanceRepository _employeeFacilityDistanceRepository; + + public EmployeeFacilityDistanceService(IEmployeeFacilityDistanceRepository employeeFacilityDistanceRepository) + { + _employeeFacilityDistanceRepository = employeeFacilityDistanceRepository; + } + + public Task> GetByFacilityIdAsync(Guid facilityId, CancellationToken cancellationToken = default) + => _employeeFacilityDistanceRepository.GetByFacilityIdAsync(facilityId, cancellationToken); + + public Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) + => _employeeFacilityDistanceRepository.GetByIdAsync(id, cancellationToken); + + public Task GetByFacilityAndEmployeeAsync(Guid facilityId, Guid employeeId, CancellationToken cancellationToken = default) + => _employeeFacilityDistanceRepository.GetByFacilityAndEmployeeAsync(facilityId, employeeId, cancellationToken); + + public async Task CreateAsync(EmployeeFacilityDistance distance, CancellationToken cancellationToken = default) + { + await _employeeFacilityDistanceRepository.AddAsync(distance, cancellationToken); + await _employeeFacilityDistanceRepository.SaveChangesAsync(cancellationToken); + return distance; + } + + public async Task UpdateAsync(Guid id, EmployeeFacilityDistance updates, CancellationToken cancellationToken = default) + { + var distance = await _employeeFacilityDistanceRepository.GetByIdAsync(id, cancellationToken); + if (distance is null) + { + return null; + } + + distance.DistanceKm = updates.DistanceKm; + distance.UpdatedAt = DateTime.UtcNow; + + await _employeeFacilityDistanceRepository.UpdateAsync(distance, cancellationToken); + await _employeeFacilityDistanceRepository.SaveChangesAsync(cancellationToken); + return distance; + } + + public async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var deleted = await _employeeFacilityDistanceRepository.SoftDeleteAsync(id, cancellationToken); + if (deleted) + { + await _employeeFacilityDistanceRepository.SaveChangesAsync(cancellationToken); + } + + return deleted; + } + + public Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + => _employeeFacilityDistanceRepository.GetDeletedAsync(search, cancellationToken); + + public async Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var restored = await _employeeFacilityDistanceRepository.RestoreAsync(id, cancellationToken); + if (restored) + { + await _employeeFacilityDistanceRepository.SaveChangesAsync(cancellationToken); + } + + return restored; + } +} diff --git a/omsorgCore/src/OmsorgCore.Application/Services/FacilityService.cs b/omsorgCore/src/OmsorgCore.Application/Services/FacilityService.cs index e1e097e..f2e3842 100644 --- a/omsorgCore/src/OmsorgCore.Application/Services/FacilityService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Services/FacilityService.cs @@ -55,6 +55,19 @@ public class FacilityService : IFacilityService facility.BillingCountry = updates.BillingCountry; facility.CrmStatus = updates.CrmStatus; facility.FollowUpDueDate = updates.FollowUpDueDate; + facility.BillingRate = updates.BillingRate; + facility.NightSurchargePercent = updates.NightSurchargePercent; + facility.SaturdaySurchargePercent = updates.SaturdaySurchargePercent; + facility.SundaySurchargePercent = updates.SundaySurchargePercent; + facility.HolidaySurchargePercent = updates.HolidaySurchargePercent; + facility.TravelCostRate = updates.TravelCostRate; + facility.TravelCostMode = updates.TravelCostMode; + facility.TravelCostPerKm = updates.TravelCostPerKm; + facility.MinimumHours = updates.MinimumHours; + facility.BreakPolicy = updates.BreakPolicy; + facility.BillingInterval = updates.BillingInterval; + facility.PaymentTermDays = updates.PaymentTermDays; + facility.IndividualAgreements = updates.IndividualAgreements; facility.UpdatedAt = DateTime.UtcNow; await _facilityRepository.UpdateAsync(facility, cancellationToken); diff --git a/omsorgCore/src/OmsorgCore.Application/Services/IEmployeeFacilityDistanceService.cs b/omsorgCore/src/OmsorgCore.Application/Services/IEmployeeFacilityDistanceService.cs new file mode 100644 index 0000000..77ae532 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Application/Services/IEmployeeFacilityDistanceService.cs @@ -0,0 +1,15 @@ +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Application.Services; + +public interface IEmployeeFacilityDistanceService +{ + Task> GetByFacilityIdAsync(Guid facilityId, CancellationToken cancellationToken = default); + Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default); + Task GetByFacilityAndEmployeeAsync(Guid facilityId, Guid employeeId, CancellationToken cancellationToken = default); + Task CreateAsync(EmployeeFacilityDistance distance, CancellationToken cancellationToken = default); + Task UpdateAsync(Guid id, EmployeeFacilityDistance 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 889c52e..f085fd8 100644 --- a/omsorgCore/src/OmsorgCore.Application/Services/IRoleService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Services/IRoleService.cs @@ -16,4 +16,7 @@ public interface IRoleService Guid roleId, IReadOnlyList<(ModuleType Module, PermissionAction Action, PermissionScope Scope)> permissions, CancellationToken cancellationToken = default); + + /// Löschschutz: schlägt fehl, solange mindestens ein User dieser Rolle zugewiesen ist (analog ValueListService.DeleteItemAsync). + Task DeleteAsync(Guid id, CancellationToken cancellationToken = default); } diff --git a/omsorgCore/src/OmsorgCore.Application/Services/RoleService.cs b/omsorgCore/src/OmsorgCore.Application/Services/RoleService.cs index cef4cab..e914e3a 100644 --- a/omsorgCore/src/OmsorgCore.Application/Services/RoleService.cs +++ b/omsorgCore/src/OmsorgCore.Application/Services/RoleService.cs @@ -8,11 +8,13 @@ public class RoleService : IRoleService { private readonly IRoleRepository _roleRepository; private readonly IPermissionService _permissionService; + private readonly IUserRepository _userRepository; - public RoleService(IRoleRepository roleRepository, IPermissionService permissionService) + public RoleService(IRoleRepository roleRepository, IPermissionService permissionService, IUserRepository userRepository) { _roleRepository = roleRepository; _permissionService = permissionService; + _userRepository = userRepository; } public Task> GetAllAsync(CancellationToken cancellationToken = default) @@ -62,4 +64,23 @@ public class RoleService : IRoleService await _permissionService.InvalidateRolePermissionsAsync(roleId, cancellationToken); return UpdateRolePermissionsResult.Ok(); } + + public async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var role = await _roleRepository.GetByIdAsync(id, cancellationToken); + if (role is null) + { + return DeleteRoleResult.Fail(DeleteRoleFailureReason.RoleNotFound); + } + + var assignedUserIds = await _userRepository.GetUserIdsByRoleAsync(id, cancellationToken); + if (assignedUserIds.Count > 0) + { + return DeleteRoleResult.Fail(DeleteRoleFailureReason.InUse, assignedUserIds.Count); + } + + await _roleRepository.RemoveAsync(role, cancellationToken); + await _roleRepository.SaveChangesAsync(cancellationToken); + return DeleteRoleResult.Ok(); + } } diff --git a/omsorgCore/src/OmsorgCore.Domain/Entities/EmployeeFacilityDistance.cs b/omsorgCore/src/OmsorgCore.Domain/Entities/EmployeeFacilityDistance.cs new file mode 100644 index 0000000..74fb992 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Domain/Entities/EmployeeFacilityDistance.cs @@ -0,0 +1,21 @@ +using OmsorgCore.Domain.Common; + +namespace OmsorgCore.Domain.Entities; + +/// +/// Entfernung (km) zwischen einem Mitarbeiter und einer Einrichtung — Grundlage für die +/// kilometerbasierte Fahrtkostenabrechnung ( == "ProKilometer", +/// FR-EIN-4). Pro Mitarbeiter/Einrichtung individuell statt an Facility allein, da jeder Mitarbeiter +/// von einem anderen Wohnort anfährt. 1:n-Unterressource von Facility, kein eigenständiges Core-Objekt +/// — exakt nach dem Muster von . +/// +public class EmployeeFacilityDistance : AuditableEntity +{ + public Guid FacilityId { get; set; } + public Facility Facility { get; set; } = null!; + + public Guid EmployeeId { get; set; } + public Employee Employee { get; set; } = null!; + + public decimal DistanceKm { get; set; } +} diff --git a/omsorgCore/src/OmsorgCore.Domain/Entities/Facility.cs b/omsorgCore/src/OmsorgCore.Domain/Entities/Facility.cs index f854514..ae85fe3 100644 --- a/omsorgCore/src/OmsorgCore.Domain/Entities/Facility.cs +++ b/omsorgCore/src/OmsorgCore.Domain/Entities/Facility.cs @@ -33,7 +33,12 @@ public class Facility : AuditableEntity public decimal? SaturdaySurchargePercent { get; set; } public decimal? SundaySurchargePercent { get; set; } public decimal? HolidaySurchargePercent { get; set; } + // TravelCostMode entscheidet, welches der beiden Felder die Rechnungserstellung (FR-RE-1, + // sobald umgesetzt) verwendet: "Pauschale" -> TravelCostRate (EUR je Einsatz), "ProKilometer" + // -> TravelCostPerKm (EUR/km) * EmployeeFacilityDistance.DistanceKm des jeweiligen Mitarbeiters. + public string TravelCostMode { get; set; } = "Pauschale"; public decimal? TravelCostRate { get; set; } + public decimal? TravelCostPerKm { get; set; } public decimal? MinimumHours { get; set; } public string? BreakPolicy { get; set; } public string? BillingInterval { get; set; } diff --git a/omsorgCore/src/OmsorgCore.Domain/Enums/ModuleType.cs b/omsorgCore/src/OmsorgCore.Domain/Enums/ModuleType.cs index 4256f91..baa30bc 100644 --- a/omsorgCore/src/OmsorgCore.Domain/Enums/ModuleType.cs +++ b/omsorgCore/src/OmsorgCore.Domain/Enums/ModuleType.cs @@ -18,5 +18,6 @@ public enum ModuleType Documents, Users, Configuration, - Absences + Absences, + EmployeeFacilityDistances } diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/DependencyInjection.cs b/omsorgCore/src/OmsorgCore.Infrastructure/DependencyInjection.cs index 6923918..74e2449 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/DependencyInjection.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/DependencyInjection.cs @@ -35,6 +35,7 @@ public static class DependencyInjection services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/EmployeeFacilityDistanceConfiguration.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/EmployeeFacilityDistanceConfiguration.cs new file mode 100644 index 0000000..062a404 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/EmployeeFacilityDistanceConfiguration.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using OmsorgCore.Domain.Entities; + +namespace OmsorgCore.Infrastructure.Persistence.Configurations; + +public class EmployeeFacilityDistanceConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("employee_facility_distances"); + builder.HasKey(d => d.Id); + builder.Property(d => d.DistanceKm).IsRequired().HasColumnType("decimal(10,2)"); + + builder.HasOne(d => d.Facility) + .WithMany() + .HasForeignKey(d => d.FacilityId) + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(d => d.Employee) + .WithMany() + .HasForeignKey(d => d.EmployeeId) + .OnDelete(DeleteBehavior.Restrict); + + // Soft-gelöschte Distanzen sind für alle normalen Queries unsichtbar. + 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 ceecde8..69ba559 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/FacilityConfiguration.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Configurations/FacilityConfiguration.cs @@ -29,6 +29,8 @@ public class FacilityConfiguration : IEntityTypeConfiguration 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.TravelCostMode).IsRequired().HasMaxLength(20); + builder.Property(f => f.TravelCostPerKm).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); diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/DbSeeder.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/DbSeeder.cs index 0ddb1e2..f808002 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/DbSeeder.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/DbSeeder.cs @@ -69,7 +69,11 @@ public static class DbSeeder /// 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 + /// vorbehalten (kein Approve-Recht hier). Ebenso EmployeeFacilityDistances.{Create,View,Edit} mit + /// PermissionScope.Own (FR-EIN-4, kilometerbasierte Fahrtkostenabrechnung, siehe + /// MyFacilityDistancesController) - der Außendienst pflegt seine eigenen Fahrtstrecken je + /// Einrichtung selbst über `omsorgWeb/mitarbeiter-app`, hat aber KEIN Facilities-Recht und sieht + /// daher keine Konditionen/CRM-Daten einer Einrichtung, nur deren Namen zur Auswahl. "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). @@ -104,7 +108,8 @@ public static class DbSeeder (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) + (ModuleType.TimeEntries, new[] { PermissionAction.Create, PermissionAction.View, PermissionAction.Edit }, PermissionScope.Own), + (ModuleType.EmployeeFacilityDistances, new[] { PermissionAction.Create, PermissionAction.View, PermissionAction.Edit }, PermissionScope.Own) }, cancellationToken); } diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810172125_AddTravelCostModeAndEmployeeFacilityDistances.Designer.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810172125_AddTravelCostModeAndEmployeeFacilityDistances.Designer.cs new file mode 100644 index 0000000..d3fada1 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810172125_AddTravelCostModeAndEmployeeFacilityDistances.Designer.cs @@ -0,0 +1,1392 @@ +// +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("20260810172125_AddTravelCostModeAndEmployeeFacilityDistances")] + partial class AddTravelCostModeAndEmployeeFacilityDistances + { + /// + 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.EmployeeFacilityDistance", 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("DistanceKm") + .HasColumnType("decimal(10,2)"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("FacilityId"); + + b.ToTable("employee_facility_distances", (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("TravelCostMode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("TravelCostPerKm") + .HasColumnType("decimal(10,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.EmployeeFacilityDistance", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + 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.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/20260810172125_AddTravelCostModeAndEmployeeFacilityDistances.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810172125_AddTravelCostModeAndEmployeeFacilityDistances.cs new file mode 100644 index 0000000..7c11b59 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/20260810172125_AddTravelCostModeAndEmployeeFacilityDistances.cs @@ -0,0 +1,84 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace OmsorgCore.Infrastructure.Persistence.Migrations +{ + /// + public partial class AddTravelCostModeAndEmployeeFacilityDistances : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "TravelCostMode", + table: "facilities", + type: "character varying(20)", + maxLength: 20, + nullable: false, + defaultValue: "Pauschale"); + + migrationBuilder.AddColumn( + name: "TravelCostPerKm", + table: "facilities", + type: "numeric(10,2)", + nullable: true); + + migrationBuilder.CreateTable( + name: "employee_facility_distances", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + FacilityId = table.Column(type: "uuid", nullable: false), + EmployeeId = table.Column(type: "uuid", nullable: false), + DistanceKm = 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_employee_facility_distances", x => x.Id); + table.ForeignKey( + name: "FK_employee_facility_distances_employees_EmployeeId", + column: x => x.EmployeeId, + principalTable: "employees", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_employee_facility_distances_facilities_FacilityId", + column: x => x.FacilityId, + principalTable: "facilities", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_employee_facility_distances_EmployeeId", + table: "employee_facility_distances", + column: "EmployeeId"); + + migrationBuilder.CreateIndex( + name: "IX_employee_facility_distances_FacilityId", + table: "employee_facility_distances", + column: "FacilityId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "employee_facility_distances"); + + migrationBuilder.DropColumn( + name: "TravelCostMode", + table: "facilities"); + + migrationBuilder.DropColumn( + name: "TravelCostPerKm", + table: "facilities"); + } + } +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/OmsorgCoreDbContextModelSnapshot.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/OmsorgCoreDbContextModelSnapshot.cs index f20e4fa..5adfb65 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/OmsorgCoreDbContextModelSnapshot.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/Migrations/OmsorgCoreDbContextModelSnapshot.cs @@ -361,6 +361,42 @@ namespace OmsorgCore.Infrastructure.Persistence.Migrations b.ToTable("employees", (string)null); }); + modelBuilder.Entity("OmsorgCore.Domain.Entities.EmployeeFacilityDistance", 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("DistanceKm") + .HasColumnType("decimal(10,2)"); + + b.Property("EmployeeId") + .HasColumnType("uuid"); + + b.Property("FacilityId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("FacilityId"); + + b.ToTable("employee_facility_distances", (string)null); + }); + modelBuilder.Entity("OmsorgCore.Domain.Entities.Facility", b => { b.Property("Id") @@ -458,6 +494,14 @@ namespace OmsorgCore.Infrastructure.Persistence.Migrations b.Property("SundaySurchargePercent") .HasColumnType("decimal(5,2)"); + b.Property("TravelCostMode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("TravelCostPerKm") + .HasColumnType("decimal(10,2)"); + b.Property("TravelCostRate") .HasColumnType("decimal(10,2)"); @@ -1121,6 +1165,25 @@ namespace OmsorgCore.Infrastructure.Persistence.Migrations b.Navigation("UploadedByUser"); }); + modelBuilder.Entity("OmsorgCore.Domain.Entities.EmployeeFacilityDistance", b => + { + b.HasOne("OmsorgCore.Domain.Entities.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") + .WithMany() + .HasForeignKey("FacilityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("Facility"); + }); + modelBuilder.Entity("OmsorgCore.Domain.Entities.FacilityContact", b => { b.HasOne("OmsorgCore.Domain.Entities.Facility", "Facility") diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/OmsorgCoreDbContext.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/OmsorgCoreDbContext.cs index d458fbe..cefbf04 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/OmsorgCoreDbContext.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Persistence/OmsorgCoreDbContext.cs @@ -13,6 +13,7 @@ public class OmsorgCoreDbContext : DbContext public DbSet Facilities => Set(); public DbSet FacilityContacts => Set(); public DbSet FacilityQualificationRates => Set(); + public DbSet EmployeeFacilityDistances => Set(); public DbSet Contracts => Set(); public DbSet Orders => Set(); public DbSet Absences => Set(); diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/EmployeeFacilityDistanceRepository.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/EmployeeFacilityDistanceRepository.cs new file mode 100644 index 0000000..51eba40 --- /dev/null +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/EmployeeFacilityDistanceRepository.cs @@ -0,0 +1,82 @@ +using Microsoft.EntityFrameworkCore; +using OmsorgCore.Application.Abstractions; +using OmsorgCore.Domain.Entities; +using OmsorgCore.Infrastructure.Persistence; + +namespace OmsorgCore.Infrastructure.Repositories; + +public class EmployeeFacilityDistanceRepository : IEmployeeFacilityDistanceRepository +{ + private readonly OmsorgCoreDbContext _db; + + public EmployeeFacilityDistanceRepository(OmsorgCoreDbContext db) + { + _db = db; + } + + public Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) + => _db.EmployeeFacilityDistances.FirstOrDefaultAsync(d => d.Id == id, cancellationToken); + + public async Task> GetByFacilityIdAsync(Guid facilityId, CancellationToken cancellationToken = default) + => await _db.EmployeeFacilityDistances + .AsNoTracking() + .Include(d => d.Employee) + .Where(d => d.FacilityId == facilityId) + .OrderBy(d => d.Employee.LastName) + .ThenBy(d => d.Employee.FirstName) + .ToListAsync(cancellationToken); + + public Task GetByFacilityAndEmployeeAsync(Guid facilityId, Guid employeeId, CancellationToken cancellationToken = default) + => _db.EmployeeFacilityDistances.FirstOrDefaultAsync(d => d.FacilityId == facilityId && d.EmployeeId == employeeId, cancellationToken); + + public async Task AddAsync(EmployeeFacilityDistance distance, CancellationToken cancellationToken = default) + => await _db.EmployeeFacilityDistances.AddAsync(distance, cancellationToken); + + public Task UpdateAsync(EmployeeFacilityDistance distance, CancellationToken cancellationToken = default) + { + _db.EmployeeFacilityDistances.Update(distance); + return Task.CompletedTask; + } + + public async Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var distance = await _db.EmployeeFacilityDistances.FirstOrDefaultAsync(d => d.Id == id, cancellationToken); + if (distance is null) + { + return false; + } + + distance.IsDeleted = true; + distance.DeletedAt = DateTime.UtcNow; + return true; + } + + public async Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + { + var query = _db.EmployeeFacilityDistances.IgnoreQueryFilters().AsNoTracking().Include(d => d.Employee).Where(d => d.IsDeleted); + + if (!string.IsNullOrWhiteSpace(search)) + { + var pattern = $"%{search.Trim()}%"; + query = query.Where(d => EF.Functions.ILike(d.Employee.FirstName, pattern) || EF.Functions.ILike(d.Employee.LastName, pattern)); + } + + return await query.OrderByDescending(d => d.DeletedAt).ToListAsync(cancellationToken); + } + + public async Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var distance = await _db.EmployeeFacilityDistances.IgnoreQueryFilters().FirstOrDefaultAsync(d => d.Id == id && d.IsDeleted, cancellationToken); + if (distance is null) + { + return false; + } + + distance.IsDeleted = false; + distance.DeletedAt = null; + return true; + } + + public Task SaveChangesAsync(CancellationToken cancellationToken = default) + => _db.SaveChangesAsync(cancellationToken); +} diff --git a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/RoleRepository.cs b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/RoleRepository.cs index ab47931..2792e3a 100644 --- a/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/RoleRepository.cs +++ b/omsorgCore/src/OmsorgCore.Infrastructure/Repositories/RoleRepository.cs @@ -34,6 +34,12 @@ public class RoleRepository : IRoleRepository public async Task AddAsync(Role role, CancellationToken cancellationToken = default) => await _db.Roles.AddAsync(role, cancellationToken); + public Task RemoveAsync(Role role, CancellationToken cancellationToken = default) + { + _db.Roles.Remove(role); + return Task.CompletedTask; + } + public Task SaveChangesAsync(CancellationToken cancellationToken = default) => _db.SaveChangesAsync(cancellationToken); } diff --git a/omsorgCore/tests/OmsorgCore.Tests/Services/EmployeeFacilityDistanceServiceTests.cs b/omsorgCore/tests/OmsorgCore.Tests/Services/EmployeeFacilityDistanceServiceTests.cs new file mode 100644 index 0000000..3c65c7b --- /dev/null +++ b/omsorgCore/tests/OmsorgCore.Tests/Services/EmployeeFacilityDistanceServiceTests.cs @@ -0,0 +1,120 @@ +using OmsorgCore.Application.Services; +using OmsorgCore.Domain.Entities; +using OmsorgCore.Tests.TestDoubles; + +namespace OmsorgCore.Tests.Services; + +public class EmployeeFacilityDistanceServiceTests +{ + [Fact] + public async Task CreateAsync_PersistsAllFields() + { + var repository = new FakeEmployeeFacilityDistanceRepository(); + var sut = new EmployeeFacilityDistanceService(repository); + var facilityId = Guid.NewGuid(); + var employeeId = Guid.NewGuid(); + + var distance = new EmployeeFacilityDistance + { + FacilityId = facilityId, + EmployeeId = employeeId, + DistanceKm = 12.5m + }; + + var created = await sut.CreateAsync(distance); + + Assert.Equal(facilityId, created.FacilityId); + Assert.Equal(employeeId, created.EmployeeId); + Assert.Equal(12.5m, created.DistanceKm); + } + + [Fact] + public async Task GetByFacilityIdAsync_ReturnsOnlyDistancesOfThatFacility() + { + var repository = new FakeEmployeeFacilityDistanceRepository(); + var sut = new EmployeeFacilityDistanceService(repository); + var facilityIdA = Guid.NewGuid(); + var facilityIdB = Guid.NewGuid(); + + await sut.CreateAsync(new EmployeeFacilityDistance { FacilityId = facilityIdA, EmployeeId = Guid.NewGuid(), DistanceKm = 5 }); + await sut.CreateAsync(new EmployeeFacilityDistance { FacilityId = facilityIdA, EmployeeId = Guid.NewGuid(), DistanceKm = 8 }); + await sut.CreateAsync(new EmployeeFacilityDistance { FacilityId = facilityIdB, EmployeeId = Guid.NewGuid(), DistanceKm = 3 }); + + var distancesForA = await sut.GetByFacilityIdAsync(facilityIdA); + + Assert.Equal(2, distancesForA.Count); + Assert.All(distancesForA, d => Assert.Equal(facilityIdA, d.FacilityId)); + } + + [Fact] + public async Task GetByFacilityAndEmployeeAsync_FindsExistingPair() + { + var repository = new FakeEmployeeFacilityDistanceRepository(); + var sut = new EmployeeFacilityDistanceService(repository); + var facilityId = Guid.NewGuid(); + var employeeId = Guid.NewGuid(); + + await sut.CreateAsync(new EmployeeFacilityDistance { FacilityId = facilityId, EmployeeId = employeeId, DistanceKm = 7 }); + + var found = await sut.GetByFacilityAndEmployeeAsync(facilityId, employeeId); + var notFound = await sut.GetByFacilityAndEmployeeAsync(facilityId, Guid.NewGuid()); + + Assert.NotNull(found); + Assert.Equal(7, found!.DistanceKm); + Assert.Null(notFound); + } + + [Fact] + public async Task UpdateAsync_UpdatesDistanceKm_AndReturnsUpdatedEntity() + { + var repository = new FakeEmployeeFacilityDistanceRepository(); + var sut = new EmployeeFacilityDistanceService(repository); + var created = await sut.CreateAsync(new EmployeeFacilityDistance { FacilityId = Guid.NewGuid(), EmployeeId = Guid.NewGuid(), DistanceKm = 5 }); + + var updated = await sut.UpdateAsync(created.Id, new EmployeeFacilityDistance { DistanceKm = 9.5m }); + + Assert.NotNull(updated); + Assert.Equal(9.5m, updated!.DistanceKm); + Assert.NotNull(updated.UpdatedAt); + } + + [Fact] + public async Task UpdateAsync_UnknownId_ReturnsNull() + { + var repository = new FakeEmployeeFacilityDistanceRepository(); + var sut = new EmployeeFacilityDistanceService(repository); + + var updated = await sut.UpdateAsync(Guid.NewGuid(), new EmployeeFacilityDistance { DistanceKm = 1 }); + + Assert.Null(updated); + } + + [Fact] + public async Task DeleteAsync_ThenGetDeletedAsync_ReturnsSoftDeletedDistance() + { + var repository = new FakeEmployeeFacilityDistanceRepository(); + var sut = new EmployeeFacilityDistanceService(repository); + var created = await sut.CreateAsync(new EmployeeFacilityDistance { FacilityId = Guid.NewGuid(), EmployeeId = Guid.NewGuid(), DistanceKm = 5 }); + + var deleted = await sut.DeleteAsync(created.Id); + var deletedList = await sut.GetDeletedAsync(null); + + Assert.True(deleted); + Assert.Contains(deletedList, d => d.Id == created.Id); + } + + [Fact] + public async Task RestoreAsync_UndoesSoftDelete() + { + var repository = new FakeEmployeeFacilityDistanceRepository(); + var sut = new EmployeeFacilityDistanceService(repository); + var created = await sut.CreateAsync(new EmployeeFacilityDistance { FacilityId = Guid.NewGuid(), EmployeeId = Guid.NewGuid(), DistanceKm = 5 }); + await sut.DeleteAsync(created.Id); + + var restored = await sut.RestoreAsync(created.Id); + var deletedList = await sut.GetDeletedAsync(null); + + Assert.True(restored); + Assert.DoesNotContain(deletedList, d => d.Id == created.Id); + } +} diff --git a/omsorgCore/tests/OmsorgCore.Tests/Services/FacilityServiceTests.cs b/omsorgCore/tests/OmsorgCore.Tests/Services/FacilityServiceTests.cs index 45fef17..4d73433 100644 --- a/omsorgCore/tests/OmsorgCore.Tests/Services/FacilityServiceTests.cs +++ b/omsorgCore/tests/OmsorgCore.Tests/Services/FacilityServiceTests.cs @@ -84,6 +84,50 @@ public class FacilityServiceTests Assert.NotNull(updated.UpdatedAt); } + [Fact] + public async Task UpdateAsync_PersistsKonditionenFields() + { + var repository = new FakeFacilityRepository(); + var sut = new FacilityService(repository); + var created = await sut.CreateAsync(new Facility { Name = "Pflegeheim Musterstadt" }); + + var updates = new Facility + { + Name = "Pflegeheim Musterstadt", + CrmStatus = "Kunde", + BillingRate = 32.5m, + NightSurchargePercent = 25, + SaturdaySurchargePercent = 15, + SundaySurchargePercent = 20, + HolidaySurchargePercent = 50, + TravelCostMode = "ProKilometer", + TravelCostRate = 10, + TravelCostPerKm = 0.42m, + MinimumHours = 4, + BreakPolicy = "30 Min. ab 6 Std.", + BillingInterval = "Monatlich", + PaymentTermDays = 14, + IndividualAgreements = "Sonderkonditionen Wochenende" + }; + + var updated = await sut.UpdateAsync(created.Id, updates); + + Assert.NotNull(updated); + Assert.Equal(32.5m, updated!.BillingRate); + Assert.Equal(25, updated.NightSurchargePercent); + Assert.Equal(15, updated.SaturdaySurchargePercent); + Assert.Equal(20, updated.SundaySurchargePercent); + Assert.Equal(50, updated.HolidaySurchargePercent); + Assert.Equal("ProKilometer", updated.TravelCostMode); + Assert.Equal(10, updated.TravelCostRate); + Assert.Equal(0.42m, updated.TravelCostPerKm); + Assert.Equal(4, updated.MinimumHours); + Assert.Equal("30 Min. ab 6 Std.", updated.BreakPolicy); + Assert.Equal("Monatlich", updated.BillingInterval); + Assert.Equal(14, updated.PaymentTermDays); + Assert.Equal("Sonderkonditionen Wochenende", updated.IndividualAgreements); + } + [Fact] public async Task UpdateAsync_UnknownId_ReturnsNull() { diff --git a/omsorgCore/tests/OmsorgCore.Tests/Services/RoleServiceTests.cs b/omsorgCore/tests/OmsorgCore.Tests/Services/RoleServiceTests.cs index 26036d7..0f18267 100644 --- a/omsorgCore/tests/OmsorgCore.Tests/Services/RoleServiceTests.cs +++ b/omsorgCore/tests/OmsorgCore.Tests/Services/RoleServiceTests.cs @@ -15,7 +15,7 @@ public class RoleServiceTests public async Task CreateAsync_WithNewName_CreatesRole() { var roles = new FakeRoleRepository(); - var sut = new RoleService(roles, CreatePermissionService()); + var sut = new RoleService(roles, CreatePermissionService(), new FakeUserRepository()); var result = await sut.CreateAsync("Aussendienst"); @@ -29,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, CreatePermissionService()); + var sut = new RoleService(roles, CreatePermissionService(), new FakeUserRepository()); var result = await sut.CreateAsync("Aussendienst"); @@ -40,7 +40,7 @@ public class RoleServiceTests [Fact] public async Task CreateAsync_WithEmptyName_Fails() { - var sut = new RoleService(new FakeRoleRepository(), CreatePermissionService()); + var sut = new RoleService(new FakeRoleRepository(), CreatePermissionService(), new FakeUserRepository()); var result = await sut.CreateAsync(" "); @@ -54,7 +54,7 @@ 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, CreatePermissionService()); + var sut = new RoleService(roles, CreatePermissionService(), new FakeUserRepository()); var result = await sut.UpdatePermissionsAsync(role.Id, new[] { (ModuleType.Invoices, PermissionAction.Edit, PermissionScope.All) }); @@ -71,7 +71,7 @@ public class RoleServiceTests { var role = new Role { Name = "Außendienst" }; var roles = new FakeRoleRepository(role); - var sut = new RoleService(roles, CreatePermissionService()); + var sut = new RoleService(roles, CreatePermissionService(), new FakeUserRepository()); var result = await sut.UpdatePermissionsAsync(role.Id, new[] { (ModuleType.Employees, PermissionAction.View, PermissionScope.Own) }); @@ -84,11 +84,51 @@ public class RoleServiceTests [Fact] public async Task UpdatePermissionsAsync_UnknownRole_Fails() { - var sut = new RoleService(new FakeRoleRepository(), CreatePermissionService()); + var sut = new RoleService(new FakeRoleRepository(), CreatePermissionService(), new FakeUserRepository()); var result = await sut.UpdatePermissionsAsync(Guid.NewGuid(), new[] { (ModuleType.Employees, PermissionAction.View, PermissionScope.All) }); Assert.False(result.Success); Assert.Equal(UpdateRolePermissionsFailureReason.RoleNotFound, result.FailureReason); } + + [Fact] + public async Task DeleteAsync_UnassignedRole_DeletesIt() + { + var role = new Role { Name = "Recruiting" }; + var roles = new FakeRoleRepository(role); + var sut = new RoleService(roles, CreatePermissionService(), new FakeUserRepository()); + + var result = await sut.DeleteAsync(role.Id); + + Assert.True(result.Success); + Assert.Empty(await roles.GetAllAsync()); + } + + [Fact] + public async Task DeleteAsync_RoleStillAssignedToUser_Fails() + { + var role = new Role { Name = "Recruiting" }; + var roles = new FakeRoleRepository(role); + var user = new User { Username = "sascha", RoleId = role.Id }; + var sut = new RoleService(roles, CreatePermissionService(), new FakeUserRepository(user)); + + var result = await sut.DeleteAsync(role.Id); + + Assert.False(result.Success); + Assert.Equal(DeleteRoleFailureReason.InUse, result.FailureReason); + Assert.Equal(1, result.AssignedUserCount); + Assert.Single(await roles.GetAllAsync()); + } + + [Fact] + public async Task DeleteAsync_UnknownRole_Fails() + { + var sut = new RoleService(new FakeRoleRepository(), CreatePermissionService(), new FakeUserRepository()); + + var result = await sut.DeleteAsync(Guid.NewGuid()); + + Assert.False(result.Success); + Assert.Equal(DeleteRoleFailureReason.RoleNotFound, result.FailureReason); + } } diff --git a/omsorgCore/tests/OmsorgCore.Tests/TestDoubles/FakeRepositories.cs b/omsorgCore/tests/OmsorgCore.Tests/TestDoubles/FakeRepositories.cs index b129551..e595694 100644 --- a/omsorgCore/tests/OmsorgCore.Tests/TestDoubles/FakeRepositories.cs +++ b/omsorgCore/tests/OmsorgCore.Tests/TestDoubles/FakeRepositories.cs @@ -386,6 +386,63 @@ public class FakeFacilityQualificationRateRepository : IFacilityQualificationRat => Task.CompletedTask; } +public class FakeEmployeeFacilityDistanceRepository : IEmployeeFacilityDistanceRepository +{ + private readonly List _distances = new(); + + public Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) + => Task.FromResult(_distances.FirstOrDefault(d => d.Id == id)); + + public Task> GetByFacilityIdAsync(Guid facilityId, CancellationToken cancellationToken = default) + => Task.FromResult>( + _distances.Where(d => d.FacilityId == facilityId).ToList()); + + public Task GetByFacilityAndEmployeeAsync(Guid facilityId, Guid employeeId, CancellationToken cancellationToken = default) + => Task.FromResult(_distances.FirstOrDefault(d => d.FacilityId == facilityId && d.EmployeeId == employeeId)); + + public Task AddAsync(EmployeeFacilityDistance distance, CancellationToken cancellationToken = default) + { + _distances.Add(distance); + return Task.CompletedTask; + } + + public Task UpdateAsync(EmployeeFacilityDistance distance, CancellationToken cancellationToken = default) + => Task.CompletedTask; + + public Task SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var distance = _distances.FirstOrDefault(d => d.Id == id); + if (distance is null) + { + return Task.FromResult(false); + } + + distance.IsDeleted = true; + distance.DeletedAt = DateTime.UtcNow; + return Task.FromResult(true); + } + + public Task> GetDeletedAsync(string? search, CancellationToken cancellationToken = default) + => Task.FromResult>( + _distances.Where(d => d.IsDeleted).OrderByDescending(d => d.DeletedAt).ToList()); + + public Task RestoreAsync(Guid id, CancellationToken cancellationToken = default) + { + var distance = _distances.FirstOrDefault(d => d.Id == id && d.IsDeleted); + if (distance is null) + { + return Task.FromResult(false); + } + + distance.IsDeleted = false; + distance.DeletedAt = null; + return Task.FromResult(true); + } + + public Task SaveChangesAsync(CancellationToken cancellationToken = default) + => Task.CompletedTask; +} + public class FakeContractRepository : IContractRepository { private readonly List _contracts = new(); @@ -505,6 +562,12 @@ public class FakeRoleRepository : IRoleRepository return Task.CompletedTask; } + public Task RemoveAsync(Role role, CancellationToken cancellationToken = default) + { + _roles.RemoveAll(r => r.Id == role.Id); + return Task.CompletedTask; + } + public Task AddPermissionRangeAsync(IEnumerable permissions, CancellationToken cancellationToken = default) { foreach (var permission in permissions) diff --git a/omsorgWeb/CLAUDE.md b/omsorgWeb/CLAUDE.md index 520019f..e85278d 100644 --- a/omsorgWeb/CLAUDE.md +++ b/omsorgWeb/CLAUDE.md @@ -9,7 +9,7 @@ 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-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. +Frischer, minimaler PHP-Flow gegen `omsorgCore` — kein Framework, gleiches Deployment-Modell wie die Legacy-App. Login/Passwort, das Abwesenheits-/Urlaubs-/Krankmeldungsformular, die strukturierte Zeiterfassung pro Schicht und die eigene Fahrtstrecken-Pflege (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. **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. @@ -17,6 +17,8 @@ Frischer, minimaler PHP-Flow gegen `omsorgCore` — kein Framework, gleiches Dep **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. +**Fahrtstrecken (FR-EIN-4, `pages/fahrtstrecken.php`):** Selbstbedienungsseite, mit der ein Mitarbeiter seine eigene Entfernung (km, einfache Strecke) je Einrichtung pflegt — Grundlage für die kilometerbasierte Fahrtkostenabrechnung, wenn eine Einrichtung in `omsorgapp` auf `TravelCostMode = "ProKilometer"` gestellt ist (siehe `omsorgCore/CLAUDE.md`, "Konditionen einer Einrichtung"). 1:1 nach dem `urlaubsantrag.php`-Muster (Liste links/Formular rechts, `mode=create|edit`, Post-Redirect-Get bei Update), aber gegen einen eigenen, engeren Endpunkt statt der vollen Facilities-API: `omsorgcore_my_facility_distances_list/create/update()` (`lib/omsorgCoreClient.php`) gegen `/api/me/facility-distances` (`MyFacilityDistancesController`) — schickt bewusst **keine** `employeeId` mit, das Backend löst den eingeloggten Mitarbeiter serverseitig auf, exakt wie bei Abwesenheiten/Zeiterfassung. Die Einrichtungsauswahl im Anlegen-Formular kommt über `omsorgcore_my_facility_distances_facility_options()` gegen `/api/me/facility-distances/facilities` — liefert bewusst nur `id`/`name`, keine Konditionen/CRM-Daten, weil der Außendienst kein `Facilities`-Recht hat (eigener `ModuleType.EmployeeFacilityDistances`, siehe `omsorgCore/CLAUDE.md`, "Rechtesystem"). Das Dropdown zeigt nur Einrichtungen, für die noch keine eigene Distanz existiert (Duplikate serverseitig ohnehin abgelehnt); Bearbeiten ändert nur die km-Zahl, die Einrichtung selbst ist danach fix. Kein `DELETE` hier — Löschen bleibt Büro-Aufgabe in `omsorgapp`. + **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. diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/.openapi-generator/FILES b/omsorgWeb/mitarbeiter-app/api-client-php/.openapi-generator/FILES index 60a584a..23e10bd 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/.openapi-generator/FILES +++ b/omsorgWeb/mitarbeiter-app/api-client-php/.openapi-generator/FILES @@ -10,11 +10,13 @@ docs/Api/AuditLogApi.md docs/Api/AuthApi.md docs/Api/ContractsApi.md docs/Api/DocumentsApi.md +docs/Api/EmployeeFacilityDistancesApi.md docs/Api/EmployeesApi.md docs/Api/FacilitiesApi.md docs/Api/FacilityContactsApi.md docs/Api/FacilityQualificationRatesApi.md docs/Api/HealthApi.md +docs/Api/MyFacilityDistancesApi.md docs/Api/OrdersApi.md docs/Api/RolesApi.md docs/Api/TimeEntriesApi.md @@ -33,19 +35,23 @@ docs/Model/ContractResponse.md docs/Model/ContractResponsePagedResponse.md docs/Model/CreateAbsenceRequest.md docs/Model/CreateContractRequest.md +docs/Model/CreateEmployeeFacilityDistanceRequest.md docs/Model/CreateEmployeeRequest.md docs/Model/CreateFacilityContactRequest.md docs/Model/CreateFacilityQualificationRateRequest.md docs/Model/CreateFacilityRequest.md +docs/Model/CreateMyFacilityDistanceRequest.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/EmployeeFacilityDistanceResponse.md docs/Model/EmployeeResponse.md docs/Model/EmployeeResponsePagedResponse.md docs/Model/FacilityContactResponse.md +docs/Model/FacilityOptionResponse.md docs/Model/FacilityQualificationRateResponse.md docs/Model/FacilityResponse.md docs/Model/FacilityResponsePagedResponse.md @@ -59,6 +65,7 @@ docs/Model/LoginResponse.md docs/Model/LogoutRequest.md docs/Model/MeResponse.md docs/Model/ModuleType.md +docs/Model/MyFacilityDistanceResponse.md docs/Model/OrderResponse.md docs/Model/OrderResponsePagedResponse.md docs/Model/PasswordPolicyResponse.md @@ -78,6 +85,7 @@ docs/Model/TimeEntryResponse.md docs/Model/TimeEntryResponsePagedResponse.md docs/Model/TrashAbsenceResponse.md docs/Model/TrashContractResponse.md +docs/Model/TrashEmployeeFacilityDistanceResponse.md docs/Model/TrashEmployeeResponse.md docs/Model/TrashFacilityContactResponse.md docs/Model/TrashFacilityQualificationRateResponse.md @@ -87,10 +95,12 @@ docs/Model/TrashTimeEntryResponse.md docs/Model/UpdateAbsenceRequest.md docs/Model/UpdateContractRequest.md docs/Model/UpdateDocumentRequest.md +docs/Model/UpdateEmployeeFacilityDistanceRequest.md docs/Model/UpdateEmployeeRequest.md docs/Model/UpdateFacilityContactRequest.md docs/Model/UpdateFacilityQualificationRateRequest.md docs/Model/UpdateFacilityRequest.md +docs/Model/UpdateMyFacilityDistanceRequest.md docs/Model/UpdateOrderRequest.md docs/Model/UpdateRolePermissionsRequest.md docs/Model/UpdateTimeEntryRequest.md @@ -111,11 +121,13 @@ lib/Api/AuditLogApi.php lib/Api/AuthApi.php lib/Api/ContractsApi.php lib/Api/DocumentsApi.php +lib/Api/EmployeeFacilityDistancesApi.php lib/Api/EmployeesApi.php lib/Api/FacilitiesApi.php lib/Api/FacilityContactsApi.php lib/Api/FacilityQualificationRatesApi.php lib/Api/HealthApi.php +lib/Api/MyFacilityDistancesApi.php lib/Api/OrdersApi.php lib/Api/RolesApi.php lib/Api/TimeEntriesApi.php @@ -138,19 +150,23 @@ lib/Model/ContractResponse.php lib/Model/ContractResponsePagedResponse.php lib/Model/CreateAbsenceRequest.php lib/Model/CreateContractRequest.php +lib/Model/CreateEmployeeFacilityDistanceRequest.php lib/Model/CreateEmployeeRequest.php lib/Model/CreateFacilityContactRequest.php lib/Model/CreateFacilityQualificationRateRequest.php lib/Model/CreateFacilityRequest.php +lib/Model/CreateMyFacilityDistanceRequest.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/EmployeeFacilityDistanceResponse.php lib/Model/EmployeeResponse.php lib/Model/EmployeeResponsePagedResponse.php lib/Model/FacilityContactResponse.php +lib/Model/FacilityOptionResponse.php lib/Model/FacilityQualificationRateResponse.php lib/Model/FacilityResponse.php lib/Model/FacilityResponsePagedResponse.php @@ -165,6 +181,7 @@ lib/Model/LogoutRequest.php lib/Model/MeResponse.php lib/Model/ModelInterface.php lib/Model/ModuleType.php +lib/Model/MyFacilityDistanceResponse.php lib/Model/OrderResponse.php lib/Model/OrderResponsePagedResponse.php lib/Model/PasswordPolicyResponse.php @@ -184,6 +201,7 @@ lib/Model/TimeEntryResponse.php lib/Model/TimeEntryResponsePagedResponse.php lib/Model/TrashAbsenceResponse.php lib/Model/TrashContractResponse.php +lib/Model/TrashEmployeeFacilityDistanceResponse.php lib/Model/TrashEmployeeResponse.php lib/Model/TrashFacilityContactResponse.php lib/Model/TrashFacilityQualificationRateResponse.php @@ -193,10 +211,12 @@ lib/Model/TrashTimeEntryResponse.php lib/Model/UpdateAbsenceRequest.php lib/Model/UpdateContractRequest.php lib/Model/UpdateDocumentRequest.php +lib/Model/UpdateEmployeeFacilityDistanceRequest.php lib/Model/UpdateEmployeeRequest.php lib/Model/UpdateFacilityContactRequest.php lib/Model/UpdateFacilityQualificationRateRequest.php lib/Model/UpdateFacilityRequest.php +lib/Model/UpdateMyFacilityDistanceRequest.php lib/Model/UpdateOrderRequest.php lib/Model/UpdateRolePermissionsRequest.php lib/Model/UpdateTimeEntryRequest.php @@ -211,3 +231,13 @@ lib/Model/ValueListTransitionResponse.php lib/Model/ValueListUsageResponse.php lib/ObjectSerializer.php phpunit.xml.dist +test/Api/EmployeeFacilityDistancesApiTest.php +test/Api/MyFacilityDistancesApiTest.php +test/Model/CreateEmployeeFacilityDistanceRequestTest.php +test/Model/CreateMyFacilityDistanceRequestTest.php +test/Model/EmployeeFacilityDistanceResponseTest.php +test/Model/FacilityOptionResponseTest.php +test/Model/MyFacilityDistanceResponseTest.php +test/Model/TrashEmployeeFacilityDistanceResponseTest.php +test/Model/UpdateEmployeeFacilityDistanceRequestTest.php +test/Model/UpdateMyFacilityDistanceRequestTest.php diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/README.md b/omsorgWeb/mitarbeiter-app/api-client-php/README.md index 3bedb46..c0e18ae 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/README.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/README.md @@ -110,6 +110,10 @@ Class | Method | HTTP request | Description *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 | +*EmployeeFacilityDistancesApi* | [**apiFacilitiesFacilityIdEmployeeDistancesGet**](docs/Api/EmployeeFacilityDistancesApi.md#apifacilitiesfacilityidemployeedistancesget) | **GET** /api/facilities/{facilityId}/employee-distances | +*EmployeeFacilityDistancesApi* | [**apiFacilitiesFacilityIdEmployeeDistancesIdDelete**](docs/Api/EmployeeFacilityDistancesApi.md#apifacilitiesfacilityidemployeedistancesiddelete) | **DELETE** /api/facilities/{facilityId}/employee-distances/{id} | +*EmployeeFacilityDistancesApi* | [**apiFacilitiesFacilityIdEmployeeDistancesIdPut**](docs/Api/EmployeeFacilityDistancesApi.md#apifacilitiesfacilityidemployeedistancesidput) | **PUT** /api/facilities/{facilityId}/employee-distances/{id} | +*EmployeeFacilityDistancesApi* | [**apiFacilitiesFacilityIdEmployeeDistancesPost**](docs/Api/EmployeeFacilityDistancesApi.md#apifacilitiesfacilityidemployeedistancespost) | **POST** /api/facilities/{facilityId}/employee-distances | *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} | @@ -129,6 +133,10 @@ Class | Method | HTTP request | Description *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 | +*MyFacilityDistancesApi* | [**apiMeFacilityDistancesFacilitiesGet**](docs/Api/MyFacilityDistancesApi.md#apimefacilitydistancesfacilitiesget) | **GET** /api/me/facility-distances/facilities | +*MyFacilityDistancesApi* | [**apiMeFacilityDistancesGet**](docs/Api/MyFacilityDistancesApi.md#apimefacilitydistancesget) | **GET** /api/me/facility-distances | +*MyFacilityDistancesApi* | [**apiMeFacilityDistancesIdPut**](docs/Api/MyFacilityDistancesApi.md#apimefacilitydistancesidput) | **PUT** /api/me/facility-distances/{id} | +*MyFacilityDistancesApi* | [**apiMeFacilityDistancesPost**](docs/Api/MyFacilityDistancesApi.md#apimefacilitydistancespost) | **POST** /api/me/facility-distances | *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} | @@ -149,6 +157,8 @@ Class | Method | HTTP request | Description *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* | [**apiTrashEmployeeFacilityDistancesGet**](docs/Api/TrashApi.md#apitrashemployeefacilitydistancesget) | **GET** /api/trash/employee-facility-distances | +*TrashApi* | [**apiTrashEmployeeFacilityDistancesIdRestorePost**](docs/Api/TrashApi.md#apitrashemployeefacilitydistancesidrestorepost) | **POST** /api/trash/employee-facility-distances/{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 | @@ -191,19 +201,23 @@ Class | Method | HTTP request | Description - [ContractResponsePagedResponse](docs/Model/ContractResponsePagedResponse.md) - [CreateAbsenceRequest](docs/Model/CreateAbsenceRequest.md) - [CreateContractRequest](docs/Model/CreateContractRequest.md) +- [CreateEmployeeFacilityDistanceRequest](docs/Model/CreateEmployeeFacilityDistanceRequest.md) - [CreateEmployeeRequest](docs/Model/CreateEmployeeRequest.md) - [CreateFacilityContactRequest](docs/Model/CreateFacilityContactRequest.md) - [CreateFacilityQualificationRateRequest](docs/Model/CreateFacilityQualificationRateRequest.md) - [CreateFacilityRequest](docs/Model/CreateFacilityRequest.md) +- [CreateMyFacilityDistanceRequest](docs/Model/CreateMyFacilityDistanceRequest.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) +- [EmployeeFacilityDistanceResponse](docs/Model/EmployeeFacilityDistanceResponse.md) - [EmployeeResponse](docs/Model/EmployeeResponse.md) - [EmployeeResponsePagedResponse](docs/Model/EmployeeResponsePagedResponse.md) - [FacilityContactResponse](docs/Model/FacilityContactResponse.md) +- [FacilityOptionResponse](docs/Model/FacilityOptionResponse.md) - [FacilityQualificationRateResponse](docs/Model/FacilityQualificationRateResponse.md) - [FacilityResponse](docs/Model/FacilityResponse.md) - [FacilityResponsePagedResponse](docs/Model/FacilityResponsePagedResponse.md) @@ -217,6 +231,7 @@ Class | Method | HTTP request | Description - [LogoutRequest](docs/Model/LogoutRequest.md) - [MeResponse](docs/Model/MeResponse.md) - [ModuleType](docs/Model/ModuleType.md) +- [MyFacilityDistanceResponse](docs/Model/MyFacilityDistanceResponse.md) - [OrderResponse](docs/Model/OrderResponse.md) - [OrderResponsePagedResponse](docs/Model/OrderResponsePagedResponse.md) - [PasswordPolicyResponse](docs/Model/PasswordPolicyResponse.md) @@ -236,6 +251,7 @@ Class | Method | HTTP request | Description - [TimeEntryResponsePagedResponse](docs/Model/TimeEntryResponsePagedResponse.md) - [TrashAbsenceResponse](docs/Model/TrashAbsenceResponse.md) - [TrashContractResponse](docs/Model/TrashContractResponse.md) +- [TrashEmployeeFacilityDistanceResponse](docs/Model/TrashEmployeeFacilityDistanceResponse.md) - [TrashEmployeeResponse](docs/Model/TrashEmployeeResponse.md) - [TrashFacilityContactResponse](docs/Model/TrashFacilityContactResponse.md) - [TrashFacilityQualificationRateResponse](docs/Model/TrashFacilityQualificationRateResponse.md) @@ -245,10 +261,12 @@ Class | Method | HTTP request | Description - [UpdateAbsenceRequest](docs/Model/UpdateAbsenceRequest.md) - [UpdateContractRequest](docs/Model/UpdateContractRequest.md) - [UpdateDocumentRequest](docs/Model/UpdateDocumentRequest.md) +- [UpdateEmployeeFacilityDistanceRequest](docs/Model/UpdateEmployeeFacilityDistanceRequest.md) - [UpdateEmployeeRequest](docs/Model/UpdateEmployeeRequest.md) - [UpdateFacilityContactRequest](docs/Model/UpdateFacilityContactRequest.md) - [UpdateFacilityQualificationRateRequest](docs/Model/UpdateFacilityQualificationRateRequest.md) - [UpdateFacilityRequest](docs/Model/UpdateFacilityRequest.md) +- [UpdateMyFacilityDistanceRequest](docs/Model/UpdateMyFacilityDistanceRequest.md) - [UpdateOrderRequest](docs/Model/UpdateOrderRequest.md) - [UpdateRolePermissionsRequest](docs/Model/UpdateRolePermissionsRequest.md) - [UpdateTimeEntryRequest](docs/Model/UpdateTimeEntryRequest.md) diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/EmployeeFacilityDistancesApi.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/EmployeeFacilityDistancesApi.md new file mode 100644 index 0000000..7d7830c --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/EmployeeFacilityDistancesApi.md @@ -0,0 +1,250 @@ +# OmsorgCoreClient\EmployeeFacilityDistancesApi + +All URIs are relative to http://localhost, except if the operation defines another base path. + +| Method | HTTP request | Description | +| ------------- | ------------- | ------------- | +| [**apiFacilitiesFacilityIdEmployeeDistancesGet()**](EmployeeFacilityDistancesApi.md#apiFacilitiesFacilityIdEmployeeDistancesGet) | **GET** /api/facilities/{facilityId}/employee-distances | | +| [**apiFacilitiesFacilityIdEmployeeDistancesIdDelete()**](EmployeeFacilityDistancesApi.md#apiFacilitiesFacilityIdEmployeeDistancesIdDelete) | **DELETE** /api/facilities/{facilityId}/employee-distances/{id} | | +| [**apiFacilitiesFacilityIdEmployeeDistancesIdPut()**](EmployeeFacilityDistancesApi.md#apiFacilitiesFacilityIdEmployeeDistancesIdPut) | **PUT** /api/facilities/{facilityId}/employee-distances/{id} | | +| [**apiFacilitiesFacilityIdEmployeeDistancesPost()**](EmployeeFacilityDistancesApi.md#apiFacilitiesFacilityIdEmployeeDistancesPost) | **POST** /api/facilities/{facilityId}/employee-distances | | + + +## `apiFacilitiesFacilityIdEmployeeDistancesGet()` + +```php +apiFacilitiesFacilityIdEmployeeDistancesGet($facility_id): \OmsorgCoreClient\Model\EmployeeFacilityDistanceResponse[] +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\EmployeeFacilityDistancesApi( + // 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->apiFacilitiesFacilityIdEmployeeDistancesGet($facility_id); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling EmployeeFacilityDistancesApi->apiFacilitiesFacilityIdEmployeeDistancesGet: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **facility_id** | **string**| | | + +### Return type + +[**\OmsorgCoreClient\Model\EmployeeFacilityDistanceResponse[]**](../Model/EmployeeFacilityDistanceResponse.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) + +## `apiFacilitiesFacilityIdEmployeeDistancesIdDelete()` + +```php +apiFacilitiesFacilityIdEmployeeDistancesIdDelete($facility_id, $id) +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\EmployeeFacilityDistancesApi( + // 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->apiFacilitiesFacilityIdEmployeeDistancesIdDelete($facility_id, $id); +} catch (Exception $e) { + echo 'Exception when calling EmployeeFacilityDistancesApi->apiFacilitiesFacilityIdEmployeeDistancesIdDelete: ', $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) + +## `apiFacilitiesFacilityIdEmployeeDistancesIdPut()` + +```php +apiFacilitiesFacilityIdEmployeeDistancesIdPut($facility_id, $id, $update_employee_facility_distance_request): \OmsorgCoreClient\Model\EmployeeFacilityDistanceResponse +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\EmployeeFacilityDistancesApi( + // 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_employee_facility_distance_request = new \OmsorgCoreClient\Model\UpdateEmployeeFacilityDistanceRequest(); // \OmsorgCoreClient\Model\UpdateEmployeeFacilityDistanceRequest + +try { + $result = $apiInstance->apiFacilitiesFacilityIdEmployeeDistancesIdPut($facility_id, $id, $update_employee_facility_distance_request); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling EmployeeFacilityDistancesApi->apiFacilitiesFacilityIdEmployeeDistancesIdPut: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **facility_id** | **string**| | | +| **id** | **string**| | | +| **update_employee_facility_distance_request** | [**\OmsorgCoreClient\Model\UpdateEmployeeFacilityDistanceRequest**](../Model/UpdateEmployeeFacilityDistanceRequest.md)| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\EmployeeFacilityDistanceResponse**](../Model/EmployeeFacilityDistanceResponse.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) + +## `apiFacilitiesFacilityIdEmployeeDistancesPost()` + +```php +apiFacilitiesFacilityIdEmployeeDistancesPost($facility_id, $create_employee_facility_distance_request): \OmsorgCoreClient\Model\EmployeeFacilityDistanceResponse +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\EmployeeFacilityDistancesApi( + // 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_employee_facility_distance_request = new \OmsorgCoreClient\Model\CreateEmployeeFacilityDistanceRequest(); // \OmsorgCoreClient\Model\CreateEmployeeFacilityDistanceRequest + +try { + $result = $apiInstance->apiFacilitiesFacilityIdEmployeeDistancesPost($facility_id, $create_employee_facility_distance_request); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling EmployeeFacilityDistancesApi->apiFacilitiesFacilityIdEmployeeDistancesPost: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **facility_id** | **string**| | | +| **create_employee_facility_distance_request** | [**\OmsorgCoreClient\Model\CreateEmployeeFacilityDistanceRequest**](../Model/CreateEmployeeFacilityDistanceRequest.md)| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\EmployeeFacilityDistanceResponse**](../Model/EmployeeFacilityDistanceResponse.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/MyFacilityDistancesApi.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/MyFacilityDistancesApi.md new file mode 100644 index 0000000..06db2cb --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/MyFacilityDistancesApi.md @@ -0,0 +1,239 @@ +# OmsorgCoreClient\MyFacilityDistancesApi + +All URIs are relative to http://localhost, except if the operation defines another base path. + +| Method | HTTP request | Description | +| ------------- | ------------- | ------------- | +| [**apiMeFacilityDistancesFacilitiesGet()**](MyFacilityDistancesApi.md#apiMeFacilityDistancesFacilitiesGet) | **GET** /api/me/facility-distances/facilities | | +| [**apiMeFacilityDistancesGet()**](MyFacilityDistancesApi.md#apiMeFacilityDistancesGet) | **GET** /api/me/facility-distances | | +| [**apiMeFacilityDistancesIdPut()**](MyFacilityDistancesApi.md#apiMeFacilityDistancesIdPut) | **PUT** /api/me/facility-distances/{id} | | +| [**apiMeFacilityDistancesPost()**](MyFacilityDistancesApi.md#apiMeFacilityDistancesPost) | **POST** /api/me/facility-distances | | + + +## `apiMeFacilityDistancesFacilitiesGet()` + +```php +apiMeFacilityDistancesFacilitiesGet(): \OmsorgCoreClient\Model\FacilityOptionResponse[] +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\MyFacilityDistancesApi( + // 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 +); + +try { + $result = $apiInstance->apiMeFacilityDistancesFacilitiesGet(); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling MyFacilityDistancesApi->apiMeFacilityDistancesFacilitiesGet: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +This endpoint does not need any parameter. + +### Return type + +[**\OmsorgCoreClient\Model\FacilityOptionResponse[]**](../Model/FacilityOptionResponse.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) + +## `apiMeFacilityDistancesGet()` + +```php +apiMeFacilityDistancesGet(): \OmsorgCoreClient\Model\MyFacilityDistanceResponse[] +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\MyFacilityDistancesApi( + // 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 +); + +try { + $result = $apiInstance->apiMeFacilityDistancesGet(); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling MyFacilityDistancesApi->apiMeFacilityDistancesGet: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +This endpoint does not need any parameter. + +### Return type + +[**\OmsorgCoreClient\Model\MyFacilityDistanceResponse[]**](../Model/MyFacilityDistanceResponse.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) + +## `apiMeFacilityDistancesIdPut()` + +```php +apiMeFacilityDistancesIdPut($id, $update_my_facility_distance_request): \OmsorgCoreClient\Model\MyFacilityDistanceResponse +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\MyFacilityDistancesApi( + // 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_my_facility_distance_request = new \OmsorgCoreClient\Model\UpdateMyFacilityDistanceRequest(); // \OmsorgCoreClient\Model\UpdateMyFacilityDistanceRequest + +try { + $result = $apiInstance->apiMeFacilityDistancesIdPut($id, $update_my_facility_distance_request); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling MyFacilityDistancesApi->apiMeFacilityDistancesIdPut: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **id** | **string**| | | +| **update_my_facility_distance_request** | [**\OmsorgCoreClient\Model\UpdateMyFacilityDistanceRequest**](../Model/UpdateMyFacilityDistanceRequest.md)| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\MyFacilityDistanceResponse**](../Model/MyFacilityDistanceResponse.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) + +## `apiMeFacilityDistancesPost()` + +```php +apiMeFacilityDistancesPost($create_my_facility_distance_request): \OmsorgCoreClient\Model\MyFacilityDistanceResponse +``` + + + +### Example + +```php +setAccessToken('YOUR_ACCESS_TOKEN'); + + +$apiInstance = new OmsorgCoreClient\Api\MyFacilityDistancesApi( + // 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_my_facility_distance_request = new \OmsorgCoreClient\Model\CreateMyFacilityDistanceRequest(); // \OmsorgCoreClient\Model\CreateMyFacilityDistanceRequest + +try { + $result = $apiInstance->apiMeFacilityDistancesPost($create_my_facility_distance_request); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling MyFacilityDistancesApi->apiMeFacilityDistancesPost: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **create_my_facility_distance_request** | [**\OmsorgCoreClient\Model\CreateMyFacilityDistanceRequest**](../Model/CreateMyFacilityDistanceRequest.md)| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\MyFacilityDistanceResponse**](../Model/MyFacilityDistanceResponse.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 index 9dfd684..b0b0e87 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/TrashApi.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Api/TrashApi.md @@ -8,6 +8,8 @@ All URIs are relative to http://localhost, except if the operation defines anoth | [**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 | | +| [**apiTrashEmployeeFacilityDistancesGet()**](TrashApi.md#apiTrashEmployeeFacilityDistancesGet) | **GET** /api/trash/employee-facility-distances | | +| [**apiTrashEmployeeFacilityDistancesIdRestorePost()**](TrashApi.md#apiTrashEmployeeFacilityDistancesIdRestorePost) | **POST** /api/trash/employee-facility-distances/{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 | | @@ -252,6 +254,121 @@ void (empty response body) [[Back to Model list]](../../README.md#models) [[Back to README]](../../README.md) +## `apiTrashEmployeeFacilityDistancesGet()` + +```php +apiTrashEmployeeFacilityDistancesGet($search): \OmsorgCoreClient\Model\TrashEmployeeFacilityDistanceResponse[] +``` + + + +### 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->apiTrashEmployeeFacilityDistancesGet($search); + print_r($result); +} catch (Exception $e) { + echo 'Exception when calling TrashApi->apiTrashEmployeeFacilityDistancesGet: ', $e->getMessage(), PHP_EOL; +} +``` + +### Parameters + +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +| **search** | **string**| | [optional] | + +### Return type + +[**\OmsorgCoreClient\Model\TrashEmployeeFacilityDistanceResponse[]**](../Model/TrashEmployeeFacilityDistanceResponse.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) + +## `apiTrashEmployeeFacilityDistancesIdRestorePost()` + +```php +apiTrashEmployeeFacilityDistancesIdRestorePost($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->apiTrashEmployeeFacilityDistancesIdRestorePost($id); +} catch (Exception $e) { + echo 'Exception when calling TrashApi->apiTrashEmployeeFacilityDistancesIdRestorePost: ', $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 diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateEmployeeFacilityDistanceRequest.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateEmployeeFacilityDistanceRequest.md new file mode 100644 index 0000000..2505b8c --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateEmployeeFacilityDistanceRequest.md @@ -0,0 +1,10 @@ +# # CreateEmployeeFacilityDistanceRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**employee_id** | **string** | | [optional] +**distance_km** | **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/CreateMyFacilityDistanceRequest.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateMyFacilityDistanceRequest.md new file mode 100644 index 0000000..dd79b36 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/CreateMyFacilityDistanceRequest.md @@ -0,0 +1,10 @@ +# # CreateMyFacilityDistanceRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**facility_id** | **string** | | [optional] +**distance_km** | **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/EmployeeFacilityDistanceResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/EmployeeFacilityDistanceResponse.md new file mode 100644 index 0000000..eb44e23 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/EmployeeFacilityDistanceResponse.md @@ -0,0 +1,14 @@ +# # EmployeeFacilityDistanceResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **string** | | [optional] +**facility_id** | **string** | | [optional] +**employee_id** | **string** | | [optional] +**employee_first_name** | **string** | | [optional] +**employee_last_name** | **string** | | [optional] +**distance_km** | **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/FacilityOptionResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/FacilityOptionResponse.md new file mode 100644 index 0000000..faa7f8a --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/FacilityOptionResponse.md @@ -0,0 +1,10 @@ +# # FacilityOptionResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **string** | | [optional] +**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/FacilityResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/FacilityResponse.md index 963dfca..ccaf2f9 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/FacilityResponse.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/FacilityResponse.md @@ -24,6 +24,8 @@ Name | Type | Description | Notes **sunday_surcharge_percent** | **float** | | [optional] **holiday_surcharge_percent** | **float** | | [optional] **travel_cost_rate** | **float** | | [optional] +**travel_cost_mode** | **string** | | [optional] +**travel_cost_per_km** | **float** | | [optional] **minimum_hours** | **float** | | [optional] **break_policy** | **string** | | [optional] **billing_interval** | **string** | | [optional] diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/MyFacilityDistanceResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/MyFacilityDistanceResponse.md new file mode 100644 index 0000000..20a5d6c --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/MyFacilityDistanceResponse.md @@ -0,0 +1,12 @@ +# # MyFacilityDistanceResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **string** | | [optional] +**facility_id** | **string** | | [optional] +**facility_name** | **string** | | [optional] +**distance_km** | **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/TrashEmployeeFacilityDistanceResponse.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashEmployeeFacilityDistanceResponse.md new file mode 100644 index 0000000..83ec28e --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/TrashEmployeeFacilityDistanceResponse.md @@ -0,0 +1,13 @@ +# # TrashEmployeeFacilityDistanceResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **string** | | [optional] +**facility_id** | **string** | | [optional] +**employee_id** | **string** | | [optional] +**distance_km** | **float** | | [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/UpdateEmployeeFacilityDistanceRequest.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateEmployeeFacilityDistanceRequest.md new file mode 100644 index 0000000..3336fe2 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateEmployeeFacilityDistanceRequest.md @@ -0,0 +1,9 @@ +# # UpdateEmployeeFacilityDistanceRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**distance_km** | **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 9bc8291..eac394e 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateFacilityRequest.md +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateFacilityRequest.md @@ -23,6 +23,8 @@ Name | Type | Description | Notes **sunday_surcharge_percent** | **float** | | [optional] **holiday_surcharge_percent** | **float** | | [optional] **travel_cost_rate** | **float** | | [optional] +**travel_cost_mode** | **string** | | [optional] +**travel_cost_per_km** | **float** | | [optional] **minimum_hours** | **float** | | [optional] **break_policy** | **string** | | [optional] **billing_interval** | **string** | | [optional] diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateMyFacilityDistanceRequest.md b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateMyFacilityDistanceRequest.md new file mode 100644 index 0000000..38c97e7 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/docs/Model/UpdateMyFacilityDistanceRequest.md @@ -0,0 +1,9 @@ +# # UpdateMyFacilityDistanceRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**distance_km** | **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/lib/Api/EmployeeFacilityDistancesApi.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/EmployeeFacilityDistancesApi.php new file mode 100644 index 0000000..2c987a6 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/EmployeeFacilityDistancesApi.php @@ -0,0 +1,1274 @@ + [ + 'application/json', + ], + 'apiFacilitiesFacilityIdEmployeeDistancesIdDelete' => [ + 'application/json', + ], + 'apiFacilitiesFacilityIdEmployeeDistancesIdPut' => [ + 'application/json', + 'text/json', + 'application/*+json', + ], + 'apiFacilitiesFacilityIdEmployeeDistancesPost' => [ + '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 apiFacilitiesFacilityIdEmployeeDistancesGet + * + * @param string $facility_id facility_id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesGet'] 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\EmployeeFacilityDistanceResponse[] + */ + public function apiFacilitiesFacilityIdEmployeeDistancesGet($facility_id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesGet'][0]) + { + list($response) = $this->apiFacilitiesFacilityIdEmployeeDistancesGetWithHttpInfo($facility_id, $contentType); + return $response; + } + + /** + * Operation apiFacilitiesFacilityIdEmployeeDistancesGetWithHttpInfo + * + * @param string $facility_id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesGet'] 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\EmployeeFacilityDistanceResponse[], HTTP status code, HTTP response headers (array of strings) + */ + public function apiFacilitiesFacilityIdEmployeeDistancesGetWithHttpInfo($facility_id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesGet'][0]) + { + $request = $this->apiFacilitiesFacilityIdEmployeeDistancesGetRequest($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\EmployeeFacilityDistanceResponse[]', + $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\EmployeeFacilityDistanceResponse[]', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\EmployeeFacilityDistanceResponse[]', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiFacilitiesFacilityIdEmployeeDistancesGetAsync + * + * @param string $facility_id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiFacilitiesFacilityIdEmployeeDistancesGetAsync($facility_id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesGet'][0]) + { + return $this->apiFacilitiesFacilityIdEmployeeDistancesGetAsyncWithHttpInfo($facility_id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiFacilitiesFacilityIdEmployeeDistancesGetAsyncWithHttpInfo + * + * @param string $facility_id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiFacilitiesFacilityIdEmployeeDistancesGetAsyncWithHttpInfo($facility_id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesGet'][0]) + { + $returnType = '\OmsorgCoreClient\Model\EmployeeFacilityDistanceResponse[]'; + $request = $this->apiFacilitiesFacilityIdEmployeeDistancesGetRequest($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 'apiFacilitiesFacilityIdEmployeeDistancesGet' + * + * @param string $facility_id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiFacilitiesFacilityIdEmployeeDistancesGetRequest($facility_id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesGet'][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 apiFacilitiesFacilityIdEmployeeDistancesGet' + ); + } + + + $resourcePath = '/api/facilities/{facilityId}/employee-distances'; + $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 apiFacilitiesFacilityIdEmployeeDistancesIdDelete + * + * @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['apiFacilitiesFacilityIdEmployeeDistancesIdDelete'] 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 apiFacilitiesFacilityIdEmployeeDistancesIdDelete($facility_id, $id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesIdDelete'][0]) + { + $this->apiFacilitiesFacilityIdEmployeeDistancesIdDeleteWithHttpInfo($facility_id, $id, $contentType); + } + + /** + * Operation apiFacilitiesFacilityIdEmployeeDistancesIdDeleteWithHttpInfo + * + * @param string $facility_id (required) + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesIdDelete'] 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 apiFacilitiesFacilityIdEmployeeDistancesIdDeleteWithHttpInfo($facility_id, $id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesIdDelete'][0]) + { + $request = $this->apiFacilitiesFacilityIdEmployeeDistancesIdDeleteRequest($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 apiFacilitiesFacilityIdEmployeeDistancesIdDeleteAsync + * + * @param string $facility_id (required) + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiFacilitiesFacilityIdEmployeeDistancesIdDeleteAsync($facility_id, $id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesIdDelete'][0]) + { + return $this->apiFacilitiesFacilityIdEmployeeDistancesIdDeleteAsyncWithHttpInfo($facility_id, $id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiFacilitiesFacilityIdEmployeeDistancesIdDeleteAsyncWithHttpInfo + * + * @param string $facility_id (required) + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiFacilitiesFacilityIdEmployeeDistancesIdDeleteAsyncWithHttpInfo($facility_id, $id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesIdDelete'][0]) + { + $returnType = ''; + $request = $this->apiFacilitiesFacilityIdEmployeeDistancesIdDeleteRequest($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 'apiFacilitiesFacilityIdEmployeeDistancesIdDelete' + * + * @param string $facility_id (required) + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesIdDelete'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiFacilitiesFacilityIdEmployeeDistancesIdDeleteRequest($facility_id, $id, string $contentType = self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesIdDelete'][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 apiFacilitiesFacilityIdEmployeeDistancesIdDelete' + ); + } + + // 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 apiFacilitiesFacilityIdEmployeeDistancesIdDelete' + ); + } + + + $resourcePath = '/api/facilities/{facilityId}/employee-distances/{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 apiFacilitiesFacilityIdEmployeeDistancesIdPut + * + * @param string $facility_id facility_id (required) + * @param string $id id (required) + * @param \OmsorgCoreClient\Model\UpdateEmployeeFacilityDistanceRequest|null $update_employee_facility_distance_request update_employee_facility_distance_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesIdPut'] 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\EmployeeFacilityDistanceResponse + */ + public function apiFacilitiesFacilityIdEmployeeDistancesIdPut($facility_id, $id, $update_employee_facility_distance_request = null, string $contentType = self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesIdPut'][0]) + { + list($response) = $this->apiFacilitiesFacilityIdEmployeeDistancesIdPutWithHttpInfo($facility_id, $id, $update_employee_facility_distance_request, $contentType); + return $response; + } + + /** + * Operation apiFacilitiesFacilityIdEmployeeDistancesIdPutWithHttpInfo + * + * @param string $facility_id (required) + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateEmployeeFacilityDistanceRequest|null $update_employee_facility_distance_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesIdPut'] 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\EmployeeFacilityDistanceResponse, HTTP status code, HTTP response headers (array of strings) + */ + public function apiFacilitiesFacilityIdEmployeeDistancesIdPutWithHttpInfo($facility_id, $id, $update_employee_facility_distance_request = null, string $contentType = self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesIdPut'][0]) + { + $request = $this->apiFacilitiesFacilityIdEmployeeDistancesIdPutRequest($facility_id, $id, $update_employee_facility_distance_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\EmployeeFacilityDistanceResponse', + $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\EmployeeFacilityDistanceResponse', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\EmployeeFacilityDistanceResponse', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiFacilitiesFacilityIdEmployeeDistancesIdPutAsync + * + * @param string $facility_id (required) + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateEmployeeFacilityDistanceRequest|null $update_employee_facility_distance_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesIdPut'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiFacilitiesFacilityIdEmployeeDistancesIdPutAsync($facility_id, $id, $update_employee_facility_distance_request = null, string $contentType = self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesIdPut'][0]) + { + return $this->apiFacilitiesFacilityIdEmployeeDistancesIdPutAsyncWithHttpInfo($facility_id, $id, $update_employee_facility_distance_request, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiFacilitiesFacilityIdEmployeeDistancesIdPutAsyncWithHttpInfo + * + * @param string $facility_id (required) + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateEmployeeFacilityDistanceRequest|null $update_employee_facility_distance_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesIdPut'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiFacilitiesFacilityIdEmployeeDistancesIdPutAsyncWithHttpInfo($facility_id, $id, $update_employee_facility_distance_request = null, string $contentType = self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesIdPut'][0]) + { + $returnType = '\OmsorgCoreClient\Model\EmployeeFacilityDistanceResponse'; + $request = $this->apiFacilitiesFacilityIdEmployeeDistancesIdPutRequest($facility_id, $id, $update_employee_facility_distance_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 'apiFacilitiesFacilityIdEmployeeDistancesIdPut' + * + * @param string $facility_id (required) + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateEmployeeFacilityDistanceRequest|null $update_employee_facility_distance_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesIdPut'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiFacilitiesFacilityIdEmployeeDistancesIdPutRequest($facility_id, $id, $update_employee_facility_distance_request = null, string $contentType = self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesIdPut'][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 apiFacilitiesFacilityIdEmployeeDistancesIdPut' + ); + } + + // 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 apiFacilitiesFacilityIdEmployeeDistancesIdPut' + ); + } + + + + $resourcePath = '/api/facilities/{facilityId}/employee-distances/{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_employee_facility_distance_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_employee_facility_distance_request)); + } else { + $httpBody = $update_employee_facility_distance_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 apiFacilitiesFacilityIdEmployeeDistancesPost + * + * @param string $facility_id facility_id (required) + * @param \OmsorgCoreClient\Model\CreateEmployeeFacilityDistanceRequest|null $create_employee_facility_distance_request create_employee_facility_distance_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesPost'] 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\EmployeeFacilityDistanceResponse + */ + public function apiFacilitiesFacilityIdEmployeeDistancesPost($facility_id, $create_employee_facility_distance_request = null, string $contentType = self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesPost'][0]) + { + list($response) = $this->apiFacilitiesFacilityIdEmployeeDistancesPostWithHttpInfo($facility_id, $create_employee_facility_distance_request, $contentType); + return $response; + } + + /** + * Operation apiFacilitiesFacilityIdEmployeeDistancesPostWithHttpInfo + * + * @param string $facility_id (required) + * @param \OmsorgCoreClient\Model\CreateEmployeeFacilityDistanceRequest|null $create_employee_facility_distance_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesPost'] 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\EmployeeFacilityDistanceResponse, HTTP status code, HTTP response headers (array of strings) + */ + public function apiFacilitiesFacilityIdEmployeeDistancesPostWithHttpInfo($facility_id, $create_employee_facility_distance_request = null, string $contentType = self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesPost'][0]) + { + $request = $this->apiFacilitiesFacilityIdEmployeeDistancesPostRequest($facility_id, $create_employee_facility_distance_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\EmployeeFacilityDistanceResponse', + $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\EmployeeFacilityDistanceResponse', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\EmployeeFacilityDistanceResponse', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiFacilitiesFacilityIdEmployeeDistancesPostAsync + * + * @param string $facility_id (required) + * @param \OmsorgCoreClient\Model\CreateEmployeeFacilityDistanceRequest|null $create_employee_facility_distance_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiFacilitiesFacilityIdEmployeeDistancesPostAsync($facility_id, $create_employee_facility_distance_request = null, string $contentType = self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesPost'][0]) + { + return $this->apiFacilitiesFacilityIdEmployeeDistancesPostAsyncWithHttpInfo($facility_id, $create_employee_facility_distance_request, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiFacilitiesFacilityIdEmployeeDistancesPostAsyncWithHttpInfo + * + * @param string $facility_id (required) + * @param \OmsorgCoreClient\Model\CreateEmployeeFacilityDistanceRequest|null $create_employee_facility_distance_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiFacilitiesFacilityIdEmployeeDistancesPostAsyncWithHttpInfo($facility_id, $create_employee_facility_distance_request = null, string $contentType = self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesPost'][0]) + { + $returnType = '\OmsorgCoreClient\Model\EmployeeFacilityDistanceResponse'; + $request = $this->apiFacilitiesFacilityIdEmployeeDistancesPostRequest($facility_id, $create_employee_facility_distance_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 'apiFacilitiesFacilityIdEmployeeDistancesPost' + * + * @param string $facility_id (required) + * @param \OmsorgCoreClient\Model\CreateEmployeeFacilityDistanceRequest|null $create_employee_facility_distance_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiFacilitiesFacilityIdEmployeeDistancesPostRequest($facility_id, $create_employee_facility_distance_request = null, string $contentType = self::contentTypes['apiFacilitiesFacilityIdEmployeeDistancesPost'][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 apiFacilitiesFacilityIdEmployeeDistancesPost' + ); + } + + + + $resourcePath = '/api/facilities/{facilityId}/employee-distances'; + $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_employee_facility_distance_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_employee_facility_distance_request)); + } else { + $httpBody = $create_employee_facility_distance_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/MyFacilityDistancesApi.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/MyFacilityDistancesApi.php new file mode 100644 index 0000000..fabd769 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/MyFacilityDistancesApi.php @@ -0,0 +1,1224 @@ + [ + 'application/json', + ], + 'apiMeFacilityDistancesGet' => [ + 'application/json', + ], + 'apiMeFacilityDistancesIdPut' => [ + 'application/json', + 'text/json', + 'application/*+json', + ], + 'apiMeFacilityDistancesPost' => [ + '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 apiMeFacilityDistancesFacilitiesGet + * + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiMeFacilityDistancesFacilitiesGet'] 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\FacilityOptionResponse[] + */ + public function apiMeFacilityDistancesFacilitiesGet(string $contentType = self::contentTypes['apiMeFacilityDistancesFacilitiesGet'][0]) + { + list($response) = $this->apiMeFacilityDistancesFacilitiesGetWithHttpInfo($contentType); + return $response; + } + + /** + * Operation apiMeFacilityDistancesFacilitiesGetWithHttpInfo + * + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiMeFacilityDistancesFacilitiesGet'] 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\FacilityOptionResponse[], HTTP status code, HTTP response headers (array of strings) + */ + public function apiMeFacilityDistancesFacilitiesGetWithHttpInfo(string $contentType = self::contentTypes['apiMeFacilityDistancesFacilitiesGet'][0]) + { + $request = $this->apiMeFacilityDistancesFacilitiesGetRequest($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\FacilityOptionResponse[]', + $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\FacilityOptionResponse[]', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\FacilityOptionResponse[]', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiMeFacilityDistancesFacilitiesGetAsync + * + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiMeFacilityDistancesFacilitiesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiMeFacilityDistancesFacilitiesGetAsync(string $contentType = self::contentTypes['apiMeFacilityDistancesFacilitiesGet'][0]) + { + return $this->apiMeFacilityDistancesFacilitiesGetAsyncWithHttpInfo($contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiMeFacilityDistancesFacilitiesGetAsyncWithHttpInfo + * + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiMeFacilityDistancesFacilitiesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiMeFacilityDistancesFacilitiesGetAsyncWithHttpInfo(string $contentType = self::contentTypes['apiMeFacilityDistancesFacilitiesGet'][0]) + { + $returnType = '\OmsorgCoreClient\Model\FacilityOptionResponse[]'; + $request = $this->apiMeFacilityDistancesFacilitiesGetRequest($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 'apiMeFacilityDistancesFacilitiesGet' + * + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiMeFacilityDistancesFacilitiesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiMeFacilityDistancesFacilitiesGetRequest(string $contentType = self::contentTypes['apiMeFacilityDistancesFacilitiesGet'][0]) + { + + + $resourcePath = '/api/me/facility-distances/facilities'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + + + $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 apiMeFacilityDistancesGet + * + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiMeFacilityDistancesGet'] 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\MyFacilityDistanceResponse[] + */ + public function apiMeFacilityDistancesGet(string $contentType = self::contentTypes['apiMeFacilityDistancesGet'][0]) + { + list($response) = $this->apiMeFacilityDistancesGetWithHttpInfo($contentType); + return $response; + } + + /** + * Operation apiMeFacilityDistancesGetWithHttpInfo + * + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiMeFacilityDistancesGet'] 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\MyFacilityDistanceResponse[], HTTP status code, HTTP response headers (array of strings) + */ + public function apiMeFacilityDistancesGetWithHttpInfo(string $contentType = self::contentTypes['apiMeFacilityDistancesGet'][0]) + { + $request = $this->apiMeFacilityDistancesGetRequest($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\MyFacilityDistanceResponse[]', + $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\MyFacilityDistanceResponse[]', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\MyFacilityDistanceResponse[]', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiMeFacilityDistancesGetAsync + * + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiMeFacilityDistancesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiMeFacilityDistancesGetAsync(string $contentType = self::contentTypes['apiMeFacilityDistancesGet'][0]) + { + return $this->apiMeFacilityDistancesGetAsyncWithHttpInfo($contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiMeFacilityDistancesGetAsyncWithHttpInfo + * + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiMeFacilityDistancesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiMeFacilityDistancesGetAsyncWithHttpInfo(string $contentType = self::contentTypes['apiMeFacilityDistancesGet'][0]) + { + $returnType = '\OmsorgCoreClient\Model\MyFacilityDistanceResponse[]'; + $request = $this->apiMeFacilityDistancesGetRequest($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 'apiMeFacilityDistancesGet' + * + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiMeFacilityDistancesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiMeFacilityDistancesGetRequest(string $contentType = self::contentTypes['apiMeFacilityDistancesGet'][0]) + { + + + $resourcePath = '/api/me/facility-distances'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + + + + $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 apiMeFacilityDistancesIdPut + * + * @param string $id id (required) + * @param \OmsorgCoreClient\Model\UpdateMyFacilityDistanceRequest|null $update_my_facility_distance_request update_my_facility_distance_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiMeFacilityDistancesIdPut'] 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\MyFacilityDistanceResponse + */ + public function apiMeFacilityDistancesIdPut($id, $update_my_facility_distance_request = null, string $contentType = self::contentTypes['apiMeFacilityDistancesIdPut'][0]) + { + list($response) = $this->apiMeFacilityDistancesIdPutWithHttpInfo($id, $update_my_facility_distance_request, $contentType); + return $response; + } + + /** + * Operation apiMeFacilityDistancesIdPutWithHttpInfo + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateMyFacilityDistanceRequest|null $update_my_facility_distance_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiMeFacilityDistancesIdPut'] 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\MyFacilityDistanceResponse, HTTP status code, HTTP response headers (array of strings) + */ + public function apiMeFacilityDistancesIdPutWithHttpInfo($id, $update_my_facility_distance_request = null, string $contentType = self::contentTypes['apiMeFacilityDistancesIdPut'][0]) + { + $request = $this->apiMeFacilityDistancesIdPutRequest($id, $update_my_facility_distance_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\MyFacilityDistanceResponse', + $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\MyFacilityDistanceResponse', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\MyFacilityDistanceResponse', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiMeFacilityDistancesIdPutAsync + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateMyFacilityDistanceRequest|null $update_my_facility_distance_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiMeFacilityDistancesIdPut'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiMeFacilityDistancesIdPutAsync($id, $update_my_facility_distance_request = null, string $contentType = self::contentTypes['apiMeFacilityDistancesIdPut'][0]) + { + return $this->apiMeFacilityDistancesIdPutAsyncWithHttpInfo($id, $update_my_facility_distance_request, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiMeFacilityDistancesIdPutAsyncWithHttpInfo + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateMyFacilityDistanceRequest|null $update_my_facility_distance_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiMeFacilityDistancesIdPut'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiMeFacilityDistancesIdPutAsyncWithHttpInfo($id, $update_my_facility_distance_request = null, string $contentType = self::contentTypes['apiMeFacilityDistancesIdPut'][0]) + { + $returnType = '\OmsorgCoreClient\Model\MyFacilityDistanceResponse'; + $request = $this->apiMeFacilityDistancesIdPutRequest($id, $update_my_facility_distance_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 'apiMeFacilityDistancesIdPut' + * + * @param string $id (required) + * @param \OmsorgCoreClient\Model\UpdateMyFacilityDistanceRequest|null $update_my_facility_distance_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiMeFacilityDistancesIdPut'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiMeFacilityDistancesIdPutRequest($id, $update_my_facility_distance_request = null, string $contentType = self::contentTypes['apiMeFacilityDistancesIdPut'][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 apiMeFacilityDistancesIdPut' + ); + } + + + + $resourcePath = '/api/me/facility-distances/{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_my_facility_distance_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_my_facility_distance_request)); + } else { + $httpBody = $update_my_facility_distance_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 apiMeFacilityDistancesPost + * + * @param \OmsorgCoreClient\Model\CreateMyFacilityDistanceRequest|null $create_my_facility_distance_request create_my_facility_distance_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiMeFacilityDistancesPost'] 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\MyFacilityDistanceResponse + */ + public function apiMeFacilityDistancesPost($create_my_facility_distance_request = null, string $contentType = self::contentTypes['apiMeFacilityDistancesPost'][0]) + { + list($response) = $this->apiMeFacilityDistancesPostWithHttpInfo($create_my_facility_distance_request, $contentType); + return $response; + } + + /** + * Operation apiMeFacilityDistancesPostWithHttpInfo + * + * @param \OmsorgCoreClient\Model\CreateMyFacilityDistanceRequest|null $create_my_facility_distance_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiMeFacilityDistancesPost'] 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\MyFacilityDistanceResponse, HTTP status code, HTTP response headers (array of strings) + */ + public function apiMeFacilityDistancesPostWithHttpInfo($create_my_facility_distance_request = null, string $contentType = self::contentTypes['apiMeFacilityDistancesPost'][0]) + { + $request = $this->apiMeFacilityDistancesPostRequest($create_my_facility_distance_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\MyFacilityDistanceResponse', + $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\MyFacilityDistanceResponse', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\MyFacilityDistanceResponse', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiMeFacilityDistancesPostAsync + * + * @param \OmsorgCoreClient\Model\CreateMyFacilityDistanceRequest|null $create_my_facility_distance_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiMeFacilityDistancesPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiMeFacilityDistancesPostAsync($create_my_facility_distance_request = null, string $contentType = self::contentTypes['apiMeFacilityDistancesPost'][0]) + { + return $this->apiMeFacilityDistancesPostAsyncWithHttpInfo($create_my_facility_distance_request, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiMeFacilityDistancesPostAsyncWithHttpInfo + * + * @param \OmsorgCoreClient\Model\CreateMyFacilityDistanceRequest|null $create_my_facility_distance_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiMeFacilityDistancesPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiMeFacilityDistancesPostAsyncWithHttpInfo($create_my_facility_distance_request = null, string $contentType = self::contentTypes['apiMeFacilityDistancesPost'][0]) + { + $returnType = '\OmsorgCoreClient\Model\MyFacilityDistanceResponse'; + $request = $this->apiMeFacilityDistancesPostRequest($create_my_facility_distance_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 'apiMeFacilityDistancesPost' + * + * @param \OmsorgCoreClient\Model\CreateMyFacilityDistanceRequest|null $create_my_facility_distance_request (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiMeFacilityDistancesPost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiMeFacilityDistancesPostRequest($create_my_facility_distance_request = null, string $contentType = self::contentTypes['apiMeFacilityDistancesPost'][0]) + { + + + + $resourcePath = '/api/me/facility-distances'; + $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_my_facility_distance_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_my_facility_distance_request)); + } else { + $httpBody = $create_my_facility_distance_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 index ba0fd8a..d31f217 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/TrashApi.php +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Api/TrashApi.php @@ -86,6 +86,12 @@ class TrashApi 'apiTrashContractsIdRestorePost' => [ 'application/json', ], + 'apiTrashEmployeeFacilityDistancesGet' => [ + 'application/json', + ], + 'apiTrashEmployeeFacilityDistancesIdRestorePost' => [ + 'application/json', + ], 'apiTrashEmployeesGet' => [ 'application/json', ], @@ -1049,6 +1055,479 @@ class TrashApi + // 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 apiTrashEmployeeFacilityDistancesGet + * + * @param string|null $search search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashEmployeeFacilityDistancesGet'] 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\TrashEmployeeFacilityDistanceResponse[] + */ + public function apiTrashEmployeeFacilityDistancesGet($search = null, string $contentType = self::contentTypes['apiTrashEmployeeFacilityDistancesGet'][0]) + { + list($response) = $this->apiTrashEmployeeFacilityDistancesGetWithHttpInfo($search, $contentType); + return $response; + } + + /** + * Operation apiTrashEmployeeFacilityDistancesGetWithHttpInfo + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashEmployeeFacilityDistancesGet'] 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\TrashEmployeeFacilityDistanceResponse[], HTTP status code, HTTP response headers (array of strings) + */ + public function apiTrashEmployeeFacilityDistancesGetWithHttpInfo($search = null, string $contentType = self::contentTypes['apiTrashEmployeeFacilityDistancesGet'][0]) + { + $request = $this->apiTrashEmployeeFacilityDistancesGetRequest($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\TrashEmployeeFacilityDistanceResponse[]', + $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\TrashEmployeeFacilityDistanceResponse[]', + $request, + $response, + ); + } catch (ApiException $e) { + switch ($e->getCode()) { + case 200: + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '\OmsorgCoreClient\Model\TrashEmployeeFacilityDistanceResponse[]', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + throw $e; + } + + + throw $e; + } + } + + /** + * Operation apiTrashEmployeeFacilityDistancesGetAsync + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashEmployeeFacilityDistancesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashEmployeeFacilityDistancesGetAsync($search = null, string $contentType = self::contentTypes['apiTrashEmployeeFacilityDistancesGet'][0]) + { + return $this->apiTrashEmployeeFacilityDistancesGetAsyncWithHttpInfo($search, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTrashEmployeeFacilityDistancesGetAsyncWithHttpInfo + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashEmployeeFacilityDistancesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashEmployeeFacilityDistancesGetAsyncWithHttpInfo($search = null, string $contentType = self::contentTypes['apiTrashEmployeeFacilityDistancesGet'][0]) + { + $returnType = '\OmsorgCoreClient\Model\TrashEmployeeFacilityDistanceResponse[]'; + $request = $this->apiTrashEmployeeFacilityDistancesGetRequest($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 'apiTrashEmployeeFacilityDistancesGet' + * + * @param string|null $search (optional) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashEmployeeFacilityDistancesGet'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTrashEmployeeFacilityDistancesGetRequest($search = null, string $contentType = self::contentTypes['apiTrashEmployeeFacilityDistancesGet'][0]) + { + + + + $resourcePath = '/api/trash/employee-facility-distances'; + $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 apiTrashEmployeeFacilityDistancesIdRestorePost + * + * @param string $id id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashEmployeeFacilityDistancesIdRestorePost'] 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 apiTrashEmployeeFacilityDistancesIdRestorePost($id, string $contentType = self::contentTypes['apiTrashEmployeeFacilityDistancesIdRestorePost'][0]) + { + $this->apiTrashEmployeeFacilityDistancesIdRestorePostWithHttpInfo($id, $contentType); + } + + /** + * Operation apiTrashEmployeeFacilityDistancesIdRestorePostWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashEmployeeFacilityDistancesIdRestorePost'] 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 apiTrashEmployeeFacilityDistancesIdRestorePostWithHttpInfo($id, string $contentType = self::contentTypes['apiTrashEmployeeFacilityDistancesIdRestorePost'][0]) + { + $request = $this->apiTrashEmployeeFacilityDistancesIdRestorePostRequest($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 apiTrashEmployeeFacilityDistancesIdRestorePostAsync + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashEmployeeFacilityDistancesIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashEmployeeFacilityDistancesIdRestorePostAsync($id, string $contentType = self::contentTypes['apiTrashEmployeeFacilityDistancesIdRestorePost'][0]) + { + return $this->apiTrashEmployeeFacilityDistancesIdRestorePostAsyncWithHttpInfo($id, $contentType) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation apiTrashEmployeeFacilityDistancesIdRestorePostAsyncWithHttpInfo + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashEmployeeFacilityDistancesIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function apiTrashEmployeeFacilityDistancesIdRestorePostAsyncWithHttpInfo($id, string $contentType = self::contentTypes['apiTrashEmployeeFacilityDistancesIdRestorePost'][0]) + { + $returnType = ''; + $request = $this->apiTrashEmployeeFacilityDistancesIdRestorePostRequest($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 'apiTrashEmployeeFacilityDistancesIdRestorePost' + * + * @param string $id (required) + * @param string $contentType The value for the Content-Type header. Check self::contentTypes['apiTrashEmployeeFacilityDistancesIdRestorePost'] to see the possible values for this operation + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + public function apiTrashEmployeeFacilityDistancesIdRestorePostRequest($id, string $contentType = self::contentTypes['apiTrashEmployeeFacilityDistancesIdRestorePost'][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 apiTrashEmployeeFacilityDistancesIdRestorePost' + ); + } + + + $resourcePath = '/api/trash/employee-facility-distances/{id}/restore'; + $formParams = []; + $queryParams = []; + $headerParams = []; + $httpBody = ''; + $multipart = false; + + + // path params if ($id !== null) { $resourcePath = str_replace( diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/CreateEmployeeFacilityDistanceRequest.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/CreateEmployeeFacilityDistanceRequest.php new file mode 100644 index 0000000..b624534 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/CreateEmployeeFacilityDistanceRequest.php @@ -0,0 +1,443 @@ + + */ +class CreateEmployeeFacilityDistanceRequest implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'CreateEmployeeFacilityDistanceRequest'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'employee_id' => 'string', + 'distance_km' => 'float' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'employee_id' => 'uuid', + 'distance_km' => 'double' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'employee_id' => false, + 'distance_km' => 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 = [ + 'employee_id' => 'employeeId', + 'distance_km' => 'distanceKm' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'employee_id' => 'setEmployeeId', + 'distance_km' => 'setDistanceKm' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'employee_id' => 'getEmployeeId', + 'distance_km' => 'getDistanceKm' + ]; + + /** + * 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('employee_id', $data ?? [], null); + $this->setIfExists('distance_km', $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 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 distance_km + * + * @return float|null + */ + public function getDistanceKm() + { + return $this->container['distance_km']; + } + + /** + * Sets distance_km + * + * @param float|null $distance_km distance_km + * + * @return self + */ + public function setDistanceKm($distance_km) + { + if (is_null($distance_km)) { + throw new \InvalidArgumentException('non-nullable distance_km cannot be null'); + } + $this->container['distance_km'] = $distance_km; + + 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/CreateMyFacilityDistanceRequest.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/CreateMyFacilityDistanceRequest.php new file mode 100644 index 0000000..711e8a2 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/CreateMyFacilityDistanceRequest.php @@ -0,0 +1,443 @@ + + */ +class CreateMyFacilityDistanceRequest implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'CreateMyFacilityDistanceRequest'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'facility_id' => 'string', + 'distance_km' => 'float' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'facility_id' => 'uuid', + 'distance_km' => 'double' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'facility_id' => false, + 'distance_km' => 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 = [ + 'facility_id' => 'facilityId', + 'distance_km' => 'distanceKm' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'facility_id' => 'setFacilityId', + 'distance_km' => 'setDistanceKm' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'facility_id' => 'getFacilityId', + 'distance_km' => 'getDistanceKm' + ]; + + /** + * 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('facility_id', $data ?? [], null); + $this->setIfExists('distance_km', $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 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 distance_km + * + * @return float|null + */ + public function getDistanceKm() + { + return $this->container['distance_km']; + } + + /** + * Sets distance_km + * + * @param float|null $distance_km distance_km + * + * @return self + */ + public function setDistanceKm($distance_km) + { + if (is_null($distance_km)) { + throw new \InvalidArgumentException('non-nullable distance_km cannot be null'); + } + $this->container['distance_km'] = $distance_km; + + 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/EmployeeFacilityDistanceResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/EmployeeFacilityDistanceResponse.php new file mode 100644 index 0000000..4fc8665 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/EmployeeFacilityDistanceResponse.php @@ -0,0 +1,593 @@ + + */ +class EmployeeFacilityDistanceResponse implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'EmployeeFacilityDistanceResponse'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'id' => 'string', + 'facility_id' => 'string', + 'employee_id' => 'string', + 'employee_first_name' => 'string', + 'employee_last_name' => 'string', + 'distance_km' => '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', + 'employee_id' => 'uuid', + 'employee_first_name' => null, + 'employee_last_name' => null, + 'distance_km' => 'double' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'id' => false, + 'facility_id' => false, + 'employee_id' => false, + 'employee_first_name' => true, + 'employee_last_name' => true, + 'distance_km' => 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', + 'employee_id' => 'employeeId', + 'employee_first_name' => 'employeeFirstName', + 'employee_last_name' => 'employeeLastName', + 'distance_km' => 'distanceKm' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'id' => 'setId', + 'facility_id' => 'setFacilityId', + 'employee_id' => 'setEmployeeId', + 'employee_first_name' => 'setEmployeeFirstName', + 'employee_last_name' => 'setEmployeeLastName', + 'distance_km' => 'setDistanceKm' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'id' => 'getId', + 'facility_id' => 'getFacilityId', + 'employee_id' => 'getEmployeeId', + 'employee_first_name' => 'getEmployeeFirstName', + 'employee_last_name' => 'getEmployeeLastName', + 'distance_km' => 'getDistanceKm' + ]; + + /** + * 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('employee_id', $data ?? [], null); + $this->setIfExists('employee_first_name', $data ?? [], null); + $this->setIfExists('employee_last_name', $data ?? [], null); + $this->setIfExists('distance_km', $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 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_first_name + * + * @return string|null + */ + public function getEmployeeFirstName() + { + return $this->container['employee_first_name']; + } + + /** + * Sets employee_first_name + * + * @param string|null $employee_first_name employee_first_name + * + * @return self + */ + public function setEmployeeFirstName($employee_first_name) + { + if (is_null($employee_first_name)) { + array_push($this->openAPINullablesSetToNull, 'employee_first_name'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('employee_first_name', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['employee_first_name'] = $employee_first_name; + + return $this; + } + + /** + * Gets employee_last_name + * + * @return string|null + */ + public function getEmployeeLastName() + { + return $this->container['employee_last_name']; + } + + /** + * Sets employee_last_name + * + * @param string|null $employee_last_name employee_last_name + * + * @return self + */ + public function setEmployeeLastName($employee_last_name) + { + if (is_null($employee_last_name)) { + array_push($this->openAPINullablesSetToNull, 'employee_last_name'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('employee_last_name', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['employee_last_name'] = $employee_last_name; + + return $this; + } + + /** + * Gets distance_km + * + * @return float|null + */ + public function getDistanceKm() + { + return $this->container['distance_km']; + } + + /** + * Sets distance_km + * + * @param float|null $distance_km distance_km + * + * @return self + */ + public function setDistanceKm($distance_km) + { + if (is_null($distance_km)) { + throw new \InvalidArgumentException('non-nullable distance_km cannot be null'); + } + $this->container['distance_km'] = $distance_km; + + 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/FacilityOptionResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/FacilityOptionResponse.php new file mode 100644 index 0000000..0e24739 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/FacilityOptionResponse.php @@ -0,0 +1,450 @@ + + */ +class FacilityOptionResponse implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'FacilityOptionResponse'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'id' => 'string', + 'name' => 'string' + ]; + + /** + * 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 + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'id' => false, + '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 = [ + 'id' => 'id', + 'name' => 'name' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'id' => 'setId', + 'name' => 'setName' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'id' => 'getId', + 'name' => 'getName' + ]; + + /** + * 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); + } + + /** + * 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; + } + /** + * 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 0cbad7a..8812490 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/FacilityResponse.php +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/FacilityResponse.php @@ -77,6 +77,8 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable 'sunday_surcharge_percent' => 'float', 'holiday_surcharge_percent' => 'float', 'travel_cost_rate' => 'float', + 'travel_cost_mode' => 'string', + 'travel_cost_per_km' => 'float', 'minimum_hours' => 'float', 'break_policy' => 'string', 'billing_interval' => 'string', @@ -112,6 +114,8 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable 'sunday_surcharge_percent' => 'double', 'holiday_surcharge_percent' => 'double', 'travel_cost_rate' => 'double', + 'travel_cost_mode' => null, + 'travel_cost_per_km' => 'double', 'minimum_hours' => 'double', 'break_policy' => null, 'billing_interval' => null, @@ -145,6 +149,8 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable 'sunday_surcharge_percent' => true, 'holiday_surcharge_percent' => true, 'travel_cost_rate' => true, + 'travel_cost_mode' => true, + 'travel_cost_per_km' => true, 'minimum_hours' => true, 'break_policy' => true, 'billing_interval' => true, @@ -258,6 +264,8 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable 'sunday_surcharge_percent' => 'sundaySurchargePercent', 'holiday_surcharge_percent' => 'holidaySurchargePercent', 'travel_cost_rate' => 'travelCostRate', + 'travel_cost_mode' => 'travelCostMode', + 'travel_cost_per_km' => 'travelCostPerKm', 'minimum_hours' => 'minimumHours', 'break_policy' => 'breakPolicy', 'billing_interval' => 'billingInterval', @@ -291,6 +299,8 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable 'sunday_surcharge_percent' => 'setSundaySurchargePercent', 'holiday_surcharge_percent' => 'setHolidaySurchargePercent', 'travel_cost_rate' => 'setTravelCostRate', + 'travel_cost_mode' => 'setTravelCostMode', + 'travel_cost_per_km' => 'setTravelCostPerKm', 'minimum_hours' => 'setMinimumHours', 'break_policy' => 'setBreakPolicy', 'billing_interval' => 'setBillingInterval', @@ -324,6 +334,8 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable 'sunday_surcharge_percent' => 'getSundaySurchargePercent', 'holiday_surcharge_percent' => 'getHolidaySurchargePercent', 'travel_cost_rate' => 'getTravelCostRate', + 'travel_cost_mode' => 'getTravelCostMode', + 'travel_cost_per_km' => 'getTravelCostPerKm', 'minimum_hours' => 'getMinimumHours', 'break_policy' => 'getBreakPolicy', 'billing_interval' => 'getBillingInterval', @@ -408,6 +420,8 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable $this->setIfExists('sunday_surcharge_percent', $data ?? [], null); $this->setIfExists('holiday_surcharge_percent', $data ?? [], null); $this->setIfExists('travel_cost_rate', $data ?? [], null); + $this->setIfExists('travel_cost_mode', $data ?? [], null); + $this->setIfExists('travel_cost_per_km', $data ?? [], null); $this->setIfExists('minimum_hours', $data ?? [], null); $this->setIfExists('break_policy', $data ?? [], null); $this->setIfExists('billing_interval', $data ?? [], null); @@ -1130,6 +1144,74 @@ class FacilityResponse implements ModelInterface, ArrayAccess, \JsonSerializable return $this; } + /** + * Gets travel_cost_mode + * + * @return string|null + */ + public function getTravelCostMode() + { + return $this->container['travel_cost_mode']; + } + + /** + * Sets travel_cost_mode + * + * @param string|null $travel_cost_mode travel_cost_mode + * + * @return self + */ + public function setTravelCostMode($travel_cost_mode) + { + if (is_null($travel_cost_mode)) { + array_push($this->openAPINullablesSetToNull, 'travel_cost_mode'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('travel_cost_mode', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['travel_cost_mode'] = $travel_cost_mode; + + return $this; + } + + /** + * Gets travel_cost_per_km + * + * @return float|null + */ + public function getTravelCostPerKm() + { + return $this->container['travel_cost_per_km']; + } + + /** + * Sets travel_cost_per_km + * + * @param float|null $travel_cost_per_km travel_cost_per_km + * + * @return self + */ + public function setTravelCostPerKm($travel_cost_per_km) + { + if (is_null($travel_cost_per_km)) { + array_push($this->openAPINullablesSetToNull, 'travel_cost_per_km'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('travel_cost_per_km', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['travel_cost_per_km'] = $travel_cost_per_km; + + return $this; + } + /** * Gets minimum_hours * diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/ModuleType.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/ModuleType.php index e484e43..aaee769 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/ModuleType.php +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/ModuleType.php @@ -70,6 +70,8 @@ class ModuleType public const ABSENCES = 'Absences'; + public const EMPLOYEE_FACILITY_DISTANCES = 'EmployeeFacilityDistances'; + /** * Gets allowable values of the enum * @return string[] @@ -90,7 +92,8 @@ class ModuleType self::DOCUMENTS, self::USERS, self::CONFIGURATION, - self::ABSENCES + self::ABSENCES, + self::EMPLOYEE_FACILITY_DISTANCES ]; } } diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/MyFacilityDistanceResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/MyFacilityDistanceResponse.php new file mode 100644 index 0000000..e0a1de3 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/MyFacilityDistanceResponse.php @@ -0,0 +1,518 @@ + + */ +class MyFacilityDistanceResponse implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'MyFacilityDistanceResponse'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'id' => 'string', + 'facility_id' => 'string', + 'facility_name' => 'string', + 'distance_km' => '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', + 'facility_name' => null, + 'distance_km' => 'double' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'id' => false, + 'facility_id' => false, + 'facility_name' => true, + 'distance_km' => 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', + 'facility_name' => 'facilityName', + 'distance_km' => 'distanceKm' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'id' => 'setId', + 'facility_id' => 'setFacilityId', + 'facility_name' => 'setFacilityName', + 'distance_km' => 'setDistanceKm' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'id' => 'getId', + 'facility_id' => 'getFacilityId', + 'facility_name' => 'getFacilityName', + 'distance_km' => 'getDistanceKm' + ]; + + /** + * 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('facility_name', $data ?? [], null); + $this->setIfExists('distance_km', $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 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 distance_km + * + * @return float|null + */ + public function getDistanceKm() + { + return $this->container['distance_km']; + } + + /** + * Sets distance_km + * + * @param float|null $distance_km distance_km + * + * @return self + */ + public function setDistanceKm($distance_km) + { + if (is_null($distance_km)) { + throw new \InvalidArgumentException('non-nullable distance_km cannot be null'); + } + $this->container['distance_km'] = $distance_km; + + 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/TrashEmployeeFacilityDistanceResponse.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashEmployeeFacilityDistanceResponse.php new file mode 100644 index 0000000..55a34a7 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/TrashEmployeeFacilityDistanceResponse.php @@ -0,0 +1,552 @@ + + */ +class TrashEmployeeFacilityDistanceResponse implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'TrashEmployeeFacilityDistanceResponse'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'id' => 'string', + 'facility_id' => 'string', + 'employee_id' => 'string', + 'distance_km' => 'float', + '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', + 'employee_id' => 'uuid', + 'distance_km' => 'double', + 'deleted_at' => 'date-time' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'id' => false, + 'facility_id' => false, + 'employee_id' => false, + 'distance_km' => 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', + 'facility_id' => 'facilityId', + 'employee_id' => 'employeeId', + 'distance_km' => 'distanceKm', + 'deleted_at' => 'deletedAt' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'id' => 'setId', + 'facility_id' => 'setFacilityId', + 'employee_id' => 'setEmployeeId', + 'distance_km' => 'setDistanceKm', + 'deleted_at' => 'setDeletedAt' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'id' => 'getId', + 'facility_id' => 'getFacilityId', + 'employee_id' => 'getEmployeeId', + 'distance_km' => 'getDistanceKm', + '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('employee_id', $data ?? [], null); + $this->setIfExists('distance_km', $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 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 distance_km + * + * @return float|null + */ + public function getDistanceKm() + { + return $this->container['distance_km']; + } + + /** + * Sets distance_km + * + * @param float|null $distance_km distance_km + * + * @return self + */ + public function setDistanceKm($distance_km) + { + if (is_null($distance_km)) { + throw new \InvalidArgumentException('non-nullable distance_km cannot be null'); + } + $this->container['distance_km'] = $distance_km; + + 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/UpdateEmployeeFacilityDistanceRequest.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateEmployeeFacilityDistanceRequest.php new file mode 100644 index 0000000..2af1920 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateEmployeeFacilityDistanceRequest.php @@ -0,0 +1,409 @@ + + */ +class UpdateEmployeeFacilityDistanceRequest implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'UpdateEmployeeFacilityDistanceRequest'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'distance_km' => 'float' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'distance_km' => 'double' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'distance_km' => 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 = [ + 'distance_km' => 'distanceKm' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'distance_km' => 'setDistanceKm' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'distance_km' => 'getDistanceKm' + ]; + + /** + * 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('distance_km', $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 distance_km + * + * @return float|null + */ + public function getDistanceKm() + { + return $this->container['distance_km']; + } + + /** + * Sets distance_km + * + * @param float|null $distance_km distance_km + * + * @return self + */ + public function setDistanceKm($distance_km) + { + if (is_null($distance_km)) { + throw new \InvalidArgumentException('non-nullable distance_km cannot be null'); + } + $this->container['distance_km'] = $distance_km; + + 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 d9c2c64..d22f636 100644 --- a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateFacilityRequest.php +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateFacilityRequest.php @@ -76,6 +76,8 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali 'sunday_surcharge_percent' => 'float', 'holiday_surcharge_percent' => 'float', 'travel_cost_rate' => 'float', + 'travel_cost_mode' => 'string', + 'travel_cost_per_km' => 'float', 'minimum_hours' => 'float', 'break_policy' => 'string', 'billing_interval' => 'string', @@ -110,6 +112,8 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali 'sunday_surcharge_percent' => 'double', 'holiday_surcharge_percent' => 'double', 'travel_cost_rate' => 'double', + 'travel_cost_mode' => null, + 'travel_cost_per_km' => 'double', 'minimum_hours' => 'double', 'break_policy' => null, 'billing_interval' => null, @@ -142,6 +146,8 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali 'sunday_surcharge_percent' => true, 'holiday_surcharge_percent' => true, 'travel_cost_rate' => true, + 'travel_cost_mode' => true, + 'travel_cost_per_km' => true, 'minimum_hours' => true, 'break_policy' => true, 'billing_interval' => true, @@ -254,6 +260,8 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali 'sunday_surcharge_percent' => 'sundaySurchargePercent', 'holiday_surcharge_percent' => 'holidaySurchargePercent', 'travel_cost_rate' => 'travelCostRate', + 'travel_cost_mode' => 'travelCostMode', + 'travel_cost_per_km' => 'travelCostPerKm', 'minimum_hours' => 'minimumHours', 'break_policy' => 'breakPolicy', 'billing_interval' => 'billingInterval', @@ -286,6 +294,8 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali 'sunday_surcharge_percent' => 'setSundaySurchargePercent', 'holiday_surcharge_percent' => 'setHolidaySurchargePercent', 'travel_cost_rate' => 'setTravelCostRate', + 'travel_cost_mode' => 'setTravelCostMode', + 'travel_cost_per_km' => 'setTravelCostPerKm', 'minimum_hours' => 'setMinimumHours', 'break_policy' => 'setBreakPolicy', 'billing_interval' => 'setBillingInterval', @@ -318,6 +328,8 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali 'sunday_surcharge_percent' => 'getSundaySurchargePercent', 'holiday_surcharge_percent' => 'getHolidaySurchargePercent', 'travel_cost_rate' => 'getTravelCostRate', + 'travel_cost_mode' => 'getTravelCostMode', + 'travel_cost_per_km' => 'getTravelCostPerKm', 'minimum_hours' => 'getMinimumHours', 'break_policy' => 'getBreakPolicy', 'billing_interval' => 'getBillingInterval', @@ -401,6 +413,8 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali $this->setIfExists('sunday_surcharge_percent', $data ?? [], null); $this->setIfExists('holiday_surcharge_percent', $data ?? [], null); $this->setIfExists('travel_cost_rate', $data ?? [], null); + $this->setIfExists('travel_cost_mode', $data ?? [], null); + $this->setIfExists('travel_cost_per_km', $data ?? [], null); $this->setIfExists('minimum_hours', $data ?? [], null); $this->setIfExists('break_policy', $data ?? [], null); $this->setIfExists('billing_interval', $data ?? [], null); @@ -1096,6 +1110,74 @@ class UpdateFacilityRequest implements ModelInterface, ArrayAccess, \JsonSeriali return $this; } + /** + * Gets travel_cost_mode + * + * @return string|null + */ + public function getTravelCostMode() + { + return $this->container['travel_cost_mode']; + } + + /** + * Sets travel_cost_mode + * + * @param string|null $travel_cost_mode travel_cost_mode + * + * @return self + */ + public function setTravelCostMode($travel_cost_mode) + { + if (is_null($travel_cost_mode)) { + array_push($this->openAPINullablesSetToNull, 'travel_cost_mode'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('travel_cost_mode', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['travel_cost_mode'] = $travel_cost_mode; + + return $this; + } + + /** + * Gets travel_cost_per_km + * + * @return float|null + */ + public function getTravelCostPerKm() + { + return $this->container['travel_cost_per_km']; + } + + /** + * Sets travel_cost_per_km + * + * @param float|null $travel_cost_per_km travel_cost_per_km + * + * @return self + */ + public function setTravelCostPerKm($travel_cost_per_km) + { + if (is_null($travel_cost_per_km)) { + array_push($this->openAPINullablesSetToNull, 'travel_cost_per_km'); + } else { + $nullablesSetToNull = $this->getOpenAPINullablesSetToNull(); + $index = array_search('travel_cost_per_km', $nullablesSetToNull); + if ($index !== FALSE) { + unset($nullablesSetToNull[$index]); + $this->setOpenAPINullablesSetToNull($nullablesSetToNull); + } + } + $this->container['travel_cost_per_km'] = $travel_cost_per_km; + + return $this; + } + /** * Gets minimum_hours * diff --git a/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateMyFacilityDistanceRequest.php b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateMyFacilityDistanceRequest.php new file mode 100644 index 0000000..52d6e45 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/lib/Model/UpdateMyFacilityDistanceRequest.php @@ -0,0 +1,409 @@ + + */ +class UpdateMyFacilityDistanceRequest implements ModelInterface, ArrayAccess, \JsonSerializable +{ + public const DISCRIMINATOR = null; + + /** + * The original name of the model. + * + * @var string + */ + protected static $openAPIModelName = 'UpdateMyFacilityDistanceRequest'; + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @var string[] + */ + protected static $openAPITypes = [ + 'distance_km' => 'float' + ]; + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @var string[] + * @phpstan-var array + * @psalm-var array + */ + protected static $openAPIFormats = [ + 'distance_km' => 'double' + ]; + + /** + * Array of nullable properties. Used for (de)serialization + * + * @var boolean[] + */ + protected static array $openAPINullables = [ + 'distance_km' => 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 = [ + 'distance_km' => 'distanceKm' + ]; + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @var string[] + */ + protected static $setters = [ + 'distance_km' => 'setDistanceKm' + ]; + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @var string[] + */ + protected static $getters = [ + 'distance_km' => 'getDistanceKm' + ]; + + /** + * 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('distance_km', $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 distance_km + * + * @return float|null + */ + public function getDistanceKm() + { + return $this->container['distance_km']; + } + + /** + * Sets distance_km + * + * @param float|null $distance_km distance_km + * + * @return self + */ + public function setDistanceKm($distance_km) + { + if (is_null($distance_km)) { + throw new \InvalidArgumentException('non-nullable distance_km cannot be null'); + } + $this->container['distance_km'] = $distance_km; + + 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/test/Api/EmployeeFacilityDistancesApiTest.php b/omsorgWeb/mitarbeiter-app/api-client-php/test/Api/EmployeeFacilityDistancesApiTest.php new file mode 100644 index 0000000..bfc9d82 --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/api-client-php/test/Api/EmployeeFacilityDistancesApiTest.php @@ -0,0 +1,121 @@ + Zeiterfassung + + + 🚗 Fahrtstrecken + + Einstellungen diff --git a/omsorgWeb/mitarbeiter-app/lib/omsorgCoreClient.php b/omsorgWeb/mitarbeiter-app/lib/omsorgCoreClient.php index ccabfef..7f98a31 100644 --- a/omsorgWeb/mitarbeiter-app/lib/omsorgCoreClient.php +++ b/omsorgWeb/mitarbeiter-app/lib/omsorgCoreClient.php @@ -258,3 +258,52 @@ function omsorgcore_time_entries_submit(array $config, string $accessToken, stri ->apiTimeEntriesIdSubmitPostWithHttpInfo($id); }); } + +// --- EmployeeFacilityDistances (eigene Fahrtstrecke km je Einrichtung, FR-EIN-4) --- +// Own-Scope: die EmployeeId wird serverseitig aus dem JWT aufgelöst (siehe +// MyFacilityDistancesController in omsorgCore), Connect schickt hier bewusst nie eine employeeId mit. +// Kein Facilities-Recht nötig - die Einrichtungsauswahl kommt über einen eigenen, minimalen +// Endpoint (nur Id/Name, keine Konditionen/CRM-Daten). + +function omsorgcore_my_facility_distances_list(array $config, string $accessToken): array +{ + return omsorgcore_call($config, function ($cfg) use ($accessToken) { + $cfg->setAccessToken($accessToken); + return (new \OmsorgCoreClient\Api\MyFacilityDistancesApi(new \GuzzleHttp\Client(), $cfg)) + ->apiMeFacilityDistancesGetWithHttpInfo(); + }); +} + +function omsorgcore_my_facility_distances_facility_options(array $config, string $accessToken): array +{ + return omsorgcore_call($config, function ($cfg) use ($accessToken) { + $cfg->setAccessToken($accessToken); + return (new \OmsorgCoreClient\Api\MyFacilityDistancesApi(new \GuzzleHttp\Client(), $cfg)) + ->apiMeFacilityDistancesFacilitiesGetWithHttpInfo(); + }); +} + +function omsorgcore_my_facility_distances_create(array $config, string $accessToken, array $payload): array +{ + $request = new \OmsorgCoreClient\Model\CreateMyFacilityDistanceRequest([ + 'facility_id' => $payload['facilityId'], + 'distance_km' => $payload['distanceKm'], + ]); + return omsorgcore_call($config, function ($cfg) use ($accessToken, $request) { + $cfg->setAccessToken($accessToken); + return (new \OmsorgCoreClient\Api\MyFacilityDistancesApi(new \GuzzleHttp\Client(), $cfg)) + ->apiMeFacilityDistancesPostWithHttpInfo($request); + }); +} + +function omsorgcore_my_facility_distances_update(array $config, string $accessToken, string $id, array $payload): array +{ + $request = new \OmsorgCoreClient\Model\UpdateMyFacilityDistanceRequest([ + 'distance_km' => $payload['distanceKm'], + ]); + return omsorgcore_call($config, function ($cfg) use ($accessToken, $id, $request) { + $cfg->setAccessToken($accessToken); + return (new \OmsorgCoreClient\Api\MyFacilityDistancesApi(new \GuzzleHttp\Client(), $cfg)) + ->apiMeFacilityDistancesIdPutWithHttpInfo($id, $request); + }); +} diff --git a/omsorgWeb/mitarbeiter-app/pages/fahrtstrecken.php b/omsorgWeb/mitarbeiter-app/pages/fahrtstrecken.php new file mode 100644 index 0000000..038ce9b --- /dev/null +++ b/omsorgWeb/mitarbeiter-app/pages/fahrtstrecken.php @@ -0,0 +1,193 @@ + (float) $distanceKm]); + + if ($result['ok']) { + header('Location: fahrtstrecken.php?updated=1'); + exit; + } + + $editId = $distanceId; + $error = $result['status'] === 403 + ? 'Keine Berechtigung, diese Entfernung zu bearbeiten.' + : (is_string($result['data'] ?? null) ? $result['data'] : 'Entfernung konnte nicht gespeichert werden.'); + } + } elseif (!$canCreate) { + $error = 'Keine Berechtigung, eine Entfernung anzulegen.'; + } else { + $facilityId = $_POST['facility_id'] ?? ''; + + if ($facilityId === '') { + $error = 'Einrichtung ist erforderlich.'; + } else { + $result = omsorgcore_my_facility_distances_create(_omsorgcore_config(), $_SESSION['omsorgcore_access_token'], [ + 'facilityId' => $facilityId, + 'distanceKm' => (float) $distanceKm, + ]); + + if ($result['ok']) { + $success = true; + } elseif ($result['status'] === 403) { + $error = 'Keine Berechtigung, eine Entfernung anzulegen.'; + } else { + $error = is_string($result['data'] ?? null) ? $result['data'] : 'Entfernung konnte nicht angelegt werden.'; + } + } + } +} elseif (isset($_GET['edit'])) { + $editId = $_GET['edit']; +} + +$listResult = omsorgcore_my_facility_distances_list(_omsorgcore_config(), $_SESSION['omsorgcore_access_token']); +$distances = $listResult['ok'] ? $listResult['data'] : []; + +$editDistance = null; +if ($editId !== null) { + foreach ($distances as $distance) { + if ($distance['id'] === $editId) { + $editDistance = $distance; + break; + } + } + // Fremde/ungültige Entfernung - Own-Scope-Filterung passiert serverseitig in + // omsorgcore_my_facility_distances_list, hier nur zusätzlich zurück zu "Neue Entfernung". + if ($editDistance === null) { + $editId = null; + } +} + +$facilitiesWithoutDistance = array_values(array_filter($facilities, function ($facility) use ($distances) { + foreach ($distances as $distance) { + if ($distance['facilityId'] === $facility['id']) { + return false; + } + } + return true; +})); + +layout_start('Fahrtstrecken – Mitarbeiter-App', 'fahrtstrecken'); +?> +

Fahrtstrecken

+

+ Trage hier die Entfernung (einfache Strecke, km) von deinem Wohnort zu den Einrichtungen ein, bei denen du im Einsatz bist. Diese Angabe ist Grundlage für die kilometerbasierte Fahrtkostenabrechnung. +

+ +
+
+

Meine Fahrtstrecken

+ + +

Noch keine Entfernung erfasst.

+ + +
+ km + + + +
+ + +
+ +
+

+ +

+ + +
Entfernung wurde angelegt.
+ + + +
Entfernung wurde aktualisiert.
+ + + +
+ + + +
+ + + + + + +
+

+ Abbrechen +

+ + +

Für alle Einrichtungen ist bereits eine Entfernung erfasst.

+ +
+ + + + + +
+ + +

Keine Berechtigung, eine Entfernung anzulegen.

+ +
+
+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`). +- `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`, `employeeFacilityDistancesApi.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), 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`. +- `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), EmployeeFacilityDistancesList/EmployeeFacilityDistanceForm für die kilometerbasierte Fahrtkostenabrechnung (nur sichtbar, wenn `facility.travelCostMode === "ProKilometer"`) — 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`. @@ -39,8 +39,8 @@ npm run dev # Vite-Devserver, http://127.0.0.1:5173 im Browser öffnen ## Aktueller Ist-Stand (siehe auch `REQUIREMENTS.md`) -- 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). +- 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, "Konditionen"-Fieldset in `FacilityForm.jsx` + `FacilityQualificationRatesList` im `FacilityDetailPanel`, über `src/api/facilityQualificationRatesApi.js` gegen `/api/facilities/{id}/qualification-rates`) sind jetzt ebenfalls umgesetzt — Fahrtkosten (seit 2026-08-10) haben zwei Abrechnungsarten: `travelCostMode` "Pauschale" (Feld `travelCostRate`) oder "ProKilometer" (Feld `travelCostPerKm`, EUR/km); im Pro-Kilometer-Modus zeigt `FacilityDetailPanel` zusätzlich `EmployeeFacilityDistancesList` (Entfernung je Mitarbeiter in km, über `src/api/employeeFacilityDistancesApi.js` gegen `/api/facilities/{id}/employee-distances` — eigene 1:n-Unterressource, weil jeder Mitarbeiter von einem anderen Wohnort anfährt, siehe `omsorgCore/CLAUDE.md` "Konditionen einer Einrichtung"). 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`/`employeeFacilityDistances`/`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 `