feat: plantilla base workspace SaaS
This commit is contained in:
7
backend/api/v1/modules/core/dashboard/__init__.py
Normal file
7
backend/api/v1/modules/core/dashboard/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
"""
|
||||
Módulo de dashboard para estadísticas y métricas empresariales
|
||||
"""
|
||||
|
||||
from .routes import router
|
||||
|
||||
__all__ = ["router"]
|
||||
109
backend/api/v1/modules/core/dashboard/dto.py
Normal file
109
backend/api/v1/modules/core/dashboard/dto.py
Normal file
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
DTOs para el dashboard de estadísticas y métricas empresariales
|
||||
"""
|
||||
|
||||
from typing import Dict, List, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class KPIMetric(BaseModel):
|
||||
"""Métrica individual de KPI"""
|
||||
|
||||
label: str = Field(..., description="Nombre del indicador")
|
||||
value: int | float = Field(..., description="Valor actual")
|
||||
previous_value: Optional[int | float] = Field(
|
||||
None, description="Valor anterior para comparación"
|
||||
)
|
||||
percentage_change: Optional[float] = Field(None, description="Porcentaje de cambio")
|
||||
trend: Optional[str] = Field(None, description="up, down, stable")
|
||||
unit: Optional[str] = Field(None, description="Unidad de medida (%, USD, etc)")
|
||||
|
||||
|
||||
class ActivityItem(BaseModel):
|
||||
"""Item de actividad reciente"""
|
||||
|
||||
id: int
|
||||
type: str = Field(
|
||||
..., description="Tipo de actividad: invoice, pedimento, client, etc"
|
||||
)
|
||||
title: str = Field(..., description="Título descriptivo")
|
||||
description: Optional[str] = Field(None, description="Descripción adicional")
|
||||
timestamp: datetime
|
||||
status: Optional[str] = Field(None, description="Estado del item")
|
||||
icon: Optional[str] = Field(None, description="Icono a mostrar")
|
||||
|
||||
|
||||
class ChartDataPoint(BaseModel):
|
||||
"""Punto de datos para gráficas"""
|
||||
|
||||
label: str
|
||||
value: float
|
||||
category: Optional[str] = None
|
||||
|
||||
|
||||
class DashboardStats(BaseModel):
|
||||
"""Estadísticas generales del dashboard"""
|
||||
|
||||
# KPIs principales
|
||||
total_invoices: KPIMetric
|
||||
total_pedimentos: KPIMetric
|
||||
total_clients: KPIMetric
|
||||
total_providers: KPIMetric
|
||||
active_items: KPIMetric
|
||||
pending_approvals: KPIMetric
|
||||
|
||||
# Estadísticas financieras
|
||||
total_value_imports: Optional[float] = Field(
|
||||
None, description="Valor total de importaciones"
|
||||
)
|
||||
total_value_exports: Optional[float] = Field(
|
||||
None, description="Valor total de exportaciones"
|
||||
)
|
||||
|
||||
# Datos para gráficas
|
||||
invoices_by_month: List[ChartDataPoint] = Field(default_factory=list)
|
||||
pedimentos_by_month: List[ChartDataPoint] = Field(default_factory=list)
|
||||
operations_by_type: List[ChartDataPoint] = Field(default_factory=list)
|
||||
top_clients: List[ChartDataPoint] = Field(default_factory=list)
|
||||
top_providers: List[ChartDataPoint] = Field(default_factory=list)
|
||||
|
||||
# Actividad reciente
|
||||
recent_activity: List[ActivityItem] = Field(default_factory=list)
|
||||
|
||||
# Metadata
|
||||
generated_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
company_id: int
|
||||
company_name: Optional[str] = None
|
||||
|
||||
|
||||
class OperationsOverview(BaseModel):
|
||||
"""Vista general de operaciones"""
|
||||
|
||||
total_operations: int
|
||||
by_type: Dict[str, int] = Field(default_factory=dict)
|
||||
by_status: Dict[str, int] = Field(default_factory=dict)
|
||||
avg_processing_time: Optional[float] = Field(
|
||||
None, description="Tiempo promedio en días"
|
||||
)
|
||||
|
||||
|
||||
class InventoryMetrics(BaseModel):
|
||||
"""Métricas de inventario"""
|
||||
|
||||
total_items: int
|
||||
items_in_stock: int
|
||||
items_low_stock: int
|
||||
total_value: Optional[float] = None
|
||||
by_category: Dict[str, int] = Field(default_factory=dict)
|
||||
|
||||
|
||||
class ComplianceMetrics(BaseModel):
|
||||
"""Métricas de cumplimiento normativo"""
|
||||
|
||||
pending_documents: int
|
||||
expired_permits: int
|
||||
upcoming_deadlines: int
|
||||
compliance_score: Optional[float] = Field(
|
||||
None, description="Score de cumplimiento 0-100"
|
||||
)
|
||||
84
backend/api/v1/modules/core/dashboard/routes.py
Normal file
84
backend/api/v1/modules/core/dashboard/routes.py
Normal file
@@ -0,0 +1,84 @@
|
||||
"""
|
||||
Endpoints del dashboard para estadísticas y métricas empresariales
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.database import get_core_db
|
||||
from core.security import get_current_user, validate_access_to_resource
|
||||
|
||||
from .dto import DashboardStats, OperationsOverview, InventoryMetrics
|
||||
from .service import DashboardService
|
||||
|
||||
router = APIRouter(prefix="/dashboard", tags=["Dashboard"])
|
||||
|
||||
|
||||
@router.get("/stats", response_model=DashboardStats)
|
||||
async def get_dashboard_stats(
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
Obtiene estadísticas completas del dashboard para la compañía especificada.
|
||||
|
||||
Incluye:
|
||||
- KPIs principales (facturas, pedimentos, clientes, proveedores, items)
|
||||
- Gráficas de tendencias (facturas por mes, operaciones por tipo)
|
||||
- Top clientes y proveedores
|
||||
- Actividad reciente
|
||||
"""
|
||||
# Validar acceso
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
|
||||
# Generar estadísticas
|
||||
service = DashboardService(db, tenant_id, company_id)
|
||||
stats = service.get_complete_dashboard_stats()
|
||||
|
||||
return stats
|
||||
|
||||
|
||||
@router.get("/operations-overview", response_model=OperationsOverview)
|
||||
async def get_operations_overview(
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
Obtiene una vista general de las operaciones
|
||||
"""
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
|
||||
service = DashboardService(db, tenant_id, company_id)
|
||||
|
||||
# Implementación básica
|
||||
ops_by_type = service.get_operations_by_type()
|
||||
|
||||
return OperationsOverview(
|
||||
total_operations=sum(int(op.value) for op in ops_by_type),
|
||||
by_type={op.label: int(op.value) for op in ops_by_type},
|
||||
by_status={},
|
||||
)
|
||||
|
||||
|
||||
@router.get("/inventory-metrics", response_model=InventoryMetrics)
|
||||
async def get_inventory_metrics(
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
Obtiene métricas de inventario
|
||||
"""
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
|
||||
service = DashboardService(db, tenant_id, company_id)
|
||||
items_kpi = service.get_items_metrics()
|
||||
|
||||
return InventoryMetrics(
|
||||
total_items=int(items_kpi.value),
|
||||
items_in_stock=int(items_kpi.value), # Simplificado
|
||||
items_low_stock=0,
|
||||
by_category={},
|
||||
)
|
||||
43
backend/api/v1/modules/core/dashboard/service.py
Normal file
43
backend/api/v1/modules/core/dashboard/service.py
Normal file
@@ -0,0 +1,43 @@
|
||||
"""
|
||||
Servicio del dashboard — STUB.
|
||||
Implementa las métricas de tu proyecto aquí.
|
||||
"""
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from .dto import DashboardStats, KPIMetric, OperationsOverview, InventoryMetrics
|
||||
|
||||
|
||||
class DashboardService:
|
||||
"""Stub — reemplaza con las consultas de tu proyecto."""
|
||||
|
||||
def __init__(self, db: Session, tenant_id: int, company_id: int):
|
||||
self.db = db
|
||||
self.tenant_id = tenant_id
|
||||
self.company_id = company_id
|
||||
|
||||
def get_stats(self) -> DashboardStats:
|
||||
empty_kpi = KPIMetric(label="", value=0, trend="stable")
|
||||
return DashboardStats(
|
||||
company_id=self.company_id,
|
||||
generated_at="",
|
||||
total_invoices=empty_kpi,
|
||||
total_pedimentos=empty_kpi,
|
||||
total_clients=empty_kpi,
|
||||
total_providers=empty_kpi,
|
||||
active_items=empty_kpi,
|
||||
pending_approvals=empty_kpi,
|
||||
invoices_by_month=[],
|
||||
operations_by_type=[],
|
||||
top_clients=[],
|
||||
top_providers=[],
|
||||
recent_activity=[],
|
||||
)
|
||||
|
||||
def get_operations_overview(self) -> OperationsOverview:
|
||||
return OperationsOverview(total_operations=0, by_type={}, by_status={})
|
||||
|
||||
def get_inventory_metrics(self) -> InventoryMetrics:
|
||||
return InventoryMetrics(
|
||||
total_items=0, items_in_stock=0, items_low_stock=0, by_category={}
|
||||
)
|
||||
Reference in New Issue
Block a user