Aller au contenu

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 (List) — type, employeeId, details - autoClosedYesterday (List) — employeeId, name, scheduledEnd, autoClosedAt

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@Scheduled patterns, batch job with tenant iteration
  • docs/standards/api-guidelines.md
  • docs/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 action
  • SharedDeviceDetector: 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=true on DailyAttendance
  • Verify anomaly appears in dashboard response
  • Verify manager GET /attendance/today returns only direct reports
  • HR GET /attendance/today returns 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 test is green
  • [ ] ./gradlew spotlessApply passes