PRD 5.16 (draft) — propagare design uniform peste aplicatie: - fonturi standard web (system font stack), scala uniforma --fs-* (carduri aerisite) - RAR online = dot in antet (datetime pe hover) + meniu burger; banda doar cand e blocat - antet branded "ROMFAST AUTOPASS" + nume service + badge plan (gate is_authenticated) - /login profesional (antet minimal pre-login), selector tema stil landing - bug-uri editare: denumiri in picker, adaugare operatie extra, fix save no-op, fix Renunta - dashboard compact: strip-less, contoare separate (mobil = bara numere), import colapsat, ordine carduri->import->tab-uri->lista, meniu cu separatoare - wizard import (4 pasi) + editare/corectie aliniate la design PRD 5.17 (draft) — tipuri de cont (Gratuit/Standard/Pro/Premium) + trial Pro 30 zile: - model accounts.tier + trial_until, app/plans.py sursa unica - enforcement DUR: limita Gratuit 60/luna (era 100) + API doar Pro+ - downgrade automat la expirare trial; aliniere landing (60, "Pro gratuit 30 zile") Mockup-uri vizuale (docs/mockups/prd-5.16-*.html): fonturi, header+login+tema, dashboard desktop+mobil, wizard import. Doar documentatie + mockup-uri; fara cod aplicatie. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
318 lines
21 KiB
Markdown
318 lines
21 KiB
Markdown
# PRD 5.17 — Tipuri de cont (planuri) + trial Pro 30 zile + enforcement
|
||
|
||
**Stare**: draft
|
||
|
||
> Proces complet: `docs/ROADMAP.md` §5. Contract RAR (sursa de adevar): `docs/api-rar-contract.md`.
|
||
> Landing comercial cu planurile: `app/web/templates/landing.html` (sectiunea PRICING).
|
||
> Lifecycle cont existent: `app/accounts.py`, `app/schema.sql` (tabela `accounts`, coloana `status`).
|
||
> Signup: `app/web/auth_routes.py` (`signup_post`, butoanele landing trimit `data-plan`).
|
||
> Starea trece: `draft -> aprobat -> in-executie -> verify-pass -> inchis`.
|
||
|
||
## 1. Introducere
|
||
|
||
Landing-ul comercial promite patru planuri — **Gratuit**, **Standard (39 lei)**, **Pro (59 lei, cu
|
||
API)**, **Premium (la cerere)** — si afirma ca **fiecare cont incepe cu acces gratuit 30 de zile** la
|
||
un plan superior. In aplicatie insa **nu exista deloc conceptul de tip de cont**: tabela `accounts`
|
||
are doar `status` (pending/active/blocked/archived/deleted) si `on_unmapped_error_default`. Nimic nu
|
||
diferentiaza un cont gratuit de unul platit, nimic nu aplica limita de volum sau gate-ul de API, si nu
|
||
exista niciun trial.
|
||
|
||
In plus, userul a decis doua corectii fata de landing-ul actual:
|
||
1. Trial-ul de 30 de zile e pe **Pro**, NU pe Premium (landing-ul scrie azi "Premium gratuit 30 de
|
||
zile" — gresit; trebuie "Pro 30 de zile").
|
||
2. Limita planului **Gratuit** scade de la **100** la **60 de prestatii/luna** — actualizata si in
|
||
landing si in aplicatie.
|
||
|
||
5.17 introduce modelul de tipuri de cont, trial-ul Pro de 30 de zile, **enforcement DUR** al
|
||
diferentelor (volum lunar + acces API), si downgrade automat la expirarea trial-ului. NU include
|
||
integrare de plata (nu exista inca sistem de facturare) — alocarea planului platit ramane manuala
|
||
(admin), iar trial-ul porneste automat la creare cont.
|
||
|
||
## 2. Obiective
|
||
|
||
### Obiectiv principal
|
||
Aplicatia sa sustina real diferentele dintre planuri pe care landing-ul le promite: cont nou →
|
||
trial Pro 30 zile → la expirare downgrade pe Gratuit (60/luna, fara API), cu enforcement efectiv.
|
||
|
||
### Obiective secundare
|
||
- Sursa unica de adevar pentru definitia planurilor (limite + capabilitati), consumata de backend si UI.
|
||
- Mesaje oneste cand un cont atinge limita sau cere o capabilitate neinclusa (3 niveluri, ca 5.4).
|
||
- Vizibilitate in dashboard: planul curent + zile ramase din trial + consum lunar.
|
||
|
||
### Metrici de succes
|
||
- Un cont Gratuit care depaseste 60 prestatii/luna primeste un raspuns clar de respingere (API + web),
|
||
iar contoarele lunare se reseteaza corect la inceput de luna (timp local RO).
|
||
- Un cont fara plan Pro+ primeste 403 onest pe `/v1/*` de import API.
|
||
- Un cont nou are trial Pro activ; dupa 30 zile (sau setand `trial_until` in trecut in test) trece
|
||
automat pe Gratuit, cu enforcement-ul aferent.
|
||
- Landing + app afiseaza coerent "60 prestatii/luna" si "Pro gratuit 30 de zile".
|
||
|
||
## 3. User Stories
|
||
|
||
> Database → backend → API → UI (ordinea dependentelor). Un singur autor pe `accounts.py`/`schema.sql`
|
||
> in valul de model.
|
||
|
||
### US-001: Schema — `accounts.tier` + `trial_until` + definitia planurilor
|
||
**Ca** sistem **vreau** sa stiu planul fiecarui cont si pana cand e in trial **pentru ca** restul
|
||
logicii depinde de asta.
|
||
|
||
- **Depinde de**: —
|
||
- **Fisiere**: `app/schema.sql` (coloane noi + migrare defensiva), `app/accounts.py` (helperi),
|
||
`app/plans.py` (NOU — definitia planurilor, sursa de adevar), `tests/test_accounts.py` /
|
||
`tests/test_plans.py` (~4 fisiere)
|
||
- **Test intai (RED)**: `test_migrare_tier_trial_defensiva`, `test_plan_definitii`,
|
||
`test_cont_nou_trial_pro_30z`
|
||
- **Acceptance criteria**:
|
||
- [ ] `accounts` capata (migrare aditiva defensiva, ca `email`/`status` in 5.5/5.12):
|
||
`tier TEXT NOT NULL DEFAULT 'free' CHECK (tier IN ('free','standard','pro','premium'))`
|
||
si `trial_until TEXT` (nullable; ISO datetime UTC sau NULL daca nu e in trial).
|
||
- [ ] `app/plans.py` = SINGURA sursa de adevar: dict `PLANS` cu, per plan,
|
||
`{label, monthly_limit, api_access, ...}`. Valori: `free` → `monthly_limit=60`, `api_access=False`;
|
||
`standard` → `monthly_limit=None` (nelimitat), `api_access=False`; `pro` → `monthly_limit=None`,
|
||
`api_access=True`; `premium` → `monthly_limit=None`, `api_access=True`. (Aliniat landing-ului,
|
||
cu limita Gratuit 60.)
|
||
- [ ] Helper `effective_tier(account)`: daca `trial_until` e in viitor → randeaza ca `pro`
|
||
(trial); altfel `tier`. (Trial-ul = acces Pro temporar peste tier-ul de baza `free`.)
|
||
- [ ] `create_account` seteaza `tier='free'` si `trial_until = now + 30 zile` (trial Pro automat la
|
||
creare). Contul implicit id=1 (dev) e exceptat / setat coerent (nu blocheaza dev-ul).
|
||
- [ ] Migrare idempotenta (re-rulabila); conturile legacy fara `tier` primesc `free` + fara trial
|
||
(sau trial calculat din `created_at` — decizie la executie; implicit: legacy → free fara trial).
|
||
- **Verificare E2E**: creez cont nou → `tier=free`, `trial_until ≈ now+30z`, `effective_tier=pro`.
|
||
|
||
### US-002: Numarator de consum lunar (prestatii/luna pe cont)
|
||
**Ca** sistem **vreau** sa stiu cate prestatii a trimis un cont in luna curenta **pentru ca** limita
|
||
Gratuit (60/luna) se aplica pe acest numar.
|
||
|
||
- **Depinde de**: US-001
|
||
- **Fisiere**: `app/accounts.py` SAU `app/plans.py` (`monthly_usage(conn, account_id)`),
|
||
`tests/test_plans.py` (~2 fisiere)
|
||
- **Test intai (RED)**: `test_consum_lunar_numara_sent_si_queued`, `test_consum_lunar_timp_local_ro`,
|
||
`test_consum_lunar_resetare_luna_noua`
|
||
- **Acceptance criteria**:
|
||
- [ ] `monthly_usage(conn, account_id)` numara prestatiile contului in luna calendaristica curenta.
|
||
**Definitia "prestatie consumata"** (de fixat la executie, propus): randuri `submissions` ale
|
||
contului cu `status` in (`queued`,`sending`,`sent`) cu `created_at` in luna curenta — adica
|
||
prestatiile ACCEPTATE in coada, nu cele respinse/blocate. (Justificare: limita e pe ce trimitem
|
||
la RAR, nu pe incercari esuate.) Alternativ doar `sent` — de decis; implicit: acceptate-in-coada.
|
||
- [ ] **Timp local RO** (ca E7 din 5.15): bucketarea lunii foloseste offset RO (`created_at,'+3 hours'`
|
||
sau echivalent), nu UTC pur, ca prestatiile de la granita de luna sa cada corect. Test la granita.
|
||
- [ ] Scoped strict pe cont (nu numara cross-account).
|
||
- [ ] Fara coloana noua daca `submissions.created_at` ajunge (respecta non-goal migrare minima).
|
||
- **Verificare E2E**: cont cu N trimiteri in luna → `monthly_usage == N`; luna urmatoare → reset la 0.
|
||
|
||
### US-003: Enforcement DUR — limita lunara Gratuit (60) pe ambele canale
|
||
**Ca** owner **vreau** ca un cont Gratuit care depaseste 60 prestatii/luna sa fie oprit **pentru ca**
|
||
asa sustinem diferenta de plan promisa.
|
||
|
||
- **Depinde de**: US-001, US-002
|
||
- **Fisiere**: `app/api/v1/router.py` (`create_prezentari`), `app/api/v1/import_router.py`
|
||
(commit import), `app/errors.py` (cod nou `PLAN_LIMITA_LUNARA`), `app/web/routes.py` (commit web),
|
||
`tests/test_api_scope.py` / `tests/test_web_*` / `tests/test_plans.py` (~6 fisiere)
|
||
- **Test intai (RED)**: `test_free_peste_60_respins_api`, `test_free_peste_60_respins_import_web`,
|
||
`test_pro_si_trial_nelimitat`, `test_eroare_3_niveluri_plan_limita`
|
||
- **Acceptance criteria**:
|
||
- [ ] La enqueue (API `POST /v1/prezentari` + commit import web + commit import API), daca
|
||
`effective_tier` are `monthly_limit` si `monthly_usage + nr_cerut > monthly_limit` → cererea
|
||
e respinsa (sau respinsa partial, la limita) cu eroare 3 niveluri (`app/errors.py`, cod
|
||
`PLAN_LIMITA_LUNARA`: problema "Ai atins limita planului Gratuit (60/luna)", cauza, fix
|
||
"Treci pe Standard/Pro sau astepti luna viitoare"). NU se face enqueue peste limita.
|
||
- [ ] `standard`/`pro`/`premium` si conturile in **trial Pro** → fara limita de volum.
|
||
- [ ] Comportament la cerere de lot care depaseste partial limita (ex. 50 folosite, vin 20):
|
||
decizie la executie — implicit RESPINGERE clara a intregului lot cu mesaj cat mai e disponibil
|
||
("mai poti trimite 10 luna asta"), NU enqueue partial tacut (evita surprize). De confirmat.
|
||
- [ ] Enforcement aliniat cu `AUTOPASS_REQUIRE_API_KEY` (dev vs prod): in dev, contul id=1 nu e
|
||
blocat artificial (trial/standard coerent), ca dogfooding-ul sa nu se loveasca de limita.
|
||
- [ ] **Idempotenta neatinsa**: respingerea pe limita se face INAINTE de `build_key`/enqueue; un
|
||
retry idempotent al unei prestatii deja acceptate nu consuma din nou cota.
|
||
- **Verificare E2E**: cont free cu 60 trimise → a 61-a respinsa cu mesaj 3 niveluri (API si import web);
|
||
cont pro → trece.
|
||
|
||
### US-004: Enforcement DUR — gate API doar pe Pro/Premium
|
||
**Ca** owner **vreau** ca importul prin API sa fie disponibil doar pe Pro+ **pentru ca** landing-ul
|
||
spune ca API-ul e o capabilitate Pro.
|
||
|
||
- **Depinde de**: US-001
|
||
- **Fisiere**: `app/auth.py` (sau dependinta de ruta), `app/api/v1/router.py`,
|
||
`app/api/v1/import_router.py`, `app/errors.py` (cod `PLAN_FARA_API`), `tests/test_api_scope.py`
|
||
(~5 fisiere)
|
||
- **Test intai (RED)**: `test_free_fara_api_403`, `test_standard_fara_api_403`, `test_pro_api_ok`,
|
||
`test_trial_pro_api_ok`, `test_dry_run_valideaza_ramane_permis`
|
||
- **Acceptance criteria**:
|
||
- [ ] Rutele de **import/ingestie prin API** (`POST /v1/prezentari`, `POST /v1/import`, etc.)
|
||
cer `effective_tier.api_access == True` (pro/premium sau trial Pro). Altfel 403 cu eroare
|
||
3 niveluri (`PLAN_FARA_API`: "Importul prin API e disponibil pe planul Pro", fix).
|
||
- [ ] **Canalul web ramane neafectat** — operatorii pe plan gratuit pot folosi import xlsx/csv prin
|
||
dashboard (asa promite landing-ul: Gratuit are import manual, NU API). Doar suprafata API e gated.
|
||
- [ ] `GET /v1/nomenclator` ramane public (coduri RAR, fara PII) — invariant CLAUDE.md.
|
||
- [ ] `POST /v1/prezentari/valideaza` (dry-run) — decizie: ramane permis pe orice plan (read-only,
|
||
ajuta integrarea inainte de upgrade) SAU gated ca restul API. Implicit: PERMIS (read-only,
|
||
fara enqueue). De confirmat.
|
||
- [ ] In dev (`AUTOPASS_REQUIRE_API_KEY=false`), contul id=1 are acces API (tier coerent), ca testele
|
||
API existente sa nu pice.
|
||
- **Verificare E2E**: cheie API pe cont free → 403 onest pe import; cheie pe cont pro/trial → 200.
|
||
|
||
### US-005: Downgrade automat la expirarea trial-ului
|
||
**Ca** owner **vreau** ca la expirarea celor 30 de zile contul sa treaca automat pe Gratuit **pentru ca**
|
||
landing-ul spune "apoi trece automat pe Gratuit, fara plata".
|
||
|
||
- **Depinde de**: US-001, US-003, US-004
|
||
- **Fisiere**: `app/plans.py` (`effective_tier` deja trateaza expirarea — lazy), optional
|
||
`app/worker/__main__.py` SAU un job de intretinere (eager), `tests/test_plans.py` (~3 fisiere)
|
||
- **Test intai (RED)**: `test_trial_expirat_efective_free`, `test_trial_expirat_aplica_limita_60`,
|
||
`test_trial_expirat_pierde_api`
|
||
- **Acceptance criteria**:
|
||
- [ ] **Lazy-first**: `effective_tier` returneaza `tier` de baza (`free`) imediat ce
|
||
`trial_until <= now` — fara job necesar pentru corectitudine (enforcement-ul US-003/004 se
|
||
bazeaza pe `effective_tier`, deci downgrade-ul e automat la prima cerere dupa expirare).
|
||
- [ ] Optional (eager, non-blocant): un pas in purjarea orara a worker-ului (T16 existent) poate
|
||
normaliza `trial_until` expirat → NULL pentru igiena (NU obligatoriu pentru corectitudine).
|
||
- [ ] Un cont cu `tier='standard'/'pro'/'premium'` setat de admin NU e downgradat de expirarea
|
||
trial-ului (trial-ul e un BONUS peste `free`; un plan platit alocat persista).
|
||
- [ ] Mesajele de limita/API dupa expirare sunt cele 3-niveluri din US-003/004.
|
||
- **Verificare E2E**: setez `trial_until` in trecut → contul aplica limita 60 + pierde API, fara restart.
|
||
|
||
### US-006: UI dashboard — plan curent + zile ramase din trial + consum lunar
|
||
**Ca** operator **vreau** sa vad pe ce plan sunt, cat mi-a mai ramas din trial si cat am consumat
|
||
luna asta **pentru ca** vreau sa stiu cand ma apropii de limita.
|
||
|
||
- **Depinde de**: US-001, US-002
|
||
- **Fisiere**: `app/web/routes.py` (context), `app/web/templates/_status.html` SAU `_cont.html`
|
||
(afisaj plan), `tests/test_web_status.py` / `tests/test_dashboard.py` (~4 fisiere)
|
||
- **Test intai (RED)**: `test_afisaj_plan_si_zile_trial`, `test_afisaj_consum_lunar`,
|
||
`test_avertizare_aproape_de_limita`
|
||
- **Acceptance criteria**:
|
||
- [ ] Dashboard-ul afiseaza discret planul curent (ex. "Plan: Pro · trial 18 zile ramase" sau
|
||
"Plan: Gratuit · 47/60 luna asta"). In trial → eticheta "trial" + zile ramase; pe Gratuit →
|
||
consum `N/60`.
|
||
- [ ] **Plasare (aliniat cu PRD 5.16)**: planul apare ca **badge in titlul din antet**
|
||
(`Gratuit`/`Standard`/`Pro`/`Premium`) SI ca linie in **meniul burger** ("Plan: <tier> [· trial
|
||
N zile]"), nu doar intr-un card pe Acasa. Vezi mockup-urile 5.16
|
||
(`docs/mockups/prd-5.16-dashboard.html` / `...-mobil.html`). 5.16 furnizeaza locul de afisare
|
||
(antet + meniu); 5.17 furnizeaza datele (tier, trial, consum).
|
||
- [ ] Avertizare vizuala cand consumul Gratuit se apropie de limita (ex. ≥80% → ton warn), fara a
|
||
ingropa stripul de sanatate (zero-silent-failures pastrat).
|
||
- [ ] Scoped pe cont; design conform 5.15/5.16 (tokeni, fonturi system, fara hex hardcodat).
|
||
- [ ] Pagina "Cont" arata planul + (daca exista) o explicatie "cum trec pe alt plan" (contact, ca
|
||
nu exista plata self-service inca).
|
||
- **Verificare E2E**: cont trial → "trial N zile"; cont free aproape de 60 → avertizare; cont pro →
|
||
fara contor de limita.
|
||
|
||
### US-007: Aliniere landing — limita 60 + trial pe Pro (nu Premium)
|
||
**Ca** vizitator **vreau** ca landing-ul sa spuna adevarul **pentru ca** azi promite "100/luna" si
|
||
"Premium gratuit 30 zile", dar realitatea va fi 60/luna si trial pe Pro.
|
||
|
||
- **Depinde de**: — (copy-only; aliniaza cu modelul din US-001)
|
||
- **Fisiere**: `app/web/templates/landing.html`, `tests/test_web_*` (~2 fisiere)
|
||
- **Test intai (RED)**: `test_landing_limita_60`, `test_landing_trial_pro_nu_premium`
|
||
- **Acceptance criteria**:
|
||
- [ ] Toate aparitiile "100 de prestatii/luna" / "100/luna" / `meta description`
|
||
(`landing.html:7,65,266` + oriunde apar) → **60**. Inclusiv cardul Gratuit din sectiunea PRICING.
|
||
- [ ] Textul "Fiecare cont incepe cu **Premium gratuit 30 de zile**" (`landing.html:256`) →
|
||
"**Pro gratuit 30 de zile**" (planul corect). Restul frazei ("Apoi trece automat pe Gratuit…")
|
||
ramane.
|
||
- [ ] Coerenta: orice alt loc care implica trial/limita reflecta 60 + Pro.
|
||
- [ ] Fara alte schimbari de pret/continut (39/59 lei raman).
|
||
- **Verificare E2E**: landing in browser — "60 prestatii/luna" peste tot, "Pro gratuit 30 de zile".
|
||
|
||
### US-008: Admin — alocare manuala de plan (fara plata self-service)
|
||
**Ca** admin **vreau** sa pot seta planul unui cont **pentru ca** nu exista inca facturare automata,
|
||
dar trebuie sa pot acorda Standard/Pro/Premium.
|
||
|
||
- **Depinde de**: US-001
|
||
- **Fisiere**: `tools/account.py` (CLI `set-tier`), optional `app/web/routes.py` (`/admin` actiune),
|
||
`tests/test_accounts.py` / `tests/test_web_admin*.py` (~3 fisiere)
|
||
- **Test intai (RED)**: `test_cli_set_tier`, `test_admin_set_tier_scoped`, `test_tier_invalid_respins`
|
||
- **Acceptance criteria**:
|
||
- [ ] CLI `python3 -m tools.account set-tier --account N --tier pro [--trial-days 30|--no-trial]`
|
||
seteaza `tier`/`trial_until`. Tier invalid → eroare clara.
|
||
- [ ] Optional (la executie): actiune in panoul `/admin` pentru a seta planul unui cont (scoped,
|
||
CSRF, ca bulk-ul de status din 5.5). Daca nu intra in 5.17, CLI e suficient (admin-only).
|
||
- [ ] Alocarea unui plan platit de catre admin NU e suprascrisa de expirarea trial-ului (US-005).
|
||
- [ ] Audit: schimbarea de plan se logheaza in `app_events` (reuse jurnalul din 5.6), fara PII nou.
|
||
- **Verificare E2E**: `set-tier --account 2 --tier pro` → contul 2 are API + volum nelimitat.
|
||
|
||
### US-009: Teste de regresie + E2E plan/trial/enforcement
|
||
**Ca** dezvoltator **vreau** acoperire completa **pentru ca** enforcement-ul atinge ambele canale de
|
||
ingestie si nu vreau sa blochez gresit conturi legitime.
|
||
|
||
- **Depinde de**: US-003, US-004, US-005, US-006, US-007
|
||
- **Fisiere**: `tests/test_plans.py`, `tests/test_api_scope.py`, `tests/test_web_*` (~3 fisiere)
|
||
- **Test intai (RED)**: matricea plan × capabilitate (volum, API) × canal (API, web) × trial activ/expirat.
|
||
- **Acceptance criteria**:
|
||
- [ ] `python3 -m pytest -q -m "not live"` verde; regresia de aur (`POST /v1/prezentari` → queued
|
||
pe un cont cu drept) ramane verde.
|
||
- [ ] Matrice testata: free(volum-blocat/API-blocat), standard(volum-ok/API-blocat),
|
||
pro(ok/ok), trial-pro(ok/ok), trial-expirat(=free).
|
||
- [ ] Contoarele lunare resetate la luna noua (test la granita timp local RO).
|
||
- [ ] Dev (id=1) nu e blocat de enforcement (dogfooding).
|
||
- **Verificare E2E**: rulare completa documentata in Raportul VERIFY.
|
||
|
||
## 4. Cerinte functionale (rezumat)
|
||
1. [REQ-001] `accounts.tier` ∈ {free,standard,pro,premium} + `trial_until`; migrare aditiva defensiva.
|
||
2. [REQ-002] `app/plans.py` = sursa unica: limite (free=60/luna) + capabilitati (API doar Pro+).
|
||
3. [REQ-003] Cont nou → trial Pro 30 zile automat; `effective_tier` randeaza Pro in trial, free dupa.
|
||
4. [REQ-004] Enforcement DUR: free peste 60/luna respins (API + import web) cu eroare 3 niveluri.
|
||
5. [REQ-005] Enforcement DUR: import API gated pe Pro+ (403 onest); canalul web ramane liber.
|
||
6. [REQ-006] Downgrade automat la expirare trial (lazy via `effective_tier`).
|
||
7. [REQ-007] Dashboard arata plan + zile trial + consum lunar; landing aliniat (60, Pro).
|
||
8. [REQ-008] Admin aloca planuri manual (CLI `set-tier`), audit in `app_events`.
|
||
|
||
## 5. Non-Goals (anti scope-creep)
|
||
- **Fara integrare de plata / facturare / abonamente** (Stripe etc.) — alocarea platita = manuala (admin).
|
||
- Fara self-service upgrade din UI (doar afisare plan + "contacteaza-ne"); plata vine intr-un PRD viitor.
|
||
- Fara modificari pe backend-ul de trimitere (worker, masina de stari, idempotenta `build_key`,
|
||
reconciliere, contract RAR). Enforcement-ul se face la ingestie/enqueue, INAINTE de coada.
|
||
- Fara schimbarea capabilitatilor de produs in sine (sugestii/mapare exista deja pe toate planurile in
|
||
cod; diferentierea 5.17 e pe VOLUM + ACCES API, exact ce promite landing-ul ca diferentiator hard).
|
||
- Fara modificari de design (tipografia/temele sunt 5.16/5.15); doar reuse-ul stilurilor existente.
|
||
|
||
## 6. Consideratii tehnice
|
||
- **Stack**: SQLite (migrare aditiva defensiva ca 5.5/5.12), FastAPI, Jinja2/HTMX.
|
||
- **Patterns de urmat**: sursa unica (`app/plans.py` ca `app/errors.py`); eroare 3 niveluri (5.4);
|
||
scope pe cont (5.15/US-011); timp local RO la bucketare (5.15/E7); audit `app_events` (5.6).
|
||
- **Riscuri**:
|
||
- **Blocare gresita a unui cont legitim** (enforcement prea agresiv) — risc de business. Mitigare:
|
||
dev id=1 exceptat; teste matrice; mesaje 3 niveluri cu cale de iesire; respingere INAINTE de enqueue
|
||
(nu pierde date).
|
||
- **Definitia "prestatie consumata"** (acceptate-in-coada vs sent) schimba cand musca limita.
|
||
Mitigare: o decidem explicit (US-002 AC) + test; documentam.
|
||
- **Granita de luna / fus orar** — off-by-a-day la reset. Mitigare: timp local RO + test la granita
|
||
(lectia E7 din 5.15).
|
||
- **Idempotenta vs cota** — un retry idempotent nu trebuie sa consume cota de doua ori. Mitigare:
|
||
enforce inainte de `build_key`; testul de retry.
|
||
- **Conturi legacy fara tier** — migrare le pune `free`; un cont real activ ar putea fi limitat brusc
|
||
la 60. Mitigare: decizie de migrare (legacy activ → ce plan?) confirmata cu user inainte de deploy.
|
||
|
||
## 7. Consideratii UI/UX
|
||
- Afisaj plan discret, conform 5.16 (fonturi system, tokeni `--fs-*`, fara hex).
|
||
- Stari: trial activ (zile ramase) / free (consum N/60, warn la ≥80%) / platit (fara contor limita).
|
||
- Mesaje de respingere oneste, actionabile (cum trec pe alt plan), nu doar "403".
|
||
|
||
## 8. Open Questions
|
||
- [ ] "Prestatie consumata" = acceptate-in-coada (queued+sending+sent) sau doar `sent`? (implicit: acceptate)
|
||
- [ ] Lot care depaseste partial limita → respingere totala sau enqueue partial? (implicit: respingere totala clara)
|
||
- [ ] `POST /v1/prezentari/valideaza` (dry-run) — gated pe Pro sau permis tuturor? (implicit: permis)
|
||
- [ ] Migrare conturi legacy active: raman `free` (risc limitare brusca) sau primesc un trial/plan? (de confirmat cu user)
|
||
- [ ] Standard (39 lei) si Premium difera de Pro doar prin API + suport in landing — pastram exact maparea
|
||
de capabilitati din landing in `plans.py`? (implicit: da)
|
||
|
||
## 9. Valuri de executie
|
||
|
||
```
|
||
Val 1: [US-001] schema tier+trial + app/plans.py (autor unic schema/accounts)
|
||
Val 2: [US-002] numarator consum lunar (dupa model) ||
|
||
[US-007] landing copy 60 + Pro (independent, copy-only)
|
||
Val 3: [US-003] [US-004] [US-005] enforcement volum + API + downgrade (consuma plans.py)
|
||
Val 4: [US-006] [US-008] UI dashboard plan/consum || admin set-tier
|
||
Val 5: [US-009] regresie + E2E matrice (dupa toate)
|
||
```
|
||
|
||
> Secventiere fata de 5.16: independent (5.16 = design/tipografie; 5.17 = model de cont). Pot rula in
|
||
> paralel; doar US-006 (afisaj plan in `_status.html`) atinge un fisier pe care 5.16/US-003 il modifica
|
||
> (dot RAR) — serializeaza acel template daca ambele PRD-uri sunt in executie simultan.
|
||
|
||
---
|
||
|
||
> Acest PRD nu a fost inca trecut prin `/plan-ceo-review` / `/plan-eng-review`. Recomandat inainte de
|
||
> executie (enforcement de business cu risc de blocare gresita + decizia de migrare a conturilor legacy).
|