Fix enum mismatch: Update Python enums and seed data to match database uppercase values

- Fixed ProductType enum values from lowercase to uppercase (INGREDIENT, FINISHED_PRODUCT)
- Fixed UnitOfMeasure enum values from lowercase/abbreviated to uppercase (KILOGRAMS, LITERS, etc.)
- Fixed IngredientCategory enum values from lowercase to uppercase (FLOUR, YEAST, etc.)
- Fixed ProductCategory enum values from lowercase to uppercase (BREAD, CROISSANTS, etc.)
- Updated seed data files to use correct uppercase enum values
- Fixed hardcoded enum references throughout the codebase
- This resolves the InvalidTextRepresentationError when inserting inventory data

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
Urtzi Alfaro
2025-12-13 16:49:04 +01:00
parent e116ac244c
commit 10c779858a
7 changed files with 1354 additions and 47 deletions

View File

@@ -26,8 +26,8 @@ class DashboardRepository:
query = text("""
SELECT
COUNT(*) as total_ingredients,
COUNT(CASE WHEN product_type::text = 'finished_product' THEN 1 END) as finished_products,
COUNT(CASE WHEN product_type::text = 'ingredient' THEN 1 END) as raw_ingredients,
COUNT(CASE WHEN product_type::text = 'FINISHED_PRODUCT' THEN 1 END) as finished_products,
COUNT(CASE WHEN product_type::text = 'INGREDIENT' THEN 1 END) as raw_ingredients,
COUNT(DISTINCT st.supplier_id) as supplier_count,
AVG(CASE WHEN s.available_quantity IS NOT NULL THEN s.available_quantity ELSE 0 END) as avg_stock_level
FROM ingredients i

View File

@@ -40,7 +40,7 @@ class IngredientRepository(BaseRepository[Ingredient, IngredientCreate, Ingredie
ingredient_name=create_data.get('name'),
tenant_id=tenant_id
)
product_type_value = 'ingredient'
product_type_value = 'INGREDIENT'
if 'product_type' in create_data:
from app.models.inventory import ProductType
@@ -73,7 +73,7 @@ class IngredientRepository(BaseRepository[Ingredient, IngredientCreate, Ingredie
if 'category' in create_data:
category_value = create_data.pop('category')
if product_type_value == 'finished_product' or product_type_value == 'FINISHED_PRODUCT':
if product_type_value == 'FINISHED_PRODUCT':
# Map to product_category for finished products
from app.models.inventory import ProductCategory
if category_value:
@@ -166,15 +166,15 @@ class IngredientRepository(BaseRepository[Ingredient, IngredientCreate, Ingredie
# Handle category mapping based on product type
if 'category' in update_data:
category_value = update_data.pop('category')
product_type_value = update_data.get('product_type', 'ingredient')
product_type_value = update_data.get('product_type', 'INGREDIENT')
# Get current product if we need to determine type
if 'product_type' not in update_data:
current_record = await self.get_by_id(record_id)
if current_record:
product_type_value = current_record.product_type.value if current_record.product_type else 'ingredient'
product_type_value = current_record.product_type.value if current_record.product_type else 'INGREDIENT'
if product_type_value == 'finished_product' or product_type_value == 'FINISHED_PRODUCT':
if product_type_value == 'FINISHED_PRODUCT':
# Map to product_category for finished products
from app.models.inventory import ProductCategory
if category_value:
@@ -559,4 +559,110 @@ class IngredientRepository(BaseRepository[Ingredient, IngredientCreate, Ingredie
except Exception as e:
logger.error("Failed to get active tenants from ingredients", error=str(e))
return []
return []
async def get_critical_stock_shortages(self) -> List[Dict[str, Any]]:
"""
Get critical stock shortages across all tenants using CTE analysis.
Returns ingredients that are critically low on stock.
"""
try:
from sqlalchemy import text
query = text("""
WITH stock_analysis AS (
SELECT
i.id as ingredient_id,
i.name as ingredient_name,
i.tenant_id,
i.reorder_point,
COALESCE(SUM(s.current_quantity), 0) as current_quantity,
i.low_stock_threshold,
GREATEST(0, i.low_stock_threshold - COALESCE(SUM(s.current_quantity), 0)) as shortage_amount,
CASE
WHEN COALESCE(SUM(s.current_quantity), 0) < i.low_stock_threshold THEN 'critical'
WHEN COALESCE(SUM(s.current_quantity), 0) < i.low_stock_threshold * 1.2 THEN 'low'
ELSE 'normal'
END as status
FROM ingredients i
LEFT JOIN stock s ON s.ingredient_id = i.id AND s.is_available = true
WHERE i.is_active = true
GROUP BY i.id, i.name, i.tenant_id, i.reorder_point, i.low_stock_threshold
)
SELECT
ingredient_id,
ingredient_name,
tenant_id,
current_quantity,
reorder_point,
shortage_amount
FROM stock_analysis
WHERE status = 'critical'
ORDER BY shortage_amount DESC
""")
result = await self.session.execute(query)
rows = result.fetchall()
shortages = []
for row in rows:
shortages.append({
'ingredient_id': row.ingredient_id,
'ingredient_name': row.ingredient_name,
'tenant_id': row.tenant_id,
'current_quantity': float(row.current_quantity) if row.current_quantity else 0,
'required_quantity': float(row.reorder_point) if row.reorder_point else 0,
'shortage_amount': float(row.shortage_amount) if row.shortage_amount else 0
})
return shortages
except Exception as e:
logger.error("Failed to get critical stock shortages", error=str(e))
raise
async def get_stock_issues(self, tenant_id: UUID) -> List[Dict[str, Any]]:
"""
Get stock level issues with CTE analysis for a specific tenant
Returns list of critical, low, and overstock situations
"""
try:
from sqlalchemy import text
query = text("""
WITH stock_analysis AS (
SELECT
i.id, i.name, i.tenant_id,
COALESCE(SUM(s.current_quantity), 0) as current_stock,
i.low_stock_threshold as minimum_stock,
i.max_stock_level as maximum_stock,
i.reorder_point,
0 as tomorrow_needed,
0 as avg_daily_usage,
7 as lead_time_days,
CASE
WHEN COALESCE(SUM(s.current_quantity), 0) < i.low_stock_threshold THEN 'critical'
WHEN COALESCE(SUM(s.current_quantity), 0) < i.low_stock_threshold * 1.2 THEN 'low'
WHEN i.max_stock_level IS NOT NULL AND COALESCE(SUM(s.current_quantity), 0) > i.max_stock_level THEN 'overstock'
ELSE 'normal'
END as status,
GREATEST(0, i.low_stock_threshold - COALESCE(SUM(s.current_quantity), 0)) as shortage_amount
FROM ingredients i
LEFT JOIN stock s ON s.ingredient_id = i.id AND s.is_available = true
WHERE i.tenant_id = :tenant_id AND i.is_active = true
GROUP BY i.id, i.name, i.tenant_id, i.low_stock_threshold, i.max_stock_level, i.reorder_point
)
SELECT * FROM stock_analysis WHERE status != 'normal'
ORDER BY
CASE status
WHEN 'critical' THEN 1
WHEN 'low' THEN 2
WHEN 'overstock' THEN 3
END,
shortage_amount DESC
""")
result = await self.session.execute(query, {"tenant_id": tenant_id})
return [dict(row._mapping) for row in result.fetchall()]
except Exception as e:
logger.error("Failed to get stock issues", error=str(e), tenant_id=str(tenant_id))
raise