Skip to content

Commit 1580b25

Browse files
fix: assignable manager logic, UI hints, repair step
- Add TeamResolverService::hasAssignableManagerForEmployee() (app-teams: explicit managers only; legacy: colleagues proxy) to prevent approval deadlocks. - Align AbsenceService and TimeEntryController auto-approve with same rule. - Add ReleaseStuckPendingAbsences repair step and AbsenceService helper. - Absences UI: info callout and defensive warning; l10n updates. - Docs: CHANGELOG [Unreleased], user manuals, developer doc, workflow matrix. - Unit tests updated. Made-with: Cursor
1 parent 5e799f3 commit 1580b25

21 files changed

Lines changed: 930 additions & 54 deletions

CHANGELOG.de.md

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

3+
### Behoben
4+
5+
- **Genehmigungs-Deadlock (App-Teams)**: Abwesenheiten und Zeiteintrags-Korrekturen behandeln „hat Kolleg:innen“ nicht mehr wie „hat eine:n Vorgesetzte:n“. Auto-Genehmigung, wenn **kein zuweisbarer Genehmiger** existiert, folgt `TeamResolverService::hasAssignableManagerForEmployee()` (explizite Team-Manager bei App-Teams; Legacy-Gruppenmodus weiterhin Kollegen-Proxy). Verhindert Anträge, die dauerhaft auf Managerfreigabe warten, obwohl niemand freigeben darf.
6+
- **Zeiteintrags-Korrekturen**: Gleiche Zuweisbarkeitsregel wie bei Abwesenheiten (zuvor nur Kollegen-IDs).
7+
8+
### Hinzugefügt
9+
10+
- **Repair-Schritt** `ReleaseStuckPendingAbsences`: setzt nach Migration verbliebene `pending`-Abwesenheiten unter derselben Bedingung automatisch auf genehmigt (idempotent).
11+
12+
### Geändert
13+
14+
- **UX**: Abwesenheiten zeigen einen Hinweis, wenn App-Teams aktiv sind und kein Genehmiger zugeordnet ist; in der Detailansicht erscheint bei veralteten hängenden Anträgen ein Warnhinweis (bis Repair/Admin die Teamkonfiguration korrigiert).
15+
16+
### Dokumentation
17+
18+
- Nutzerhandbücher (EN/DE), `tests/WORKFLOW_ROLE_MATRIX.md` und Entwicklerdokumentation zur Semantik „zuweisbarer Manager“ und zum Repair-Schritt ergänzt.
19+
20+
## 1.1.13 – 2026-04-13
21+
322
### Hinzugefügt
423

524
- **Monatsabschluss: Karenz und Auto-Finalisierung**: Admin-Einstellung `month_closure_grace_days_after_eom` (0–90, Standard 0). Nach Monatsende haben Mitarbeitende so viele Kalendertage zur manuellen Finalisierung; ist der Monat danach noch offen, finalisiert ein täglicher Hintergrundauftrag automatisch (gleicher Snapshot wie manuell). Ausstehende Zeiteintragsfreigaben und offene Abwesenheits-Workflows blockieren die Auto-Finalisierung. Wiederöffnen bleibt Administrator:innen vorbehalten.

CHANGELOG.md

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

88
## [Unreleased]
99

10+
### Fixed
11+
12+
- **Approver deadlock (app teams)**: Absence and time-entry correction workflows no longer treat “has colleagues” as “has a manager”. Auto-approval when **no assignable approver** exists now follows `TeamResolverService::hasAssignableManagerForEmployee()` (explicit team managers in app-teams mode; legacy group mode still uses colleagues as a proxy). Prevents requests stuck in “awaiting manager approval” when nobody can approve.
13+
- **Time entry corrections**: Same assignability rule as absences (previously used colleague IDs only).
14+
15+
### Added
16+
17+
- **Repair step** `ReleaseStuckPendingAbsences`: post-migration repair auto-approves legacy `pending` absences that still match the “no assignable approver” condition (idempotent).
18+
19+
### Changed
20+
21+
- **UX**: Absences UI shows an informational callout when app teams are enabled and no approver is assigned; detail view shows a defensive warning if an old `pending` row is still stuck (until repair/admin fixes team setup).
22+
23+
### Documentation
24+
25+
- User manuals (EN/DE), `tests/WORKFLOW_ROLE_MATRIX.md`, and developer documentation updated for assignable-manager semantics and repair step.
26+
27+
## 1.1.13 - 2026-04-13
28+
1029
### Added
1130

