""" Orders Service - Tenant Data Deletion Handles deletion of all order-related data for a tenant """ from typing import Dict from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, delete, func import structlog from shared.services.tenant_deletion import BaseTenantDataDeletionService, TenantDataDeletionResult from app.models.order import CustomerOrder, OrderItem, OrderStatusHistory from app.models.customer import Customer, CustomerContact logger = structlog.get_logger() class OrdersTenantDeletionService(BaseTenantDataDeletionService): """Service for deleting all orders-related data for a tenant""" def __init__(self, db_session: AsyncSession): super().__init__("orders-service") self.db = db_session async def get_tenant_data_preview(self, tenant_id: str) -> Dict[str, int]: """Get counts of what would be deleted""" try: preview = {} # Count orders order_count = await self.db.scalar( select(func.count(CustomerOrder.id)).where(CustomerOrder.tenant_id == tenant_id) ) preview["orders"] = order_count or 0 # Count order items (will be deleted via CASCADE) order_item_count = await self.db.scalar( select(func.count(OrderItem.id)) .join(CustomerOrder) .where(CustomerOrder.tenant_id == tenant_id) ) preview["order_items"] = order_item_count or 0 # Count order status history (will be deleted via CASCADE) status_history_count = await self.db.scalar( select(func.count(OrderStatusHistory.id)) .join(CustomerOrder) .where(CustomerOrder.tenant_id == tenant_id) ) preview["order_status_history"] = status_history_count or 0 # Count customers customer_count = await self.db.scalar( select(func.count(Customer.id)).where(Customer.tenant_id == tenant_id) ) preview["customers"] = customer_count or 0 # Count customer contacts (will be deleted via CASCADE) contact_count = await self.db.scalar( select(func.count(CustomerContact.id)) .join(Customer) .where(Customer.tenant_id == tenant_id) ) preview["customer_contacts"] = contact_count or 0 return preview except Exception as e: logger.error("Error getting deletion preview", tenant_id=tenant_id, error=str(e)) return {} async def delete_tenant_data(self, tenant_id: str) -> TenantDataDeletionResult: """Delete all data for a tenant""" result = TenantDataDeletionResult(tenant_id, self.service_name) try: # Get preview before deletion for reporting preview = await self.get_tenant_data_preview(tenant_id) # Delete customers (CASCADE will delete customer_contacts) try: customer_delete = await self.db.execute( delete(Customer).where(Customer.tenant_id == tenant_id) ) deleted_customers = customer_delete.rowcount result.add_deleted_items("customers", deleted_customers) # Customer contacts are deleted via CASCADE result.add_deleted_items("customer_contacts", preview.get("customer_contacts", 0)) logger.info("Deleted customers for tenant", tenant_id=tenant_id, count=deleted_customers) except Exception as e: logger.error("Error deleting customers", tenant_id=tenant_id, error=str(e)) result.add_error(f"Customer deletion: {str(e)}") # Delete orders (CASCADE will delete order_items and order_status_history) try: order_delete = await self.db.execute( delete(CustomerOrder).where(CustomerOrder.tenant_id == tenant_id) ) deleted_orders = order_delete.rowcount result.add_deleted_items("orders", deleted_orders) # Order items and status history are deleted via CASCADE result.add_deleted_items("order_items", preview.get("order_items", 0)) result.add_deleted_items("order_status_history", preview.get("order_status_history", 0)) logger.info("Deleted orders for tenant", tenant_id=tenant_id, count=deleted_orders) except Exception as e: logger.error("Error deleting orders", tenant_id=tenant_id, error=str(e)) result.add_error(f"Order deletion: {str(e)}") # Commit all deletions await self.db.commit() logger.info("Tenant data deletion completed", tenant_id=tenant_id, deleted_counts=result.deleted_counts) except Exception as e: logger.error("Fatal error during tenant data deletion", tenant_id=tenant_id, error=str(e)) await self.db.rollback() result.add_error(f"Fatal error: {str(e)}") return result