Add service platform foundation
This commit is contained in:
@@ -2,6 +2,7 @@ from datetime import date, datetime
|
||||
from decimal import Decimal
|
||||
|
||||
from sqlalchemy import (
|
||||
JSON,
|
||||
Date,
|
||||
DateTime,
|
||||
ForeignKey,
|
||||
@@ -29,6 +30,10 @@ class Car(Base):
|
||||
year: Mapped[int | None]
|
||||
plate_number: Mapped[str | None] = mapped_column(String(32))
|
||||
vin: Mapped[str | None] = mapped_column(String(32))
|
||||
license_plate_display: Mapped[str | None] = mapped_column(String(32))
|
||||
license_plate_normalized: Mapped[str | None] = mapped_column(String(32), index=True)
|
||||
license_plate_country: Mapped[str | None] = mapped_column(String(2), index=True)
|
||||
vin_normalized: Mapped[str | None] = mapped_column(String(17), unique=True, index=True)
|
||||
fuel_type: Mapped[str | None] = mapped_column(String(32))
|
||||
target_consumption_l_per_100km: Mapped[Decimal | None] = mapped_column(Numeric(6, 2))
|
||||
fuel_tank_volume_l: Mapped[Decimal | None] = mapped_column(Numeric(6, 2))
|
||||
@@ -102,13 +107,25 @@ class ServiceCenter(Base):
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
name: Mapped[str] = mapped_column(String(160), unique=True, index=True)
|
||||
legal_name: Mapped[str | None] = mapped_column(String(240))
|
||||
display_name: Mapped[str | None] = mapped_column(String(160), index=True)
|
||||
country: Mapped[str | None] = mapped_column(String(2), index=True)
|
||||
city: Mapped[str | None] = mapped_column(String(120))
|
||||
telegram_chat_id: Mapped[str | None] = mapped_column(String(80), unique=True, index=True)
|
||||
phone: Mapped[str | None] = mapped_column(String(40))
|
||||
contact_phone: Mapped[str | None] = mapped_column(String(40))
|
||||
address: Mapped[str | None] = mapped_column(String(240))
|
||||
business_registration_number: Mapped[str | None] = mapped_column(String(80))
|
||||
verification_status: Mapped[str] = mapped_column(String(24), default="pending", server_default="pending", index=True)
|
||||
owner_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), index=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
verified_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
suspended_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
|
||||
car_links = relationship("CarServiceLink", back_populates="service_center", cascade="all, delete-orphan")
|
||||
inbox_messages = relationship("ServiceInboxMessage", back_populates="service_center")
|
||||
employees = relationship("ServiceEmployee", back_populates="service_center", cascade="all, delete-orphan")
|
||||
visits = relationship("ServiceVisit", back_populates="service_center")
|
||||
|
||||
|
||||
class CarServiceLink(Base):
|
||||
@@ -140,3 +157,118 @@ class ServiceInboxMessage(Base):
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
service_center = relationship("ServiceCenter", back_populates="inbox_messages")
|
||||
|
||||
|
||||
class VehicleAccess(Base):
|
||||
__tablename__ = "vehicle_access"
|
||||
__table_args__ = (UniqueConstraint("vehicle_id", "user_id", "role", name="uq_vehicle_access_user_role"),)
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
vehicle_id: Mapped[int] = mapped_column(ForeignKey("cars.id", ondelete="CASCADE"), index=True)
|
||||
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
||||
role: Mapped[str] = mapped_column(String(24), default="owner", server_default="owner", index=True)
|
||||
status: Mapped[str] = mapped_column(String(24), default="active", server_default="active", index=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
revoked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
|
||||
|
||||
class ServiceCenterVerification(Base):
|
||||
__tablename__ = "service_center_verifications"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
service_center_id: Mapped[int] = mapped_column(ForeignKey("service_centers.id", ondelete="CASCADE"), index=True)
|
||||
submitted_documents: Mapped[list | None] = mapped_column(JSON)
|
||||
comment: Mapped[str | None] = mapped_column(Text)
|
||||
status: Mapped[str] = mapped_column(String(24), default="pending", server_default="pending", index=True)
|
||||
reviewed_by: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), index=True)
|
||||
reviewed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
|
||||
class ServiceEmployee(Base):
|
||||
__tablename__ = "service_employees"
|
||||
__table_args__ = (UniqueConstraint("service_center_id", "user_id", name="uq_service_employee_user"),)
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
service_center_id: Mapped[int] = mapped_column(ForeignKey("service_centers.id", ondelete="CASCADE"), index=True)
|
||||
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
||||
role: Mapped[str] = mapped_column(String(32), default="receptionist", server_default="receptionist", index=True)
|
||||
permissions: Mapped[dict | None] = mapped_column(JSON)
|
||||
status: Mapped[str] = mapped_column(String(24), default="active", server_default="active", index=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
service_center = relationship("ServiceCenter", back_populates="employees")
|
||||
|
||||
|
||||
class ServiceVisit(Base):
|
||||
__tablename__ = "service_visits"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
service_center_id: Mapped[int] = mapped_column(ForeignKey("service_centers.id", ondelete="CASCADE"), index=True)
|
||||
vehicle_id: Mapped[int] = mapped_column(ForeignKey("cars.id", ondelete="CASCADE"), index=True)
|
||||
created_by_employee_id: Mapped[int | None] = mapped_column(ForeignKey("service_employees.id", ondelete="SET NULL"), index=True)
|
||||
visit_date: Mapped[date] = mapped_column(Date, index=True)
|
||||
odometer: Mapped[int | None]
|
||||
status: Mapped[str] = mapped_column(String(40), default="draft", server_default="draft", index=True)
|
||||
notes: Mapped[str | None] = mapped_column(Text)
|
||||
total_cost: Mapped[Decimal | None] = mapped_column(Numeric(12, 2))
|
||||
currency: Mapped[str] = mapped_column(String(3), default="RUB", server_default="RUB")
|
||||
owner_resolved_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
||||
)
|
||||
|
||||
service_center = relationship("ServiceCenter", back_populates="visits")
|
||||
work_items = relationship("ServiceWorkItem", back_populates="visit", cascade="all, delete-orphan")
|
||||
|
||||
|
||||
class ServiceWorkItem(Base):
|
||||
__tablename__ = "service_work_items"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
service_visit_id: Mapped[int] = mapped_column(ForeignKey("service_visits.id", ondelete="CASCADE"), index=True)
|
||||
work_type: Mapped[str] = mapped_column(String(40), default="other", server_default="other", index=True)
|
||||
title: Mapped[str] = mapped_column(String(180))
|
||||
description: Mapped[str | None] = mapped_column(Text)
|
||||
parts: Mapped[list | None] = mapped_column(JSON)
|
||||
oil_brand: Mapped[str | None] = mapped_column(String(80))
|
||||
oil_viscosity: Mapped[str | None] = mapped_column(String(40))
|
||||
oil_volume: Mapped[Decimal | None] = mapped_column(Numeric(5, 2))
|
||||
next_due_odometer: Mapped[int | None]
|
||||
next_due_date: Mapped[date | None] = mapped_column(Date)
|
||||
price: Mapped[Decimal | None] = mapped_column(Numeric(12, 2))
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
visit = relationship("ServiceVisit", back_populates="work_items")
|
||||
|
||||
|
||||
class VehicleDataChangeRequest(Base):
|
||||
__tablename__ = "vehicle_data_change_requests"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
vehicle_id: Mapped[int] = mapped_column(ForeignKey("cars.id", ondelete="CASCADE"), index=True)
|
||||
requested_by_service_center_id: Mapped[int | None] = mapped_column(ForeignKey("service_centers.id", ondelete="SET NULL"), index=True)
|
||||
requested_by_employee_id: Mapped[int | None] = mapped_column(ForeignKey("service_employees.id", ondelete="SET NULL"), index=True)
|
||||
field_name: Mapped[str] = mapped_column(String(80), index=True)
|
||||
old_value: Mapped[str | None] = mapped_column(Text)
|
||||
new_value: Mapped[str | None] = mapped_column(Text)
|
||||
status: Mapped[str] = mapped_column(String(24), default="pending", server_default="pending", index=True)
|
||||
owner_user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
resolved_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
|
||||
|
||||
class AuditLog(Base):
|
||||
__tablename__ = "audit_logs"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
actor_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), index=True)
|
||||
actor_role: Mapped[str | None] = mapped_column(String(64))
|
||||
action: Mapped[str] = mapped_column(String(120), index=True)
|
||||
target_type: Mapped[str] = mapped_column(String(80), index=True)
|
||||
target_id: Mapped[str | None] = mapped_column(String(80), index=True)
|
||||
ip: Mapped[str | None] = mapped_column(String(64))
|
||||
user_agent: Mapped[str | None] = mapped_column(String(256))
|
||||
metadata_json: Mapped[dict | None] = mapped_column(JSON)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), index=True)
|
||||
|
||||
@@ -14,6 +14,7 @@ class User(Base):
|
||||
username: Mapped[str | None] = mapped_column(String(128))
|
||||
first_name: Mapped[str | None] = mapped_column(String(128))
|
||||
last_name: Mapped[str | None] = mapped_column(String(128))
|
||||
platform_role: Mapped[str] = mapped_column(String(32), default="user", server_default="user", index=True)
|
||||
locale: Mapped[str] = mapped_column(String(8), default="ru", server_default="ru")
|
||||
currency: Mapped[str] = mapped_column(String(3), default="RUB", server_default="RUB")
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
Reference in New Issue
Block a user