Story QR-010: Dashboard Anomalies + Auto-Close Batch + Manager Scope¶
@assignee: @m-kouassi
Module: qr-attendance Slice: QR-T3-002 + QR-T3-004 + US-QR-010 (remaining ACs) Brand context: [BOTH] Papillon HR Suite + ALTARYS ENTERPRISE HR Suite Priority: 10 Depends on: QR-009 (base dashboard) Can develop concurrently with: None (extends QR-009) Merge order: After QR-009 Estimated complexity: M PRD User Stories: US-QR-002 (AC-002), US-QR-010 (AC-003, AC-004)
Objective¶
Extend the daily attendance dashboard with three capabilities: (1) an auto-close batch job that creates synthetic clock-out events for employees who forget to clock out, (2) shared device anomaly detection that flags when multiple employees use the same device, and (3) manager scope filtering so managers see only their direct reports. These features complete the dashboard's operational readiness for daily HR workflows.
Backend Scope¶
Entities & Records¶
No new tables. Extends dashboard and adds batch jobs.
Flyway Migrations¶
None.
Repository Layer¶
No new repositories. Extends existing queries.
Service Layer¶
AutoCloseService + AutoCloseScheduledJob:
- @Scheduled daily job at configurable time (default 23:59 from AttendanceModuleConfig.autoCloseTime)
- Iterates active tenants with QRCONTR module activated
- For each employee with open clock-in (IN without OUT) where scheduled end time has passed:
- Create synthetic clock-out at scheduled end time, source=SYSTEM, autoCloseReason="MISSED_CLOCK_OUT"
- Set DailyAttendance.isAutoClosed=true, NO overtime credited
- Flag: "Pointage incomplet (fermeture automatique)"
- If employee has no schedule: do NOT auto-close, flag "Pointage incomplet — pas d'horaire configure", require manual regularization
SharedDeviceDetector:
- Check: if 2+ different employees have clock events with same deviceIdHash on same day -> generate anomaly
- Add to DailyAttendance.anomalyDetails JSONB: {"type":"SHARED_DEVICE","employeeIds":["uuid1","uuid2"],"details":"Appareil partage avec [name]"}
- Set hasAnomaly=true
- Clock events NOT rejected — informational flag only
Extend DashboardService:
- Add anomalies list to dashboard response
- Add autoClosedYesterday list (consolidated for HR morning review)
- Manager scope: filter employeeIds by managerId from CachedEmployeeData.currentManagerId
API Endpoints¶
Same endpoints as QR-009, extended responses:
| Method | Path | Changes |
|---|---|---|
| GET | /api/v1/qrcontr/attendance/today |
DashboardResponse now includes anomalies and autoClosedYesterday |
| GET | /api/v1/qrcontr/attendance/daily |
Same extensions |
DashboardResponse now includes:
- anomalies (ListautoClosedYesterday (List
Manager filter: when authenticated user role is MANAGER, automatically filter to direct reports only.
Validation Rules¶
- Auto-close only for employees with a configured schedule
- Shared device detection is informational only — no event rejection
- Manager scope is enforced server-side based on authentication context
Multi-Tenant Considerations¶
Auto-close batch iterates tenants individually. Shared device detection is tenant-scoped (same deviceIdHash in different tenants is not an anomaly).
Frontend Scope¶
Extends DashboardPage from QR-009.
Components to Add/Extend¶
AnomalyBadge:
- Colored badge on employee row (tap to expand details)
- Types: SHARED_DEVICE (warning), AUTO_CLOSED (clock), NO_SCHEDULE (calendar)
AnomalyDetailPanel:
- Inline expansion (200ms height animation) showing anomaly details
AutoClosedList:
- Section at top of dashboard showing yesterday's auto-closed records
Extend EmployeeAttendanceRow:
- Add anomaly badge support, different icon for auto-closed
UI States¶
| State | Behavior | French Copy |
|---|---|---|
| Anomaly badge | Colored badge on employee row, tappable | "Alerte : appareil partage" / "Pointage incomplet (fermeture automatique)" |
| Manager view | Filtered to team only, no team selector | Header shows "Mon equipe" |
| Auto-closed list | Highlighted section at top | "Fermetures automatiques d'hier" |
Responsive Behavior¶
- 360px: Anomaly badges as small icons, tap to expand inline. Auto-closed list as collapsible accordion.
- 768px: Badges with short text, auto-closed list visible by default.
- 1024px+: Full anomaly details visible, auto-closed list as table.
Interactions¶
- Anomaly badge tap: inline expand with details (200ms)
- Swipe left on employee row (mobile, HR only): reveals "Regularisation directe" quick action (feeds into QR-013)
- Manager badge in header to indicate team scope
Acceptance Criteria¶
AC-001: Missed clock-out auto-close
Given I forget to clock out
When my scheduled end time passes
Then the system auto-closes my record at the scheduled end time, flags it as SYSTEM-generated, and does NOT credit overtime
AC-002: Anomaly visual flagging on dashboard
Given an anomaly exists (incomplete record, shared device alert)
When I view the dashboard
Then the anomaly is visually flagged with a colored badge
AC-003: Manager team-only view
Given I am a manager
When I view the dashboard
Then I see only my team's data (direct reports)
OHADA & Regulatory Rules¶
- Auto-close does NOT credit overtime (protective for employer — prevents phantom overtime from forgotten clock-outs)
- Original clock events preserved, synthetic events marked
source=SYSTEM(immutable audit trail, ADR-10) - Shared device detection is informational only (FR-QR-092) — events not rejected
Standards & Conventions¶
docs/standards/java-spring-guidelines.md—@Scheduledpatterns, batch job with tenant iterationdocs/standards/api-guidelines.mddocs/standards/react-typescript-guidelines.md
Testing Requirements¶
Unit Tests¶
AutoCloseService: scheduled end passed -> synthetic event created, no schedule -> not auto-closed + flag, no overtime credited on auto-close, already closed -> no actionSharedDeviceDetector: same device different employees -> anomaly created, same device same employee -> no anomaly, different devices -> no anomaly- Manager scope filtering: manager sees only direct reports, HR sees all
Integration Tests¶
- Run auto-close job, verify synthetic clock-out events created with
source=SYSTEM - Verify
isAutoClosed=trueonDailyAttendance - Verify anomaly appears in dashboard response
- Verify manager GET
/attendance/todayreturns only direct reports - HR GET
/attendance/todayreturns all employees - Tenant isolation: auto-close job processes tenants independently
What QA Will Validate¶
- Trigger auto-close (test with fixed clock), verify synthetic events
- Verify anomaly badges on dashboard (shared device, auto-closed)
- Verify manager sees only team data
- Verify auto-closed list appears at top of dashboard next morning
- Tap anomaly badge, verify detail expansion
Out of Scope¶
- Regularization to correct auto-closed records (QR-012)
- No-show detection (already in QR-009)
- Dashboard export (QR-017)
Definition of Done¶
- [ ] Auto-close batch job runs correctly, creates synthetic events
- [ ] Shared device detection creates anomaly entries
- [ ] Manager scope filtering works for dashboard
- [ ] Anomaly badges render on dashboard with correct icons and colors
- [ ] All acceptance criteria pass manually
- [ ] Unit tests and integration tests passing
- [ ] Multi-tenant isolation verified
- [ ] No TypeScript
any, no Java raw types - [ ] Audit trail entries for auto-close events
- [ ]
./gradlew testis green - [ ]
./gradlew spotlessApplypasses