"""打印中心 async ViewSets。 - PrintTemplateViewSet:模板 CRUD - PrintSettingsViewSet:抬头读取/更新(单例) - render_document_view:渲染整页打印 HTML(前端 axios 取回后 window.print()) """ from asgiref.sync import sync_to_async from adrf.decorators import api_view from rest_framework import status from rest_framework.response import Response from rest_framework.exceptions import ValidationError, NotFound from apps.core.viewset import BaseTenantViewSet from .models import PrintSettings, PrintTemplate from .serializers import PrintSettingsSerializer, PrintTemplateSerializer from . import services as printing_services class PrintTemplateViewSet(BaseTenantViewSet): model = PrintTemplate serializer_class = PrintTemplateSerializer search_fields = ["code", "name", "doc_type"] @api_view(["GET", "PUT", "PATCH"]) async def print_settings_detail(request): """打印抬头:租户单例。GET 读取 / PUT(整更) / PATCH(部分更)。""" from apps.core.viewset import resolve_tenant def _resolve(): return resolve_tenant(request.META.get("HTTP_X_TENANT_ID", "")) tenant = await sync_to_async(_resolve)() if tenant is None: raise ValidationError({"tenant": "无法识别租户"}) obj = await sync_to_async(printing_services.ensure_settings)(tenant) if request.method == "GET": serializer = PrintSettingsSerializer(obj) data = await serializer.adata if hasattr(serializer, "adata") else serializer.data return Response(serializer.data) partial = request.method == "PATCH" serializer = PrintSettingsSerializer(obj, data=request.data, partial=partial) await sync_to_async(serializer.is_valid)(raise_exception=True) await sync_to_async(serializer.save)( updated_by=request.user if request.user.is_authenticated else None ) return Response(serializer.data) @api_view(["GET"]) async def render_document_view(request, doc_type, document_id): """GET /api/v1/printing/render///?template=code&autoprint=0 返回整页 HTML(Content-Type: text/html)。鉴权走 JWT + 租户中间件。 """ from django.http import HttpResponse from apps.core.viewset import resolve_tenant if doc_type not in printing_services.DOC_CONTEXT_BUILDERS: raise ValidationError({"doc_type": f"不支持的文档类型 {doc_type}"}) def _resolve(): code = request.META.get("HTTP_X_TENANT_ID", "") return resolve_tenant(code) tenant = await sync_to_async(_resolve)() if tenant is None: raise ValidationError({"tenant": "无法识别租户"}) module_name, model_name, _ = printing_services.DOC_CONTEXT_BUILDERS[doc_type] model = getattr(__import__(module_name, fromlist=[model_name]), model_name) def _load_doc(): qs = model.objects.filter(tenant=tenant, pk=document_id) if doc_type == "sales_bill": return qs.select_related( "customer", "warehouse", ).prefetch_related( "lines__product__base_unit", "lines__source_unit", ).first() return qs.select_related( "supplier", "warehouse", ).prefetch_related( "lines__product__base_unit", "lines__source_unit", ).first() document = await sync_to_async(_load_doc)() if document is None: raise NotFound("document not found") template_code = request.GET.get("template", "") or "" autoprint = request.GET.get("autoprint", "1") not in ("0", "false", "False") html_out = await sync_to_async(printing_services.render_document)( tenant=tenant, doc_type=doc_type, document=document, template_code=template_code, autoprint=autoprint, ) return HttpResponse(html_out, content_type="text/html; charset=utf-8")