86 lines
3.7 KiB
Python
86 lines
3.7 KiB
Python
"""报表中心 ViewSets:
|
||
1. ReportDefinitionViewSet: 报表元数据 CRUD + run 执行报表 + init_system 预置报表
|
||
2. DashboardViewSet: 经营大盘总览指标、销售排行、库存透视
|
||
"""
|
||
|
||
from rest_framework import status
|
||
from rest_framework.decorators import action
|
||
from rest_framework.response import Response
|
||
from rest_framework.exceptions import ValidationError
|
||
from asgiref.sync import sync_to_async
|
||
from adrf.viewsets import ViewSet
|
||
|
||
from apps.core.viewset import BaseTenantViewSet
|
||
from .models import ReportDefinition
|
||
from .serializers import ReportDefinitionSerializer
|
||
from . import services as report_services
|
||
|
||
|
||
class ReportDefinitionViewSet(BaseTenantViewSet):
|
||
model = ReportDefinition
|
||
serializer_class = ReportDefinitionSerializer
|
||
search_fields = ["code", "name", "description"]
|
||
|
||
@action(detail=False, methods=["post"], url_path="init-system")
|
||
async def init_system(self, request):
|
||
"""一键初始化系统预置标准报表。"""
|
||
tenant = await self.get_tenant()
|
||
if not tenant:
|
||
raise ValidationError({"tenant": "无法识别租户"})
|
||
n = await sync_to_async(report_services.init_system_reports)(tenant)
|
||
return Response({"ok": True, "created_count": n, "message": f"成功初始化 {n} 个系统报表定义"})
|
||
|
||
@action(detail=True, methods=["post", "get"], url_path="run")
|
||
async def run_report(self, request, pk=None):
|
||
"""执行指定报表定义并返回数据。"""
|
||
tenant = await self.get_tenant()
|
||
try:
|
||
report_def = await sync_to_async(self.model.objects.get)(tenant=tenant, pk=pk)
|
||
except self.model.DoesNotExist:
|
||
return Response({"detail": "报表不存在"}, status=status.HTTP_404_NOT_FOUND)
|
||
|
||
data = await sync_to_async(report_services.execute_report_definition)(tenant, report_def)
|
||
return Response(data)
|
||
|
||
|
||
class DashboardViewSet(ViewSet):
|
||
"""进销存与财务经营大盘。"""
|
||
|
||
async def get_tenant(self):
|
||
from apps.core.viewset import resolve_tenant
|
||
from django.conf import settings
|
||
code = self.request.META.get("HTTP_X_TENANT_ID", settings.TENANT_DEFAULT)
|
||
return await sync_to_async(resolve_tenant)(code)
|
||
|
||
@action(detail=False, methods=["get"], url_path="summary")
|
||
async def summary(self, request):
|
||
"""获取本月经营总览指标(销售额、采购额、库存估值、应收应付总额)。"""
|
||
tenant = await self.get_tenant()
|
||
if not tenant:
|
||
raise ValidationError({"tenant": "无法识别租户"})
|
||
data = await sync_to_async(report_services.get_dashboard_summary)(tenant)
|
||
return Response(data)
|
||
|
||
@action(detail=False, methods=["get"], url_path="sales-rank")
|
||
async def sales_rank(self, request):
|
||
"""销售排行:参数 rank_by=product/customer,top_n=10。"""
|
||
tenant = await self.get_tenant()
|
||
if not tenant:
|
||
raise ValidationError({"tenant": "无法识别租户"})
|
||
rank_by = request.query_params.get("rank_by", "product")
|
||
try:
|
||
top_n = int(request.query_params.get("top_n", 10))
|
||
except ValueError:
|
||
top_n = 10
|
||
data = await sync_to_async(report_services.get_sales_rank)(tenant, rank_by=rank_by, top_n=top_n)
|
||
return Response({"rank_by": rank_by, "results": data})
|
||
|
||
@action(detail=False, methods=["get"], url_path="inventory-status")
|
||
async def inventory_status(self, request):
|
||
"""各仓库库存分布与资产估值。"""
|
||
tenant = await self.get_tenant()
|
||
if not tenant:
|
||
raise ValidationError({"tenant": "无法识别租户"})
|
||
data = await sync_to_async(report_services.get_inventory_status_report)(tenant)
|
||
return Response(data)
|