Disallow empty ingredients

This commit is contained in:
jableader 2025-11-02 18:14:03 +11:00
parent a8f1050af9
commit ec9fbc071d
4 changed files with 38 additions and 0 deletions

View file

@ -242,6 +242,13 @@ async def create_meal_scoped(
) )
if not has_product and name == "" and line == "": if not has_product and name == "" and line == "":
return error_response(request, 400, "Ingredient must include a name or line or a product") return error_response(request, 400, "Ingredient must include a name or line or a product")
# quantity must be > 0
try:
q = float(getattr(ing, "quantity", 0))
except Exception:
q = getattr(ing, "quantity", 0)
if isinstance(q, (int, float)) and q <= 0:
return error_response(request, 400, "Ingredient quantity must be greater than 0")
# Proactive validation via repositories # Proactive validation via repositories
hid = household["id"] hid = household["id"]
# Validate members exist as users (do not require household membership here to preserve existing behavior/tests) # Validate members exist as users (do not require household membership here to preserve existing behavior/tests)
@ -386,6 +393,12 @@ async def update_meal_scoped(
) )
if not has_product and name == "" and line == "": if not has_product and name == "" and line == "":
return error_response(request, 400, "Ingredient must include a name or line or a product") return error_response(request, 400, "Ingredient must include a name or line or a product")
try:
q = float(getattr(ing, "quantity", 0))
except Exception:
q = getattr(ing, "quantity", 0)
if isinstance(q, (int, float)) and q <= 0:
return error_response(request, 400, "Ingredient quantity must be greater than 0")
await meals.update_meal(conn, domain_meal) await meals.update_meal(conn, domain_meal)
# Return updated state # Return updated state

View file

@ -198,6 +198,13 @@ async def create_recipe(
) )
if not has_product and name == "" and line == "": if not has_product and name == "" and line == "":
return error_response(None, 400, "Ingredient must include a name or line or a product") return error_response(None, 400, "Ingredient must include a name or line or a product")
# quantity must be > 0
try:
q = float(getattr(ing, "quantity", 0))
except Exception:
q = getattr(ing, "quantity", 0)
if isinstance(q, (int, float)) and q <= 0:
return error_response(None, 400, "Ingredient quantity must be greater than 0")
hid = household["id"] hid = household["id"]
# Build domain model and insert # Build domain model and insert
r = recipes.Recipe( r = recipes.Recipe(

View file

@ -47,6 +47,16 @@ class Ingredient(ApiModel):
return v return v
return v return v
# Quantity must be > 0
@field_validator("quantity")
@classmethod
def _positive_quantity(cls, v: float) -> float:
if v is None:
return v
if isinstance(v, (int, float)) and v <= 0:
raise ValueError("Ingredient quantity must be greater than 0")
return v
# Backward compatibility: DB may contain NULL preparation; normalize to empty string # Backward compatibility: DB may contain NULL preparation; normalize to empty string
@field_validator("preparation", mode="before") @field_validator("preparation", mode="before")
@classmethod @classmethod

View file

@ -44,6 +44,14 @@ async def insert_ingredient(conn, ingredient: Ingredient):
if not has_product and name == "" and line == "": if not has_product and name == "" and line == "":
raise ValueError("Ingredient must include a name or line or a product") raise ValueError("Ingredient must include a name or line or a product")
# Enforce positive quantity at repository layer as well
try:
q = float(ingredient.quantity)
except Exception:
q = ingredient.quantity
if isinstance(q, (int, float)) and q <= 0:
raise ValueError("Ingredient quantity must be greater than 0")
async with conn.execute( async with conn.execute(
""" """
INSERT INTO Ingredient (name, line, preparation, unit, quantity, product_id, recipe_id, meal_id) INSERT INTO Ingredient (name, line, preparation, unit, quantity, product_id, recipe_id, meal_id)