- Add is_banner, soldout fields to Car model
- Add banner toggle API (POST /hero-banners/admin/toggle/{car_id})
- Add soldout APIs (POST/DELETE /cars/{car_id}/soldout)
- Add nightly soldout checker in agent (runs at 3:00 AM)
- Update Local Cars UI with banner checkbox and status column
- Remove hero-banners admin page (functionality moved to Cars page)
- Banner cars sorted to top with purple background
- Soldout cars displayed with gray overlay
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
113 lines
4.6 KiB
Python
113 lines
4.6 KiB
Python
from sqlalchemy import Column, Integer, String, BigInteger, Boolean, ForeignKey, DateTime, Text, DECIMAL
|
|
from sqlalchemy.orm import relationship
|
|
from sqlalchemy.sql import func
|
|
from ..database import Base
|
|
|
|
|
|
class CarMaker(Base):
|
|
__tablename__ = "car_makers"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
code = Column(String(10), unique=True, nullable=False, index=True)
|
|
name = Column(String(100), nullable=False)
|
|
name_en = Column(String(100))
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
models = relationship("CarModel", back_populates="maker")
|
|
cars = relationship("Car", back_populates="maker")
|
|
|
|
|
|
class CarModel(Base):
|
|
__tablename__ = "car_models"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
code = Column(String(10), nullable=False, index=True)
|
|
maker_id = Column(Integer, ForeignKey("car_makers.id"), nullable=False)
|
|
name = Column(String(100), nullable=False)
|
|
name_en = Column(String(100))
|
|
|
|
maker = relationship("CarMaker", back_populates="models")
|
|
cars = relationship("Car", back_populates="model")
|
|
|
|
|
|
class Car(Base):
|
|
__tablename__ = "cars"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
source = Column(String(50), nullable=False, default="carmodoo")
|
|
source_id = Column(String(50), nullable=False, index=True)
|
|
source_key = Column(Text)
|
|
|
|
maker_id = Column(Integer, ForeignKey("car_makers.id"))
|
|
model_id = Column(Integer, ForeignKey("car_models.id"))
|
|
car_name = Column(String(200))
|
|
|
|
year = Column(Integer, index=True)
|
|
month = Column(Integer)
|
|
mileage = Column(Integer)
|
|
price_krw = Column(BigInteger, index=True)
|
|
margin_krw = Column(BigInteger, default=0) # Korean margin amount in KRW
|
|
margin_mn = Column(BigInteger, default=0) # Mongolian margin amount in KRW
|
|
price_usd = Column(DECIMAL(12, 2))
|
|
is_displayed = Column(Boolean, default=False, index=True) # Show to users
|
|
is_banner = Column(Boolean, default=False, index=True) # Registered as hero banner
|
|
soldout = Column(Boolean, default=False, index=True) # Sold to another buyer (checked nightly vs Carmodoo)
|
|
|
|
fuel = Column(String(20))
|
|
transmission = Column(String(20))
|
|
color = Column(String(50))
|
|
displacement = Column(Integer)
|
|
car_number = Column(String(20))
|
|
|
|
seize_count = Column(Integer, default=0)
|
|
collateral_count = Column(Integer, default=0)
|
|
|
|
check_num = Column(String(50))
|
|
dealer_name = Column(String(100))
|
|
dealer_phone = Column(String(50))
|
|
shop_name = Column(String(100))
|
|
dealer_description = Column(Text) # 딜러가 작성한 차량 상세설명 (한국어 원문)
|
|
dealer_description_en = Column(Text) # 영어 번역
|
|
dealer_description_mn = Column(Text) # 몽골어 번역
|
|
dealer_description_ru = Column(Text) # 러시아어 번역
|
|
|
|
memo = Column(Text)
|
|
|
|
status = Column(String(20), default="active", index=True)
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
|
synced_at = Column(DateTime(timezone=True))
|
|
|
|
maker = relationship("CarMaker", back_populates="cars")
|
|
model = relationship("CarModel", back_populates="cars")
|
|
images = relationship("CarImage", back_populates="car", cascade="all, delete-orphan")
|
|
options = relationship("CarOption", back_populates="car", cascade="all, delete-orphan")
|
|
# inquiries relationship disabled due to schema mismatch - use raw SQL for inquiry operations
|
|
# inquiries = relationship("Inquiry", back_populates="car")
|
|
views = relationship("CarView", back_populates="car", cascade="all, delete-orphan")
|
|
performance_check = relationship("CarPerformanceCheck", back_populates="car", uselist=False, cascade="all, delete-orphan")
|
|
specification = relationship("CarSpecification", back_populates="car", uselist=False, cascade="all, delete-orphan")
|
|
|
|
|
|
class CarImage(Base):
|
|
__tablename__ = "car_images"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
car_id = Column(Integer, ForeignKey("cars.id", ondelete="CASCADE"), nullable=False)
|
|
url = Column(String(500))
|
|
local_path = Column(String(500))
|
|
is_main = Column(Boolean, default=False)
|
|
sort_order = Column(Integer, default=0)
|
|
|
|
car = relationship("Car", back_populates="images")
|
|
|
|
|
|
class CarOption(Base):
|
|
__tablename__ = "car_options"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
car_id = Column(Integer, ForeignKey("cars.id", ondelete="CASCADE"), nullable=False)
|
|
option_name = Column(String(100))
|
|
|
|
car = relationship("Car", back_populates="options")
|