Absence Management — PRD+FSD Hybrid¶
Module Code: ABSMGT Product: [BOTH] (Papillon HR Suite + ALTARYS ENTERPRISE HR Suite) Country Scope: CI (Côte d'Ivoire) — MVP Version: 1.0 Date: 02/03/2026 Author: ANALYST (Claude Code) Input documents: - docs/specs/platform-vision.md (Tier 1 human-written spec) - docs/prd/platform-vision-prd.md (Platform Vision PRD v1.0) - docs/research/3_validated/absence-management-legal-validated.md (legal research audit — validated) - docs/research/2_audit/absence-management-legal-audit.md (legal audit gaps)
PART A — Product Requirements (PRD)¶
A.1 Executive Summary¶
The Problem¶
Businesses in Côte d'Ivoire manage employee absences through WhatsApp messages, paper forms, and Excel spreadsheets. This creates:
- Compliance risk: CI's Code du Travail 2015 and the Convention Collective Interprofessionnelle (CCI 1977) impose specific rules on annual leave accrual (2.2 jours ouvrables/month), seniority bonuses, maternity protection, sick leave indemnization tables, and permissions exceptionnelles. Manual tracking means errors in leave balances, illegal denial of maternity rights, and incorrect payroll deductions.
- Payroll errors: Without a system linking absences to payroll, deductions for unpaid leave, sick leave half-pay transitions, and maternity CNPS reimbursement claims are computed manually — leading to over/under-payments.
- Employee frustration: Employees don't know their leave balance, can't easily check request status, and often lose leave days because carry-over isn't tracked.
- Manager blindness: No team calendar, no visibility into who's off when, no way to plan for coverage.
The Solution¶
The Absence Management module (ABSMGT) provides:
- Automated leave accrual based on CI legal rules (2.2 j. ouvrables/month + ancienneté + children bonuses), with three-unit support (jours ouvrables, jours calendaires, jours ouvrés)
- Self-service leave requests with offline capability (auto-submit on sync)
- Org-unit-based approval workflow (N+1 chain per Platform Vision)
- Full CCI indemnization tables for sick leave (category + seniority-based)
- Maternity leave auto-creation (legal right, no approval needed)
- Team calendar (privacy-respecting: shows presence/absence only)
- Ledger-based balance tracking for full auditability
- Payroll integration via
LeaveApproved/LeaveCancelledSpring Application Events
Scale Context¶
- Papillon HR Suite [BOTH]: Primary focus. SMEs (2-350 employees) in CI. Mobile-first, offline-first, French UI.
- ALTARYS ENTERPRISE [ENTERPRISE ONLY — V2]: Dense data, batch operations, delegation, advanced leave planning at scale. API designed to support both from day one.
A.2 User Personas¶
P4a: Employé / Employee — [BOTH]¶
- Uses: Submit leave requests, check balance, view team calendar, upload supporting documents
- Digital literacy: Low to medium
- Device: Android phone (entry-level, intermittent connectivity)
- Key concern: "How many days do I have left? When can I take my congé?"
- Papillon context: May be offline when submitting. Needs absolute simplicity — 3 taps to request leave.
- Pain point: Currently texts their boss on WhatsApp and waits days for a response with no tracking.
P2a: Responsable RH / HR Manager — [BOTH]¶
- Uses: Configure leave types, manage employee balances, override accruals, view dashboards, export data
- Digital literacy: Medium
- Device: Desktop primarily, phone for urgent approvals
- Key concern: "Are all employees' leave balances correct? Is the company compliant with CI law?"
- Papillon context: Often the DG's spouse or a trusted employee. Transitioning from Excel. Needs hand-holding.
- Enterprise context [V2]: Professional HR Director managing a team. Batch balance adjustments, delegation.
P1a: Manager / Approver — [BOTH]¶
- Uses: Approve/reject leave requests, view team calendar, check team coverage
- Digital literacy: Medium to high
- Device: Laptop + phone (approvals on the go)
- Key concern: "Who's off this week? Can I approve this without leaving the team short-staffed?"
P1b: Directeur Général (DG) / Company Owner — [BOTH]¶
- Uses: Fallback approver, self-approves own leave, overview dashboards
- Digital literacy: Medium to high
- Device: Laptop + phone
- Key concern: "Are my employees taking their legal leave? Are we compliant?"
- Papillon context: In S1-S2 companies (2-12 employees), the DG IS the HR manager AND the approver.
A.3 User Stories¶
US-ABS-001: Request Annual Leave [BOTH]¶
As an employee, I want to submit an annual leave request from my phone (even offline), So that I can plan my time off without visiting the HR office.
Acceptance Criteria: - Given I am an employee with an active account, When I open the Absence module, Then I see my current leave balance (by type, in jours calendaires). - Given I fill out a leave request form offline, When connectivity returns, Then the request is auto-submitted to my manager for approval. - Given I submit a request, When the start date is < 15 days from today, Then I see a soft warning about the 15-day notice period but can still submit. - Given I submit a request for annual leave and have NOT yet taken a 14-consecutive-day block this year, Then I see a soft warning about the legal 14-day minimum requirement.
US-ABS-002: Approve/Reject Leave [BOTH]¶
As a manager, I want to approve or reject leave requests with one tap, So that my team can plan their time off quickly.
Acceptance Criteria: - Given a leave request is pending, When I view it, Then I see: employee name, leave type, dates, number of days, employee's current balance, team calendar showing who else is off during that period. - Given I reject a request, When I submit the rejection, Then I must provide a reason (mandatory text field). The employee is notified with the reason. - Given the leave type requires a supporting document (sick leave, maternity), When the document is not uploaded, Then I cannot approve — the approve button is disabled with explanation.
US-ABS-003: View Team Calendar [BOTH]¶
As a manager, I want to see a team calendar showing who's off and when, So that I can ensure adequate coverage.
Acceptance Criteria: - Given I am a manager, When I view the team calendar, Then I see all my direct reports' absences for the current month. - Given a team member is on leave, When I view the calendar, Then I see "Absent" (not the leave type) to protect privacy. - Given I am HR_MANAGER or DG, When I view the calendar, Then I can filter by department and see all employees.
US-ABS-004: Maternity Leave Auto-Creation [BOTH]¶
As an HR manager, I want to record a maternity declaration and have the system auto-create the leave, So that I don't need to process maternity as a "request" (it's a legal right).
Acceptance Criteria:
- Given I enter an expected delivery date, When I save the maternity declaration, Then the system creates a 14-week leave block (6 weeks before expected delivery + 8 weeks after).
- Given maternity leave is created, When I view it, Then it shows status AUTO_APPROVED — no manager approval step.
- Given the employee has other approved leaves overlapping with the maternity period, When maternity is created, Then overlapping annual leave days are credited back to the employee's balance.
US-ABS-005: Sick Leave with Certificate [BOTH]¶
As an employee, I want to declare sick leave and upload my medical certificate, So that my absence is properly recorded and my pay is calculated per the CCI table.
Acceptance Criteria:
- Given I submit a sick leave request, When I don't upload a medical certificate, Then the request stays in PENDING_DOCUMENT status — it is not sent for approval.
- Given I upload the certificate, When I confirm, Then the request moves to PENDING_APPROVAL and is sent to my manager.
- Given my sick leave is approved, When Payroll processes the month, Then my pay is calculated per the CCI indemnization table (full pay → half pay → zero pay, based on category and seniority).
US-ABS-006: View Leave Balance [BOTH]¶
As an employee, I want to see my current leave balance broken down by type, So that I know how many days I can take.
Acceptance Criteria: - Given I open the Absence module, When I view my balance, Then I see balances for each leave type in jours calendaires (my tenant's configured deduction unit). - Given I am a new employee with < 12 months service, When I view my balance, Then I see my accrued balance with a note: "Disponible après 12 mois de service" (unless anticipated leave is configured). - Given my balance includes ancienneté bonus days, When I view the breakdown, Then I see: base accrual + ancienneté bonus + children bonus = total.
US-ABS-007: Permission Exceptionnelle [BOTH]¶
As an employee, I want to request a permission exceptionnelle for a family event (marriage, birth, death), So that I can take my legal leave days with pay.
Acceptance Criteria: - Given I select "Permission exceptionnelle" as leave type, When I choose the event type (mariage, naissance, décès, etc.), Then the system pre-fills the number of days based on the CCI minimum for CI. - Given the tenant has configured more generous durations (above CCI minimum), When I select the event type, Then the system pre-fills the tenant's duration. - Given the tenant tries to configure a duration below the CCI minimum, When they save, Then the system blocks with: "La durée minimale légale (CCI) pour cet événement est de X jours."
US-ABS-008: HR Dashboard [BOTH]¶
As an HR manager, I want a dashboard showing key absence metrics at a glance, So that I can monitor the company's absence situation.
Acceptance Criteria: - Given I am HR_MANAGER, When I open the Absence dashboard, Then I see: team leave calendar, pending approval count (badge), leave balance summary per employee, absence rate (%), upcoming leaves this week/month. - Given I click on an employee's balance, When the detail view opens, Then I see the full ledger of accruals, deductions, adjustments, and carry-overs.
US-ABS-009: Cancel Leave [BOTH]¶
As an employee, I want to cancel a leave request I've submitted, So that I can change my plans.
Acceptance Criteria: - Given my leave request is PENDING_APPROVAL, When I cancel it, Then it moves to CANCELLED and disappears from the approver's queue. - Given my leave is APPROVED but has not started yet, When I cancel it, Then the days are credited back to my balance. Manager and HR are notified. - Given my leave is APPROVED and has already started (I'm currently on leave), When I try to cancel, Then I cannot — I must request an early return through HR.
US-ABS-010: Employee Termination Handling [BOTH]¶
As a system,
When an EmployeeTerminated or EmployeeOffboarded event is received,
I want to auto-cancel pending leave requests and compute the remaining balance,
So that Payroll can calculate the indemnité compensatrice de congés payés.
Acceptance Criteria:
- Given an employee is terminated, When the event is received, Then all PENDING requests are auto-cancelled.
- Given an employee is terminated, When the event is received, Then APPROVED future leaves within the notice period are preserved; those after the termination effective date are cancelled.
- Given an employee is terminated, When the balance is computed, Then a LeaveBalanceSettlement event is published to Payroll with the remaining balance in jours calendaires.
A.4 Non-Functional Requirements¶
| Requirement | Target | Notes |
|---|---|---|
| Response time | < 2 seconds for leave request submission on 3G | Mobile-first, offline-first |
| Balance calculation | < 500ms for any employee's current balance | Cached total from ledger |
| Team calendar | < 1 second to render 30-day view for a team of 50 | Must be fast for managers |
| Offline capability | Leave requests created offline, auto-submitted on sync | Forms never lose data |
| Concurrent submissions | Handle simultaneous requests from same employee (prevent double-booking) | Optimistic locking on leave balance |
| Audit trail | Every balance change produces an immutable audit entry | Ledger-based — inherently auditable |
| Data isolation | Zero cross-tenant leave data leakage | tenant_id on every query |
| Scale | Support 100,000+ tenants × average 20 employees × 12 months of leave data | Indexed queries, partitioning strategy |
A.5 MVP Scope¶
In MVP (V1)¶
- Country: CI only
- Leave types: 7 (annual, sick, maternity, paternity, permissions exceptionnelles, unpaid [track only], unjustified absence)
- Accrual engine: 2.2 j. ouvrables/month + ancienneté bonus + children bonus
- Three-unit system: Jours ouvrables (accrual) → Jours calendaires (× 1.25, CI default) → Jours ouvrés (5-day week, configurable)
- Approval: Standard N+1 org-unit-based (per Platform Vision FR-PV-020 to FR-PV-024)
- Maternity: Auto-create (14 weeks, no approval needed). Employer pays 100%, claims CNPS reimbursement.
- Sick leave: Full CCI indemnization tables (by category + seniority)
- Permissions exceptionnelles: CCI minimum as floor, tenant can configure above
- Half-day support: Morning/afternoon half-day requests
- Supporting documents: Required before approval for sick leave and maternity (hard block)
- Offline: Full request creation offline, auto-submit on sync
- Dashboard: Team calendar + pending approvals + balance summary + absence rate + upcoming leaves
- Notifications: 4 core (submit → approver, approve → employee, reject → employee, cancel → approver+HR)
- Balance model: Ledger (transaction-based)
- Carry-over: Configurable per tenant (strict 12-month expiry | partial carry-over | unlimited). Default: strict.
- Reference period: Configurable (calendar year | employee anniversary). Default: calendar year.
- Public holidays: Platform admin sets CI defaults (fixed + variable). Tenant can override variable dates.
- Team calendar: Privacy-respecting (shows "Absent" not leave type)
- Self-service: Balance view, request history, submit/cancel requests
- Payroll integration:
LeaveApproved,LeaveCancelled,LeaveBalanceSettlementevents
Out of MVP (V2+)¶
- [V2] BJ and CM country rules — Bénin and Cameroun leave types, accrual rates, and public holidays (Month 9)
- [V2] Leave planning calendar at enterprise scale (team coverage rules, planning des congés)
- [V2] Exportable absence register (registre des absences for labour inspection)
- [V2] Low balance warning notification
- [V2] Maternity return reminder notification (2 weeks before end)
- [V2] Multi-level approval chains (amount-based, type-based escalation)
- [V2] HR delegation of approval authority
- [V2] Batch balance adjustments for Enterprise
- [V2] Congé de formation (training leave)
- [V2] Congé syndical (union leave)
- [V2] Advanced absence reporting and analytics
- [V2] Leave type-specific approval flows (sick auto-approve with cert, maternity auto-approve)
- [V2] Convention collective configuration (sector-specific conventions overriding CCI)
- [V2] Paternity 21 days (when CI décrets d'application are published)
- [ENTERPRISE ONLY] Batch operations — mass leave creation, mass balance adjustments
- [ENTERPRISE ONLY] Advanced leave planning — coverage rules, minimum staffing levels
- [ENTERPRISE ONLY] Delegation workflows — HR officers managing subsets of employees
- Country-specific leave policies beyond CI are V2
- Monetary leave allocation calculation (Payroll's responsibility)
- Native mobile apps (iOS/Android) — PWA only for MVP
A.6 Open Questions¶
| # | Question | Priority | Status |
|---|---|---|---|
| OQ-ABS-01 | ITS exoneration of CNPS maternity indemnités: The legal research flags this as SOURCE UNKNOWN. Confirm whether maternity indemnités paid by CNPS are truly exonerated from ITS in CI, and identify the specific CGI article. | 🟡 HIGH | Unresolved — deferred to Payroll PRD |
| OQ-ABS-02 | CCI Art. 25.X numbering format: Verify that the CI Code du Travail 2015 (Loi n° 2015-532) actually uses decimal sub-numbering (Art. 25.1, 25.2, etc.). If the real format is different, all the article references in this PRD need re-mapping. | 🟢 MEDIUM | Needs one-time verification |
| OQ-ABS-03 | Loi n° 96-5 (jours fériés CI): Confirm this 1996 law is still current. In particular, was "15 novembre — Journée Nationale de la Paix" added by amendment? | 🟢 MEDIUM | Needs verification |
| OQ-ABS-04 | CCI 1977 current status: Confirm whether the CCI 1977 is still the applicable convention collective interprofessionnelle in CI, or if it has been revised/renegotiated. Severance, notice periods, and leave rules all depend on it. | 🟡 HIGH | Needs verification |
| OQ-ABS-05 | Délégué du personnel threshold: At what CI employee headcount are délégués du personnel required? How does this affect absence management (e.g., consultation on leave planning)? | 🟢 MEDIUM | V2 relevance |
| OQ-ABS-06 | Leave accrual during probation: Confirmed that accrual starts day 1 and taking leave requires 12 months service. But does the 12-month threshold apply per leave TYPE or globally? Can a new employee take a permission exceptionnelle (e.g., birth of child) before 12 months? | 🟡 HIGH | Assumed yes (legal right) — needs verification |
| OQ-ABS-07 | Conversion formula validation: The 1.25 factor (ouvrables → calendaires) is confirmed by the client. Verify against CI legal/practice sources: is this 30/24 (calendar days / ouvrables per month)? | 🟢 MEDIUM | Temporarily validated |
A.7 Temporarily Validated¶
| # | Item | Current Value | Risk | Action Required |
|---|---|---|---|---|
| TV-ABS-01 | Maternity pay mechanism | Employer pays 100% salary, claims full CNPS reimbursement. Net employer cost = 0. | Client-confirmed but legal citation is ambiguous (CCI Art. 31 says "l'employeur verse la moitié, CNPS rembourse"). | Verify with CNPS CI or labor lawyer before go-live. |
| TV-ABS-02 | CNPS contribution base during sick leave | CNPS contributions calculated on the amount actually paid (not full notional salary). | Client-confirmed. Legal research says sick leave "considered period of employment" for CNPS but is ambiguous on contribution base. | Verify with CNPS CI. |
| TV-ABS-03 | Paternity leave duration | 2 jours ouvrables (current enforceable law). System designed with configurable parameter for future change to 21 days when décrets d'application are published. | 2023 law adopted but décrets not yet published as of 03/2026. | Monitor for publication of décrets d'application. Update configuration when published. |
| TV-ABS-04 | Ouvrables → calendaires conversion factor | 1.25 (i.e., jours calendaires = jours ouvrables × 1.25) | Client-confirmed. Mathematically consistent with 30/24 ratio. | Verify against CI legal sources. |
| TV-ABS-05 | Leave accrual continues during sick/maternity | Annual leave accrues at 2.2 j. ouvrables/month during sick leave and maternity leave. | Client-confirmed. Consistent with "assimilated to effective work" principle in CI labor law. | Verify specific CI legal article. |
| TV-ABS-06 | Payroll deduction formula | salaire mensuel / nombre de jours ouvrables du mois × nombre de jours d'absence. | Practice-based — the legal research notes "Pas de formule légale imposée." | Implemented as is. Flagged as unverified. |
PART B — Functional Specifications (FSD)¶
B.1 Absence Type Catalog [ALL-SCOPED: CI]¶
Leave Type Reference Table¶
| Code | Name (FR) | Paid? | Accrual-based? | Document Required? | Approval? | Balance Impact | Payroll Impact |
|---|---|---|---|---|---|---|---|
ANNUAL |
Congé annuel | ✅ Yes (100%) | ✅ Yes | ❌ No | ✅ N+1 approval | Deducts from annual balance | Paid leave (allocation de congé) |
SICK |
Congé maladie | ✅ Yes (CCI table) | ❌ No | ✅ Certificat médical | ✅ N+1 approval | Separate sick leave counter | Full pay → half pay → zero (per CCI) |
MATERNITY |
Congé maternité | ✅ Yes (100%) | ❌ No | ✅ Attestation médicale | ❌ Auto-created | No balance impact | Employer pays 100%, CNPS reimburses |
PATERNITY |
Congé paternité | ✅ Yes (100%) | ❌ No | ✅ Acte de naissance | ✅ N+1 approval | Deducts from permissions | Paid by employer |
EXCEPTIONAL |
Permission exceptionnelle | ✅ Yes (100%) | ❌ No | ✅ Justificatif | ✅ N+1 approval | Deducts from 10j/year cap | Paid by employer |
UNPAID |
Congé sans solde | ❌ No | ❌ No | ❌ No | ✅ N+1 approval | Track only (MVP) | Deduction: full daily rate × days |
UNJUSTIFIED |
Absence injustifiée | ❌ No | ❌ No | ❌ No | ❌ HR records | No balance (disciplinary) | Deduction + possible sanctions |
Permissions Exceptionnelles Detail Table [CI]¶
| Event Code | Event Name (FR) | CCI Minimum (CI) | Configurable? | Max Total/Year |
|---|---|---|---|---|
MARRIAGE_SELF |
Mariage du salarié | 4 jours | ✅ Tenant can increase | 10 j./an (all events combined) |
MARRIAGE_CHILD |
Mariage d'un enfant | 2 jours | ✅ Tenant can increase | 10 j./an |
BIRTH_CHILD |
Naissance d'un enfant | 2 jours | ✅ Tenant can increase | 10 j./an |
DEATH_SPOUSE |
Décès du conjoint | 5 jours | ✅ Tenant can increase | 10 j./an |
DEATH_CHILD |
Décès d'un enfant | 5 jours | ✅ Tenant can increase | 10 j./an |
DEATH_PARENT |
Décès père/mère | 5 jours | ✅ Tenant can increase | 10 j./an |
DEATH_SIBLING |
Décès frère/sœur | 2 jours | ✅ Tenant can increase | 10 j./an |
DEATH_IN_LAW |
Décès beau-père/belle-mère | 2 jours | ✅ Tenant can increase | 10 j./an |
MOVE |
Déménagement | 2 jours | ✅ Tenant can increase | 10 j./an |
FR-ABS-PERM-01: IF a tenant tries to configure a permission exceptionnelle duration BELOW the CCI minimum THEN the system MUST block the save with message: "La durée minimale légale (CCI) pour cet événement est de {min_days} jours."
FR-ABS-PERM-02: IF the total permissions exceptionnelles taken in the current reference period exceeds 10 jours THEN the system MUST warn the HR manager and block the request. Additional days beyond 10 must be handled as congé sans solde or congé annuel.
B.2 Functional Rules — Leave Accrual Engine¶
Base Accrual¶
FR-ABS-001: Every employee with status = ACTIVE accrues annual leave at a rate of 2.2 jours ouvrables per month of effective service. This is the CCI rate (more favorable than the Code du Travail's 2 j./month).
FR-ABS-002: The accrual unit is jours ouvrables (6-day week: Mon–Sat). This is the legal reference unit for CI.
FR-ABS-003: For display and deduction purposes, the system supports THREE units: - Jours ouvrables (JO): Legal accrual base. 1 month = 2.2 JO. - Jours calendaires (JC): JO × 1.25. The CI standard deduction unit. 1 month = 2.75 JC. - Jours ouvrés (JOV): 5-day week equivalent. JO × 5/6. 1 month ≈ 1.83 JOV.
FR-ABS-004: Each tenant configures its deduction unit preference:
- IF tenant.leave_deduction_unit = JOURS_CALENDAIRES (default for CI) THEN balances are displayed and deducted in JC.
- IF tenant.leave_deduction_unit = JOURS_OUVRES THEN balances are displayed and deducted in JOV.
- IF tenant.leave_deduction_unit = JOURS_OUVRABLES THEN balances are displayed and deducted in JO.
- Internal storage is ALWAYS in jours ouvrables (source of truth). Conversion happens at display and deduction time.
FR-ABS-005: Accrual starts from the employee's hire date (date d'embauche). Leave accrues from day 1, regardless of probation period.
FR-ABS-006: An employee CANNOT take annual leave until they have completed 12 months of continuous service with the current employer.
- IF employee.hire_date + 12 months > today THEN the "Submit annual leave request" action MUST display: "Vos congés seront disponibles à partir du {hire_date + 12 months}."
- The accrued balance is visible but marked as "non disponible."
- EXCEPTION: Permissions exceptionnelles (family events) are available from day 1 — they are a separate entitlement from annual leave.
Ancienneté (Seniority) Bonus Days [CI — CCI Art. 46]¶
FR-ABS-010: In addition to the base 2.2 j./month, employees earn bonus annual leave days based on seniority (ancienneté) with the current employer:
| Seniority | Bonus (jours ouvrables/year) |
|---|---|
| 5 years | +1 |
| 10 years | +2 |
| 15 years | +3 |
| 20 years | +5 |
| 25 years | +7 |
FR-ABS-011: Ancienneté bonus is calculated once per reference period, based on the employee's seniority at the START of the reference period. The bonus is added as a single ledger transaction at the beginning of the period.
FR-ABS-012: IF the employee's seniority bracket changes MID-period (e.g., they reach 10 years in June) THEN the new bonus is applied at the START of the NEXT reference period. No mid-period adjustments.
Children Bonus Days [CI — CCI]¶
FR-ABS-013: Employees receive +1 jour ouvrable per child under 21 years old as additional annual leave.
FR-ABS-014: Children data is sourced from the employee's profile:
- IF HR Core module is active THEN children are read from the HR Core employee profile (date of birth required).
- IF HR Core is NOT active THEN children are managed directly in the Absence module as a simple count field (number_of_children_under_21), manually set by HR.
FR-ABS-015: The children bonus is recalculated at the START of each reference period. A child who turns 21 during the period retains their bonus for that period.
Accrual During Absences¶
FR-ABS-016: Annual leave accrual (2.2 j. ouvrables/month) CONTINUES during: - Sick leave (congé maladie) - Maternity leave (congé maternité) - Paternity leave (congé paternité) - Permissions exceptionnelles - Work-related accidents (accident du travail)
These periods are assimilated to effective work for accrual purposes.
FR-ABS-017: Annual leave accrual PAUSES during: - Congé sans solde (unpaid leave) - Absence injustifiée (unjustified absence) - Suspension disciplinaire (if applicable)
B.3 Functional Rules — Reference Period & Carry-Over¶
Reference Period¶
FR-ABS-020: Each tenant configures a reference period for leave accrual and carry-over:
- CALENDAR_YEAR (default): January 1 – December 31
- ANNIVERSARY: Based on each employee's hire date (e.g., hired March 15 → reference period March 15 – March 14 of following year)
FR-ABS-021: At the end of each reference period, the system: 1. Calculates the employee's total accrued leave for the period (base + ancienneté + children) 2. Determines untaken days from the PREVIOUS period 3. Applies the tenant's carry-over policy (FR-ABS-022) 4. Generates ledger transactions for: new accrual, expired days (if any), carried-over days
Carry-Over Policy¶
FR-ABS-022: Each tenant configures its carry-over policy:
- STRICT_EXPIRY (default — per CI law): Untaken leave from the previous reference period expires. Lost days are recorded as EXPIRED in the ledger. A warning notification is sent to the employee 30 days before expiry.
- PARTIAL_CARRY_OVER: Maximum of N days can be carried over (tenant configures N). Excess expires.
- UNLIMITED: All untaken days carry over indefinitely.
FR-ABS-023: Compensatory payment in lieu of untaken leave is forbidden during the employment contract (Art. 25.12 CT 2015). The system MUST NOT provide a "pay out leave balance" feature during active employment.
FR-ABS-024: Upon employment termination (departure), the remaining leave balance MUST be paid out as indemnité compensatrice de congés payés. This is mandatory per CI law. The Absence module computes the balance and publishes a LeaveBalanceSettlement event to Payroll.
B.4 Functional Rules — Leave Request Workflow¶
Request Creation¶
FR-ABS-030: A leave request contains:
- employee_id, tenant_id (from session context)
- leave_type (from catalog: ANNUAL, SICK, MATERNITY, etc.)
- start_date, end_date (inclusive)
- half_day_start (boolean: if true, employee works the morning and is absent afternoon of start_date)
- half_day_end (boolean: if true, employee is absent morning and works afternoon of end_date)
- event_type (only for EXCEPTIONAL — e.g., MARRIAGE_SELF, DEATH_SPOUSE)
- reason (optional free text)
- supporting_document_id (required for certain leave types — see B.1)
FR-ABS-031: When calculating the number of leave days:
- IF no half-days THEN number_of_days = business days between start_date and end_date (inclusive), converted to tenant's deduction unit.
- IF half_day_start = true THEN start_date counts as 0.5 days.
- IF half_day_end = true THEN end_date counts as 0.5 days.
- Public holidays falling within the leave period are EXCLUDED from the count (they do not consume leave days and extend the effective absence by 1 day each).
FR-ABS-032: The system computes number_of_days automatically from the date range and tenant's working schedule. The employee sees the computed total before submitting and can adjust.
Validation Rules¶
FR-ABS-040: IF leave_type = ANNUAL AND employee.hire_date + 12 months > today THEN block the request. Message: "Vous devez compléter 12 mois de service avant de prendre un congé annuel."
FR-ABS-041: IF leave_type = ANNUAL AND number_of_days > employee.annual_leave_balance THEN block the request. Message: "Solde insuffisant. Votre solde disponible est de {balance} jours."
FR-ABS-042: IF leave_type = EXCEPTIONAL AND total_exceptional_taken_this_period + number_of_days > 10 THEN block the request. Message: "Vous avez atteint le plafond annuel de 10 jours de permissions exceptionnelles."
FR-ABS-043: IF start_date < today (retroactive request) THEN allow the request BUT mark it with flag is_retroactive = true for audit trail purposes.
FR-ABS-044: IF start_date < today + 15 days THEN display soft warning: "Attention : le préavis légal est de 15 jours avant le début du congé." The request is NOT blocked.
FR-ABS-045: IF the employee has NOT taken a block of ≥ 14 consecutive jours ouvrables this reference period AND leave_type = ANNUAL THEN display soft warning: "Rappel : la loi impose un congé principal d'au moins 14 jours ouvrables consécutifs par an." The request is NOT blocked.
FR-ABS-046: IF the leave request overlaps with an already-approved leave THEN apply the overlap resolution rule: - IF the new request is for SICK or MATERNITY (higher priority) THEN automatically split the existing leave: overlapping days convert to the new leave type, and the freed annual leave days are credited back to the employee's balance. - IF the new request is for ANNUAL or EXCEPTIONAL overlapping with ANNUAL THEN block: "Vous avez déjà un congé approuvé sur cette période. Veuillez l'annuler d'abord."
Document Requirements¶
FR-ABS-050: For leave types where a supporting document is REQUIRED (see B.1 table):
- IF supporting_document_id IS NULL THEN the request enters status PENDING_DOCUMENT instead of PENDING_APPROVAL.
- The employee sees: "Veuillez joindre le justificatif requis pour que votre demande soit transmise à votre responsable."
- Once the document is uploaded, the status transitions to PENDING_APPROVAL.
FR-ABS-051: Supporting documents are stored via the platform's basic document attachment (MinIO). IF Advanced Document Management (ADVDOC) is active, documents benefit from versioning and retention. IF ADVDOC is not active, basic upload/download applies.
FR-ABS-052: The approver (manager) can see: leave type, dates, number of days, employee balance, team calendar view. The approver can see that a document was uploaded and its filename. The approver CANNOT view the actual medical certificate or document content — only HR_MANAGER and DG have document access for medical leave.
Approval Workflow¶
FR-ABS-060: Leave approval follows the Platform Vision org-unit-based chain (FR-PV-020 to FR-PV-024):
1. Employee submits → request goes to org unit's manager_id
2. IF no manager on the unit → falls back to Company Owner (DG)
3. IF employee IS the unit's manager → request goes to parent unit's manager
4. DG self-approves (status = SELF_APPROVED, logged in audit trail)
FR-ABS-061: The approver can:
- APPROVE: Request moves to APPROVED. Balance is deducted (ledger transaction). Employee notified. LeaveApproved event published.
- REJECT: Request moves to REJECTED. Approver MUST provide a rejection reason (mandatory field). Employee notified with reason. No balance impact.
FR-ABS-062: Maternity leave is NOT subject to approval workflow:
- HR creates maternity declaration → system creates leave with status AUTO_APPROVED
- No manager approval step
- Employee and manager are notified of the maternity leave dates
Cancellation¶
FR-ABS-070: IF request status is PENDING_APPROVAL or PENDING_DOCUMENT THEN the employee can cancel freely. Status → CANCELLED_BY_EMPLOYEE. No balance impact (no deduction was made).
FR-ABS-071: IF request status is APPROVED AND start_date > today (leave hasn't started yet) THEN the employee can cancel. Status → CANCELLED_BY_EMPLOYEE. Balance is credited back (ledger transaction: CANCEL_CREDIT). Manager and HR are notified. LeaveCancelled event published.
FR-ABS-072: IF request status is APPROVED AND start_date <= today (leave is in progress) THEN the employee CANNOT self-cancel. They must request early return through HR. HR can modify the end date and credit back unused days.
FR-ABS-073: HR_MANAGER and DG can cancel ANY leave (including in-progress) on behalf of an employee. A reason is required. The employee is notified.
B.5 Functional Rules — Sick Leave Indemnization [CI — CCI]¶
Indemnization Tables¶
FR-ABS-080: Sick leave pay in CI follows the CCI indemnization tables, differentiated by employee category and seniority (ancienneté):
Ouvriers & Employés¶
| Seniority | Full Pay Period | Half Pay Period | Zero Pay After |
|---|---|---|---|
| < 1 year | 1 month | 3 months | 4 months |
| 1–5 years | 1 month | 3 months | 4 months |
| 6–10 years | 2 months | 3 months | 5 months |
| 11–15 years | 3 months | 3 months | 6 months |
| > 15 years | 4 months | 4 months | 8 months |
Cadres & Agents de Maîtrise¶
| Seniority | Full Pay Period | Half Pay Period | Zero Pay After |
|---|---|---|---|
| < 1 year | 1 month | 3 months | 4 months |
| 1–5 years | 2 months | 3 months | 5 months |
| 6–10 years | 3 months | 4 months | 7 months |
| 11–15 years | 4 months | 4 months | 8 months |
| > 15 years | 5 months | 5 months | 10 months |
FR-ABS-081: The Absence module tracks which phase (full pay / half pay / zero pay) the employee is currently in, based on the cumulative sick leave days in the current 12-month rolling period. This phase information is included in the LeaveApproved event payload for Payroll to calculate the correct pay.
FR-ABS-082: IF an employee exhausts their sick leave indemnization (reaches zero pay phase) THEN the system alerts HR. The remaining absence is unpaid. This may trigger licenciement pour inaptitude procedures (handled by Employee Management module, V2).
FR-ABS-083: CNPS contributions during sick leave are calculated on the amount actually paid (e.g., during half-pay, CNPS contributions are based on the half-pay amount, not the full salary).
B.6 Functional Rules — Maternity Leave [CI]¶
FR-ABS-090: Maternity leave is 14 weeks total: 6 weeks before expected delivery date + 8 weeks after.
FR-ABS-091: HR creates a maternity declaration by entering: - Employee ID - Expected delivery date (date présumée d'accouchement) - (Optional) Actual delivery date (updated after birth)
FR-ABS-092: Upon saving the maternity declaration, the system:
1. Calculates the 14-week period: start_date = expected_delivery_date - 42 days, end_date = expected_delivery_date + 56 days
2. Creates a leave record with status AUTO_APPROVED and type MATERNITY
3. Checks for overlapping approved leaves → credits back any overlapping annual leave days
4. Notifies the employee and their manager
5. Publishes LeaveApproved event with leaveType = MATERNITY, isPaid = true, payRate = 100
FR-ABS-093: IF the actual delivery date differs from the expected date THEN HR can update the maternity declaration. The system recalculates the 8-week post-delivery period based on the actual date. The pre-delivery period remains as originally taken.
FR-ABS-094: Maternity leave pay mechanism [CI — temporarily validated TV-ABS-01]: - Employer pays 100% of the employee's salary during maternity. - Employer subsequently claims full reimbursement from CNPS. - Net employer cost for salary = 0 (after reimbursement). - The CNPS reimbursement claim is tracked but NOT processed by the Absence module — this is an accounting/HR process outside the system for MVP.
FR-ABS-095: The employee is protected from dismissal during the ENTIRE maternity period (6 weeks before through 8 weeks after delivery). The system does NOT enforce this directly — it is enforced by the Employee Management module. However, the Absence module publishes MaternityLeaveActive status that Employee Management can check.
B.7 Functional Rules — Public Holidays [CI]¶
Fixed Public Holidays (CI)¶
FR-ABS-100: The system pre-loads the following fixed CI public holidays:
| Date | Holiday Name (FR) |
|---|---|
| 1er janvier | Jour de l'An |
| 1er mai | Fête du Travail |
| 7 août | Fête de l'Indépendance |
| 15 août | Assomption |
| 1er novembre | Toussaint |
| 15 novembre | Journée Nationale de la Paix |
| 25 décembre | Noël |
| Lundi de Pâques | Lundi de Pâques (variable — fixed by calendar) |
| Jeudi de l'Ascension | Ascension (variable — fixed by calendar) |
| Lundi de Pentecôte | Pentecôte (variable — fixed by calendar) |
Variable Islamic Holidays (CI)¶
FR-ABS-101: The following 4 Islamic holidays have variable dates set by observation:
| Holiday Code | Holiday Name (FR) |
|---|---|
EID_FITR |
Fête de la fin du Ramadan (Aïd el-Fitr) |
EID_ADHA |
Fête du sacrifice (Aïd el-Adha / Tabaski) |
MAWLID |
Anniversaire du Prophète (Mawlid) |
LAYLAT_QADR |
Nuit du Destin (Laylat al-Qadr) |
FR-ABS-102: Variable holiday dates are managed at two levels: 1. Platform admin sets default dates for all CI tenants each year (entered once, inherited by all CI tenants) 2. Tenant admin can override specific variable holiday dates for their company (e.g., if they observe a different date)
FR-ABS-103: IF a public holiday falls on a Sunday THEN the following Monday is observed as the public holiday (substitution rule per CI practice).
Holiday Impact on Leave¶
FR-ABS-104: IF a public holiday falls during an employee's annual leave period THEN the holiday is NOT counted as a leave day. The leave balance is not deducted for that day. Effectively, the leave is extended by 1 day per overlapping holiday.
B.8 Functional Rules — Payroll Integration¶
Events Published¶
FR-ABS-110: The Absence module publishes the following Spring Application Events (consumed by Payroll):
LeaveApproved¶
Published when: a leave request is approved (or auto-approved for maternity).
{
employeeId: UUID,
tenantId: UUID,
leaveRequestId: UUID,
leaveType: "ANNUAL" | "SICK" | "MATERNITY" | "PATERNITY" | "EXCEPTIONAL" | "UNPAID" | "UNJUSTIFIED",
startDate: LocalDate,
endDate: LocalDate,
numberOfDays: BigDecimal, // in jours calendaires
numberOfDaysOuvrables: BigDecimal, // source of truth for balance deduction
isPaid: boolean,
payRate: BigDecimal, // 100, 50, or 0 (percentage)
sickLeavePhase: "FULL_PAY" | "HALF_PAY" | "ZERO_PAY" | null, // only for SICK
halfDayStart: boolean,
halfDayEnd: boolean
}
LeaveCancelled¶
Published when: an approved leave is cancelled.
{
employeeId: UUID,
tenantId: UUID,
leaveRequestId: UUID,
leaveType: String,
startDate: LocalDate,
endDate: LocalDate,
numberOfDaysCredited: BigDecimal // days to reverse in payroll
}
LeaveBalanceSettlement¶
Published when: an employee is terminated and has remaining leave balance.
{
employeeId: UUID,
tenantId: UUID,
remainingBalance: BigDecimal, // in jours calendaires
lastAccrualDate: LocalDate,
terminationDate: LocalDate
}
Events Consumed¶
FR-ABS-111: The Absence module consumes the following events:
| Event | Published By | Action |
|---|---|---|
EmployeeCreated |
Control Plane | Initialize leave balance (all types = 0). Start accrual from hire_date. |
EmployeeTerminated |
Employee Mgmt / CP | Auto-cancel pending requests. Preserve approved leaves within notice period. Compute remaining balance. Publish LeaveBalanceSettlement. |
EmployeeOffboarded |
Employee Mgmt | Same as EmployeeTerminated — triggers settlement. |
EmployeeReassigned |
Employee Mgmt | Update the approval chain for any pending requests (new org unit = new manager). |
ManagerChanged |
Org & Master Data | Update approval routing for all pending requests from affected employees. |
API Dependencies (Synchronous)¶
FR-ABS-112: The Absence module calls the following Control Plane APIs:
| Endpoint | Purpose |
|---|---|
GET /api/v1/cp/org-units/{id}/manager |
Resolve the approver for a leave request |
GET /api/v1/cp/employees/{id} |
Retrieve employee data (category, hire date, seniority) for accrual calculation |
GET /api/v1/cp/employees/{id}/children |
Retrieve children count for bonus calculation (or from HR Core if active) |
GET /api/v1/cp/auth/permissions |
Check user permissions for RBAC enforcement |
B.9 State Machine — Leave Request¶
stateDiagram-v2
[*] --> DRAFT : Employee creates offline / saves draft
DRAFT --> PENDING_DOCUMENT : Submit (document required but missing)
DRAFT --> PENDING_APPROVAL : Submit (no document required OR document attached)
PENDING_DOCUMENT --> PENDING_APPROVAL : Document uploaded
PENDING_DOCUMENT --> CANCELLED_BY_EMPLOYEE : Employee cancels
PENDING_APPROVAL --> APPROVED : Manager approves
PENDING_APPROVAL --> REJECTED : Manager rejects (reason required)
PENDING_APPROVAL --> CANCELLED_BY_EMPLOYEE : Employee cancels
APPROVED --> CANCELLED_BY_EMPLOYEE : Employee cancels (before start date)
APPROVED --> CANCELLED_BY_HR : HR cancels (any time, reason required)
APPROVED --> IN_PROGRESS : Start date reached (automatic transition)
IN_PROGRESS --> COMPLETED : End date reached (automatic transition)
IN_PROGRESS --> EARLY_RETURN : HR records early return
IN_PROGRESS --> CANCELLED_BY_HR : HR cancels mid-leave
REJECTED --> [*]
CANCELLED_BY_EMPLOYEE --> [*]
CANCELLED_BY_HR --> [*]
COMPLETED --> [*]
EARLY_RETURN --> [*]
[*] --> AUTO_APPROVED : Maternity declaration by HR / DG self-approval
AUTO_APPROVED --> IN_PROGRESS : Start date reached
AUTO_APPROVED --> CANCELLED_BY_HR : HR cancels
State Descriptions¶
| State | Description | Balance Impact |
|---|---|---|
DRAFT |
Saved offline or as draft. Not yet submitted. | None |
PENDING_DOCUMENT |
Submitted but missing required supporting document. | None |
PENDING_APPROVAL |
Submitted and waiting for manager approval. | None (balance reserved but not deducted) |
APPROVED |
Manager approved. Leave is confirmed. | Balance deducted (ledger DEBIT transaction) |
AUTO_APPROVED |
Maternity leave or DG self-approval. No manual approval step. | Balance deducted (or N/A for maternity) |
REJECTED |
Manager rejected with reason. | None |
CANCELLED_BY_EMPLOYEE |
Employee cancelled the request. | If was APPROVED: balance credited back (ledger CREDIT transaction) |
CANCELLED_BY_HR |
HR/DG cancelled the request. | If was APPROVED/IN_PROGRESS: balance credited back proportionally |
IN_PROGRESS |
Leave has started (current date ≥ start_date). | Already deducted at APPROVED |
COMPLETED |
Leave period has ended. | No additional impact |
EARLY_RETURN |
Employee returned before end_date. HR adjusted end date. | Unused days credited back |
B.10 Functional Rules — Balance Ledger¶
Ledger Transaction Types¶
FR-ABS-120: Every leave balance change is recorded as an immutable ledger transaction:
| Transaction Type | Description | Amount Sign |
|---|---|---|
MONTHLY_ACCRUAL |
Regular monthly accrual (2.2 j. ouvrables) | + (credit) |
SENIORITY_BONUS |
Annual ancienneté bonus at start of period | + (credit) |
CHILDREN_BONUS |
Annual children bonus at start of period | + (credit) |
LEAVE_DEBIT |
Days deducted when leave is approved | – (debit) |
CANCEL_CREDIT |
Days credited back when leave is cancelled | + (credit) |
EARLY_RETURN_CREDIT |
Days credited back on early return | + (credit) |
OVERLAP_CREDIT |
Days credited back when higher-priority leave replaces existing | + (credit) |
CARRY_OVER |
Days carried over from previous period | + (credit) |
EXPIRY |
Days expired at end of reference period (carry-over policy) | – (debit) |
MANUAL_ADJUSTMENT |
HR manually adjusts balance (with reason) | +/– |
SETTLEMENT |
Balance zeroed on termination (remaining sent to Payroll) | – (debit) |
FR-ABS-121: Every ledger transaction records:
- id (UUID)
- tenant_id
- employee_id
- leave_type (ANNUAL, SICK counter, EXCEPTIONAL counter, etc.)
- transaction_type (from table above)
- amount (BigDecimal, in jours ouvrables — source of truth)
- amount_calendaires (BigDecimal, computed: amount × 1.25)
- amount_ouvres (BigDecimal, computed: amount × 5/6)
- reference_id (FK to leave_request or adjustment — nullable)
- reason (text — required for MANUAL_ADJUSTMENT, EXPIRY, SETTLEMENT)
- created_at (timestamp)
- created_by (user_id)
FR-ABS-122: Current balance for an employee/leave_type is calculated as:
For performance, a cached current_balance field is maintained on the employee's leave profile and updated on each transaction. The ledger is the source of truth — cached balance can be recomputed at any time.
FR-ABS-123: All ledger transactions are immutable — no updates, no deletes. Corrections are made by adding a new reversing transaction (e.g., CANCEL_CREDIT to reverse a LEAVE_DEBIT).
B.11 Decision Table — Document Requirements¶
| Leave Type | Document Required? | Document Type | Hard Block? | Upload Deadline |
|---|---|---|---|---|
| ANNUAL | ❌ No | — | — | — |
| SICK | ✅ Yes | Certificat médical | ✅ Yes — cannot approve without | Before approval |
| MATERNITY | ✅ Yes | Attestation médicale (grossesse) | ✅ Yes — cannot create maternity without | At maternity declaration |
| PATERNITY | ✅ Yes | Acte de naissance | ✅ Yes — cannot approve without | Before approval |
| EXCEPTIONAL | ✅ Yes | Justificatif (marriage cert, death cert, etc.) | ✅ Yes — cannot approve without | Before approval |
| UNPAID | ❌ No | — | — | — |
| UNJUSTIFIED | ❌ No | — | — | — |
B.12 Decision Table — Overlap Resolution¶
| Existing Leave | New Request | Resolution |
|---|---|---|
| ANNUAL (approved) | SICK | Auto-split: overlapping days become SICK. Annual days credited back. |
| ANNUAL (approved) | MATERNITY | Auto-split: overlapping days become MATERNITY. Annual days credited back. |
| ANNUAL (approved) | ANNUAL | Blocked: "Vous avez déjà un congé approuvé sur cette période." |
| ANNUAL (approved) | EXCEPTIONAL | Blocked: "Vous avez déjà un congé approuvé sur cette période." |
| SICK (approved) | MATERNITY | Auto-split: overlapping days become MATERNITY. Sick leave adjusted. |
| SICK (approved) | ANNUAL | Blocked. |
| MATERNITY (auto-approved) | Any type | Blocked: "Vous êtes en congé maternité sur cette période." |
| Any (PENDING) | Any | Warn: "Vous avez une demande en attente sur cette période." Allow submission. |
B.13 Data Requirements¶
Key Entities¶
leave_type_config (Tenant-configurable leave types)¶
| Field | Type | Nullable? | Description |
|---|---|---|---|
id |
UUID | ❌ | Primary key |
tenant_id |
UUID | ❌ | Tenant isolation |
code |
VARCHAR(20) | ❌ | ANNUAL, SICK, MATERNITY, etc. |
name_fr |
VARCHAR(100) | ❌ | Display name in French |
is_paid |
BOOLEAN | ❌ | Whether this leave type is paid |
requires_document |
BOOLEAN | ❌ | Whether supporting document is required |
requires_approval |
BOOLEAN | ❌ | Whether manager approval is needed (false for maternity) |
accrual_based |
BOOLEAN | ❌ | Whether balance is tracked (true for annual, false for sick) |
max_days_per_year |
DECIMAL | ✅ | Annual cap (e.g., 10 for permissions exceptionnelles) |
is_system |
BOOLEAN | ❌ | True for system-defined types (cannot be deleted) |
is_active |
BOOLEAN | ❌ | Soft delete |
country_code |
VARCHAR(2) | ❌ | CI, BJ, CM — for multi-country support |
leave_request (Individual leave requests)¶
| Field | Type | Nullable? | Description |
|---|---|---|---|
id |
UUID | ❌ | Primary key |
tenant_id |
UUID | ❌ | Tenant isolation |
employee_id |
UUID | ❌ | FK to employee |
leave_type_code |
VARCHAR(20) | ❌ | FK to leave_type_config.code |
event_type_code |
VARCHAR(30) | ✅ | For EXCEPTIONAL: MARRIAGE_SELF, DEATH_SPOUSE, etc. |
start_date |
DATE | ❌ | First day of absence |
end_date |
DATE | ❌ | Last day of absence (inclusive) |
half_day_start |
BOOLEAN | ❌ | True = afternoon only on start_date |
half_day_end |
BOOLEAN | ❌ | True = morning only on end_date |
number_of_days |
DECIMAL | ❌ | Computed: in tenant's deduction unit |
number_of_days_ouvrables |
DECIMAL | ❌ | Always stored in jours ouvrables (source of truth) |
status |
VARCHAR(30) | ❌ | See state machine (B.9) |
reason |
TEXT | ✅ | Employee's reason for requesting leave |
rejection_reason |
TEXT | ✅ | Manager's reason for rejection |
cancellation_reason |
TEXT | ✅ | Reason for cancellation (required for HR cancellations) |
approver_id |
UUID | ✅ | FK to user — who approved/rejected |
is_retroactive |
BOOLEAN | ❌ | True if start_date < submission date |
supporting_document_id |
UUID | ✅ | FK to document (MinIO) |
submitted_offline |
BOOLEAN | ❌ | True if created offline and auto-submitted |
created_at |
TIMESTAMP | ❌ | |
updated_at |
TIMESTAMP | ❌ | |
created_by |
UUID | ❌ | FK to user |
leave_balance_ledger (Transaction ledger)¶
See FR-ABS-121 for full field list.
leave_employee_profile (Per-employee leave configuration)¶
| Field | Type | Nullable? | Description |
|---|---|---|---|
id |
UUID | ❌ | Primary key |
tenant_id |
UUID | ❌ | Tenant isolation |
employee_id |
UUID | ❌ | FK to employee (unique per tenant) |
annual_balance_ouvrables |
DECIMAL | ❌ | Cached current annual leave balance (jours ouvrables) |
exceptional_days_used_this_period |
DECIMAL | ❌ | Counter for 10-day cap |
sick_days_current_rolling_12m |
DECIMAL | ❌ | Rolling 12-month sick day counter (for CCI table phase) |
sick_leave_phase |
VARCHAR(20) | ✅ | FULL_PAY, HALF_PAY, ZERO_PAY — current sick leave phase |
number_of_children_under_21 |
INTEGER | ❌ | For children bonus (if HR Core not active) |
reference_period_start |
DATE | ❌ | Start of current reference period |
last_accrual_date |
DATE | ✅ | Last month for which accrual was processed |
public_holiday (Public holidays per country per year)¶
| Field | Type | Nullable? | Description |
|---|---|---|---|
id |
UUID | ❌ | Primary key |
country_code |
VARCHAR(2) | ❌ | CI, BJ, CM |
year |
INTEGER | ❌ | Calendar year |
date |
DATE | ❌ | The holiday date |
name_fr |
VARCHAR(100) | ❌ | Holiday name |
holiday_code |
VARCHAR(30) | ❌ | EID_FITR, CHRISTMAS, etc. |
is_fixed |
BOOLEAN | ❌ | True for fixed-date holidays |
source |
VARCHAR(20) | ❌ | PLATFORM (admin) or TENANT (override) |
tenant_id |
UUID | ✅ | NULL for platform-level; tenant_id for tenant override |
exceptional_leave_config (Tenant-specific permission exceptionnelle durations)¶
| Field | Type | Nullable? | Description |
|---|---|---|---|
id |
UUID | ❌ | Primary key |
tenant_id |
UUID | ❌ | Tenant isolation |
event_type_code |
VARCHAR(30) | ❌ | MARRIAGE_SELF, DEATH_SPOUSE, etc. |
duration_days |
DECIMAL | ❌ | Tenant-configured duration (must be ≥ CCI minimum) |
cci_minimum_days |
DECIMAL | ❌ | CCI legal minimum (system-enforced floor) |
country_code |
VARCHAR(2) | ❌ | CI |
maternity_declaration (Maternity tracking)¶
| Field | Type | Nullable? | Description |
|---|---|---|---|
id |
UUID | ❌ | Primary key |
tenant_id |
UUID | ❌ | Tenant isolation |
employee_id |
UUID | ❌ | FK to employee |
expected_delivery_date |
DATE | ❌ | Date présumée d'accouchement |
actual_delivery_date |
DATE | ✅ | Updated after birth |
leave_request_id |
UUID | ❌ | FK to the auto-created leave_request |
created_at |
TIMESTAMP | ❌ | |
created_by |
UUID | ❌ | HR who recorded the declaration |
B.14 Multi-Tenant Implications¶
FR-ABS-130: ALL database queries in the Absence module MUST include tenant_id in the WHERE clause. No exceptions.
FR-ABS-131: Leave type configurations, permission exceptionnelle durations, carry-over policies, and public holiday overrides are tenant-scoped. Each tenant has its own copy seeded from CI defaults at tenant creation.
FR-ABS-132: Multi-tenant behavior by database profile:
| Profile | Behavior |
|---|---|
| Shared DB | All tenants in one database. tenant_id column on every table. Row-level isolation. |
| Schema-per-tenant | Each tenant in its own schema. tenant_id still present for consistency but schema provides additional isolation. |
| DB-per-tenant | Each tenant in its own database. Connection routed per tenant. |
| BYO-DB | Tenant provides their own database. Absence module schema migrated to their DB. |
FR-ABS-133: Tenant-specific configurations (leave types, exceptional leave durations, carry-over policies) are seeded with CI legal defaults when a new CI tenant is created. The tenant can then customize.
FR-ABS-134: Platform-level public holidays (fixed + platform-admin-set variable dates) are stored separately from tenant-level overrides. The effective holiday list for a tenant is: platform holidays + tenant overrides (tenant overrides replace platform defaults for the same holiday_code).
B.15 Cross-Module Dependencies¶
Dependencies ON Other Modules (Absence CONSUMES)¶
| Module | Dependency Type | What Absence Needs |
|---|---|---|
| Control Plane | Hard (must exist) | Employee entity, org unit hierarchy, manager resolution, RBAC permissions, tenant configuration |
| HR Core | Soft (optional enhancement) | If active: richer employee data (children with DOB for bonus calc). If not active: Absence module manages number_of_children_under_21 field directly. |
| Employee Management | Soft (events only) | Consumes EmployeeTerminated, EmployeeOffboarded, EmployeeReassigned events |
| Org & Master Data | Soft (events only) | Consumes ManagerChanged event to update approval routing |
| Advanced Doc Mgmt | Soft (optional enhancement) | If active: supporting documents get versioning and retention. If not: basic MinIO upload. |
Dependencies FROM Other Modules (Absence PROVIDES)¶
| Module | Dependency Type | What Absence Provides |
|---|---|---|
| Payroll | Events | LeaveApproved, LeaveCancelled, LeaveBalanceSettlement events with full payload (see B.8) |
| QR Attendance | Informational | Absence data can be used by Attendance to auto-mark expected absences (V2 integration) |
PART C — Constraints & Boundaries¶
C.1 Regulatory Compliance [CI]¶
Code du Travail 2015 (Loi n° 2015-532)¶
| Rule | Article | Implementation |
|---|---|---|
| Annual leave: 2 j./month minimum | Art. 25.1 | System uses CCI rate (2.2 j./month, more favorable) |
| Leave after 12 months of service | Art. 25.5 | FR-ABS-006: block annual leave before 12 months |
| Minimum 14 consecutive days | Art. 25.6 | FR-ABS-045: soft warning |
| Employer gives 15 days notice | Art. 25.7 | FR-ABS-044: soft warning |
| No compensatory payment during contract | Art. 25.12 | FR-ABS-023: no pay-out feature |
| Mandatory pay-out on departure | Art. 25.9 | FR-ABS-024: settlement event to Payroll |
| Maternity: 14 weeks | Art. 23.X | FR-ABS-090 |
| Maternity protection from dismissal | Art. 23.X | FR-ABS-095: status published to Employee Mgmt |
Convention Collective Interprofessionnelle (CCI 1977)¶
| Rule | Article | Implementation |
|---|---|---|
| Annual leave: 2.2 j./month (more favorable) | CCI Art. 25 | FR-ABS-001: base accrual rate |
| Ancienneté bonuses | CCI Art. 46 | FR-ABS-010: seniority bonus table |
| Permissions exceptionnelles | CCI Art. 25 | B.1: full event table with 10-day cap |
| Sick leave indemnization tables | CCI Art. 28-29 | B.5: full CCI tables by category and seniority |
CNPS (Caisse Nationale de Prévoyance Sociale)¶
| Rule | Implementation |
|---|---|
| Maternity: CNPS reimburses employer | FR-ABS-094: Tracked but CNPS claim process outside system for MVP |
| Sick leave CNPS contributions on actual pay | FR-ABS-083: CNPS base = amount actually paid |
| Absences considered periods of employment for CNPS | FR-ABS-016: Accrual continues during authorized absences |
C.2 Localization Requirements¶
| Dimension | Value | Notes |
|---|---|---|
| Primary language | French | All UI labels, error messages, notifications in French |
| Date format | DD/MM/YYYY | "15/03/2026" not "2026-03-15" |
| Currency | XOF | 0 decimal places. "15 000 FCFA" not "15,000.00 FCFA" |
| Day counting | Jours ouvrables as legal base, with conversion to calendaires/ouvrés | See FR-ABS-003 |
| Thousands separator | Space | "1 000" not "1,000" |
| i18n keys | All user-facing strings externalized | Ready for future English (CM bilingual) |
| Name conventions | "NOM Prénom" format | Last name in uppercase is common in CI |
C.3 Security Requirements¶
RBAC Permissions (Absence Module)¶
| Permission | EMPLOYEE | MANAGER | HR_MANAGER | DG |
|---|---|---|---|---|
| View own balance | ✅ | ✅ | ✅ | ✅ |
| Submit leave request | ✅ (own) | ✅ (own) | ✅ (own + any employee) | ✅ (own, self-approved) |
| Cancel own request | ✅ | ✅ | ✅ | ✅ |
| View team calendar | ❌ | ✅ (direct reports) | ✅ (all employees) | ✅ (all employees) |
| Approve/reject requests | ❌ | ✅ (direct reports) | ✅ (any employee) | ✅ (any employee) |
| Cancel others' leave | ❌ | ❌ | ✅ (with reason) | ✅ (with reason) |
| Override balance (manual adjustment) | ❌ | ❌ | ✅ | ✅ |
| Configure leave types | ❌ | ❌ | ✅ | ✅ |
| Configure public holidays | ❌ | ❌ | ✅ | ✅ |
| Configure carry-over policy | ❌ | ❌ | ✅ | ✅ |
| View medical documents | ❌ | ❌ | ✅ | ✅ |
| Create maternity declaration | ❌ | ❌ | ✅ | ✅ |
| View absence dashboard | ❌ | ✅ (team) | ✅ (all) | ✅ (all) |
Data Access Rules¶
FR-ABS-140: MANAGER can see leave type and dates for their direct reports, but CANNOT view supporting documents (medical certificates, etc.). Only HR_MANAGER and DG can view uploaded documents.
FR-ABS-141: EMPLOYEE can only see their own leave data. They can see the team calendar but only presence/absence status (not leave types of others).
FR-ABS-142: All leave balance adjustments (MANUAL_ADJUSTMENT) require a reason and produce an audit trail entry visible to the tenant admin.
FR-ABS-143: Audit trail for the Absence module captures: - Every leave request creation, status change, cancellation - Every balance adjustment (manual or automatic) - Every configuration change (leave types, holidays, carry-over policy) - Who did what, when, with before/after values
C.4 Out of Scope¶
This module does NOT handle:
- Payroll calculations — The Absence module determines leave types, days, and phases (full/half/zero pay). Payroll calculates the actual monetary deductions and indemnizations.
- CNPS reimbursement claims — Maternity CNPS reimbursement is an accounting/HR process outside the system for MVP.
- Licenciement pour inaptitude — If an employee exhausts sick leave, the Absence module alerts HR. The actual dismissal process is Employee Management's responsibility (V2).
- Convention collective configuration — MVP uses the CCI 1977 as the baseline. Sector-specific conventions that override CCI leave rules are V2.
- Time tracking / attendance — Absence records whether an employee is absent. It does NOT track arrival/departure times (QR Attendance module).
- Leave planning at enterprise scale — Coverage rules, minimum staffing levels, and calendar-based planning are ENTERPRISE ONLY V2.
- Exportable absence register — Formal registre des absences for labour inspection export is V2.
- BJ and CM country rules — Leave types, accrual rates, and public holidays for Bénin and Cameroun are V2 (Month 9).
- SMS notifications — MVP uses in-app + email only. SMS is platform-wide V2.
- Congé de formation / Congé syndical — Training and union leave types are V2.
- Advanced approval workflows — Type-based auto-approval, amount-based escalation, delegation are V2.
- Batch operations — Mass leave creation, mass balance adjustments are ENTERPRISE ONLY V2.
- Leave accrual for stagiaires / journaliers — Specific contract types may have different accrual rules. MVP assumes all active employees accrue at CCI rate. Contract-type-specific rules are V2.
End of PRD+FSD Hybrid — Absence Management (ABSMGT) v1.0