Files
site/apps/api/app/modules/zootech/wesp_compat_feed_accounting.py
T
2026-07-17 12:57:18 +03:00

210 lines
7.2 KiB
Python

"""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")