"""WESP-shaped feed accounting API (/api/feed-accounting) for consumption.html.""" from __future__ import annotations from fastapi import APIRouter, Depends, HTTPException, Query, status from fastapi.responses import Response from pydantic import BaseModel, Field from app.modules.sync.tenant import TenantContext, require_enterprise_zootech from app.modules.zootech import wesp_feed_accounting_service as fa_service router = APIRouter() def _require_ent(enterprise_id: str, tenant: TenantContext) -> None: if tenant.enterprise_id != enterprise_id: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="ENTERPRISE_FORBIDDEN") class OrgSettingsBody(BaseModel): organization_name: str = "" okpo: str = "" department: str = "" farm_name: str = "" brigade: str = "" animal_type: str = "" responsible_person: str = "" zootechnician: str = "" warehouse_keeper: str = "" document_number_prefix: str = "" class SignatureBody(BaseModel): png_base64: str = Field(min_length=10) @router.get("/feed-accounting/org-settings") def get_org_settings_wesp( enterprise_id: str = Query(...), tenant: TenantContext = Depends(require_enterprise_zootech), ): _require_ent(enterprise_id, tenant) return fa_service.get_org_settings(enterprise_id) @router.put("/feed-accounting/org-settings") def put_org_settings_wesp( body: OrgSettingsBody, enterprise_id: str = Query(...), tenant: TenantContext = Depends(require_enterprise_zootech), ): _require_ent(enterprise_id, tenant) return fa_service.save_org_settings(enterprise_id, body.model_dump()) @router.get("/feed-accounting/signatures/{role}.png") def get_signature_png_wesp( role: str, enterprise_id: str = Query(...), tenant: TenantContext = Depends(require_enterprise_zootech), ): _require_ent(enterprise_id, tenant) png = fa_service.get_signature_png(enterprise_id, role) if not png: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="SIGNATURE_NOT_FOUND") return Response(content=png, media_type="image/png") @router.post("/feed-accounting/signatures/{role}") def post_signature_wesp( role: str, body: SignatureBody, enterprise_id: str = Query(...), tenant: TenantContext = Depends(require_enterprise_zootech), ): _require_ent(enterprise_id, tenant) result = fa_service.save_signature(enterprise_id, role, body.png_base64) if not result.get("success"): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail={"message": result.get("message") or "Ошибка сохранения подписи"}, ) return result @router.get("/feed-accounting/sp20-preview") def sp20_preview_wesp( month: str = Query(...), enterprise_id: str = Query(...), tenant: TenantContext = Depends(require_enterprise_zootech), ): _require_ent(enterprise_id, tenant) return fa_service.sp20_preview(enterprise_id, month) def _xlsx_response(content: bytes, filename: str) -> Response: return Response( content=content, media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", headers={"Content-Disposition": f'attachment; filename="{filename}"'}, ) @router.get("/feed-accounting/consumption.xlsx") def export_consumption_xlsx_wesp( date_from: str = Query(...), date_to: str = Query(...), enterprise_id: str = Query(...), tenant: TenantContext = Depends(require_enterprise_zootech), ): _require_ent(enterprise_id, tenant) content = fa_service.build_consumption_xlsx(enterprise_id, date_from, date_to) filename = f"consumption_{fa_service.safe_filename_part(date_from)}_{fa_service.safe_filename_part(date_to)}.xlsx" return _xlsx_response(content, filename) @router.get("/feed-accounting/stock-balances.xlsx") def export_stock_xlsx_wesp( date_from: str = Query(...), date_to: str = Query(...), enterprise_id: str = Query(...), tenant: TenantContext = Depends(require_enterprise_zootech), ): _require_ent(enterprise_id, tenant) content = fa_service.build_stock_balances_xlsx(enterprise_id, date_from, date_to) filename = f"stock_{fa_service.safe_filename_part(date_from)}_{fa_service.safe_filename_part(date_to)}.xlsx" return _xlsx_response(content, filename) @router.get("/feed-accounting/sp20.xlsx") def export_sp20_xlsx_wesp( month: str = Query(...), enterprise_id: str = Query(...), tenant: TenantContext = Depends(require_enterprise_zootech), ): _require_ent(enterprise_id, tenant) content = fa_service.build_sp20_xlsx(enterprise_id, month) return _xlsx_response(content, f"sp20_{fa_service.safe_filename_part(month)}.xlsx") @router.get("/feed-accounting/journal.xlsx") def export_journal_xlsx_wesp( month: str = Query(...), enterprise_id: str = Query(...), tenant: TenantContext = Depends(require_enterprise_zootech), ): _require_ent(enterprise_id, tenant) content = fa_service.build_journal_xlsx(enterprise_id, month) return _xlsx_response(content, f"journal_{fa_service.safe_filename_part(month)}.xlsx") @router.get("/feed-accounting/documents.zip") def export_documents_zip_wesp( date_from: str = Query(...), date_to: str = Query(...), month: str = Query(...), enterprise_id: str = Query(...), tenant: TenantContext = Depends(require_enterprise_zootech), ): _require_ent(enterprise_id, tenant) content = fa_service.build_documents_zip(enterprise_id, date_from, date_to, month) return Response( content=content, media_type="application/zip", headers={"Content-Disposition": f'attachment; filename="documents_{fa_service.safe_filename_part(month)}.zip"'}, ) def _pdf_response(content: bytes, filename: str) -> Response: return Response( content=content, media_type="application/pdf", headers={"Content-Disposition": f'attachment; filename="{filename}"'}, ) @router.get("/feed-accounting/consumption.pdf") def export_consumption_pdf_wesp( date_from: str = Query(...), date_to: str = Query(...), enterprise_id: str = Query(...), tenant: TenantContext = Depends(require_enterprise_zootech), ): _require_ent(enterprise_id, tenant) content = fa_service.build_consumption_pdf(enterprise_id, date_from, date_to) filename = f"consumption_{fa_service.safe_filename_part(date_from)}_{fa_service.safe_filename_part(date_to)}.pdf" return _pdf_response(content, filename) @router.get("/feed-accounting/sp20.pdf") def export_sp20_pdf_wesp( month: str = Query(...), enterprise_id: str = Query(...), tenant: TenantContext = Depends(require_enterprise_zootech), ): _require_ent(enterprise_id, tenant) content = fa_service.build_sp20_pdf(enterprise_id, month) return _pdf_response(content, f"sp20_{fa_service.safe_filename_part(month)}.pdf") @router.get("/feed-accounting/journal.pdf") def export_journal_pdf_wesp( month: str = Query(...), enterprise_id: str = Query(...), tenant: TenantContext = Depends(require_enterprise_zootech), ): _require_ent(enterprise_id, tenant) content = fa_service.build_journal_pdf(enterprise_id, month) return _pdf_response(content, f"journal_{fa_service.safe_filename_part(month)}.pdf")