Improve demo seed

This commit is contained in:
Urtzi Alfaro
2025-10-17 07:31:14 +02:00
parent b6cb800758
commit d4060962e4
56 changed files with 8235 additions and 339 deletions

View File

@@ -17,6 +17,8 @@ from app.core.database import get_db
from app.models.order import CustomerOrder, OrderItem
from app.models.procurement import ProcurementPlan, ProcurementRequirement
from app.models.customer import Customer
from shared.utils.demo_dates import adjust_date_for_demo, BASE_REFERENCE_DATE
from shared.utils.alert_generator import generate_order_alerts
logger = structlog.get_logger()
router = APIRouter(prefix="/internal/demo", tags=["internal"])
@@ -43,6 +45,7 @@ async def clone_demo_data(
virtual_tenant_id: str,
demo_account_type: str,
session_id: Optional[str] = None,
session_created_at: Optional[str] = None,
db: AsyncSession = Depends(get_db),
_: bool = Depends(verify_internal_api_key)
):
@@ -66,12 +69,22 @@ async def clone_demo_data(
"""
start_time = datetime.now(timezone.utc)
# Parse session creation time for date adjustment
if session_created_at:
try:
session_time = datetime.fromisoformat(session_created_at.replace('Z', '+00:00'))
except (ValueError, AttributeError):
session_time = start_time
else:
session_time = start_time
logger.info(
"Starting orders data cloning",
base_tenant_id=base_tenant_id,
virtual_tenant_id=virtual_tenant_id,
demo_account_type=demo_account_type,
session_id=session_id
session_id=session_id,
session_created_at=session_created_at
)
try:
@@ -85,7 +98,8 @@ async def clone_demo_data(
"customer_orders": 0,
"order_line_items": 0,
"procurement_plans": 0,
"procurement_requirements": 0
"procurement_requirements": 0,
"alerts_generated": 0
}
# Customer ID mapping (old -> new)
@@ -110,23 +124,36 @@ async def clone_demo_data(
new_customer = Customer(
id=new_customer_id,
tenant_id=virtual_uuid,
customer_name=customer.customer_name,
customer_type=customer.customer_type,
customer_code=customer.customer_code,
name=customer.name,
business_name=customer.business_name,
contact_person=customer.contact_person,
customer_type=customer.customer_type,
tax_id=customer.tax_id,
email=customer.email,
phone=customer.phone,
address=customer.address,
tax_id=customer.tax_id,
credit_limit=customer.credit_limit,
payment_terms=customer.payment_terms,
discount_percentage=customer.discount_percentage,
address_line1=customer.address_line1,
address_line2=customer.address_line2,
city=customer.city,
state=customer.state,
postal_code=customer.postal_code,
country=customer.country,
business_license=customer.business_license,
is_active=customer.is_active,
notes=customer.notes,
tags=customer.tags,
metadata_=customer.metadata_,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
preferred_delivery_method=customer.preferred_delivery_method,
payment_terms=customer.payment_terms,
credit_limit=customer.credit_limit,
discount_percentage=customer.discount_percentage,
customer_segment=customer.customer_segment,
priority_level=customer.priority_level,
special_instructions=customer.special_instructions,
delivery_preferences=customer.delivery_preferences,
product_preferences=customer.product_preferences,
total_orders=customer.total_orders,
total_spent=customer.total_spent,
average_order_value=customer.average_order_value,
last_order_date=customer.last_order_date,
created_at=session_time,
updated_at=session_time
)
db.add(new_customer)
stats["customers"] += 1
@@ -143,20 +170,32 @@ async def clone_demo_data(
base_tenant=str(base_uuid)
)
# Calculate date offset
if base_orders:
max_date = max(order.order_date for order in base_orders)
today = datetime.now(timezone.utc)
date_offset = today - max_date
else:
date_offset = timedelta(days=0)
order_id_map = {}
for order in base_orders:
new_order_id = uuid.uuid4()
order_id_map[order.id] = new_order_id
# Adjust dates using demo_dates utility
adjusted_order_date = adjust_date_for_demo(
order.order_date, session_time, BASE_REFERENCE_DATE
)
adjusted_requested_delivery = adjust_date_for_demo(
order.requested_delivery_date, session_time, BASE_REFERENCE_DATE
)
adjusted_confirmed_delivery = adjust_date_for_demo(
order.confirmed_delivery_date, session_time, BASE_REFERENCE_DATE
)
adjusted_actual_delivery = adjust_date_for_demo(
order.actual_delivery_date, session_time, BASE_REFERENCE_DATE
)
adjusted_window_start = adjust_date_for_demo(
order.delivery_window_start, session_time, BASE_REFERENCE_DATE
)
adjusted_window_end = adjust_date_for_demo(
order.delivery_window_end, session_time, BASE_REFERENCE_DATE
)
new_order = CustomerOrder(
id=new_order_id,
tenant_id=virtual_uuid,
@@ -165,28 +204,30 @@ async def clone_demo_data(
status=order.status,
order_type=order.order_type,
priority=order.priority,
order_date=order.order_date + date_offset if order.order_date else None,
requested_delivery_date=order.requested_delivery_date + date_offset if order.requested_delivery_date else None,
confirmed_delivery_date=order.confirmed_delivery_date + date_offset if order.confirmed_delivery_date else None,
actual_delivery_date=order.actual_delivery_date + date_offset if order.actual_delivery_date else None,
order_date=adjusted_order_date,
requested_delivery_date=adjusted_requested_delivery,
confirmed_delivery_date=adjusted_confirmed_delivery,
actual_delivery_date=adjusted_actual_delivery,
delivery_method=order.delivery_method,
delivery_address=order.delivery_address,
delivery_instructions=order.delivery_instructions,
delivery_window_start=order.delivery_window_start + date_offset if order.delivery_window_start else None,
delivery_window_end=order.delivery_window_end + date_offset if order.delivery_window_end else None,
delivery_window_start=adjusted_window_start,
delivery_window_end=adjusted_window_end,
subtotal=order.subtotal,
tax_amount=order.tax_amount,
discount_amount=order.discount_amount,
discount_percentage=order.discount_percentage,
delivery_fee=order.delivery_fee,
total_amount=order.total_amount,
payment_status=order.payment_status,
payment_method=order.payment_method,
notes=order.notes,
internal_notes=order.internal_notes,
tags=order.tags,
metadata_=order.metadata_,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
payment_terms=order.payment_terms,
payment_due_date=order.payment_due_date,
special_instructions=order.special_instructions,
order_source=order.order_source,
sales_channel=order.sales_channel,
created_at=session_time,
updated_at=session_time
)
db.add(new_order)
stats["customer_orders"] += 1
@@ -202,16 +243,15 @@ async def clone_demo_data(
new_item = OrderItem(
id=uuid.uuid4(),
order_id=new_order_id,
product_id=item.product_id, # Keep product reference
product_id=item.product_id,
product_name=item.product_name,
product_sku=item.product_sku,
quantity=item.quantity,
unit_of_measure=item.unit_of_measure,
unit_price=item.unit_price,
subtotal=item.subtotal,
discount_amount=item.discount_amount,
tax_amount=item.tax_amount,
total_amount=item.total_amount,
notes=item.notes,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
line_discount=item.line_discount,
line_total=item.line_total,
status=item.status
)
db.add(new_item)
stats["order_line_items"] += 1
@@ -247,9 +287,9 @@ async def clone_demo_data(
id=new_plan_id,
tenant_id=virtual_uuid,
plan_number=f"PROC-{uuid.uuid4().hex[:8].upper()}",
plan_date=plan.plan_date + plan_date_offset.days if plan.plan_date else None,
plan_period_start=plan.plan_period_start + plan_date_offset.days if plan.plan_period_start else None,
plan_period_end=plan.plan_period_end + plan_date_offset.days if plan.plan_period_end else None,
plan_date=plan.plan_date + plan_date_offset if plan.plan_date else None,
plan_period_start=plan.plan_period_start + plan_date_offset if plan.plan_period_start else None,
plan_period_end=plan.plan_period_end + plan_date_offset if plan.plan_period_end else None,
planning_horizon_days=plan.planning_horizon_days,
status=plan.status,
plan_type=plan.plan_type,
@@ -260,7 +300,6 @@ async def clone_demo_data(
total_estimated_cost=plan.total_estimated_cost,
total_approved_cost=plan.total_approved_cost,
cost_variance=plan.cost_variance,
notes=plan.notes,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
@@ -270,32 +309,91 @@ async def clone_demo_data(
# Clone Procurement Requirements
for old_plan_id, new_plan_id in plan_id_map.items():
result = await db.execute(
select(ProcurementRequirement).where(ProcurementRequirement.procurement_plan_id == old_plan_id)
select(ProcurementRequirement).where(ProcurementRequirement.plan_id == old_plan_id)
)
requirements = result.scalars().all()
for req in requirements:
new_req = ProcurementRequirement(
id=uuid.uuid4(),
procurement_plan_id=new_plan_id,
ingredient_id=req.ingredient_id, # Keep ingredient reference
plan_id=new_plan_id,
requirement_number=req.requirement_number,
product_id=req.product_id,
product_name=req.product_name,
product_sku=req.product_sku,
product_category=req.product_category,
product_type=req.product_type,
required_quantity=req.required_quantity,
unit_of_measure=req.unit_of_measure,
safety_stock_quantity=req.safety_stock_quantity,
total_quantity_needed=req.total_quantity_needed,
current_stock_level=req.current_stock_level,
reserved_stock=req.reserved_stock,
available_stock=req.available_stock,
net_requirement=req.net_requirement,
order_demand=req.order_demand,
production_demand=req.production_demand,
forecast_demand=req.forecast_demand,
buffer_demand=req.buffer_demand,
preferred_supplier_id=req.preferred_supplier_id,
backup_supplier_id=req.backup_supplier_id,
supplier_name=req.supplier_name,
supplier_lead_time_days=req.supplier_lead_time_days,
minimum_order_quantity=req.minimum_order_quantity,
estimated_unit_cost=req.estimated_unit_cost,
estimated_total_cost=req.estimated_total_cost,
required_by_date=req.required_by_date + plan_date_offset.days if req.required_by_date else None,
last_purchase_cost=req.last_purchase_cost,
cost_variance=req.cost_variance,
required_by_date=req.required_by_date + plan_date_offset if req.required_by_date else None,
lead_time_buffer_days=req.lead_time_buffer_days,
suggested_order_date=req.suggested_order_date + plan_date_offset if req.suggested_order_date else None,
latest_order_date=req.latest_order_date + plan_date_offset if req.latest_order_date else None,
quality_specifications=req.quality_specifications,
special_requirements=req.special_requirements,
storage_requirements=req.storage_requirements,
shelf_life_days=req.shelf_life_days,
status=req.status,
priority=req.priority,
source=req.source,
notes=req.notes,
risk_level=req.risk_level,
purchase_order_id=req.purchase_order_id,
purchase_order_number=req.purchase_order_number,
ordered_quantity=req.ordered_quantity,
ordered_at=req.ordered_at,
expected_delivery_date=req.expected_delivery_date + plan_date_offset if req.expected_delivery_date else None,
actual_delivery_date=req.actual_delivery_date + plan_date_offset if req.actual_delivery_date else None,
received_quantity=req.received_quantity,
delivery_status=req.delivery_status,
fulfillment_rate=req.fulfillment_rate,
on_time_delivery=req.on_time_delivery,
quality_rating=req.quality_rating,
source_orders=req.source_orders,
source_production_batches=req.source_production_batches,
demand_analysis=req.demand_analysis,
approved_quantity=req.approved_quantity,
approved_cost=req.approved_cost,
approved_at=req.approved_at,
approved_by=req.approved_by,
procurement_notes=req.procurement_notes,
supplier_communication=req.supplier_communication,
requirement_metadata=req.requirement_metadata,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
db.add(new_req)
stats["procurement_requirements"] += 1
# Commit all changes
# Commit cloned data first
await db.commit()
# Generate order alerts (urgent, delayed, upcoming deliveries)
try:
alerts_count = await generate_order_alerts(db, virtual_uuid, session_time)
stats["alerts_generated"] += alerts_count
await db.commit()
logger.info(f"Generated {alerts_count} order alerts")
except Exception as alert_error:
logger.warning(f"Alert generation failed: {alert_error}", exc_info=True)
total_records = sum(stats.values())
duration_ms = int((datetime.now(timezone.utc) - start_time).total_seconds() * 1000)