chore: plan implementare Agent Teams + API contract

- docs/PLAN.md - plan arhitectural complet (creat anterior)
- docs/api-contract.json - contract API intre backend/frontend agenti
- docs/superpowers/plans/2026-03-13-roaauto-implementation.md - plan implementare cu Agent Teams
- HANDOFF.md - context pentru sesiuni viitoare

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 17:07:04 +02:00
commit b5085bf2fa
3 changed files with 2354 additions and 0 deletions

50
HANDOFF.md Normal file
View File

@@ -0,0 +1,50 @@
# Handoff - ROA AUTO SaaS
## Ce s-a facut in aceasta sesiune
### Planificare completa
1. Citit `docs/PLAN.md` - planul arhitectural complet al proiectului
2. Creat plan de implementare cu **Claude Code Agent Teams** (functionalitate experimentala reala)
### Fisiere create
- `docs/superpowers/plans/2026-03-13-roaauto-implementation.md` - planul complet de implementare
### Despre planul creat
Planul foloseste **Claude Code Agent Teams** (nu subagenti):
- Necesita `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` in `~/.claude/settings.json`
- Necesita Claude Code v2.1.32+
- 3 teammates specializati: `backend-agent`, `frontend-agent`, `devops-agent`
- Task list comun cu 11 taskuri (TASK-001 pana la TASK-011)
- Comunicare directa intre agenti via mailbox
- `plan_approval_required: true` pentru TASK-002 si TASK-003
### Structura taskuri
```
TASK-001 (Lead) → API contract + structura directoare
TASK-002 (backend) → FastAPI + libSQL + Auth [paralel cu 003]
TASK-003 (frontend) → Vue 3 + wa-sqlite + Sync [paralel cu 002]
TASK-004 (devops) → Docker dev + Makefile [paralel cu 002+003]
TASK-005 (backend) → Sync endpoints + Models + Seed + Orders
TASK-006 (frontend) → Dashboard + Orders UI + Vehicle Picker
TASK-007 (backend) → PDF WeasyPrint + Portal Client + SMS + Invoices
TASK-008 (frontend) → Portal public + Order Detail + PDF download
TASK-009 (backend) → Invite system + User management
TASK-010 (devops) → Docker production + nginx
TASK-011 (frontend) → PWA + Backup/Restore + Upgrade prompts
```
### Cum pornesti implementarea
1. Activeaza Agent Teams in `~/.claude/settings.json`
2. Deschide Claude Code in `/mnt/e/proiecte/roaauto/`
3. Spune: *"Citeste docs/superpowers/plans/2026-03-13-roaauto-implementation.md si creeaza un agent team cu 3 teammates (backend-agent, frontend-agent, devops-agent) sa implementeze proiectul ROA AUTO SaaS."*
### Stare repo
- Git init facut (fara commits inca)
- Remote: `git@gitea.romfast.ro:marius/roaauto.git`
- Nu s-a scris niciun cod de implementare - doar planul

609
docs/PLAN.md Normal file
View File

