from sqlalchemy import Boolean, Column, DateTime, Float, ForeignKey, Integer, String from sqlalchemy.orm import relationship from app import db from .base import SoftDeleteMixin, TimestampMixin, default_uuid class Recipe(db.Model, TimestampMixin, SoftDeleteMixin): """Рацион (рецепт) кормления.""" __tablename__ = "recipe" id = Column(String(36), primary_key=True, default=default_uuid) name = Column(String(100), nullable=False) heads_per_trip = Column(Integer, default=1) mixing_time = Column(Integer, default=0) trip_percent = Column(Float, default=100.0) dry_matter_locked = Column(Boolean, nullable=False, default=False) unloading_link_broken = Column(Boolean, nullable=False, default=False) target_component_id = Column( String(36), ForeignKey("component.id"), nullable=True, index=True ) ration_type = Column(String(10), nullable=True) version = Column(Integer, default=1, nullable=False) created_by = Column(String(50), nullable=False, default="system") updated_by = Column(String(50), nullable=False, default="system") sync_timestamp = Column(DateTime, nullable=True) sync_status = Column(String(20), default="pending", nullable=False) content_hash = Column(String(64), nullable=False, default="") ingredients = relationship( "Ingredient", back_populates="recipe", cascade="all, delete-orphan", passive_deletes=True, ) unloading_groups = relationship( "UnloadingGroup", back_populates="recipe", cascade="all, delete-orphan", passive_deletes=True, ) trips = relationship( "Trip", back_populates="recipe", cascade="all, delete-orphan", passive_deletes=True, ) period_recipes_rel = relationship( "PeriodRecipe", cascade="all, delete-orphan", passive_deletes=True, overlaps="feeding_periods", ) class UnloadingGroup(db.Model, TimestampMixin, SoftDeleteMixin): """Группа выгрузки для рецепта.""" __tablename__ = "unloading_group" id = Column(String(36), primary_key=True, default=default_uuid) name = Column(String(100), nullable=False) # 'percent' или 'heads' distribution_type = Column(String(20), nullable=False) # процент или количество голов value = Column(Float, nullable=False) # вес в кг weight = Column(Float, nullable=True) # порядок выгрузки order = Column(Integer, nullable=False) recipe_id = Column( String(36), ForeignKey("recipe.id"), nullable=False, index=True ) version = Column(Integer, default=1, nullable=False) created_by = Column(String(50), nullable=False, default="system") updated_by = Column(String(50), nullable=False, default="system") sync_timestamp = Column(DateTime, nullable=True) sync_status = Column(String(20), default="pending", nullable=False) content_hash = Column(String(64), nullable=False, default="") recipe = relationship("Recipe", back_populates="unloading_groups") class PeriodRecipe(db.Model, TimestampMixin, SoftDeleteMixin): """Связка периода кормления и рецепта.""" __tablename__ = "period_recipes" period_id = Column( String(36), ForeignKey("feeding_period.id"), primary_key=True ) recipe_id = Column( String(36), ForeignKey("recipe.id"), primary_key=True, index=True ) order = Column(Integer, nullable=False, default=0) version = Column(Integer, default=1, nullable=False) # created_at / updated_at — из TimestampMixin (БД: NOT NULL у обоих) created_by = Column(String(50), nullable=False, default="system") updated_by = Column(String(50), nullable=False, default="system") sync_timestamp = Column(DateTime, nullable=True) sync_status = Column(String(20), default="pending", nullable=False) content_hash = Column(String(64), nullable=False, default="")