demo seed change 3
This commit is contained in:
@@ -315,7 +315,7 @@ async def clone_demo_data_internal(
|
||||
records_cloned += 1
|
||||
|
||||
# Clone stock batches
|
||||
for stock_data in seed_data.get('stock_batches', []):
|
||||
for stock_data in seed_data.get('stock', []):
|
||||
# Transform ID - handle both UUID and string IDs
|
||||
from shared.utils.demo_id_transformer import transform_id
|
||||
try:
|
||||
@@ -358,6 +358,40 @@ async def clone_demo_data_internal(
|
||||
# Remove original id and tenant_id from stock_data to avoid conflict
|
||||
stock_data.pop('id', None)
|
||||
stock_data.pop('tenant_id', None)
|
||||
# Remove notes field as it doesn't exist in the Stock model
|
||||
stock_data.pop('notes', None)
|
||||
|
||||
# Transform ingredient_id to match transformed ingredient IDs
|
||||
if 'ingredient_id' in stock_data:
|
||||
ingredient_id_str = stock_data['ingredient_id']
|
||||
try:
|
||||
ingredient_uuid = UUID(ingredient_id_str)
|
||||
transformed_ingredient_id = transform_id(ingredient_id_str, tenant_uuid)
|
||||
stock_data['ingredient_id'] = str(transformed_ingredient_id)
|
||||
except ValueError as e:
|
||||
logger.error("Failed to transform ingredient_id",
|
||||
original_ingredient_id=ingredient_id_str,
|
||||
error=str(e))
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Invalid ingredient_id format: {str(e)}"
|
||||
)
|
||||
|
||||
# Transform supplier_id if present
|
||||
if 'supplier_id' in stock_data:
|
||||
supplier_id_str = stock_data['supplier_id']
|
||||
try:
|
||||
supplier_uuid = UUID(supplier_id_str)
|
||||
transformed_supplier_id = transform_id(supplier_id_str, tenant_uuid)
|
||||
stock_data['supplier_id'] = str(transformed_supplier_id)
|
||||
except ValueError as e:
|
||||
logger.error("Failed to transform supplier_id",
|
||||
original_supplier_id=supplier_id_str,
|
||||
error=str(e))
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Invalid supplier_id format: {str(e)}"
|
||||
)
|
||||
|
||||
# Create stock batch
|
||||
stock = Stock(
|
||||
@@ -368,88 +402,16 @@ async def clone_demo_data_internal(
|
||||
db.add(stock)
|
||||
records_cloned += 1
|
||||
|
||||
# Add deterministic edge case stock records
|
||||
edge_times = calculate_edge_case_times(session_time)
|
||||
|
||||
# Get sample ingredients for edge cases (flour and dairy)
|
||||
flour_ingredient_id = None
|
||||
dairy_ingredient_id = None
|
||||
for ing in seed_data.get('ingredients', []):
|
||||
if ing.get('ingredient_category') == 'FLOUR' and not flour_ingredient_id and 'id' in ing:
|
||||
from shared.utils.demo_id_transformer import transform_id
|
||||
flour_ingredient_id = str(transform_id(ing['id'], UUID(virtual_tenant_id)))
|
||||
elif ing.get('ingredient_category') == 'DAIRY' and not dairy_ingredient_id and 'id' in ing:
|
||||
from shared.utils.demo_id_transformer import transform_id
|
||||
dairy_ingredient_id = str(transform_id(ing['id'], UUID(virtual_tenant_id)))
|
||||
|
||||
# Edge Case 1: Expiring Soon Stock (expires in 2 days)
|
||||
if flour_ingredient_id:
|
||||
expiring_stock = Stock(
|
||||
id=str(uuid.uuid4()),
|
||||
tenant_id=str(virtual_tenant_id),
|
||||
inventory_product_id=flour_ingredient_id,
|
||||
batch_number=f"{session_id[:8]}-EDGE-EXPIRING",
|
||||
quantity=25.0,
|
||||
received_date=session_time - timedelta(days=12),
|
||||
expiration_date=session_time + timedelta(days=2),
|
||||
best_before_date=session_time + timedelta(days=2),
|
||||
supplier_id=None,
|
||||
purchase_order_id=None,
|
||||
lot_number=f"LOT-EXPIRING-{session_id[:8]}",
|
||||
storage_location="Almacén A - Estante 3",
|
||||
quality_grade="GOOD",
|
||||
notes="⚠️ EDGE CASE: Expires in 2 days - triggers orange 'Caducidad próxima' alert"
|
||||
)
|
||||
db.add(expiring_stock)
|
||||
records_cloned += 1
|
||||
|
||||
# Edge Case 2: Low Stock (below reorder point)
|
||||
if dairy_ingredient_id:
|
||||
low_stock = Stock(
|
||||
id=str(uuid.uuid4()),
|
||||
tenant_id=str(virtual_tenant_id),
|
||||
inventory_product_id=dairy_ingredient_id,
|
||||
batch_number=f"{session_id[:8]}-EDGE-LOWSTOCK",
|
||||
quantity=3.0,
|
||||
received_date=session_time - timedelta(days=5),
|
||||
expiration_date=session_time + timedelta(days=10),
|
||||
best_before_date=session_time + timedelta(days=10),
|
||||
supplier_id=None,
|
||||
purchase_order_id=None,
|
||||
lot_number=f"LOT-LOWSTOCK-{session_id[:8]}",
|
||||
storage_location="Cámara Fría 1",
|
||||
quality_grade="GOOD",
|
||||
notes="⚠️ EDGE CASE: Below reorder point - triggers inventory alert if no pending PO"
|
||||
)
|
||||
db.add(low_stock)
|
||||
records_cloned += 1
|
||||
|
||||
# Edge Case 3: Just Received Stock (received today)
|
||||
if flour_ingredient_id:
|
||||
fresh_stock = Stock(
|
||||
id=str(uuid.uuid4()),
|
||||
tenant_id=str(virtual_tenant_id),
|
||||
inventory_product_id=flour_ingredient_id,
|
||||
batch_number=f"{session_id[:8]}-EDGE-FRESH",
|
||||
quantity=200.0,
|
||||
received_date=session_time - timedelta(hours=2),
|
||||
expiration_date=session_time + timedelta(days=180),
|
||||
best_before_date=session_time + timedelta(days=180),
|
||||
supplier_id=None,
|
||||
purchase_order_id=None,
|
||||
lot_number=f"LOT-FRESH-{session_id[:8]}",
|
||||
storage_location="Almacén A - Estante 1",
|
||||
quality_grade="EXCELLENT",
|
||||
notes="⚠️ EDGE CASE: Just received 2 hours ago - shows as new stock"
|
||||
)
|
||||
db.add(fresh_stock)
|
||||
records_cloned += 1
|
||||
|
||||
# Note: Edge cases are now handled exclusively through JSON seed data
|
||||
# The seed data files already contain comprehensive edge cases including:
|
||||
# - Low stock items below reorder points
|
||||
# - Items expiring soon
|
||||
# - Freshly received stock
|
||||
# This ensures standardization and single source of truth for demo data
|
||||
|
||||
logger.info(
|
||||
"Added deterministic edge case stock records",
|
||||
edge_cases_added=3,
|
||||
expiring_date=(session_time + timedelta(days=2)).isoformat(),
|
||||
low_stock_qty=3.0
|
||||
"Edge cases handled by JSON seed data - no manual creation needed",
|
||||
seed_data_edge_cases="low_stock, expiring_soon, fresh_stock"
|
||||
)
|
||||
|
||||
await db.commit()
|
||||
@@ -462,7 +424,7 @@ async def clone_demo_data_internal(
|
||||
records_cloned=records_cloned,
|
||||
duration_ms=duration_ms,
|
||||
ingredients_cloned=len(seed_data.get('ingredients', [])),
|
||||
stock_batches_cloned=len(seed_data.get('stock_batches', []))
|
||||
stock_batches_cloned=len(seed_data.get('stock', []))
|
||||
)
|
||||
|
||||
return {
|
||||
@@ -472,7 +434,7 @@ async def clone_demo_data_internal(
|
||||
"duration_ms": duration_ms,
|
||||
"details": {
|
||||
"ingredients": len(seed_data.get('ingredients', [])),
|
||||
"stock_batches": len(seed_data.get('stock_batches', [])),
|
||||
"stock": len(seed_data.get('stock', [])),
|
||||
"virtual_tenant_id": str(virtual_tenant_id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ async def trigger_safety_stock_optimization(
|
||||
|
||||
try:
|
||||
# Fetch sales data for this product
|
||||
sales_response = await sales_client.get_sales_by_product(
|
||||
sales_response = await sales_client.get_sales_data(
|
||||
tenant_id=tenant_id,
|
||||
product_id=product_id,
|
||||
start_date=start_date.strftime('%Y-%m-%d'),
|
||||
|
||||
@@ -212,6 +212,9 @@ class DashboardService:
|
||||
ingredients = await repos['ingredient_repo'].get_ingredients_by_tenant(tenant_id, limit=1000)
|
||||
stock_summary = await repos['stock_repo'].get_stock_summary_by_tenant(tenant_id)
|
||||
|
||||
# Get dashboard repository
|
||||
dashboard_repo = repos['dashboard_repo']
|
||||
|
||||
# Get current stock levels for all ingredients using repository
|
||||
ingredient_stock_levels = {}
|
||||
try:
|
||||
@@ -693,6 +696,9 @@ class DashboardService:
|
||||
try:
|
||||
repos = self._get_repositories(db)
|
||||
|
||||
# Get dashboard repository
|
||||
dashboard_repo = repos['dashboard_repo']
|
||||
|
||||
# Get stock summary for total costs
|
||||
stock_summary = await repos['stock_repo'].get_stock_summary_by_tenant(tenant_id)
|
||||
total_inventory_cost = Decimal(str(stock_summary['total_stock_value']))
|
||||
@@ -703,7 +709,7 @@ class DashboardService:
|
||||
# Get current stock levels for all ingredients using repository
|
||||
ingredient_stock_levels = {}
|
||||
try:
|
||||
ingredient_stock_levels = await repos['dashboard_repo'].get_ingredient_stock_levels(tenant_id)
|
||||
ingredient_stock_levels = await dashboard_repo.get_ingredient_stock_levels(tenant_id)
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not fetch current stock levels for cost analysis: {e}")
|
||||
|
||||
|
||||
@@ -199,9 +199,14 @@ class InventoryScheduler:
|
||||
alerts_generated += 1
|
||||
|
||||
except Exception as e:
|
||||
# Ensure ingredient_id is converted to string for logging to prevent UUID issues
|
||||
ingredient_id_val = shortage.get("ingredient_id", "unknown")
|
||||
if hasattr(ingredient_id_val, '__str__') and not isinstance(ingredient_id_val, str):
|
||||
ingredient_id_val = str(ingredient_id_val)
|
||||
|
||||
logger.error(
|
||||
"Error emitting critical stock shortage alert",
|
||||
ingredient_id=shortage.get("ingredient_id", "unknown"),
|
||||
ingredient_id=ingredient_id_val,
|
||||
error=str(e)
|
||||
)
|
||||
continue
|
||||
@@ -531,10 +536,15 @@ class InventoryScheduler:
|
||||
alerts_generated += 1
|
||||
|
||||
except Exception as e:
|
||||
# Ensure ingredient_id is converted to string for logging to prevent UUID issues
|
||||
ingredient_id_val = shortage.get("id", "unknown")
|
||||
if hasattr(ingredient_id_val, '__str__') and not isinstance(ingredient_id_val, str):
|
||||
ingredient_id_val = str(ingredient_id_val)
|
||||
|
||||
logger.error(
|
||||
"Error emitting critical stock shortage alert",
|
||||
tenant_id=str(tenant_id),
|
||||
ingredient_id=shortage.get("id", "unknown"),
|
||||
ingredient_id=ingredient_id_val,
|
||||
error=str(e)
|
||||
)
|
||||
continue
|
||||
@@ -744,10 +754,19 @@ class InventoryScheduler:
|
||||
alerts_generated += 1
|
||||
|
||||
except Exception as e:
|
||||
# Ensure ingredient_id and tenant_id are converted to strings for logging to prevent UUID issues
|
||||
ingredient_id_val = shortage.get("id", "unknown")
|
||||
if hasattr(ingredient_id_val, '__str__') and not isinstance(ingredient_id_val, str):
|
||||
ingredient_id_val = str(ingredient_id_val)
|
||||
|
||||
tenant_id_val = shortage.get("tenant_id", "unknown")
|
||||
if hasattr(tenant_id_val, '__str__') and not isinstance(tenant_id_val, str):
|
||||
tenant_id_val = str(tenant_id_val)
|
||||
|
||||
logger.error(
|
||||
"Error emitting critical stock shortage alert",
|
||||
ingredient_id=shortage.get("id", "unknown"),
|
||||
tenant_id=shortage.get("tenant_id", "unknown"),
|
||||
ingredient_id=ingredient_id_val,
|
||||
tenant_id=tenant_id_val,
|
||||
error=str(e)
|
||||
)
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user