Project

General

Profile

Actions

Feature #1148

open
RA

Feature #1147: Tiers réels — 2 pools (maintenance/évolutions) + flux/SLA + page /offres + jauge à 2 indicateurs

Backend — Modèle tiers à 2 pools (maintenance/évolutions illimitables) + reseed + API + ajustement/alertes par pool

Feature #1148: Backend — Modèle tiers à 2 pools (maintenance/évolutions illimitables) + reseed + API + ajustement/alertes par pool

Added by Redmine Admin about 1 month ago. Updated about 1 month ago.

Status:
QA
Priority:
Normal
Assignee:
-
Start date:
06/22/2026
Due date:
% Done:

0%

Estimated time:
spec_ref:

issue:1147 — spec dans l'épopée + description

build_status:
build_number:
ci_run_url:
scan_status:
scan_report_url:
deploy_status:
preprod_url:
deployed_at:
branch:
feat/1148-two-pool-tiers
pr_url:
https://github.com/omdev-tech/PipeLiner-Client/pull/72
security_key:
severity:
paused:

Description

## Objectif
Faire évoluer le modèle billing de #1133 d'un pool unique de crédits vers **deux pools** (maintenance + évolutions), chacun étant un entier OU illimité (NULL). Racine de l'épopée #1147 — le frontend (#offres + jauge) en dépend.

