- New clients table with PF/PJ support, fiscal data (CUI, IBAN, eFactura fields) - Full CRUD API for clients with search, sync integration - Order lifecycle: edit header (DRAFT), devalidate (VALIDAT→DRAFT), delete order/invoice - Invoice types: FACTURA (B2B) vs BON_FISCAL (B2C) with different nr formats - OrderCreateView redesigned as multi-step flow (client→vehicle→details) - Autocomplete from catalog_norme/catalog_preturi in OrderLineForm - Dashboard now combines stats + full orders table with filter tabs and search - ClientPicker and VehiclePicker with inline creation capability - Frontend schema aligned with backend (missing columns causing sync errors) - Mobile responsive fixes for OrderDetailView buttons Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5.9 KiB
5.9 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
ROA AUTO is a multi-tenant auto service management PWA. The architecture is offline-first: the Vue 3 frontend uses wa-sqlite (WebAssembly SQLite) running entirely in the browser as :memory:, with a sync engine that pulls from and pushes to the FastAPI backend. All business data lives locally in the browser's in-memory SQLite DB and is periodically synced.
Development Commands
Backend (FastAPI)
cd backend
source .venv/bin/activate
uvicorn app.main:app --port 8000 --reload # start dev server
pytest tests/ -v # run all tests
pytest tests/test_orders.py -v # run single test file
pytest tests/test_orders.py::test_name -v # run single test
Frontend (Vue 3 + Vite)
cd frontend
npm run dev # start dev server (port 5173)
npm run build # production build
Docker (preferred for full stack)
make dev # start all services (docker-compose.dev.yml)
make test # run backend tests in container
make migrate # run alembic migrations
make seed # seed demo data
Architecture
Sync Model (critical to understand)
- Frontend writes locally first, then queues changes in
_sync_queue(SQLite table) SyncEngine(frontend/src/db/sync.js) handles three operations:fullSync()— downloads all tenant data from/api/sync/fullon loginincrementalSync()— downloads changes sincelast_sync_atpushQueue()— uploads queued local changes to/api/sync/push
- The backend sync service (
backend/app/sync/service.py) definesSYNCABLE_TABLES— only these tables are synced - IDs are
crypto.randomUUID()generated client-side; backend usesINSERT OR REPLACE
Frontend (frontend/src/)
db/database.js— singleton wa-sqlite instance. Uses:memory:only (no IndexedDB persistence).execSQL(sql, params)is the only query interface. Parameterized queries must usesqlite3.statements()async generator —exec()does not support?params. Do NOT useprepare_v2(doesn't exist in this library).db/schema.js— full SQLite schema as a string constant, applied on initdb/sync.js—SyncEngineclass, exported as singletonsyncEnginestores/— Pinia stores (auth.js,orders.js,vehicles.js) wrappingexecSQLcallsviews/— one file per route; data loaded viaexecSQLdirectly or through stores- Reactivity —
notifyTableChanged(table)/onTableChange(table, cb)indatabase.jsprovide a pub/sub for cross-component updates. CallnotifyTableChangedafter any write.
Backend (backend/app/)
- Multi-tenant: every model has
tenant_id. All queries filter bytenant_idfrom the JWT. - Auth: JWT in
Authorization: Bearerheader.get_tenant_iddep extracts tenant from token. deps.py—get_current_userandget_tenant_idFastAPI dependencies used across all routerssync/— The most complex module.apply_pushdoesINSERT OR REPLACEfor all ops.pdf/— WeasyPrint + Jinja2 HTML templates to generate PDF deviz/facturaclient_portal/— Public routes (no auth) for client-facing deviz view viatoken_client- DB: SQLite via SQLAlchemy async (
aiosqlite). Alembic for migrations (files inalembic/versions/).
Key Data Flow: Order Lifecycle
DRAFT → VALIDAT → FACTURAT
- Order lines (manopera/material) added only in DRAFT
- PDF deviz available after VALIDAT
- Invoice created locally in
handleFactureaza()then queued for sync
Critical wa-sqlite Notes
- Import:
wa-sqlite.mjs(sync WASM build), NOTwa-sqlite-async.mjs - All API methods (
open_v2,exec,step,finalize) return Promises — alwaysawait - For parameterized queries:
for await (const stmt of sqlite3.statements(db, sql))thensqlite3.bind_collection(stmt, params) vite.config.jsexcludes@journeyapps/wa-sqlitefromoptimizeDeps- COOP/COEP headers (
Cross-Origin-Opener-Policy: same-origin+Cross-Origin-Embedder-Policy: require-corp) are required for SharedArrayBuffer used by wa-sqlite — set in vite dev server and nginx
WSL2 Note
Running on WSL2 with code on Windows NTFS (/mnt/e/): Vite is configured with server.watch.usePolling: true, interval: 1000 to work around inotify not firing on NTFS mounts.
Testing
- Backend tests use an in-memory SQLite DB (overrides
get_dbviaapp.dependency_overrides) asyncio_mode = autoset inpytest.ini— no need to mark tests with@pytest.mark.asyncioauth_headersfixture registers a user and returnsAuthorizationheader for authenticated tests- Demo credentials (after
make seed):demo@roaauto.ro/demo123
Execution Preferences
- Executia task-urilor se face cu team agents (nu cu skill superpowers subagents)
- Playwright testing obligatoriu: orice implementare majora trebuie testata E2E cu Playwright
- Desktop: 1280x720, Mobile: 375x812
- Verificari: responsive, elemente nu se suprapun, nu ies din ecran, butoane cu gap suficient
- Raportul Playwright se salveaza in
docs/playwright-report-YYYY-MM-DD.md
Data Model: Clients
- Tabel
clients- nomenclator clienti cu date eFactura ANAF, separat de vehicule - Un client poate avea mai multe vehicule (1:N prin client_id pe vehicles)
tip_persoana: PF (persoana fizica) / PJ (persoana juridica)- Campuri eFactura: cod_fiscal, reg_com, adresa, judet, oras, cod_postal, cont_iban, banca
Data Model: Invoices
tip_document: FACTURA (B2B, eFactura ANAF) sau BON_FISCAL (B2C, casa de marcat)- Factura: necesita date client complete (CUI, adresa)
- Bon fiscal: format simplificat
Order Lifecycle
DRAFT→VALIDAT→FACTURAT(cu devalidare VALIDAT → DRAFT)- Stergere: orice nefacturat; FACTURAT = sterge factura intai
- Edit header doar in DRAFT