From a83f55c646ceb1035e3a90ba324309a2d418d8d0 Mon Sep 17 00:00:00 2001 From: VPN SaaS Dev Date: Thu, 14 May 2026 19:43:51 +0900 Subject: [PATCH] Add service center card interactions --- app/api/service_centers.py | 38 +++++++++++++ tests/test_platform.py | 15 ++++++ web/index.html | 1 + web/static/app.js | 106 +++++++++++++++++++++++++++++++++++-- web/static/styles.css | 33 ++++++++++++ 5 files changed, 190 insertions(+), 3 deletions(-) diff --git a/app/api/service_centers.py b/app/api/service_centers.py index 8c898ea..db3cf6c 100644 --- a/app/api/service_centers.py +++ b/app/api/service_centers.py @@ -393,6 +393,44 @@ async def request_vehicle_link( return link +@router.post("/{service_center_id}/vehicle-links/owner-attach", response_model=CarServiceLinkRead) +async def owner_attach_vehicle_link( + service_center_id: int, + payload: ServiceCenterAccessRequest, + session: AsyncSession = Depends(get_session), + current_user: User = Depends(get_current_telegram_user), +) -> CarServiceLink: + await ensure_service_center_approved(session, service_center_id) + vehicle = await session.get(Car, payload.car_id) + if vehicle is None: + raise HTTPException(status_code=404, detail="Vehicle not found") + if vehicle.owner_id != current_user.id: + raise HTTPException(status_code=403, detail="Forbidden") + link = await upsert_service_link( + session, + car_id=payload.car_id, + service_center_id=service_center_id, + requested_by_user_id=current_user.id, + access_level=payload.access_level, + external_vehicle_ref=payload.external_vehicle_ref, + status_value="approved", + ) + link.approved_by_user_id = current_user.id + link.approved_at = datetime.now(UTC) + link.revoked_at = None + await log_audit( + session, + actor=current_user, + action="car_service_link.owner_attach", + target_type="car_service_link", + target_id=link.id, + metadata={"car_id": payload.car_id, "service_center_id": service_center_id}, + ) + await session.commit() + await session.refresh(link) + return link + + @router.post("/links/{link_id}/approve", response_model=CarServiceLinkRead) async def approve_vehicle_link( link_id: int, diff --git a/tests/test_platform.py b/tests/test_platform.py index bc558c3..67b4384 100644 --- a/tests/test_platform.py +++ b/tests/test_platform.py @@ -139,6 +139,21 @@ async def test_public_service_center_and_review_flow( refreshed = await client.get(f"/api/service-centers/{center['id']}", headers=auth_headers) assert refreshed.json()["reviews_count"] == 1 + vehicle = ( + await client.post( + "/api/my/vehicles", + headers=auth_headers, + json={"name": "Review car"}, + ) + ).json() + link = await client.post( + f"/api/service-centers/{center['id']}/vehicle-links/owner-attach", + headers=auth_headers, + json={"car_id": vehicle["id"], "access_level": "basic"}, + ) + assert link.status_code == 200 + assert link.json()["status"] == "approved" + @pytest.mark.asyncio async def test_ocr_candidates_do_not_write_vehicle_data(client, auth_headers) -> None: diff --git a/web/index.html b/web/index.html index 1d30f62..f0d27bc 100644 --- a/web/index.html +++ b/web/index.html @@ -367,6 +367,7 @@
Обычный профиль не показывает панель СТО. Для бизнеса отправьте заявку на проверку.
+