1231
- **Month closure grace period and auto-finalization**: Admin setting `month_closure_grace_days_after_eom` (0–90, default 0). After end-of-month, employees have that many calendar days to finalize manually; if the month is still open afterward, a daily background job finalizes it automatically (same snapshot as manual finalize). Pending time entry approvals and open absence workflow states block auto-finalization. Reopening remains admin-only.

appinfo/info.xml

Lines changed: 3 additions & 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.11</version>
57+
<version>1.1.13</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

@@ -142,6 +142,7 @@ gehosteten Nextcloud.
142142
<repair-steps>
143143
<post-migration>
144144
<step>OCA\ArbeitszeitCheck\Repair\BackfillAbsenceDays</step>
145+
<step>OCA\ArbeitszeitCheck\Repair\ReleaseStuckPendingAbsences</step>
145146
</post-migration>
146147
</repair-steps>
147148
<background-jobs>
@@ -150,5 +151,6 @@ gehosteten Nextcloud.
150151
<job>OCA\ArbeitszeitCheck\BackgroundJob\MissingTimeEntryJob</job>
151152
<job>OCA\ArbeitszeitCheck\BackgroundJob\BreakReminderJob</job>
152153
<job>OCA\ArbeitszeitCheck\BackgroundJob\VacationRolloverJob</job>
154+
<job>OCA\ArbeitszeitCheck\BackgroundJob\MonthClosureAutoFinalizeJob</job>
153155
</background-jobs>
154156
</info>

css/absences.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,19 @@
3030
display: block;
3131
}
3232

33+
/* Approval workflow info: sits under page header, before list/filter or form */
34+
.section--approval-hint {
35+
margin-top: var(--space-4, 1rem);
36+
margin-bottom: var(--space-6, 1.5rem);
37+
}
38+
.section--approval-hint .alert {
39+
margin-bottom: 0;
40+
}
41+
42+
.absence-detail-stuck-callout {
43+
margin-bottom: var(--space-6, 1.5rem);
44+
}
45+
3346
.section--form {
3447
padding: var(--space-8, 2rem);
3548
}

docs/Developer-Documentation.en.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,24 @@ php occ arbeitszeitcheck:vacation-rollover --dry-run
495495

496496
---
497497

498+
## Absence and correction approval (assignable manager)
499+
500+
**Single source of truth:** `TeamResolverService::hasAssignableManagerForEmployee(string $employeeUserId): bool`
501+
502+
- **`use_app_teams` enabled:** Returns true iff `getManagerIdsForEmployee()` is non-empty (explicit team managers; the employee’s own UID is never counted as their own manager). **Colleagues alone do not imply an approver**—this matches `PermissionService::canManageEmployee()` for non-admin actors.
503+
- **Legacy (groups only):** Returns true iff `getColleagueIds()` is non-empty (proxy only; there are no explicit manager rows). **Known product caveat:** manager HTTP APIs still require app teams + assignment for non-admins; auto-approval in legacy mode avoids deadlocks where nobody could approve.
504+
505+
**Consumers:**
506+
507+
- `AbsenceService`: auto-approves new `pending` requests (and after substitute approval when applicable) when the predicate is false; `doAutoApproveDbWork` records audit `absence_auto_approved`.
508+
- `TimeEntryController::requestCorrection`: auto-completes correction when the predicate is false (same deadlock avoidance as absences).
509+
510+
**Repair:** `OCA\ArbeitszeitCheck\Repair\ReleaseStuckPendingAbsences` (registered in `appinfo/info.xml`) calls `AbsenceService::autoApprovePendingIfNoAssignableManager()` for each `pending` absence—idempotent, safe to re-run.
511+
512+
**Tests:** `TeamResolverServiceTest`, `AbsenceServiceTest` (including auto-approve path), `TimeEntryControllerTest`; matrix notes in `tests/WORKFLOW_ROLE_MATRIX.md`.
513+
514+
---
515+
498516
## API Development
499517

500518
### Adding New Endpoints

