Improve demo seed
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -18,11 +18,13 @@ class DeliveryMethod(enum.Enum):
|
||||
"""Order delivery methods"""
|
||||
DELIVERY = "delivery"
|
||||
PICKUP = "pickup"
|
||||
STANDARD = "standard" # Standard delivery method
|
||||
|
||||
|
||||
class PaymentTerms(enum.Enum):
|
||||
"""Payment terms for customers and orders"""
|
||||
IMMEDIATE = "immediate"
|
||||
NET_15 = "net_15"
|
||||
NET_30 = "net_30"
|
||||
NET_60 = "net_60"
|
||||
|
||||
@@ -31,6 +33,8 @@ class PaymentMethod(enum.Enum):
|
||||
"""Payment methods for orders"""
|
||||
CASH = "cash"
|
||||
CARD = "card"
|
||||
CREDIT_CARD = "credit_card" # Credit card payment
|
||||
CHECK = "check" # Bank check/cheque payment
|
||||
BANK_TRANSFER = "bank_transfer"
|
||||
ACCOUNT = "account"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user