Skip to content

Commit be477f7

Browse files
feat: month closures, tariff rules, vacation entitlement engine, and NC32 compatibility
Adds MonthClosure system with auto-finalize, revision history, and PDF export; TariffRuleSet/Module for tariff-based vacation policies; VacationEntitlementEngine with snapshot service; admin notifications; NC32 migration steps (1014-1019); accessibility and responsive UI improvements; updated docs and release notes. Made-with: Cursor
1 parent a38b42e commit be477f7

143 files changed

Lines changed: 12320 additions & 24260 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.eslintrc.cjs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,38 @@ module.exports = {
1717
sourceType: 'module',
1818
},
1919
ignorePatterns: ['node_modules/', 'vendor/'],
20+
overrides: [
21+
{
22+
files: ['js/**/*.test.js'],
23+
rules: {
24+
'no-restricted-syntax': 'off',
25+
},
26+
},
27+
],
2028
rules: {
2129
'no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
2230
'no-undef': ['error', { typeof: true }],
2331
'no-inner-declarations': 'off',
2432
'no-dupe-keys': 'warn',
2533
'no-useless-escape': 'warn',
34+
'no-restricted-syntax': [
35+
'error',
36+
{
37+
selector: "CallExpression[callee.name='fetch'] > Literal:first-child[value=/^\\/apps\\/arbeitszeitcheck\\//]",
38+
message: 'Do not call fetch() with raw /apps/arbeitszeitcheck paths. Use ArbeitszeitCheckUtils.resolveUrl(...) or Utils.ajax(...).',
39+
},
40+
{
41+
selector: "CallExpression[callee.name='fetch'] > TemplateLiteral:first-child > TemplateElement[value.raw=/^\\/apps\\/arbeitszeitcheck\\//]",
42+
message: 'Do not call fetch() with raw /apps/arbeitszeitcheck paths. Use ArbeitszeitCheckUtils.resolveUrl(...) or Utils.ajax(...).',
43+
},
44+
{
45+
selector: "CallExpression[callee.name='fetch'] > Literal:first-child[value=/^(https?:)?\\/\\//]",
46+
message: 'External fetch() URLs are disallowed by default. Route through ArbeitszeitCheckUtils.ajax(..., { allowExternal: true }) with justification.',
47+
},
48+
{
49+
selector: "CallExpression[callee.name='fetch'] > TemplateLiteral:first-child > TemplateElement[value.raw=/^(https?:)?\\/\\//]",
50+
message: 'External fetch() URLs are disallowed by default. Route through ArbeitszeitCheckUtils.ajax(..., { allowExternal: true }) with justification.',
51+
},
52+
],
2653
},
2754
}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ config/config.php
3434
# Build output
3535
build/
3636

