Aller au contenu

Story CP-025: Subscription Management + Module Add/Remove

Module: control-plane Slice: Slice 9b from architecture (split from CP-009) Brand context: Papillon HR Suite Priority: 9.1 Depends on: CP-009 Estimated complexity: M


Objective

Build the tenant-facing subscription management page: subscription dashboard displaying current plan, modules, pricing, and invoice history. Enable module add/remove with pack discount auto-detection and deactivation undo flow.


Backend Scope

Service Layer (extends CP-009)

BillingService.java (additions): - getSubscriptionInfo(UUID tenantId) → SubscriptionResponse — Current subscription details: segment, active modules, pack info, next due date, billing cycle - addModule(UUID tenantId, String moduleCode) → void — Add module to subscription. Recalculate pack eligibility. Price change applies on next billing cycle. - removeModule(UUID tenantId, String moduleCode) → void — Deactivate module, warn about pack impact. Soft-delete: module data remains accessible read-only.

DTOs:

public record SubscriptionResponse(
    String segment, String segmentRange,
    List<SubscribedModuleDto> modules,
    String packName, BigDecimal packSavings,
    BigDecimal totalMonthly, BigDecimal platformFee,
    LocalDate nextDueDate, BillingCycle billingCycle,
    List<InvoiceSummaryDto> recentInvoices
) {}

public record SubscribedModuleDto(
    String moduleCode, String moduleName,
    BigDecimal price, boolean packDiscountApplied
) {}

public record InvoiceSummaryDto(
    UUID id, String invoiceNumber, LocalDate invoiceDate,
    BigDecimal totalTtc, InvoicePaymentStatus status
) {}

API Endpoints

Method Path Request Body Response Auth
GET /api/v1/billing/subscription SubscriptionResponse ROLE_DG
POST /api/v1/billing/modules/{code}/activate 204 ROLE_DG
POST /api/v1/billing/modules/{code}/deactivate 204 ROLE_DG

Validation Rules

  • Module code must exist in the module registry (CP-007)
  • Cannot deactivate a module that is already inactive
  • Cannot activate a module that is already active
  • Pack discount recalculation triggered on every add/remove

Multi-Tenant Considerations

  • All queries scoped to current tenant
  • Module activation/deactivation publishes Application Event for CP-007 module gating

Frontend Scope

Pages & Routes

  • /admin/subscription — Subscription management dashboard
  • /admin/subscription/add-module — Module catalog for adding modules

Components

  • SubscriptionDashboardPage.tsx — Status, modules, pricing, invoices
  • InvoiceListItem.tsx — Invoice card with status and PDF download
  • PaymentMethodSelector.tsx — Component spec in design §19.7
  • ModuleDeactivationConfirm.tsx — Confirmation dialog with pack impact warning

UI States (ALL REQUIRED)

Subscription Dashboard:

State Behavior French Copy
Loading Skeleton: 4 sections
Empty invoices Message in invoice section "Aucune facture pour le moment."
Error Red toast "Une erreur est survenue. Veuillez réessayer."
Offline Full page offline message "La gestion de l'abonnement n'est pas disponible hors ligne."
Success (payment) Green toast "Paiement confirmé ! Bienvenue sur Papillon."

Responsive Behavior

  • 360px: Stacked cards: subscription info, modules list, payment method, invoice history.
  • 1024px+: Two columns + sidebar layout.

Interactions

  • Add module: "Ajouter un module" → module catalog (similar to registration Step 2). Price delta shown. "Votre abonnement sera mis à jour à la prochaine échéance."
  • Deactivate module: Tap subscribed module → confirmation with pack impact warning. 10-second undo toast: "Module désactivé. Annuler ?"
  • Invoice PDF download: Tap "PDF ↓" on invoice card.
  • Payment CTA: For unpaid invoices: "Payer maintenant →" link.

French micro-copy:

Key French Text
billing.title "Abonnement"
billing.status "Statut"
billing.segment "Segment : {code} ({range} salariés)"
billing.next_due "Prochaine échéance : {date} ({days} jours)"
billing.modules_subscribed "Modules souscrits"
billing.pack_applied "Pack {name} appliqué"
billing.savings "Économie : -{amount} FCFA"
billing.total_monthly "Total mensuel : {amount} FCFA"
billing.add_module "Ajouter un module"
billing.invoices "Historique des factures"
billing.invoice.paid "Payée"
billing.invoice.pending "En attente"
billing.invoice.overdue "En retard"
billing.deactivate.title "Désactiver « {module} » ?"
billing.deactivate.data_warning "Vos données resteront accessibles en lecture seule."
billing.deactivate.pack_warning "Le Pack {name} ne sera plus applicable. Votre tarif {module} repassera à {price} FCFA/mois."
billing.deactivate.confirm "Confirmer la désactivation"
billing.deactivate.undo "Module désactivé. Annuler ?"
empty.invoices "Aucune facture pour le moment."

Acceptance Criteria

AC-078: Pack discount auto-detection
Given a tenant subscribes to ABSMGT + QRCONTR
When the price is calculated
Then Pack Contrôle is detected and the discounted pack price is used

AC-079: Module deactivation with undo
Given I deactivate module QRCONTR from the subscription page
When the confirmation appears showing pack impact
And I confirm deactivation
Then a 10-second undo toast appears: "Module désactivé. Annuler ?"
If I tap "Annuler" within 10 seconds, the module is reactivated

AC-080: Subscription dashboard displays correctly
Given I am DG and navigate to /admin/subscription
When the page loads
Then I see: status, segment, next due date, subscribed modules with prices, pack savings, payment method, invoice history

OHADA & Regulatory Rules

  • XOF zero decimal: All displayed amounts use zero decimal places. 15 000 FCFA, never 15 000,00.
  • BigDecimal ONLY: All financial calculations use BigDecimal. Never double/float.

Standards & Conventions

  • docs/standards/react-typescript-guidelines.md — §Subscription page, §Confirmation dialogs
  • docs/standards/api-guidelines.md — §Financial API patterns

Testing Requirements

Unit Tests

  • Pack auto-detection for all 5 packs (Contrôle, Finance, Essentiel, Standard, Complet)
  • Module deactivation recalculates pack eligibility
  • SubscriptionResponse DTO serialization (zero-decimal amounts)

Integration Tests

  • Module deactivation: pack impact calculated correctly
  • Add module → subscription info updated with new pricing
  • Remove module → undo within 10s → module reactivated

What QA Will Validate

  • Subscription dashboard displays all sections correctly
  • Module deactivation confirmation with pack warning
  • Undo toast works within 10 seconds
  • All amounts display with XOF formatting (space as thousands separator)

Out of Scope

  • Invoice generation logic — CP-009
  • Payment flow — CP-009
  • Billing cycle management — CP-026
  • Admin payment confirmation UI — CP-012

Definition of Done

  • [ ] All backend endpoints implemented and returning correct responses
  • [ ] All frontend pages render correctly at 360px, 768px, 1024px
  • [ ] All 5 UI states implemented (loading, empty, error, offline, success)
  • [ ] French micro-copy matches design spec exactly
  • [ ] All acceptance criteria pass manually
  • [ ] Unit tests written and passing
  • [ ] Integration tests written and passing
  • [ ] Multi-tenant isolation verified (no cross-tenant data leaks)
  • [ ] No TypeScript any — all types explicit
  • [ ] No Java raw types — all generics explicit
  • [ ] API responses follow standard envelope format
  • [ ] Audit trail entries created for every data mutation