docs/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ This folder contains the documentation included with ArbeitszeitCheck for admini
1111
| [GDPR-Compliance-Guide.en.md](GDPR-Compliance-Guide.en.md) | How to operate ArbeitszeitCheck in a GDPR-compliant way (legal basis, data minimization, employee rights, retention) |
1212
| [Compliance-Implementation.en.md](Compliance-Implementation.en.md) | Technical implementation of ArbZG compliance checks (breaks, rest periods, real-time vs batch) |
1313
| [Compliance-Implementation.de.md](Compliance-Implementation.de.md) | Same content in German |
14-
| [Developer-Documentation.en.md](Developer-Documentation.en.md) | Architecture, development setup, contribution guidelines, and technical notes (including vacation carryover / Resturlaub) |
14+
| [Developer-Documentation.en.md](Developer-Documentation.en.md) | Architecture, development setup, contribution guidelines, and technical notes (including vacation carryover / Resturlaub, assignable-manager / auto-approval semantics) |
1515

1616
## Legal Notice
1717

docs/User-Manual.de.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ Im Auswahldialog erscheinen nur Konten aus der Nextcloud-`admin`-Gruppe.
5454

5555
- **Kommen/Gehen und Pausen** über die Zeiterfassung; Korrekturen und Begründungen nach internen Regeln.
5656
- **Abwesenheiten** beantragen und ggf. auf Freigabe warten. **Resturlaub** und Überträge werden angezeigt, wenn die Administration das gepflegt hat.
57+
- **App-Teams (empfohlene Einrichtung):** Wenn Ihre Organisation **App-Teams** nutzt und in der App **kein:e Vorgesetzte:r** für Ihr Team hinterlegt ist, werden Anträge **ohne** Vertretung beim Absenden **automatisch genehmigt**—es gäbe sonst niemanden mit Managerfreigabe. Mit **Vertretung** läuft zuerst der Vertretungs-Schritt. Die Oberfläche kann dazu einen kurzen Hinweis anzeigen.
58+
- **Älteres Gruppenmodell:** Verhalten folgt dem früheren „gleiche Gruppe“-Modell; die Administration sollte sicherstellen, dass Genehmigungen für Ihre Organisation weiterhin sinnvoll möglich sind.
5759
- **Manager-Dashboard** (als Führungskraft): Unter **Ausstehende Genehmigungen** erscheint der **Abwesenheitstyp in Ihrer Sprache** (z. B. Urlaub, Krankheit), nicht technische Kurzbezeichnungen. Wo freigeschaltet, bietet **Abwesenheiten der Mitarbeitenden** eine eigene Listen-/Filteransicht.
5860
- **Berichte** für Zeiträume erstellen und erlaubte Exporte nutzen (CSV, DATEV, …).
5961
- **Compliance-Hinweise** (z. B. fehlende Pausen) nach Vorgabe des Arbeitgebers bearbeiten.

docs/User-Manual.en.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ Only users in Nextcloud's `admin` group are eligible in this picker.
5454

5555
- **Clock in / out** and **breaks**: Use the time tracking UI; follow your organization’s rules for corrections and comments.
5656
- **Absences**: Create requests; wait for approval if your workflow requires it. Vacation balances and carryover (**Resturlaub**) may be shown if your admin configured them.
57+
- **App teams (recommended setup):** If your organization uses **app-managed teams** and **no manager is assigned** to your team in the app, requests you submit **without** a substitute are **approved automatically** when you send them—there is nobody who could approve them in the manager workflow. If you **do** pick a substitute, the substitute step still runs first. The UI may show a short explanation when this applies.
58+
- **Legacy group-based setup:** Behavior follows the older “same group” model; your admin should ensure approvals remain workable for your organization.
5759
- **Manager dashboard** (if you are a team lead): Under **Pending approvals**, absence requests list each person with the **absence type in your language** (e.g. vacation vs sick leave), not raw internal codes. Where enabled, **Employee absences** provides a dedicated list/filter view of team absences.
5860
- **Reports**: Generate period reports or exports your admin allows (CSV, DATEV, etc.).
5961
- **Compliance**: The app may flag violations (e.g. missing breaks); your employer defines how those are handled.

0 commit comments

Comments
 (0)