first commit

This commit is contained in:
VPN SaaS Dev
2026-05-12 03:52:13 +09:00
commit d93c88c751
44 changed files with 4108 additions and 0 deletions

1
app/models/__init__.py Normal file
View File

@@ -0,0 +1 @@

55
app/models/car.py Normal file
View File

@@ -0,0 +1,55 @@
from datetime import date, datetime
from decimal import Decimal
from sqlalchemy import Date, DateTime, ForeignKey, Numeric, String, UniqueConstraint, func
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.db.base import Base
class Car(Base):
__tablename__ = "cars"
id: Mapped[int] = mapped_column(primary_key=True)
owner_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
name: Mapped[str] = mapped_column(String(160))
make: Mapped[str | None] = mapped_column(String(80))
model: Mapped[str | None] = mapped_column(String(80))
year: Mapped[int | None]
plate_number: Mapped[str | None] = mapped_column(String(32))
vin: Mapped[str | None] = mapped_column(String(32))
fuel_type: Mapped[str | None] = mapped_column(String(32))
purchase_date: Mapped[date | None] = mapped_column(Date)
purchase_price: Mapped[Decimal | None] = mapped_column(Numeric(12, 2))
current_odometer: Mapped[int | None]
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()
)
owner = relationship("User", back_populates="cars")
fuel_entries = relationship("FuelEntry", back_populates="car", cascade="all, delete-orphan")
service_entries = relationship("ServiceEntry", back_populates="car", cascade="all, delete-orphan")
class CarMake(Base):
__tablename__ = "car_makes"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(80), unique=True, index=True)
country: Mapped[str | None] = mapped_column(String(80))
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
models = relationship("CarModel", back_populates="make", cascade="all, delete-orphan")
class CarModel(Base):
__tablename__ = "car_models"
__table_args__ = (UniqueConstraint("make_id", "name", name="uq_car_models_make_name"),)
id: Mapped[int] = mapped_column(primary_key=True)
make_id: Mapped[int] = mapped_column(ForeignKey("car_makes.id", ondelete="CASCADE"), index=True)
name: Mapped[str] = mapped_column(String(100), index=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
make = relationship("CarMake", back_populates="models")

58
app/models/expense.py Normal file
View File

@@ -0,0 +1,58 @@
import enum
from datetime import date, datetime
from decimal import Decimal
from sqlalchemy import Date, DateTime, Enum, ForeignKey, Numeric, String, Text, func
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.db.base import Base
class ServiceType(str, enum.Enum):
maintenance = "maintenance"
repair = "repair"
fluid = "fluid"
tire = "tire"
inspection = "inspection"
insurance = "insurance"
tax = "tax"
other = "other"
class FuelEntry(Base):
__tablename__ = "fuel_entries"
id: Mapped[int] = mapped_column(primary_key=True)
car_id: Mapped[int] = mapped_column(ForeignKey("cars.id", ondelete="CASCADE"), index=True)
entry_date: Mapped[date] = mapped_column(Date, index=True)
odometer: Mapped[int]
liters: Mapped[Decimal] = mapped_column(Numeric(8, 3))
price_per_liter: Mapped[Decimal] = mapped_column(Numeric(10, 2))
total_cost: Mapped[Decimal] = mapped_column(Numeric(12, 2))
station: Mapped[str | None] = mapped_column(String(160))
fuel_brand: Mapped[str | None] = mapped_column(String(80))
is_full_tank: Mapped[bool] = mapped_column(default=True)
notes: Mapped[str | None] = mapped_column(Text)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
car = relationship("Car", back_populates="fuel_entries")
class ServiceEntry(Base):
__tablename__ = "service_entries"
id: Mapped[int] = mapped_column(primary_key=True)
car_id: Mapped[int] = mapped_column(ForeignKey("cars.id", ondelete="CASCADE"), 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)
title: Mapped[str] = mapped_column(String(180))
category: Mapped[str | None] = mapped_column(String(80))
vendor: Mapped[str | None] = mapped_column(String(160))
total_cost: Mapped[Decimal] = mapped_column(Numeric(12, 2))
next_due_date: Mapped[date | None] = mapped_column(Date)
next_due_odometer: Mapped[int | None]
notes: Mapped[str | None] = mapped_column(Text)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
car = relationship("Car", back_populates="service_entries")

24
app/models/user.py Normal file
View File

@@ -0,0 +1,24 @@
from datetime import datetime
from sqlalchemy import BigInteger, DateTime, String, func
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.db.base import Base
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
telegram_id: Mapped[int] = mapped_column(BigInteger, unique=True, index=True)
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))
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())
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
)
cars = relationship("Car", back_populates="owner", cascade="all, delete-orphan")