241 lines
8.7 KiB
Python
241 lines
8.7 KiB
Python
"""
|
|
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"]
|
|
}
|
|
]
|
|
}
|