Implement customer deletion functionality with appropriate response model and error handling (#545)
taylor_socfortress committed
Dec 2, 2025 at 11:24 UTC
e6776459fe86250013d4f5ae927b4367bbb7be43
2 files changed
+65
-17
backend/app/customers/routes/customers.py
+43
-17
@@ -4,6 +4,7 @@ from fastapi import HTTPException
4
from fastapi import Query
5
from fastapi import Security
6
from loguru import logger
7
+from sqlalchemy.exc import IntegrityError
8
from sqlalchemy.ext.asyncio import AsyncSession
9
from sqlalchemy.future import select
10
from starlette.status import HTTP_401_UNAUTHORIZED
@@ -19,6 +20,7 @@ from app.customers.schema.customers import CustomerMetaResponse
20
from app.customers.schema.customers import CustomerRequestBody
21
from app.customers.schema.customers import CustomerResponse
22
from app.customers.schema.customers import CustomersResponse
23
+from app.customers.schema.customers import DeleteCustomerResponse
24
from app.db.db_session import get_db
25
from app.db.universal_models import Agents
26
from app.db.universal_models import Customers
@@ -351,14 +353,14 @@ async def update_customer(
353
# ! TODO - Add a check to ensure that the customer_code is not being used by any agents
354
@customers_router.delete(
355
"/{customer_code}",
354
- response_model=CustomerResponse,
356
+ response_model=DeleteCustomerResponse,
357
description="Delete customer by customer_code",
358
dependencies=[Security(AuthHandler().require_any_scope("admin"))],
359
)
360
async def delete_customer(
361
customer_code: str,
362
session: AsyncSession = Depends(get_db),
361
-) -> CustomerResponse:
363
+) -> DeleteCustomerResponse:
364
"""
365
Delete a customer by customer_code.
366
@@ -367,10 +369,10 @@ async def delete_customer(
369
session (AsyncSession, optional): The database session. Defaults to Depends(get_db).
370
371
Returns:
370
- CustomerResponse: The response containing the deleted customer data.
372
+ DeleteCustomerResponse: The response containing the deletion status.
373
374
Raises:
373
- HTTPException: If the customer with the given customer_code is not found.
375
+ HTTPException: If the customer with the given customer_code is not found or has associated users.
376
"""
377
logger.info(f"Deleting customer with customer_code: {customer_code}")
378
@@ -385,21 +387,45 @@ async def delete_customer(
387
detail=f"Customer with customer_code {customer_code} not found",
388
)
389
388
- # Capture the customer data before deleting
389
- customer_data = CustomerRequestBody.from_orm(existing_customer)
390
+ try:
391
+ # Delete the customer
392
+ await session.delete(existing_customer)
393
+ await session.flush() # Flush to trigger any constraint violations
394
+ await session.commit() # Commit the transaction
395
+ await session.close() # Close the session
396
+
397
+ logger.info(f"Successfully deleted customer with customer_code: {customer_code}")
398
+ return DeleteCustomerResponse(
399
+ success=True,
400
+ message=f"Customer '{customer_code}' deleted successfully",
401
+ )
402
391
- # Delete the customer
392
- await session.delete(existing_customer)
393
- await session.flush() # Optional: Flush the changes to the database
394
- await session.commit() # Commit the transaction
395
- # Close the session
396
- await session.close()
403
+ except IntegrityError as e:
404
+ await session.rollback()
405
+ error_message = str(e.orig)
406
398
- return CustomerResponse(
399
- customer=customer_data,
400
- success=True,
401
- message="Customer deleted successfully",
402
- )
407
+ # Check if it's the user_customer_access foreign key constraint
408
+ if "user_customer_access" in error_message and "FOREIGN KEY" in error_message:
409
+ logger.error(f"Cannot delete customer {customer_code}: users are still assigned to this customer")
410
+ raise HTTPException(
411
+ status_code=400,
412
+ detail=f"Cannot delete customer '{customer_code}'. There are still users assigned to this customer. Please remove all user assignments before deleting the customer.",
413
+ )
414
+
415
+ # For other integrity errors
416
+ logger.error(f"Integrity error deleting customer {customer_code}: {error_message}")
417
+ raise HTTPException(
418
+ status_code=400,
419
+ detail="Cannot delete customer due to existing dependencies. Please ensure all related data is removed first.",
420
+ )
421
+
422
+ except Exception as e:
423
+ await session.rollback()
424
+ logger.error(f"Error deleting customer {customer_code}: {str(e)}")
425
+ raise HTTPException(
426
+ status_code=500,
427
+ detail=f"An error occurred while deleting the customer: {str(e)}",
428
+ )
429
430
431
@customers_router.post(
backend/app/customers/schema/customers.py
+22
@@ -152,3 +152,25 @@ class AgentsResponse(BaseModel):
152
agents: Optional[List[AgentModel]] = Field([], description="List of agents")
153
success: bool
154
message: str
155
+
156
+
157
+class DeleteCustomerResponse(BaseModel):
158
+ """
159
+ Response model for customer deletion.
160
+ """
161
+
162
+ success: bool
163
+ message: str
164
+
165
+ class Config:
166
+ """
167
+ Pydantic configuration class.
168
+ """
169
+
170
+ from_attributes = True
171
+ json_schema_extra = {
172
+ "example": {
173
+ "success": True,
174
+ "message": "Customer 'customer_code' deleted successfully",
175
+ },
176
+ }