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:
50
HANDOFF.md
Normal file
50
HANDOFF.md
Normal 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
609
docs/PLAN.md
Normal 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 DRAFT→VALIDAT→FACTURAT + 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 (VALIDAT→FACTURAT, 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
|
||||
1695
docs/superpowers/plans/2026-03-13-roaauto-implementation.md
Normal file
1695
docs/superpowers/plans/2026-03-13-roaauto-implementation.md
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user