37+
# Generated by occ integrity:sign-app during release signing
38+
appinfo/signature.json
39+
3740
# Release tarballs / signatures (docs + CHECKSUMS-*.txt may be committed)
3841
release/*.tar.gz
3942
release/*.tar.gz.asc

CHANGELOG.de.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,45 @@
11
## [Unreleased]
22

3+
### Hinzugefügt
4+
5+
- **Auto-Fallback mit Nachvollziehbarkeit**: Zeiteinträge speichern jetzt `ended_reason` und `policy_applied` (z. B. `manual_clock_out` oder `auto_break_fallback`) für klare Audit-/Export-Nachweise.
6+
- **Einmalige Nutzerinfo nach Auto-Ausstempeln**: Beim nächsten Statusabruf wird eine neutrale, konkrete Meldung mit Uhrzeit und Regel eingeblendet.
7+
- **Urlaubsanspruch-Policy-Engine**: Neue berechnungslogikbasierte Anspruchsermittlung mit Modi `manual_fixed`, `model_based_simple`, `tariff_rule_based` und `manual_exception` inkl. Simulations-Endpunkt für Admins.
8+
- **Tarifregel-Datenmodell und APIs**: Versionierte Tarif-Regelwerke/Module sowie Admin-Endpunkte zum Erstellen, Aktualisieren, Aktivieren, Stilllegen und Zuweisen von Urlaubs-Policies.
9+
- **Snapshots der Anspruchsberechnung**: Persistente Snapshots (`at_entitlement_snapshots`) mit Berechnungstrace/Policy-Fingerprint für Nachvollziehbarkeit und Diagnose.
10+
- **Neue Admin-Seite „Benachrichtigungen“**: Eigene Oberfläche für HR-Empfänger und Ereignis-Matrix inkl. dedizierter Notifications-API.
11+
12+
### Geändert
13+
14+
- **Fallback-Logik differenziert nach Einsatzart**: Für Schichtarbeit gilt standardmäßig eine strikte Fallback-Regel; für Nicht-Schichtmodelle eine flexible Regel mit tagsüber konfigurierbarem Ruhefenster (z. B. Familien-/Mittagsunterbrechung ohne Auto-Ausstempeln).
15+
- **Export-Transparenz**: CSV/JSON-Zeilen enthalten jetzt `ended_reason` und `policy_applied`, damit automatische Beendigungen in Reports eindeutig erkennbar sind.
16+
- **Urlaubsallokation integriert**: Jahresallokation nutzt nun die neue `VacationEntitlementEngine` und liefert Quelle/Regelwerk/Trace in der Ergebnisstruktur zurück.
17+
- **Migrations-Kompatibilität**: Bestehende Urlaubswerte aus Nutzer-Modellzuweisungen werden in Policy-Zuordnungen überführt (`Version1018Date20260420123000`), damit Bestandsinstallationen konsistent weiterlaufen.
18+
- **Admin-Einstellungsfluss für Abwesenheiten**: Carryover-/Rollover-, Vertretungs- und E-Mail-Schalter sind zentral über die neue Notifications-Seite/API steuerbar.
19+
- **Schema Arbeitszeitmodelle**: `at_models` enthält jetzt `work_days_per_week` (`Version1019Date20260420150000`) als Grundlage für Formeln.
20+
21+
### Behoben
22+
23+
- **Aufräumen bei Nutzerlöschung**: Beim Entfernen eines Nutzers werden jetzt auch Urlaubs-Policy-Zuordnungen und Entitlement-Snapshots gelöscht (keine verwaisten Policy-/Berechnungsdaten).
24+
25+
## 1.2.0 – 2026-04-15
26+
27+
### Behoben
28+
29+
- **Zeitzonen-Konsistenz (Europe/Berlin)**: Server-/PHP-Zeitzone für ArbeitszeitCheck auf Deutschland ausgerichtet; neue Migration `Version1015Date20260415120000` konvertiert bestehende UTC-DATETIME-Werte in App-Tabellen nach `Europe/Berlin` und setzt `app_timezone` explizit.
30+
- **Ausstempeln-Semantik korrigiert**: `clockOut()` finalisiert Einträge nun zuverlässig mit `end_time` und `status=completed` (statt `paused` ohne Endzeit). Dadurch sind Exporte/Reports wieder vollständig und konsistent.
31+
- **Historische Pausiert-Einträge repariert**: Migration schließt verwaiste Einträge mit `status=paused` und `end_time IS NULL` automatisch über `end_time = updated_at` und Statuswechsel auf `completed`.
32+
- **Mehrfach-Pausen ohne Datenverlust**: Beim Start einer weiteren Pause wird die zuvor abgeschlossene Pause zuerst in `breaks` (JSON) archiviert; Break-Dauern bleiben vollständig für ArbZG-Prüfungen erhalten.
33+
- **Break-Status-Berechnung korrigiert**: `getBreakStatus()` zählt aktive Sitzungszeit nicht mehr doppelt; Warnstufen und Restpausen-Hinweise sind wieder korrekt.
34+
- **Export-Spalten korrigiert**: `duration_hours` liefert jetzt Brutto-Dauer (Wall-Clock), `working_hours` Netto-Arbeitszeit (abzgl. Pausen). Vorher waren beide Spalten identisch.
35+
36+
### Geändert
37+
38+
- **Export-Transparenz**: CSV/JSON-Exporte enthalten jetzt explizite Zeitzonen-Metadaten (`timezone`, `exported_at`), damit nachgelagerte Systeme die Uhrzeiten eindeutig interpretieren.
39+
- **UI-Klarheit**: Dashboard zeigt sichtbaren Zeitzonen-Hinweis (`Europe/Berlin (MEZ/MESZ)`), Export-Hinweis auf der Zeiteintragsseite nennt die verwendete Zeitzone.
40+
- **Bediensicherheit**: Vor `Clock Out` erscheint eine Bestätigungsabfrage mit klarer Abgrenzung zwischen „Pause starten“ und „Ausstempeln“.
41+
- **Admin-Transparenz**: In den Admin-Einstellungen wird die konfigurierte Zeitzone sichtbar angezeigt.
42+
343
## 1.1.14 – 2026-04-14
444

545
### Behoben

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- **Vacation entitlement policy engine**: New policy-driven calculation flow with support for `manual_fixed`, `model_based_simple`, `tariff_rule_based`, and `manual_exception`, plus admin simulation endpoint.
13+
- **Tariff rule data model and APIs**: Added versioned tariff rule sets/modules and admin endpoints to create, update, activate, retire, and assign policies to users.
14+
- **Entitlement computation snapshots**: Added persistent entitlement snapshots (`at_entitlement_snapshots`) with calculation trace/policy fingerprint for auditability and diagnostics.
15+
- **Admin notifications page**: New dedicated admin UI (`/admin/notifications`) with HR recipient + event matrix management and a dedicated notifications settings API.
16+
17+
### Changed
18+
19+
- **Vacation allocation integration**: Year allocation now resolves entitlement via `VacationEntitlementEngine` and returns entitlement source/rule-set/trace metadata in allocation payloads.
20+
- **Policy migration compatibility**: Existing user model vacation values are backfilled into policy assignments during migration (`Version1018Date20260420123000`) to keep legacy installs consistent.
21+
- **Admin settings flow**: Absence notification-related controls (carryover expiry/cap, rollover switches, substitute-required types, iCal and substitution-mail toggles) are centralized on admin notifications APIs/UI.
22+
- **Working time model schema**: Added `work_days_per_week` to `at_models` (`Version1019Date20260420150000`) to support entitlement formulas.
23+
24+
### Fixed
25+
26+
- **User deletion cleanup**: Deleting a user now also removes vacation policy assignments and entitlement snapshots, preventing orphaned policy/computation data.
27+
1028
## 1.1.14 - 2026-04-14
1129

1230
### Fixed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ Die App läuft vollständig innerhalb Ihrer selbst gehosteten Nextcloud‑Instan
1313
- Ruhezeiten (11h) mit Blockierung von Clock‑In und manuellen Einträgen
1414
- Erkennung von Nacht‑, Sonn‑ und Feiertagsarbeit inkl. Dokumentation
1515
- **Abwesenheitsmanagement**: Urlaub, Krankheit, Sonderurlaub, unbezahlter Urlaub mit Genehmigungsworkflow; **Resturlaub / Vorjahres‑Tage** mit konfigurierbarem Ablaufdatum (z. B. 31.03.), FIFO‑Verbrauch, Admin‑Pflege und optional CSV‑Import (`occ arbeitszeitcheck:import-vacation-balance`)
16+
- **Urlaubsanspruch-Engine (neu erweitert)**: Pro Mitarbeitenden konfigurierbarer Modus (manuell, modellbasiert oder tarif-/regelbasiert), zeitlich gültige Policy-Zuordnungen, aktive Regelwerk-Versionierung und nachvollziehbare Berechnungs-Trace-Daten inkl. Snapshot-Ablage für Auswertungen
1617
- **Team‑ und Manager‑Ansicht**: Genehmigungen, Team‑Übersichten, Compliance‑Status
1718
- **Berichte & Exporte**: Tages/Wochen/Monats‑Reports, Overtime‑Reports, Absenzberichte, DATEV‑Export
1819
- **Audit‑Logs**: Lückenlose Nachvollziehbarkeit von Änderungen an Zeiten, Abwesenheiten und Einstellungen
1920
- **Revisionssichere Monatsfinalisierung** (optional, Admin‑Schalter): Kalendermonat mit Snapshot, Hash und PDF‑Nachweis abschließen; finalisierte Monate bleiben gesperrt, bis eine Administratorin/ein Administrator mit Begründung wieder öffnet
21+
- **Admin-Benachrichtigungen (neu erweitert)**: Eigene Admin-Seite für HR-E-Mail-Matrix (pro Abwesenheitstyp und Ereignis), Empfänger-Validierung/Deduplizierung sowie zentrale Steuerung von Carryover-/Rollover- und Substitutions-Mail-Einstellungen
2022
- **DSGVO‑Support**: Exporte, Löschkonzepte (unter Beachtung der gesetzlichen Aufbewahrung), DPIA‑/Verarbeitungsverzeichnis‑Vorlagen
2123

2224
> **Rechtlicher Hinweis (DE):**

appinfo/info.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ gehosteten Nextcloud.
5454
- PHP 8.1–8.4
5555
]]></description>
5656

57-
<version>1.1.14</version>
57+
<version>1.2.0</version>
5858
<licence>AGPL-3.0-or-later</licence>
5959
<author mail="info@software-by-design.de" homepage="https://software-by-design.de">Alexander Mäule</author>
6060

appinfo/routes.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@
8181
['name' => 'manager#dashboard', 'url' => '/manager', 'verb' => 'GET'],
8282
['name' => 'manager#employeeTimeEntriesPage', 'url' => '/manager/time-entries', 'verb' => 'GET'],
8383
['name' => 'manager#employeeAbsencesPage', 'url' => '/manager/absences', 'verb' => 'GET'],
84+
['name' => 'manager#monthClosuresPage', 'url' => '/manager/month-closures', 'verb' => 'GET'],
85+
['name' => 'manager#revisionPdfUsers', 'url' => '/api/manager/revision-pdf/users', 'verb' => 'GET'],
86+
['name' => 'manager#revisionPdfAvailableMonths', 'url' => '/api/manager/revision-pdf/available-months', 'verb' => 'GET'],
87+
['name' => 'manager#revisionPdfUsersForMonth', 'url' => '/api/manager/revision-pdf/users-for-month', 'verb' => 'GET'],
8488
['name' => 'manager#getTeamOverview', 'url' => '/api/manager/team-overview', 'verb' => 'GET'],
8589
['name' => 'manager#getEmployeeTimeEntries', 'url' => '/api/manager/employee-time-entries', 'verb' => 'GET'],
8690
['name' => 'manager#getEmployeeAbsences', 'url' => '/api/manager/employee-absences', 'verb' => 'GET'],
@@ -134,11 +138,14 @@
134138
['name' => 'admin#dashboard', 'url' => '/admin', 'verb' => 'GET'],
135139
['name' => 'admin#users', 'url' => '/admin/users', 'verb' => 'GET'],
136140
['name' => 'admin#settings', 'url' => '/admin/settings', 'verb' => 'GET'],
141+
['name' => 'admin#notifications', 'url' => '/admin/notifications', 'verb' => 'GET'],
137142
['name' => 'admin#holidays', 'url' => '/admin/holidays', 'verb' => 'GET'],
138143
['name' => 'admin#workingTimeModels', 'url' => '/admin/working-time-models', 'verb' => 'GET'],
139144
['name' => 'admin#auditLog', 'url' => '/admin/audit-log', 'verb' => 'GET'],
140145
['name' => 'admin#getAdminSettings', 'url' => '/api/admin/settings', 'verb' => 'GET'],
141146
['name' => 'admin#updateAdminSettings', 'url' => '/api/admin/settings', 'verb' => 'POST'],
147+
['name' => 'admin#getNotificationSettings', 'url' => '/api/admin/notifications/settings', 'verb' => 'GET'],
148+
['name' => 'admin#updateNotificationSettings', 'url' => '/api/admin/notifications/settings', 'verb' => 'POST'],
142149
// Legacy company_holidays JSON (kept for backward compatibility; new code should use state-holidays endpoints)
143150
['name' => 'admin#getCompanyHolidays', 'url' => '/api/admin/holidays', 'verb' => 'GET'],
144151
['name' => 'admin#saveCompanyHoliday', 'url' => '/api/admin/holidays', 'verb' => 'POST'],
@@ -161,6 +168,13 @@
161168
['name' => 'admin#createWorkingTimeModel', 'url' => '/api/admin/working-time-models', 'verb' => 'POST'],
162169
['name' => 'admin#updateWorkingTimeModel', 'url' => '/api/admin/working-time-models/{id}', 'verb' => 'PUT'],
163170
['name' => 'admin#deleteWorkingTimeModel', 'url' => '/api/admin/working-time-models/{id}', 'verb' => 'DELETE'],
171+
['name' => 'admin#getTariffRuleSets', 'url' => '/api/admin/tariff-rule-sets', 'verb' => 'GET'],
172+
['name' => 'admin#createTariffRuleSet', 'url' => '/api/admin/tariff-rule-sets', 'verb' => 'POST'],
173+
['name' => 'admin#updateTariffRuleSet', 'url' => '/api/admin/tariff-rule-sets/{id}', 'verb' => 'PUT'],
174+
['name' => 'admin#activateTariffRuleSet', 'url' => '/api/admin/tariff-rule-sets/{id}/activate', 'verb' => 'POST'],
175+
['name' => 'admin#retireTariffRuleSet', 'url' => '/api/admin/tariff-rule-sets/{id}/retire', 'verb' => 'POST'],
176+
['name' => 'admin#assignVacationPolicy', 'url' => '/api/admin/users/{userId}/vacation-policy', 'verb' => 'PUT'],
177+
['name' => 'admin#simulateVacationPolicy', 'url' => '/api/admin/vacation-policy/simulate', 'verb' => 'POST'],
164178

165179
// Admin teams (app-owned teams/departments)
166180
['name' => 'admin#teams', 'url' => '/admin/teams', 'verb' => 'GET'],
@@ -195,5 +209,14 @@
195209

196210
// Health check route
197211
['name' => 'health#check', 'url' => '/health', 'verb' => 'GET'],
212+
213+
// Revision-safe month closure
214+
['name' => 'month_closure#feature', 'url' => '/api/month-closure/feature', 'verb' => 'GET'],
215+
['name' => 'month_closure#periods', 'url' => '/api/month-closure/periods', 'verb' => 'GET'],
216+
['name' => 'month_closure#status', 'url' => '/api/month-closure/status', 'verb' => 'GET'],
217+
['name' => 'month_closure#finalize', 'url' => '/api/month-closure/finalize', 'verb' => 'POST'],
218+
['name' => 'month_closure#pdf', 'url' => '/api/month-closure/pdf', 'verb' => 'GET'],
219+
['name' => 'month_closure#finalizedMonths', 'url' => '/api/month-closure/finalized-months', 'verb' => 'GET'],
220+
['name' => 'month_closure#reopen', 'url' => '/api/month-closure/reopen', 'verb' => 'POST'],
198221
],
199222
];

0 commit comments

Comments
 (0)