## Portée (api/, hexagonal, TDD)
- **Domaine `Tier`** : remplacer `monthly_credits` (single) par `maintenance_credits: int | None` (None = illimité) et `evolution_credits: int | None`, ajouter `streams: int`. Garder `monthly_price_eur`. Le `flux`/`sla` textuels actuels peuvent rester ou être enrichis, mais les vraies valeurs structurées (flux=streams, etc.) priment. **Valeurs réelles** (voir #1147) : Démarrage 100/10/1flux ; Maintenance ∞/20/1 ; Évolutions ∞/∞/2 ; Pilotage ∞/∞/4.
- **`CreditBalance`** : suivre `maintenance_remaining: int | None` et `evolution_remaining: int | None` par client (None = illimité, jamais « bas », jamais décrémenté). `usage_ratio`/`is_low` deviennent par-pool (un pool illimité n'est jamais bas).
- **Migration Alembic** (nouvelle révision) : ALTER `tiers` (colonnes maintenance_credits/evolution_credits/streams, nullable) + ALTER `credit_balances` (maintenance_remaining/evolution_remaining nullable) ; **reseed** des 4 tiers avec les vraies valeurs ; migrer les `credit_balances` existants vers le nouveau schéma (recalcul depuis la définition de tier = allocation pleine, pools illimités → NULL). Gérer le down-revision proprement (chaîne sur la dernière migration billing actuelle).
- **Application** : `ChangeTier` réinitialise les deux pools selon la nouvelle formule (upgrade = pleine allocation, illimité → NULL). `AdjustCredits` doit cibler **un pool** (`pool: "maintenance" | "evolution"`) ; ajuster un pool illimité → 422 (rien à ajuster). Audit : enregistrer le pool concerné. `LowCredits` n'est émis que pour un pool **fini** passant sous 20 % (jamais pour un pool illimité).
- **Présentation/API** : mettre à jour les schémas de sortie : `TierOut { code, name, monthly_price_eur, maintenance_credits|null, evolution_credits|null, streams, ... }`, `CreditBalanceOut { maintenance_remaining|null, evolution_remaining|null, maintenance_unlimited, evolution_unlimited, ... + ratios/is_low par pool }`. `GET /billing/clients` (admin) expose les deux pools + verdict par pool. L'ajustement admin prend le `pool`. **Documenter le nouveau contrat dans le rapport** (le frontend #offres en dépend).

## Acceptance criteria
- [ ] Les 4 tiers ont maintenance/evolution (entier ou NULL=illimité) + streams, reseedés via migration ; valeurs = matrice #1147.
- [ ] `CreditBalanceOut` expose les deux pools, avec « illimité » distinct de 0, ratios/is_low par pool, pool illimité jamais « bas ».
- [ ] `ChangeTier` règle les deux pools (upgrade = plein, illimité = NULL) ; même tier → 409.
- [ ] `AdjustCredits` cible un pool ; pool illimité → 422 ; audit enregistre le pool ; non-admin → 403.
- [ ] `LowCredits` n'est émis que pour un pool fini franchissant <20 % (une fois par franchissement).
- [ ] Migration up/down propre, idempotente, credit_balances existants migrés sans perte ; `alembic upgrade head` OK.
- [ ] pytest verts (TDD), `ruff check .`, `mypy src` propres. Ne pas casser les events #1142 (PasswordChanged/TierChanged/CreditsAdjusted/LowCredits) — adapter CreditsAdjusted/LowCredits au pool.

RA Updated by Redmine Admin about 1 month ago Actions #1

  • Status changed from Submitted to Spec

RA Updated by Redmine Admin about 1 month ago Actions #2

  • Status changed from Spec to In development

RA Updated by Redmine Admin about 1 month ago Actions #3

  • Status changed from In development to QA
  • branch set to feat/1148-two-pool-tiers
  • pr_url set to https://github.com/omdev-tech/PipeLiner-Client/pull/72

PR ouverte vers `dev` : https://github.com/omdev-tech/PipeLiner-Client/pull/72

Modèle billing #1133 fait évoluer vers **2 pools** (maintenance + évolutions, illimitables) + **streams** par formule, aux vraies valeurs #1147. Migration 0014 (reseed + migration des balances existantes), API par pool, événements adaptés. Gates verts : pytest 614 passed (DB éphémère, 2×), ruff OK, mypy OK.

### Nouveau contrat API (à briefer pour le frontend #1149)

**TierOut** (`GET /billing/tiers`, `.../tier`) :
```
{ code, name, monthly_price_eur, maintenance_credits|null, evolution_credits|null, streams, flux, sla }
```
`null` = pool illimité. Valeurs : Démarrage 100/10/1 · Maintenance null/20/1 · Évolutions null/null/2 · Pilotage null/null/4.

**CreditBalanceOut** (`.../credits`, `.../profile.balance`, réponse de PUT tier & POST adjust) :
```
{ client_id, tier_code,
maintenance_remaining|null, evolution_remaining|null,
maintenance_unlimited:bool, evolution_unlimited:bool,
maintenance_usage_ratio|null, evolution_usage_ratio|null,
maintenance_is_low:bool, evolution_is_low:bool }
```
`*_remaining` et `*_usage_ratio` = `null` quand le pool est illimité (`*_unlimited` true) ; un pool illimité n'est jamais `*_is_low`.

**Liste admin** (`GET /billing/clients` → `clients[]`) :
```
{ client_id, name, email, tier_code, tier_name,
maintenance_remaining|null, evolution_remaining|null,
maintenance_unlimited:bool, evolution_unlimited:bool,
maintenance_verdict, evolution_verdict }
```
`*_verdict` ∈ `healthy|warning|critical` (pool illimité = toujours `healthy`).

**Body d'ajustement** (`POST /billing/clients/{id}/credits/adjust`) :
```
{ pool: "maintenance" | "evolution", delta: int!=0 }
```
Pool illimité → 422 ; delta 0 → 422 ; non-admin → 403.

### Payloads d'événements mis à jour (#1142)
- `CreditsAdjusted` : `{ client_id, actor_id, pool, delta, old_balance, new_balance, monthly_credits }` (+ `pool`).
- `LowCredits` : `{ client_id, pool, remaining_credits, monthly_credits, ratio, threshold_crossed }` (+ `pool` ; émis seulement pour un pool fini franchissant <20 %).
- `TierChanged` / `PasswordChanged` : inchangés.

Passage en **QA**.

Actions

Also available in: PDF Atom