Improve the frontend and repository layer
This commit is contained in:
@@ -13,6 +13,8 @@ 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()
|
||||
@@ -22,23 +24,41 @@ route_builder = RouteBuilder('pos')
|
||||
|
||||
@router.get(
|
||||
route_builder.build_base_route("configurations"),
|
||||
response_model=dict
|
||||
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:
|
||||
return {
|
||||
"configurations": [],
|
||||
"total": 0,
|
||||
"supported_systems": ["square", "toast", "lightspeed"]
|
||||
}
|
||||
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)}")
|
||||
|
||||
@@ -14,6 +14,8 @@ 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 app.services.pos_transaction_service import POSTransactionService
|
||||
from app.services.pos_config_service import POSConfigurationService
|
||||
|
||||
router = APIRouter()
|
||||
logger = structlog.get_logger()
|
||||
@@ -74,15 +76,33 @@ async def get_sync_status(
|
||||
):
|
||||
"""Get synchronization status and recent sync history"""
|
||||
try:
|
||||
transaction_service = POSTransactionService()
|
||||
|
||||
# Get sync metrics from transaction service
|
||||
sync_metrics = await transaction_service.get_sync_metrics(tenant_id)
|
||||
|
||||
# Get last successful sync time
|
||||
sync_status = sync_metrics["sync_status"]
|
||||
last_successful_sync = sync_status.get("last_sync_at")
|
||||
|
||||
# Calculate sync success rate
|
||||
total = sync_metrics["total_transactions"]
|
||||
synced = sync_status.get("synced", 0)
|
||||
success_rate = (synced / total * 100) if total > 0 else 100.0
|
||||
|
||||
return {
|
||||
"current_sync": None,
|
||||
"last_successful_sync": None,
|
||||
"recent_syncs": [],
|
||||
"last_successful_sync": last_successful_sync.isoformat() if last_successful_sync else None,
|
||||
"recent_syncs": [], # Could be enhanced with actual sync history
|
||||
"sync_health": {
|
||||
"status": "healthy",
|
||||
"success_rate": 95.5,
|
||||
"average_duration_minutes": 3.2,
|
||||
"last_error": None
|
||||
"status": "healthy" if success_rate > 90 else "degraded" if success_rate > 70 else "unhealthy",
|
||||
"success_rate": round(success_rate, 2),
|
||||
"average_duration_minutes": 3.2, # Placeholder - could calculate from actual data
|
||||
"last_error": None,
|
||||
"total_transactions": total,
|
||||
"synced_count": synced,
|
||||
"pending_count": sync_status.get("pending", 0),
|
||||
"failed_count": sync_status.get("failed", 0)
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
@@ -159,12 +179,35 @@ async def test_pos_connection(
|
||||
):
|
||||
"""Test connection to POS system (Admin/Owner only)"""
|
||||
try:
|
||||
config_service = POSConfigurationService()
|
||||
|
||||
# Get the configuration to verify it exists
|
||||
configurations = await config_service.get_configurations_by_tenant(
|
||||
tenant_id=tenant_id,
|
||||
skip=0,
|
||||
limit=100
|
||||
)
|
||||
|
||||
config = next((c for c in configurations if str(c.id) == str(config_id)), None)
|
||||
|
||||
if not config:
|
||||
raise HTTPException(status_code=404, detail="Configuration not found")
|
||||
|
||||
# For demo purposes, we assume connection is successful if config exists
|
||||
# In production, this would actually test the POS API connection
|
||||
is_connected = config.is_connected and config.is_active
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": "Connection test successful",
|
||||
"success": is_connected,
|
||||
"status": "success" if is_connected else "failed",
|
||||
"message": f"Connection test {'successful' if is_connected else 'failed'} for {config.pos_system}",
|
||||
"tested_at": datetime.utcnow().isoformat(),
|
||||
"config_id": str(config_id)
|
||||
"config_id": str(config_id),
|
||||
"pos_system": config.pos_system,
|
||||
"health_status": config.health_status
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error("Failed to test POS connection", error=str(e),
|
||||
tenant_id=tenant_id, config_id=config_id)
|
||||
|
||||
@@ -4,15 +4,22 @@ ATOMIC layer - Basic CRUD operations for POS transactions
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Path, Query
|
||||
from typing import Optional, Dict, Any
|
||||
from typing import Optional
|
||||
from uuid import UUID
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
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
|
||||
from shared.routing import RouteBuilder
|
||||
from app.services.pos_transaction_service import POSTransactionService
|
||||
from app.schemas.pos_transaction import (
|
||||
POSTransactionResponse,
|
||||
POSTransactionListResponse,
|
||||
POSTransactionDashboardSummary
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
logger = structlog.get_logger()
|
||||
@@ -21,7 +28,7 @@ route_builder = RouteBuilder('pos')
|
||||
|
||||
@router.get(
|
||||
route_builder.build_base_route("transactions"),
|
||||
response_model=dict
|
||||
response_model=POSTransactionListResponse
|
||||
)
|
||||
@require_user_role(['viewer', 'member', 'admin', 'owner'])
|
||||
async def list_pos_transactions(
|
||||
@@ -38,20 +45,46 @@ async def list_pos_transactions(
|
||||
):
|
||||
"""List POS transactions for a tenant"""
|
||||
try:
|
||||
return {
|
||||
"transactions": [],
|
||||
"total": 0,
|
||||
"has_more": False,
|
||||
"summary": {
|
||||
"total_amount": 0,
|
||||
"transaction_count": 0,
|
||||
"sync_status": {
|
||||
"synced": 0,
|
||||
"pending": 0,
|
||||
"failed": 0
|
||||
}
|
||||
service = POSTransactionService()
|
||||
|
||||
transactions = await service.get_transactions_by_tenant(
|
||||
tenant_id=tenant_id,
|
||||
pos_system=pos_system,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
status=status,
|
||||
is_synced=is_synced,
|
||||
skip=offset,
|
||||
limit=limit
|
||||
)
|
||||
|
||||
total = await service.count_transactions_by_tenant(
|
||||
tenant_id=tenant_id,
|
||||
pos_system=pos_system,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
status=status,
|
||||
is_synced=is_synced
|
||||
)
|
||||
|
||||
# Get sync metrics for summary
|
||||
sync_metrics = await service.get_sync_metrics(tenant_id)
|
||||
|
||||
# Calculate summary
|
||||
total_amount = sum(float(t.total_amount) for t in transactions if t.status == "completed")
|
||||
|
||||
has_more = (offset + limit) < total
|
||||
|
||||
return POSTransactionListResponse(
|
||||
transactions=transactions,
|
||||
total=total,
|
||||
has_more=has_more,
|
||||
summary={
|
||||
"total_amount": total_amount,
|
||||
"transaction_count": len(transactions),
|
||||
"sync_status": sync_metrics["sync_status"]
|
||||
}
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error("Failed to list POS transactions", error=str(e), tenant_id=tenant_id)
|
||||
raise HTTPException(status_code=500, detail=f"Failed to list transactions: {str(e)}")
|
||||
@@ -59,7 +92,7 @@ async def list_pos_transactions(
|
||||
|
||||
@router.get(
|
||||
route_builder.build_resource_detail_route("transactions", "transaction_id"),
|
||||
response_model=dict
|
||||
response_model=POSTransactionResponse
|
||||
)
|
||||
@require_user_role(['viewer', 'member', 'admin', 'owner'])
|
||||
async def get_pos_transaction(
|
||||
@@ -70,13 +103,46 @@ async def get_pos_transaction(
|
||||
):
|
||||
"""Get a specific POS transaction"""
|
||||
try:
|
||||
return {
|
||||
"id": str(transaction_id),
|
||||
"tenant_id": str(tenant_id),
|
||||
"status": "completed",
|
||||
"is_synced": True
|
||||
}
|
||||
service = POSTransactionService()
|
||||
|
||||
transaction = await service.get_transaction_with_items(
|
||||
transaction_id=transaction_id,
|
||||
tenant_id=tenant_id
|
||||
)
|
||||
|
||||
if not transaction:
|
||||
raise HTTPException(status_code=404, detail="Transaction not found")
|
||||
|
||||
return transaction
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error("Failed to get POS transaction", error=str(e),
|
||||
tenant_id=tenant_id, transaction_id=transaction_id)
|
||||
raise HTTPException(status_code=500, detail=f"Failed to get transaction: {str(e)}")
|
||||
|
||||
|
||||
@router.get(
|
||||
route_builder.build_operations_route("transactions-dashboard"),
|
||||
response_model=POSTransactionDashboardSummary
|
||||
)
|
||||
@require_user_role(['viewer', 'member', 'admin', 'owner'])
|
||||
async def get_transactions_dashboard(
|
||||
tenant_id: UUID = Path(...),
|
||||
current_user: dict = Depends(get_current_user_dep),
|
||||
db=Depends(get_db)
|
||||
):
|
||||
"""Get dashboard summary for POS transactions"""
|
||||
try:
|
||||
service = POSTransactionService()
|
||||
|
||||
summary = await service.get_dashboard_summary(tenant_id)
|
||||
|
||||
logger.info("Transactions dashboard retrieved",
|
||||
tenant_id=str(tenant_id),
|
||||
total_today=summary.total_transactions_today)
|
||||
|
||||
return summary
|
||||
except Exception as e:
|
||||
logger.error("Failed to get transactions dashboard", error=str(e), tenant_id=tenant_id)
|
||||
raise HTTPException(status_code=500, detail=f"Failed to get dashboard: {str(e)}")
|
||||
|
||||
Reference in New Issue
Block a user