@@ -0,0 +1,609 @@
# Plan: ROA AUTO SaaS - Proiect Nou Separat
## Context
Marius (ROMFAST SRL, Constanta) construieste un **SaaS pentru service-uri auto mici si vulcanizari** din Romania. Serverul initial e pe Proxmox la birou (nu VPS cloud), deci aplicatia trebuie sa functioneze si cand serverul e offline. Cand proiectul se autosustine financiar, se muta pe VPS cloud.
Exista un prototip in `roa2web/roa-auto-mobile/` (Ionic Vue + SQLite server, single-tenant, fara auth) - folosit doar ca referinta conceptuala. **Proiect nou de la zero, repository git separat**.
---
## Decizii Confirmate
| Decizie | Valoare |
|---------|---------|
| Nume proiect | **roaauto** |
| Director | `E:\proiecte\roaauto\` |
| Git remote | `gitea.romfast.ro:marius/roaauto` |
| Domeniu | **roaauto.romfast.ro** |
| CSS framework | **Tailwind CSS** |
| Cod existent | **De la zero** (prototip doar referinta) |
| DB server | **libSQL** (fisier, in-process cu FastAPI) |
| DB browser | **wa-sqlite** (SQLite WASM) |
| Sync | **Custom simplu** (timestamp-based) |
| Offline | **Da** - reads + writes locale, sync cand e net |
---
## Arhitectura
```
[Browser] [Server Proxmox / VPS]
┌─────────────────────┐ ┌──────────────────────┐
│ Vue 3 PWA │ │ FastAPI │
│ wa-sqlite (WASM) │ ◄──── sync ────► │ libSQL (fisier) │
│ └── OPFS storage │ │ └── roaauto.db │
│ └── _sync_queue │ │ │
│ └── ALL reads local│ │ WeasyPrint (PDF) │
│ └── writes instant │ │ SMSAPI.ro (SMS) │
│ │ │ JWT auth │
│ Service Worker │ │ │
│ └── app shell cache│ │ Cloudflare Tunnel │
└─────────────────────┘ └──────────────────────┘
```
### Cum functioneaza sync-ul
**Initial sync (la login):**
```
GET /api/sync/full?tables=orders,order_lines,vehicles,catalog_*
→ Server returneaza toate datele tenant-ului
→ Client INSERT-eaza in wa-sqlite local
```
**Write (offline OK):**
```
1. User creeaza comanda
2. INSERT in wa-sqlite local (instant, UUID generat client-side)
3. INSERT in _sync_queue (table, id, operation, data, timestamp)
4. Daca online: POST /api/sync/push → server valideaza → INSERT in libSQL
5. Daca offline: queue asteapta → sync la reconectare
```
**Incremental sync (periodic, cand e online):**
```
GET /api/sync/changes?since=2026-03-13T10:00:00
→ Server returneaza doar randurile modificate dupa timestamp
→ Client UPSERT in wa-sqlite local
```
**Conflict resolution:** Server wins (last-write-wins). Simplu, predictibil.
### De ce libSQL pe server (nu PostgreSQL)
- **Un fisier** (`roaauto.db`) - nu un server/container separat
- **In-process** - FastAPI acceseaza direct, zero overhead retea
- **Replication built-in** - backup = copie fisier sau libSQL replication
- **Schema identica** cu wa-sqlite din browser (ambele SQLite)
- **Resurse minime** - perfect pentru Proxmox la birou
- **Migrare la PostgreSQL** cand cresc cerintele (change connection string + fix SQL specifics)
**Limitari acceptate:**
- Write concurrency: un writer la un moment dat (WAL mode). OK pentru <50 useri.
- ALTER TABLE limitat: Alembic batch operations rezolva asta.
- No RLS: izolare multi-tenant application-level (WHERE tenant_id = ?).
---
## Tech Stack
| Layer | Tehnologie | De ce |
|-------|-----------|-------|
| **Backend** | FastAPI + SQLAlchemy 2.0 + Alembic | Known territory |
| **DB server** | libSQL (via `aiosqlite` dialect) | Fisier, in-process, replication |
| **DB browser** | wa-sqlite (`@journeyapps/wa-sqlite`) | SQLite WASM, OPFS persistence |
| **Sync** | Custom (timestamp-based push/pull) | Simplu, fara dependente extra |
| **Auth** | python-jose + passlib (bcrypt) | JWT 30 zile, offline valid |
| **PDF** | WeasyPrint | Server-side, diacritice ROM OK |
| **SMS** | SMSAPI.ro | Provider romanesc, REST API |
| **Frontend** | Vue 3 + Tailwind CSS 4 | Responsive, utility-first |
| **State** | Pinia + reactive SQL queries | Local-first |
| **PWA** | vite-plugin-pwa | App shell cache, install prompt |
| **Build** | Vite 6 | Fast |
| **Deploy** | Docker (single container) + Dokploy + Cloudflare Tunnel | Self-hosted Proxmox |
| **Git** | Gitea (gitea.romfast.ro) | Self-hosted |
---
## Structura Proiect
```
E:\proiecte\roaauto\
├── docker-compose.yml # Backend (FastAPI + libSQL) + frontend build
├── docker-compose.dev.yml
├── Makefile # make dev, make migrate, make seed, make backup
├── .env.example
├── backend/
│ ├── Dockerfile
│ ├── requirements.txt
│ ├── alembic.ini
│ ├── alembic/versions/
│ ├── app/
│ │ ├── main.py # FastAPI + lifespan
│ │ ├── config.py # Settings din env
│ │ ├── deps.py # get_db, get_current_user, get_tenant_id
│ │ │
│ │ ├── auth/
│ │ │ ├── router.py # register, login, refresh
│ │ │ ├── service.py # JWT 30 zile, bcrypt, trial logic
│ │ │ └── schemas.py
│ │ │
│ │ ├── sync/
│ │ │ ├── router.py # GET /sync/full, GET /sync/changes, POST /sync/push
│ │ │ └── service.py # Sync logic, conflict resolution
│ │ │
│ │ ├── tenants/ # settings, subscription
│ │ ├── users/ # invite, list, deactivate
│ │ ├── vehicles/ # CRUD
│ │ ├── orders/ # CRUD + workflow
│ │ │ ├── router.py
│ │ │ ├── service.py # DRAFT->VALIDAT->FACTURAT, recalc totals
│ │ │ └── schemas.py
│ │ ├── invoices/ # create from order
│ │ ├── catalog/ # nomenclatures
│ │ ├── appointments/ # CRUD
│ │ ├── client_portal/ # GET /p/{token} (public, no auth)
│ │ ├── sms/ # SMSAPI.ro
│ │ ├── pdf/
│ │ │ ├── service.py # WeasyPrint
│ │ │ └── templates/ # deviz.html, factura.html
│ │ │
│ │ └── db/
│ │ ├── base.py # SQLAlchemy Base, UUID mixin, TenantMixin
│ │ ├── session.py # libSQL/aiosqlite engine + session
│ │ └── models/ # tenant, user, vehicle, order, etc.
│ │
│ ├── data/ # libSQL database file(s)
│ │ └── .gitkeep
│ └── tests/
├── frontend/
│ ├── Dockerfile + nginx.conf
│ ├── package.json
│ ├── vite.config.js
│ ├── tailwind.config.js
│ ├── index.html
│ │
│ └── src/
│ ├── main.js # Vue + Pinia + wa-sqlite init
│ ├── App.vue
│ │
│ ├── db/
│ │ ├── schema.js # Tabele SQLite locale (mirror server)
│ │ ├── database.js # wa-sqlite init + OPFS
│ │ ├── sync.js # Sync engine (full sync, incremental, push queue)
│ │ └── queries.js # Reactive query helper
│ │
│ ├── router/index.js
│ │
│ ├── stores/
│ │ ├── auth.js # JWT, login/register, offline token validation
│ │ ├── orders.js # SQL queries pe wa-sqlite local
│ │ ├── vehicles.js
│ │ ├── catalog.js
│ │ └── sync.js # Sync status, last sync time
│ │
│ ├── composables/
│ │ ├── useAuth.js
│ │ ├── useBreakpoint.js
│ │ ├── useSqlQuery.js # Reactive SQL query (re-run on table change)
│ │ └── useSync.js # Online/offline status, trigger sync
│ │
│ ├── layouts/
│ │ ├── AppLayout.vue # Sidebar desktop / bottom nav mobile
│ │ ├── AuthLayout.vue
│ │ └── ClientLayout.vue # Portal client (minimal)
│ │
│ ├── views/
│ │ ├── auth/ # Login, Register, InviteAccept
│ │ ├── dashboard/ # Today's orders, quick stats
│ │ ├── orders/ # List, Create, Detail, Invoice
│ │ ├── vehicles/ # List, Detail
│ │ ├── appointments/ # Calendar/list
│ │ ├── catalog/ # Tabbed: marci, norme, preturi
│ │ ├── settings/ # Profile, Users, Subscription
│ │ └── client/ # DevizView (public), AppointmentBook
│ │
│ ├── components/
│ │ ├── common/ # Button, Input, Modal, Table, StatusBadge, SyncIndicator
│ │ ├── orders/ # OrderCard, OperationForm, MaterialForm, OrderTotals
│ │ └── vehicles/ # VehiclePicker
│ │
│ └── assets/css/
│ └── main.css # Tailwind imports
└── docs/
├── ARCHITECTURE.md
└── CLAUDE.md # Reguli pentru Claude Code in proiectul nou
```
---
## Schema Database (identica server + browser)
Toate tabelele: `id TEXT PRIMARY KEY` (UUID v7 generat client-side), `tenant_id TEXT NOT NULL`, `oracle_id INTEGER` (NULL, mapare la Oracle ROAAUTO), `updated_at TEXT` (ISO timestamp pentru sync).
```sql
-- Tenants & Users
tenants (id, nume, cui, reg_com, adresa, telefon, email, iban, banca,
plan, trial_expires_at, created_at, updated_at)
users (id, tenant_id, email, password_hash, nume, rol, activ,
created_at, updated_at)
invites (id, tenant_id, email, rol, token, expires_at, accepted_at,
created_by, created_at)
-- Nomenclatures
catalog_marci (id, tenant_id, denumire, activ)
catalog_modele (id, marca_id, denumire)
catalog_ansamble (id, tenant_id, denumire)
catalog_norme (id, tenant_id, cod, denumire, ore_normate, ansamblu_id)
catalog_preturi (id, tenant_id, denumire, pret, um)
catalog_tipuri_deviz (id, tenant_id, denumire)
catalog_tipuri_motoare (id, tenant_id, denumire)
mecanici (id, tenant_id, user_id, nume, prenume, activ)
-- Core Business
vehicles (id, tenant_id, client_nume, client_telefon, client_email,
client_cod_fiscal, client_adresa, nr_inmatriculare,
marca_id, model_id, an_fabricatie, serie_sasiu,
tip_motor_id, created_at, updated_at)
orders (id, tenant_id, nr_comanda, data_comanda, vehicle_id,
tip_deviz_id, status, km_intrare, observatii,
-- client snapshot (denormalized)
client_nume, client_telefon, nr_auto, marca_denumire, model_denumire,
-- totals
total_manopera, total_materiale, total_general,
-- client portal
token_client,
created_by, created_at, updated_at)
order_lines (id, order_id, tenant_id, tip, descriere,
norma_id, ore, pret_ora, -- manopera
um, cantitate, pret_unitar, -- material
total, mecanic_id, ordine, created_at, updated_at)
invoices (id, tenant_id, order_id, nr_factura, serie_factura,
data_factura, modalitate_plata,
client_nume, client_cod_fiscal, nr_auto,
total_fara_tva, tva, total_general, created_at, updated_at)
appointments (id, tenant_id, vehicle_id, client_nume, client_telefon,
data_ora, durata_minute, observatii, status, order_id,
created_at, updated_at)
-- Sync (doar in browser, nu pe server)
_sync_queue (id, table_name, row_id, operation, data_json, created_at, synced_at)
_sync_state (table_name, last_sync_at)
```
---
## Faze de Implementare
### Faza 0: Setup Proiect (aceasta sesiune)
**Livrabil: Director creat, plan salvat in proiect**
1. Creeaza directorul `E:\proiecte\roaauto\`
2. Salveaza acest plan ca `E:\proiecte\roaauto\docs\PLAN.md` pentru referinte viitoare
3. STOP - nu continua cu implementarea
### Faza 1: Fundatie + Auth + Sync (Saptamana 1-2)
**Livrabil: Register/Login, wa-sqlite local, sync functional**
**Backend:**
1. `git init` la `E:\proiecte\roaauto\`, remote pe Gitea
2. FastAPI skeleton: `main.py`, `config.py`, `deps.py`
3. libSQL setup: `session.py` cu `aiosqlite`
4. Alembic init + prima migrare: `tenants`, `users`, `invites`
5. Auth: `POST /auth/register` (creeaza tenant + owner), `POST /auth/login` (JWT 30 zile)
6. Sync endpoints: `GET /sync/full`, `GET /sync/changes?since=`, `POST /sync/push`
7. JWT middleware: `get_current_user`, `get_tenant_id`
**Frontend:**
8. Vue 3 + Vite + Tailwind + Pinia scaffold
9. wa-sqlite setup: `database.js` (init WASM + OPFS), `schema.js` (create tables)
10. Sync engine: `sync.js` (full sync, incremental sync, push queue)
11. Router cu auth guards
12. Pages: Login, Register
13. App layout responsive (sidebar desktop, bottom nav mobile)
14. Sync status indicator (online/offline/syncing)
**Verificare:** Register login full sync datele apar in wa-sqlite local offline: app functioneaza reconectare: sync push
### Faza 2: Business Logic Core (Saptamana 3-4)
**Livrabil: Creare comanda cu operatii/materiale, workflow complet**
1. Models + migrations: `vehicles`, `orders`, `order_lines`, `catalog_*`, `mecanici`
2. Seed data (Alembic): 24 marci, 11 ansamble, 6 tipuri deviz, 5 tipuri motoare, 3 preturi
3. OrderService: CRUD + workflow DRAFTVALIDATFACTURAT + recalc totals
4. wa-sqlite schema update (mirror noile tabele)
5. Frontend: Dashboard (comenzi azi, stats)
6. Frontend: Orders list (query local SQLite, filtrare, search)
7. Frontend: Order create (vehicle picker, operatii, materiale, totals live)
8. Frontend: Order detail (view/edit, status actions)
9. Frontend: Catalog management (tabbed)
10. Frontend: Vehicle picker (search-as-you-type pe SQLite local)
**Verificare:** Creare comanda offline operatii + materiale totals corecte reconectare sync comanda pe server
### Faza 3: Facturare + PDF (Saptamana 5)
**Livrabil: Deviz/factura PDF**
1. Model `invoices`, InvoiceService (VALIDATFACTURAT, TVA 19%)
2. WeasyPrint templates HTML/CSS (deviz.html, factura.html cu diacritice)
3. Endpoint `GET /api/orders/{id}/pdf/deviz`
4. Frontend: Invoice page, PDF download/share
**Verificare:** DRAFT operatii VALIDAT factura PDF cu diacritice corecte
### Faza 4: Portal Client + SMS (Saptamana 6)
**Livrabil: Client primeste SMS, vede deviz, accepta/refuza**
1. `GET /api/p/{token}` (public, no auth) deviz data
2. `POST /api/p/{token}/accept`, `/reject`
3. SMSAPI.ro integration
4. Frontend: DevizViewPage (public, mobile-first)
5. Frontend: "Trimite deviz" button (SMS + WhatsApp link)
6. Model `appointments` + CRUD + client booking
**Verificare:** Comanda trimite SMS client link accepta status updated
### Faza 5: Management Angajati + Settings (Saptamana 7)
**Livrabil: Owner invita angajati, role-based access**
1. Invite system (email link 48h)
2. User management (list, deactivate, roles)
3. Settings: profil service
4. Role-based UI (mecanic vede doar comenzile lui)
### Faza 6: Deployment (Saptamana 8)
**Livrabil: roaauto.romfast.ro live**
1. Dockerfile (FastAPI + libSQL, single container)
2. Frontend Dockerfile (nginx)
3. docker-compose.yml
4. Dokploy pe Proxmox + Cloudflare Tunnel
5. Trial expiry middleware
6. Backup strategy (libSQL replication / cron cp)
### Faza 7: Polish + PWA (Saptamana 9-10)
**Livrabil: Production-ready, instalabil**
1. PWA: service worker, install prompt, icons
2. Sync indicator UI (online/offline/syncing/error)
3. Error handling, toasts, loading states
4. Responsive testing (phone, tablet, desktop)
5. Reports: sumar lunar, export CSV
---
## Referinta din Prototip (doar consultare)
| Ce | Fisier | Ce portam conceptual |
|----|--------|---------------------|
| Workflow | `roa-auto-mobile/backend/services/order_service.py` | State machine, `_recalc_totals` |
| Seed data | `roa-auto-mobile/backend/seed.py` | 24 marci, ansamble, tipuri |
| PDF layout | `roa-auto-mobile/src/services/pdf.js` | Format coloane, header, totals |
| UI flow | `roa-auto-mobile/src/views/OrderCreatePage.vue` | Flow creare comanda |
| Vehicle search | `roa-auto-mobile/src/components/VehiclePicker.vue` | Search pattern |
---
## Compatibilitate ROAAUTO Oracle
SaaS-ul trebuie sa fie compatibil cu ROAAUTO VFP9+Oracle. Clientii care cresc pot migra la sistemul complet ROAAUTO cu Oracle. Coloanele SaaS mapeaza 1:1 la tabelele Oracle `MARIUSM_AUTO.dev_*`.
### Mapare tabele SaaS → Oracle ROAAUTO
| SaaS (libSQL) | Oracle (MARIUSM_AUTO) | Note |
|---------------|----------------------|------|
| `orders` | `dev_ordl` | +tenant_id, +token_client, UUID vs Integer PK |
| `order_lines` (tip=manopera) | `dev_oper` | SaaS unifica oper+materiale in order_lines |
| `order_lines` (tip=material) | `dev_estimari_produse` | Acelasi tabel, filtrat pe `tip` |
| `vehicles` | `dev_masiniclienti` | Renamed, aceleasi coloane client+vehicul |
| `catalog_marci` | `dev_nom_marci` | +tenant_id |
| `catalog_modele` | `dev_nom_masini` | Identic |
| `catalog_ansamble` | `dev_nom_ansamble` | +tenant_id |
| `catalog_norme` | `dev_nom_norme` | +tenant_id |
| `catalog_preturi` | `dev_nom_preturi` | +tenant_id |
| `catalog_tipuri_deviz` | `dev_tip_deviz` | +tenant_id |
| `catalog_tipuri_motoare` | `dev_tipuri_motoare` | +tenant_id |
| `mecanici` | `dev_mecanici` | +tenant_id, +user_id |
| `invoices` | `facturi` (local) | Identic structural |
| `tenants` | - | Doar SaaS (nu exista in Oracle) |
| `users` | - | Doar SaaS |
| `appointments` | - | Doar SaaS (feature nou) |
### Coloane compatibile (pastreaza aceleasi nume)
Coloanele business raman identice cu Oracle pentru migrare usoara:
- `nr_comanda`, `data_comanda`, `status`, `km_intrare`, `observatii`
- `client_nume`, `client_telefon`, `client_cod_fiscal`, `nr_auto`
- `marca_denumire`, `model_denumire`
- `total_manopera`, `total_materiale`, `total_general`
- `denumire`, `ore`, `pret_ora`, `cantitate`, `pret_unitar`, `total`
- `nr_inmatriculare`, `serie_sasiu`, `an_fabricatie`
- `cod`, `ore_normate`, `pret`, `um`
- `nr_factura`, `serie_factura`, `data_factura`, `modalitate_plata`
- `tva`, `total_fara_tva`
### Coloane adaugate fata de Oracle (doar in SaaS)
- `id TEXT` (UUID v7) - in loc de `id INTEGER` (Oracle sequence)
- `tenant_id TEXT` - izolare multi-tenant
- `oracle_id INTEGER` - mapare la ID-ul Oracle, NULL pana la migrare
- `token_client TEXT` - portal client (feature SaaS)
- `updated_at TEXT` - timestamp sync
- `created_at TEXT` - audit
### Script migrare SaaS → Oracle (Phase viitoare)
```python
# Migrare: export din libSQL SaaS → import in Oracle ROAAUTO
# 1. Map UUID → Integer (Oracle sequences)
# 2. Split order_lines → dev_oper (manopera) + dev_estimari_produse (material)
# 3. Rename tables: vehicles → dev_masiniclienti, orders → dev_ordl
# 4. Drop columns: tenant_id, token_client, oracle_id
# 5. Import nomenclatures shared (global marci etc)
```
---
## Sistem Tier-uri si Business Model
### Free Forever (0 RON/luna)
- **100% local** - wa-sqlite in browser, ZERO cost server per user
- Toate features-urile de baza: comenzi, operatii, materiale, vehicule, nomenclator
- **PDF generation client-side** (jsPDF) - deviz + factura
- **Web Share API** - share PDF via WhatsApp/email
- **Backup/restore manual** - export DB ca fisier JSON/SQLite pe telefon, restore din fisier
- **Un singur device** (datele in browser-ul respectiv)
- **Nu necesita cont pe server** - app-ul se incarca ca PWA, functioneaza standalone
- **Limite soft**: ~50 comenzi/luna (nu blocheaza, doar sugereaza upgrade gratios)
- NU: sync cloud, client portal, SMS, multi-device, backup automat
### Basic (49 RON/luna - de stabilit)
- Tot ce e in Free +
- **Cloud sync** (libSQL server) - datele salvate in cloud, backup automat
- **Multi-device** - lucreaza de pe telefon SI calculator
- **Client portal** - link unic pentru client sa vada devizul
- **WhatsApp link** - generare link deviz pentru WhatsApp (gratuit)
- 1 user
### Pro (99 RON/luna - de stabilit)
- Tot ce e in Basic +
- **SMS deviz** - trimite SMS cu link via SMSAPI.ro
- **Multi-user** - pana la 3 useri (owner + 2 angajati)
- **Programari** - modul appointments
- **Rapoarte** - sumar lunar, export CSV
- **Priority support**
### Cum functioneaza tehnic
**Free tier (zero server cost):**
```
1. User acceseaza roaauto.romfast.ro
2. PWA se incarca si se instaleaza (service worker cache)
3. wa-sqlite se initializeaza cu schema goala
4. User lucreaza 100% local - fara cont, fara server
5. Datele raman DOAR in browser (OPFS)
6. Daca sterge browser data → pierde tot (avertizare clara)
```
**Upgrade Free → Basic:**
```
1. User da click "Creeaza cont" (POST /auth/register)
2. Se creeaza tenant + user pe server
3. Full sync: datele locale se uploadeaza pe server (POST /sync/push)
4. De acum: sync bidirectional activ
5. Datele safe pe server + backup
```
**Downgrade Basic → Free:**
```
1. Subscription expirat sau anulat
2. Sync se opreste (server refuza sync requests)
3. Datele locale raman in browser (functioneaza ca inainte)
4. Datele pe server se pastreaza 90 zile (re-upgrade posibil)
5. Dupa 90 zile: datele server se sterg
```
**Trial flow:**
```
1. Register → 30 zile trial Basic gratuit
2. Sync activ, toate features-urile Basic
3. La expirare: downgrade automat la Free
4. Datele locale raman, sync se opreste
5. "Upgrade" button prominent
```
### Backup manual (Free tier)
```
Export: wa-sqlite → JSON blob → download ca fisier "roaauto-backup-2026-03-13.json"
- Buton "Salveaza backup" in Settings
- Foloseste File System Access API sau download fallback
- Include: toate tabelele, settings, metadata
Restore: fisier JSON → parse → INSERT in wa-sqlite
- Buton "Restaureaza din backup" in Settings
- Confirmare: "Asta va inlocui datele curente. Continui?"
- Validare: verifica schema version, integritate date
```
**UX important**: La fiecare 7 zile fara backup, reminder subtil (banner, nu modal):
"Ultima salvare: acum 12 zile. Salveaza un backup pe telefon."
### Limite soft + sugestii gratioase (stil Revolut)
**Filosofie**: Nu blocam NICIODATA. Doar sugeram, pozitiv, la momentul potrivit.
**Limite soft Free:**
- ~50 comenzi/luna (numar orientativ, nu hard limit)
- Fara limita pe vehicule/nomenclator
**Cum arata sugestiile (non-intrusive, pozitive):**
| Moment | Tip | Mesaj (exemplu) |
|--------|-----|-----------------|
| Comanda #20 | Banner mic, dismissable | "Ai creat 20 de comenzi luna asta. Stiai ca poti sincroniza datele pe mai multe dispozitive?" |
| Comanda #40 | Card in dashboard | "Afacerea ta creste! Cu planul Basic ai backup automat si poti trimite devize direct clientilor." |
| Comanda #50 | Bottom sheet (o singura data) | "Ai atins 50 de comenzi. Felicitari! Activeaza planul Basic pentru sync, backup automat si portal client. Continui gratuit, fara restrictii." |
| Share PDF manual | Tooltip subtil | "Poti trimite devizul direct pe WhatsApp clientului cu un click. Vezi planul Basic." |
| Dupa 3 luni activ | Card in dashboard | "Folosesti ROA AUTO de 3 luni. Multumim! Protejeaza-ti datele cu backup automat in cloud." |
**Reguli:**
- Fiecare sugestie apare MAX o data (dismiss = nu mai apare niciodata)
- Niciodata popup/modal blocant
- Ton pozitiv: "Felicitari", "Afacerea ta creste", "Stiai ca..."
- Buton "Nu, multumim" vizibil si respectat
- Nu afiseaza mai mult de 1 sugestie pe zi
- Dupa dismiss: minimum 7 zile pana la urmatoarea sugestie
### Logica in cod
```javascript
// Frontend: check tier before feature
const tier = authStore.tier // 'free', 'basic', 'pro' (din JWT sau local)
// Sync: doar paid
if (tier !== 'free') {
await syncEngine.start() // start bidirectional sync
}
// Client portal: doar basic+
if (tier === 'free') {
showUpgradeModal('client_portal')
} else {
generateClientLink(order.token_client)
}
// SMS: doar pro
if (tier !== 'pro') {
showUpgradeModal('sms')
}
```
```python
# Backend: middleware tier check
@app.middleware("http")
async def check_subscription(request, call_next):
if request.url.path.startswith("/api/sync"):
tenant = await get_tenant(request)
if tenant.plan == "free" or tenant.is_expired():
return JSONResponse(status_code=402, content={"detail": "Upgrade required"})
return await call_next(request)
```
---
## Migrare viitoare la PostgreSQL
Cand proiectul creste si se muta pe VPS cloud:
1. Schimba `aiosqlite` `asyncpg` in `session.py`
2. Adapteaza Alembic migrations (remove batch operations)
3. Adauga PostgreSQL RLS pentru izolare tenant
4. Optional: adauga PowerSync pentru sync automat (inlocuieste custom sync)
5. Schema e compatibila - aceleasi tabele, aceleasi coloane

File diff suppressed because it is too large Load Diff