Story QR-001: Module Skeleton + Test Infrastructure + Shared Events¶
@assignee: @m-kouassi
Module: qr-attendance Slice: QR-T0-001 + QR-T0-003 Brand context: [BOTH] Papillon HR Suite + ALTARYS ENTERPRISE HR Suite Priority: 1 Depends on: Control Plane (TenantContext, module gating) Can develop concurrently with: None Merge order: Before all other QR stories Estimated complexity: S PRD User Stories: N/A — Technical Story
Objective¶
Create the com.altarys.papillon.modules.attendance Spring Modulith module structure, add required dependencies (ZXing for QR generation, Apache POI for Excel export), set up test infrastructure (TestContainers, mock beans, test fixtures), and define shared event contracts for cross-module communication. This is the foundation that all subsequent QR-attendance stories build upon.
Backend Scope¶
Entities & Records¶
Shared Event Records (in com.altarys.papillon.events.attendance):
// AttendanceRecorded.java
public record AttendanceRecorded(
UUID tenantId,
UUID employeeId,
LocalDate attendanceDate,
String status, // PRESENT, RETARD, ABSENT, etc.
int workedMinutes,
int overtimeMinutes,
OffsetDateTime recordedAt
) {}
// WeeklyOvertimeComputed.java
public record WeeklyOvertimeComputed(
UUID tenantId,
UUID employeeId,
LocalDate weekStartDate,
LocalDate weekEndDate,
int totalWorkedMinutes,
int band1Minutes,
int band2Minutes,
int band3Minutes,
int nightMinutes,
int sundayHolidayMinutes,
int totalOvertimeMinutes,
OffsetDateTime computedAt
) {}
Shared Event Record (in com.altarys.papillon.events.holiday):
// PublicHolidayUpdated.java
public record PublicHolidayUpdated(
String countryCode,
int year,
OffsetDateTime updatedAt
) {}
Flyway Migrations¶
None — this story creates no database tables.
Repository Layer¶
None.
Service Layer¶
None — only package structure and configuration.
API Endpoints¶
None.
Validation Rules¶
None.
Multi-Tenant Considerations¶
Module configuration class must declare the attendance module boundary for Spring Modulith. All future services within this module access tenant context via TenantContextHolder.current().
Frontend Scope¶
None — technical enabler, backend only.
Acceptance Criteria¶
AC-001: Module compiles and Spring Modulith verifies module boundaries
Given the attendance module package structure is created
When the application starts with `./gradlew bootRun`
Then Spring Modulith detects the attendance module without boundary violations
And the module is registered as an Application Plane module
AC-002: Test infrastructure runs successfully
Given TestContainers is configured for attendance tests
When `./gradlew test` is executed
Then the AttendanceModuleIntegrationTest passes
And shared event records can be instantiated and serialized
OHADA & Regulatory Rules¶
None for this technical story.
Standards & Conventions¶
docs/standards/java-spring-guidelines.md— Package structure, Spring Modulith conventionsdocs/standards/database-guidelines.md— Multi-tenant design patterns (referenced for future stories)
Testing Requirements¶
Unit Tests¶
- Verify all shared event records can be instantiated with valid data
- Verify event record serialization/deserialization (JSON round-trip)
Integration Tests¶
AttendanceModuleIntegrationTest: Spring context loads with the attendance module- Spring Modulith module boundary verification test
- TestContainers PostgreSQL starts and is reachable
What QA Will Validate¶
- Not applicable (no user-facing functionality).
Out of Scope¶
- No database tables (each story creates its own tables)
- No API endpoints
- No frontend code
- No business logic
Definition of Done¶
- [ ]
com.altarys.papillon.modules.attendancepackage created withpackage-info.java - [ ] ZXing dependency added to
backend/build.gradle - [ ] Apache POI dependency added to
backend/build.gradle - [ ] Shared event records created in
com.altarys.papillon.events.attendanceandcom.altarys.papillon.events.holiday - [ ]
AttendanceModuleIntegrationTestpasses - [ ]
AttendanceTestFixtureshelper class created - [ ] Spring Modulith boundary verification passes
- [ ]
./gradlew testis green - [ ]
./gradlew spotlessApplypasses