Mechanic's work place
Some checks failed
ci / test (push) Has been cancelled

This commit is contained in:
VPN SaaS Dev
2026-05-16 10:04:56 +09:00
parent fec9635079
commit 83ad880b9d
39 changed files with 2951 additions and 74 deletions

View File

@@ -264,6 +264,10 @@ class ServiceEmployee(Base):
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)
invite_token: Mapped[str | None] = mapped_column(String(96), unique=True, index=True)
invite_expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
invite_revoked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
activated_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
service_center = relationship("ServiceCenter", back_populates="employees")
@@ -311,15 +315,35 @@ class ServiceVisit(Base):
__tablename__ = "service_visits"
id: Mapped[int] = mapped_column(primary_key=True)
work_order_number: Mapped[str | None] = mapped_column(String(40), unique=True, index=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)
owner_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), index=True)
created_by_employee_id: Mapped[int | None] = mapped_column(ForeignKey("service_employees.id", ondelete="SET NULL"), index=True)
assigned_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)
customer_complaint: Mapped[str | None] = mapped_column(Text)
diagnosis: Mapped[str | None] = mapped_column(Text)
notes: Mapped[str | None] = mapped_column(Text)
service_comment: Mapped[str | None] = mapped_column(Text)
owner_comment: Mapped[str | None] = mapped_column(Text)
recommendations_text: Mapped[str | None] = mapped_column(Text)
attachment_urls: Mapped[list | None] = mapped_column(JSON)
total_cost: Mapped[Decimal | None] = mapped_column(Numeric(12, 2))
labor_total: Mapped[Decimal] = mapped_column(Numeric(12, 2), default=0, server_default="0")
product_total: Mapped[Decimal] = mapped_column(Numeric(12, 2), default=0, server_default="0")
discount_total: Mapped[Decimal] = mapped_column(Numeric(12, 2), default=0, server_default="0")
final_total: Mapped[Decimal] = mapped_column(Numeric(12, 2), default=0, server_default="0")
price_approved_total: Mapped[Decimal | None] = mapped_column(Numeric(12, 2))
approval_required: Mapped[bool] = mapped_column(Boolean, default=False, server_default="false")
version: Mapped[int] = mapped_column(Integer, default=1, server_default="1")
completed_snapshot: Mapped[dict | None] = mapped_column(JSON)
currency: Mapped[str] = mapped_column(String(3), default="RUB", server_default="RUB")
opened_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
approved_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
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(
@@ -328,6 +352,9 @@ class ServiceVisit(Base):
service_center = relationship("ServiceCenter", back_populates="visits")
work_items = relationship("ServiceWorkItem", back_populates="visit", cascade="all, delete-orphan")
product_items = relationship("ServiceProductItem", back_populates="visit", cascade="all, delete-orphan")
status_history = relationship("WorkOrderStatusHistory", back_populates="visit", cascade="all, delete-orphan")
corrections = relationship("WorkOrderCorrection", back_populates="visit", cascade="all, delete-orphan")
class MaintenanceRecommendation(Base):
@@ -395,7 +422,12 @@ class ServiceNotification(Base):
notification_type: Mapped[str] = mapped_column(String(80), index=True)
title: Mapped[str] = mapped_column(String(180))
body: Mapped[str | None] = mapped_column(Text)
status: Mapped[str] = mapped_column(String(24), default="unread", server_default="unread", index=True)
status: Mapped[str] = mapped_column(String(24), default="pending", server_default="pending", index=True)
retry_count: Mapped[int] = mapped_column(Integer, default=0, server_default="0")
last_error: Mapped[str | None] = mapped_column(Text)
idempotency_key: Mapped[str | None] = mapped_column(String(160), unique=True, index=True)
sent_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
read_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), index=True)
@@ -406,7 +438,13 @@ class ServiceWorkItem(Base):
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))
category: Mapped[str | None] = mapped_column(String(80))
description: Mapped[str | None] = mapped_column(Text)
quantity: Mapped[Decimal] = mapped_column(Numeric(10, 3), default=1, server_default="1")
unit: Mapped[str] = mapped_column(String(24), default="pcs", server_default="pcs")
unit_price: Mapped[Decimal | None] = mapped_column(Numeric(12, 2))
discount: Mapped[Decimal] = mapped_column(Numeric(12, 2), default=0, server_default="0")
total: Mapped[Decimal | None] = mapped_column(Numeric(12, 2))
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))
@@ -414,11 +452,85 @@ class ServiceWorkItem(Base):
next_due_odometer: Mapped[int | None]
next_due_date: Mapped[date | None] = mapped_column(Date)
price: Mapped[Decimal | None] = mapped_column(Numeric(12, 2))
warranty_days: Mapped[int | None] = mapped_column(Integer)
warranty_odometer_km: Mapped[int | None] = mapped_column(Integer)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
visit = relationship("ServiceVisit", back_populates="work_items")
class ServiceProductItem(Base):
__tablename__ = "service_product_items"
id: Mapped[int] = mapped_column(primary_key=True)
service_visit_id: Mapped[int] = mapped_column(ForeignKey("service_visits.id", ondelete="CASCADE"), index=True)
title: Mapped[str] = mapped_column(String(180))
category: Mapped[str | None] = mapped_column(String(80), index=True)
product_type: Mapped[str] = mapped_column(String(40), default="other", server_default="other", index=True)
brand: Mapped[str | None] = mapped_column(String(80))
sku: Mapped[str | None] = mapped_column(String(120))
quantity: Mapped[Decimal] = mapped_column(Numeric(10, 3), default=1, server_default="1")
unit: Mapped[str] = mapped_column(String(24), default="pcs", server_default="pcs")
unit_price: Mapped[Decimal] = mapped_column(Numeric(12, 2), default=0, server_default="0")
discount: Mapped[Decimal] = mapped_column(Numeric(12, 2), default=0, server_default="0")
total: Mapped[Decimal] = mapped_column(Numeric(12, 2), default=0, server_default="0")
volume: Mapped[Decimal | None] = mapped_column(Numeric(8, 3))
viscosity: Mapped[str | None] = mapped_column(String(40))
specification: Mapped[str | None] = mapped_column(String(120))
used_volume: Mapped[Decimal | None] = mapped_column(Numeric(8, 3))
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
visit = relationship("ServiceVisit", back_populates="product_items")
class WorkOrderStatusHistory(Base):
__tablename__ = "work_order_status_history"
id: Mapped[int] = mapped_column(primary_key=True)
service_visit_id: Mapped[int] = mapped_column(ForeignKey("service_visits.id", ondelete="CASCADE"), index=True)
from_status: Mapped[str | None] = mapped_column(String(40))
to_status: Mapped[str] = mapped_column(String(40), index=True)
changed_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), index=True)
comment: Mapped[str | None] = mapped_column(Text)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), index=True)
visit = relationship("ServiceVisit", back_populates="status_history")
class WorkOrderCorrection(Base):
__tablename__ = "work_order_corrections"
id: Mapped[int] = mapped_column(primary_key=True)
service_visit_id: Mapped[int] = mapped_column(ForeignKey("service_visits.id", ondelete="CASCADE"), index=True)
requested_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), index=True)
reason: Mapped[str] = mapped_column(Text)
proposed_changes: Mapped[dict | None] = mapped_column(JSON)
status: Mapped[str] = mapped_column(String(24), default="pending", server_default="pending", index=True)
owner_approval_required: Mapped[bool] = mapped_column(Boolean, default=True, server_default="true")
created_version: Mapped[int] = mapped_column(Integer, default=1, server_default="1")
resolved_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), index=True)
visit = relationship("ServiceVisit", back_populates="corrections")
class InventoryTransaction(Base):
__tablename__ = "inventory_transactions"
id: Mapped[int] = mapped_column(primary_key=True)
service_center_id: Mapped[int] = mapped_column(ForeignKey("service_centers.id", ondelete="CASCADE"), index=True)
service_visit_id: Mapped[int | None] = mapped_column(ForeignKey("service_visits.id", ondelete="SET NULL"), index=True)
product_item_id: Mapped[int | None] = mapped_column(ForeignKey("service_product_items.id", ondelete="SET NULL"), index=True)
transaction_type: Mapped[str] = mapped_column(String(32), index=True)
sku: Mapped[str | None] = mapped_column(String(120), index=True)
title: Mapped[str | None] = mapped_column(String(180))
quantity: Mapped[Decimal] = mapped_column(Numeric(10, 3), default=0, server_default="0")
unit: Mapped[str] = mapped_column(String(24), default="pcs", server_default="pcs")
actor_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), index=True)
metadata_json: Mapped[dict | None] = mapped_column(JSON)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), index=True)
class ServiceCenterReview(Base):
__tablename__ = "service_center_reviews"
__table_args__ = (UniqueConstraint("service_center_id", "user_id", name="uq_service_review_user"),)

View File

@@ -66,6 +66,7 @@ class ServiceEntry(Base):
id: Mapped[int] = mapped_column(primary_key=True)
car_id: Mapped[int] = mapped_column(ForeignKey("cars.id", ondelete="CASCADE"), index=True)
service_visit_id: Mapped[int | None] = mapped_column(ForeignKey("service_visits.id", ondelete="SET NULL"), index=True)
entry_date: Mapped[date] = mapped_column(Date, index=True)
odometer: Mapped[int | None]
service_type: Mapped[ServiceType] = mapped_column(Enum(ServiceType), index=True)
@@ -86,6 +87,7 @@ class ExpenseEntry(Base):
id: Mapped[int] = mapped_column(primary_key=True)
car_id: Mapped[int] = mapped_column(ForeignKey("cars.id", ondelete="CASCADE"), index=True)
service_visit_id: Mapped[int | None] = mapped_column(ForeignKey("service_visits.id", ondelete="SET NULL"), index=True)
entry_date: Mapped[date] = mapped_column(Date, index=True)
category: Mapped[ExpenseCategory] = mapped_column(Enum(ExpenseCategory), index=True)
title: Mapped[str] = mapped_column(String(180))