@@ -0,0 +1,76 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from app import db
|
||||
from app.lab.commands.write_audit import write_audit
|
||||
from app.lab.db_guard import retry_locked
|
||||
from app.lab.loaders.execution_loader import load_execution
|
||||
from app.lab.loaders.ration_loader import load_ration
|
||||
from app.models import Component, Ingredient, Recipe
|
||||
from app.models.base import default_uuid
|
||||
from app.services.recipe_update_service import _enqueue_recipe_children_sync
|
||||
|
||||
|
||||
@retry_locked
|
||||
def apply_from_master(recipe_id: str, user_id: str = "system") -> dict:
|
||||
snapshot = load_ration(recipe_id)
|
||||
if not snapshot.exists:
|
||||
raise LookupError("Мастер рациона не найден")
|
||||
recipe = Recipe.query.filter_by(id=recipe_id, is_deleted=False).first()
|
||||
if recipe is None:
|
||||
raise LookupError("Рецепт не найден")
|
||||
heads = int(recipe.heads_per_trip or 0)
|
||||
if heads <= 0:
|
||||
raise ValueError("Количество голов должно быть > 0")
|
||||
|
||||
execution = load_execution(recipe_id)
|
||||
by_component = {str(l.component_id): l for l in execution.lines if l.component_id}
|
||||
master_by_component = {
|
||||
str(l.component_id): l
|
||||
for l in snapshot.lines
|
||||
if l.component_id and l.in_ration
|
||||
}
|
||||
|
||||
touched_ingredient_ids: list[str] = []
|
||||
for comp_id, master_line in master_by_component.items():
|
||||
daily_kg = float(master_line.daily_kg or 0)
|
||||
weight_per_head = daily_kg / heads
|
||||
ing = by_component.get(comp_id)
|
||||
comp = Component.query.get(comp_id)
|
||||
dm_pct = float(comp.dry_matter or 0) if comp else float(master_line.dry_matter or 0)
|
||||
if ing:
|
||||
row = Ingredient.query.get(ing.ingredient_id)
|
||||
if row is None:
|
||||
continue
|
||||
row.weight_per_head = weight_per_head
|
||||
row.amount = weight_per_head
|
||||
row.dry_matter = dm_pct
|
||||
row.updated_by = user_id
|
||||
touched_ingredient_ids.append(row.id)
|
||||
else:
|
||||
comp_name = master_line.ingredient_name or comp_id
|
||||
new_ing = Ingredient(
|
||||
id=default_uuid(),
|
||||
recipe_id=recipe_id,
|
||||
component_id=comp_id,
|
||||
name=comp_name[:100],
|
||||
amount=weight_per_head,
|
||||
weight_per_head=weight_per_head,
|
||||
dry_matter=dm_pct,
|
||||
order=len(touched_ingredient_ids),
|
||||
created_by=user_id,
|
||||
updated_by=user_id,
|
||||
)
|
||||
db.session.add(new_ing)
|
||||
touched_ingredient_ids.append(new_ing.id)
|
||||
|
||||
master_ids = set(master_by_component)
|
||||
for line in execution.lines:
|
||||
if line.component_id and str(line.component_id) not in master_ids:
|
||||
row = Ingredient.query.get(line.ingredient_id)
|
||||
if row and not row.is_deleted:
|
||||
row.soft_delete(user_id)
|
||||
|
||||
_enqueue_recipe_children_sync(recipe_id)
|
||||
write_audit("APPLY_FROM_MASTER", "lab_recipe_ration", recipe_id, user_id)
|
||||
db.session.commit()
|
||||
return {"recipeId": recipe_id, "ingredientsUpdated": len(touched_ingredient_ids)}
|
||||
Reference in New Issue
Block a user