Improve the frontend

This commit is contained in:
Urtzi Alfaro
2025-10-21 19:50:07 +02:00
parent 05da20357d
commit 8d30172483
105 changed files with 14699 additions and 4630 deletions

View File

@@ -107,8 +107,30 @@ async def clone_demo_data(
)
for ingredient in base_ingredients:
# Create new ingredient with same attributes but new ID and tenant
new_ingredient_id = uuid.uuid4()
# Transform ingredient ID using XOR to ensure consistency across services
# This formula matches the suppliers service ID transformation
# Formula: virtual_ingredient_id = virtual_tenant_id XOR base_ingredient_id
base_ingredient_int = int(ingredient.id.hex, 16)
virtual_tenant_int = int(virtual_uuid.hex, 16)
base_tenant_int = int(base_uuid.hex, 16)
# Reverse the original XOR to get the base ingredient ID
# base_ingredient = base_tenant ^ base_ingredient_id
# So: base_ingredient_id = base_tenant ^ base_ingredient
base_ingredient_id_int = base_tenant_int ^ base_ingredient_int
# Now apply virtual tenant XOR to get the new ingredient ID
new_ingredient_id = uuid.UUID(int=virtual_tenant_int ^ base_ingredient_id_int)
logger.debug(
"Transforming ingredient ID using XOR",
base_ingredient_id=str(ingredient.id),
new_ingredient_id=str(new_ingredient_id),
ingredient_sku=ingredient.sku,
ingredient_name=ingredient.name
)
new_ingredient = Ingredient(
id=new_ingredient_id,
tenant_id=virtual_uuid,

View File

@@ -11,8 +11,6 @@ from typing import List, Optional, Dict, Any
from uuid import UUID
import structlog
from shared.database.transactions import transactional
from app.core.config import settings
from app.services.inventory_service import InventoryService
from app.services.food_safety_service import FoodSafetyService
@@ -58,7 +56,6 @@ class DashboardService:
'stock_movement_repo': self._stock_movement_repository or StockMovementRepository(db)
}
@transactional
async def get_inventory_dashboard_summary(
self,
db,

View File

@@ -337,15 +337,17 @@ class FoodSafetyService:
"""Get food safety dashboard data"""
try:
# Get compliance overview
compliance_query = """
from sqlalchemy import text
compliance_query = text("""
SELECT
COUNT(*) as total,
COUNT(CASE WHEN compliance_status = 'compliant' THEN 1 END) as compliant,
COUNT(CASE WHEN compliance_status = 'non_compliant' THEN 1 END) as non_compliant,
COUNT(CASE WHEN compliance_status = 'pending_review' THEN 1 END) as pending_review
COUNT(CASE WHEN compliance_status = 'COMPLIANT' THEN 1 END) as compliant,
COUNT(CASE WHEN compliance_status = 'NON_COMPLIANT' THEN 1 END) as non_compliant,
COUNT(CASE WHEN compliance_status = 'PENDING_REVIEW' THEN 1 END) as pending_review
FROM food_safety_compliance
WHERE tenant_id = :tenant_id AND is_active = true
"""
""")
compliance_result = await db.execute(compliance_query, {"tenant_id": tenant_id})
compliance_stats = compliance_result.fetchone()
@@ -355,19 +357,19 @@ class FoodSafetyService:
compliance_percentage = (compliant_items / total_compliance * 100) if total_compliance > 0 else 0
# Get temperature monitoring status
temp_query = """
temp_query = text("""
SELECT
COUNT(DISTINCT equipment_id) as sensors_online,
COUNT(CASE WHEN NOT is_within_range AND recorded_at > NOW() - INTERVAL '24 hours' THEN 1 END) as violations_24h
FROM temperature_logs
WHERE tenant_id = :tenant_id AND recorded_at > NOW() - INTERVAL '1 hour'
"""
""")
temp_result = await db.execute(temp_query, {"tenant_id": tenant_id})
temp_stats = temp_result.fetchone()
# Get expiration tracking
expiration_query = """
expiration_query = text("""
SELECT
COUNT(CASE WHEN expiration_date::date = CURRENT_DATE THEN 1 END) as expiring_today,
COUNT(CASE WHEN expiration_date BETWEEN CURRENT_DATE AND CURRENT_DATE + INTERVAL '7 days' THEN 1 END) as expiring_week,
@@ -375,20 +377,20 @@ class FoodSafetyService:
FROM stock s
JOIN ingredients i ON s.ingredient_id = i.id
WHERE i.tenant_id = :tenant_id AND s.is_available = true
"""
""")
expiration_result = await db.execute(expiration_query, {"tenant_id": tenant_id})
expiration_stats = expiration_result.fetchone()
# Get alert counts
alert_query = """
alert_query = text("""
SELECT
COUNT(CASE WHEN severity = 'high' OR severity = 'critical' THEN 1 END) as high_risk,
COUNT(CASE WHEN severity = 'critical' THEN 1 END) as critical,
COUNT(CASE WHEN regulatory_action_required AND NOT resolved_at THEN 1 END) as regulatory_pending
COUNT(CASE WHEN regulatory_action_required = true AND resolved_at IS NULL THEN 1 END) as regulatory_pending
FROM food_safety_alerts
WHERE tenant_id = :tenant_id AND status = 'active'
"""
""")
alert_result = await db.execute(alert_query, {"tenant_id": tenant_id})
alert_stats = alert_result.fetchone()
@@ -425,7 +427,9 @@ class FoodSafetyService:
async def _validate_compliance_data(self, db, compliance_data: FoodSafetyComplianceCreate):
"""Validate compliance data for business rules"""
# Check if ingredient exists
ingredient_query = "SELECT id FROM ingredients WHERE id = :ingredient_id AND tenant_id = :tenant_id"
from sqlalchemy import text
ingredient_query = text("SELECT id FROM ingredients WHERE id = :ingredient_id AND tenant_id = :tenant_id")
result = await db.execute(ingredient_query, {
"ingredient_id": compliance_data.ingredient_id,
"tenant_id": compliance_data.tenant_id
@@ -629,4 +633,4 @@ class FoodSafetyService:
except Exception as e:
logger.warning("Failed to send alert notifications",
alert_id=str(alert.id),
error=str(e))
error=str(e))

View File

@@ -546,7 +546,21 @@ class InventoryService:
ingredients = await ingredient_repo.get_ingredients_by_tenant(tenant_id, 0, 1000)
for ingredient in ingredients:
category = ingredient.category.value if ingredient.category else 'other'
# Determine category based on product type, similar to to_dict() method
category = 'other'
if ingredient.product_type and ingredient.product_type.value == 'finished_product':
# For finished products, use product_category
if ingredient.product_category:
category = ingredient.product_category.value
elif ingredient.ingredient_category and ingredient.ingredient_category.value != 'other':
category = ingredient.ingredient_category.value
else:
# For ingredients, use ingredient_category
if ingredient.ingredient_category and ingredient.ingredient_category.value != 'other':
category = ingredient.ingredient_category.value
elif ingredient.product_category:
category = ingredient.product_category.value
if category not in stock_by_category:
stock_by_category[category] = {
'count': 0,
@@ -826,4 +840,4 @@ class InventoryService:
# This is now handled in stock creation/update validation
# Add more validations as needed
pass
pass