- All business models: Vehicle, Order, OrderLine, Invoice, Appointment, CatalogMarca/Model/Ansamblu/Norma/Pret/TipDeviz/TipMotor, Mecanic - Sync endpoints: GET /sync/full, GET /sync/changes?since=, POST /sync/push with tenant isolation and last-write-wins conflict resolution - Order CRUD with state machine: DRAFT -> VALIDAT -> FACTURAT Auto-recalculates totals (manopera + materiale) - Vehicle CRUD: list, create, get, update - Seed data: 24 marci, 11 ansamble, 6 tipuri deviz, 5 tipuri motoare, 3 preturi - Alembic migration for all business models - 13 passing tests (auth + sync + orders) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
42 lines
1.1 KiB
Python
42 lines
1.1 KiB
Python
from datetime import UTC, datetime
|
|
|
|
from fastapi import APIRouter, Depends, Query
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.db.session import get_db
|
|
from app.deps import get_tenant_id
|
|
from app.sync import schemas, service
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/full")
|
|
async def sync_full(
|
|
tenant_id: str = Depends(get_tenant_id),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
tables = await service.get_full(db, tenant_id)
|
|
return {"tables": tables, "synced_at": datetime.now(UTC).isoformat()}
|
|
|
|
|
|
@router.get("/changes")
|
|
async def sync_changes(
|
|
since: str = Query(...),
|
|
tenant_id: str = Depends(get_tenant_id),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
tables = await service.get_changes(db, tenant_id, since)
|
|
return {"tables": tables, "synced_at": datetime.now(UTC).isoformat()}
|
|
|
|
|
|
@router.post("/push", response_model=schemas.SyncPushResponse)
|
|
async def sync_push(
|
|
data: schemas.SyncPushRequest,
|
|
tenant_id: str = Depends(get_tenant_id),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
result = await service.apply_push(
|
|
db, tenant_id, [op.model_dump() for op in data.operations]
|
|
)
|
|
return result
|