from __future__ import annotations from typing import Any from app import db from app.lab.db_guard import retry_locked from app.lab.models import LabRationLine, LabRecipeRation from app.models import Recipe from app.models.base import default_uuid @retry_locked def upsert_ration(recipe_id: str, payload: dict[str, Any], user_id: str = "system") -> dict[str, Any]: recipe = Recipe.query.filter_by(id=recipe_id, is_deleted=False).first() if recipe is None: raise LookupError("Рецепт не найден") header = LabRecipeRation.query.filter_by(recipe_id=recipe_id).first() if header is None: header = LabRecipeRation(recipe_id=recipe_id, created_by=user_id, updated_by=user_id) db.session.add(header) header.animal_profile_id = payload.get("animalProfileId") or payload.get("animal_profile_id") if "params" in payload: params = payload.get("params") or {} if isinstance(params, dict): seeded = params.get("seeded_from") or params.get("synced_from") if seeded: header.seed_source = str(seeded) if "rationType" in payload or "ration_type" in payload: recipe.ration_type = (payload.get("rationType") or payload.get("ration_type") or "").upper() or None header.updated_by = user_id incoming = payload.get("lines") or [] existing = { str(l.id): l for l in LabRationLine.query.filter_by(recipe_id=recipe_id, is_deleted=False).all() } seen_ids: set[str] = set() for idx, row in enumerate(incoming): line_id = str(row.get("id") or "") line = existing.get(line_id) if line_id else None if line is None: line = LabRationLine( id=default_uuid(), recipe_id=recipe_id, created_by=user_id, updated_by=user_id, ) db.session.add(line) line.row_index = int(row.get("rowIndex", row.get("row_index", idx))) line.component_id = row.get("componentId") or row.get("component_id") line.ingredient_name = row.get("ingredientName") or row.get("ingredient_name") line.daily_kg = row.get("dailyKg", row.get("daily_kg")) line.in_ration = bool(row.get("inRation", row.get("in_ration", True))) line.in_compound = bool(row.get("inCompound", row.get("in_compound", False))) line.updated_by = user_id if line.id: seen_ids.add(str(line.id)) for lid, line in existing.items(): if lid not in seen_ids: line.soft_delete(user_id) db.session.commit() return {"recipeId": recipe_id, "ok": True}