""" POS Configuration API Endpoints ATOMIC layer - Basic CRUD operations for POS configurations """ from fastapi import APIRouter, Depends, HTTPException, Path, Query from typing import List, Optional, Dict, Any from uuid import UUID import structlog from app.core.database import get_db from shared.auth.decorators import get_current_user_dep from shared.auth.access_control import require_user_role, admin_role_required from shared.routing import RouteBuilder from shared.security import create_audit_logger, AuditSeverity, AuditAction from app.services.pos_config_service import POSConfigurationService from app.schemas.pos_config import POSConfigurationListResponse router = APIRouter() logger = structlog.get_logger() audit_logger = create_audit_logger("pos-service") route_builder = RouteBuilder('pos') @router.get( route_builder.build_base_route("configurations"), response_model=POSConfigurationListResponse ) @require_user_role(['viewer', 'member', 'admin', 'owner']) async def list_pos_configurations( tenant_id: UUID = Path(...), pos_system: Optional[str] = Query(None), is_active: Optional[bool] = Query(None), skip: int = Query(0, ge=0), limit: int = Query(100, ge=1, le=100), current_user: dict = Depends(get_current_user_dep), db=Depends(get_db) ): """List all POS configurations for a tenant""" try: service = POSConfigurationService() configurations = await service.get_configurations_by_tenant( tenant_id=tenant_id, pos_system=pos_system, is_active=is_active, skip=skip, limit=limit ) total = await service.count_configurations_by_tenant( tenant_id=tenant_id, pos_system=pos_system, is_active=is_active ) return POSConfigurationListResponse( configurations=configurations, total=total, supported_systems=["square", "toast", "lightspeed"] ) except Exception as e: logger.error("Failed to list POS configurations", error=str(e), tenant_id=tenant_id) raise HTTPException(status_code=500, detail=f"Failed to list configurations: {str(e)}") @router.post( route_builder.build_base_route("configurations"), response_model=dict, status_code=201 ) @admin_role_required async def create_pos_configuration( configuration_data: Dict[str, Any], tenant_id: UUID = Path(...), current_user: dict = Depends(get_current_user_dep), db=Depends(get_db) ): """Create a new POS configuration (Admin/Owner only)""" try: logger.info("Creating POS configuration", tenant_id=tenant_id, pos_system=configuration_data.get("pos_system"), user_id=current_user.get("user_id")) return { "message": "POS configuration created successfully", "id": "placeholder", "pos_system": configuration_data.get("pos_system") } except Exception as e: logger.error("Failed to create POS configuration", error=str(e), tenant_id=tenant_id) raise HTTPException(status_code=500, detail=f"Failed to create configuration: {str(e)}") @router.get( route_builder.build_resource_detail_route("configurations", "config_id"), response_model=dict ) @require_user_role(['viewer', 'member', 'admin', 'owner']) async def get_pos_configuration( tenant_id: UUID = Path(...), config_id: UUID = Path(...), current_user: dict = Depends(get_current_user_dep), db=Depends(get_db) ): """Get a specific POS configuration""" try: return { "id": str(config_id), "tenant_id": str(tenant_id), "pos_system": "square", "is_active": True } except Exception as e: logger.error("Failed to get POS configuration", error=str(e), tenant_id=tenant_id, config_id=config_id) raise HTTPException(status_code=500, detail=f"Failed to get configuration: {str(e)}") @router.put( route_builder.build_resource_detail_route("configurations", "config_id"), response_model=dict ) @admin_role_required async def update_pos_configuration( configuration_data: Dict[str, Any], tenant_id: UUID = Path(...), config_id: UUID = Path(...), current_user: dict = Depends(get_current_user_dep), db=Depends(get_db) ): """Update a POS configuration (Admin/Owner only)""" try: # Log HIGH severity audit event for configuration changes try: await audit_logger.log_event( db_session=db, tenant_id=str(tenant_id), user_id=current_user["user_id"], action=AuditAction.UPDATE.value, resource_type="pos_configuration", resource_id=str(config_id), severity=AuditSeverity.HIGH.value, description=f"Admin {current_user.get('email', 'unknown')} updated POS configuration", changes={"configuration_updates": configuration_data}, endpoint=f"/configurations/{config_id}", method="PUT" ) except Exception as audit_error: logger.warning("Failed to log audit event", error=str(audit_error)) logger.info("POS configuration updated", config_id=str(config_id), tenant_id=str(tenant_id), user_id=current_user["user_id"]) return {"message": "Configuration updated successfully", "id": str(config_id)} except Exception as e: logger.error("Failed to update POS configuration", error=str(e), tenant_id=tenant_id, config_id=config_id) raise HTTPException(status_code=500, detail=f"Failed to update configuration: {str(e)}") @router.delete( route_builder.build_resource_detail_route("configurations", "config_id"), response_model=dict ) @require_user_role(['owner']) async def delete_pos_configuration( tenant_id: UUID = Path(...), config_id: UUID = Path(...), current_user: dict = Depends(get_current_user_dep), db=Depends(get_db) ): """Delete a POS configuration (Owner only)""" try: # Log CRITICAL severity audit event for configuration deletion try: await audit_logger.log_deletion( db_session=db, tenant_id=str(tenant_id), user_id=current_user["user_id"], resource_type="pos_configuration", resource_id=str(config_id), severity=AuditSeverity.CRITICAL.value, description=f"Owner {current_user.get('email', 'unknown')} deleted POS configuration", endpoint=f"/configurations/{config_id}", method="DELETE" ) except Exception as audit_error: logger.warning("Failed to log audit event", error=str(audit_error)) logger.info("POS configuration deleted", config_id=str(config_id), tenant_id=str(tenant_id), user_id=current_user["user_id"]) return {"message": "Configuration deleted successfully"} except Exception as e: logger.error("Failed to delete POS configuration", error=str(e), tenant_id=tenant_id, config_id=config_id) raise HTTPException(status_code=500, detail=f"Failed to delete configuration: {str(e)}") # ============================================================================ # Reference Data # ============================================================================ @router.get( route_builder.build_global_route("supported-systems"), response_model=dict ) async def get_supported_pos_systems(): """Get list of supported POS systems (no tenant context required)""" return { "systems": [ { "id": "square", "name": "Square POS", "description": "Square Point of Sale system", "features": ["payments", "inventory", "analytics", "webhooks"], "supported_regions": ["US", "CA", "AU", "JP", "GB", "IE", "ES", "FR"] }, { "id": "toast", "name": "Toast POS", "description": "Toast restaurant POS system", "features": ["orders", "payments", "menu_management", "webhooks"], "supported_regions": ["US", "CA", "IE", "ES"] }, { "id": "lightspeed", "name": "Lightspeed Restaurant", "description": "Lightspeed restaurant management system", "features": ["orders", "inventory", "reservations", "webhooks"], "supported_regions": ["US", "CA", "EU", "AU"] } ] }