140 lines
5.0 KiB
Python
140 lines
5.0 KiB
Python
"""PDF-экспорт учётных документов (reportlab)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import io
|
|
from pathlib import Path
|
|
from typing import List
|
|
|
|
from flask import Flask
|
|
from reportlab.lib import colors
|
|
from reportlab.lib.pagesizes import A4, landscape
|
|
from reportlab.lib.styles import getSampleStyleSheet
|
|
from reportlab.lib.units import mm
|
|
from reportlab.platypus import Paragraph, SimpleDocTemplate, Spacer, Table, TableStyle
|
|
|
|
from app.services.feed_accounting.sp20_report_builder import build_journal_rows, build_sp20_report
|
|
from app.services.org_settings import get_org_settings, signature_path
|
|
from app.services.pdf_cyrillic_fonts import (
|
|
register_cyrillic_font as _register_cyrillic_font,
|
|
register_cyrillic_font_bold as _register_cyrillic_font_bold,
|
|
)
|
|
|
|
|
|
def _styles():
|
|
font = _register_cyrillic_font()
|
|
styles = getSampleStyleSheet()
|
|
styles["Title"].fontName = font
|
|
styles["Normal"].fontName = font
|
|
return styles, font
|
|
|
|
|
|
def _signature_image_flowable(role: str, width=40 * mm, height=15 * mm):
|
|
try:
|
|
from reportlab.platypus import Image
|
|
|
|
p = signature_path(role)
|
|
if p.is_file():
|
|
return Image(str(p), width=width, height=height)
|
|
except Exception:
|
|
pass
|
|
return None
|
|
|
|
|
|
def build_sp20_pdf(app: Flask, year: int, month: int) -> bytes:
|
|
report = build_sp20_report(app, year, month)
|
|
org = report.org
|
|
buf = io.BytesIO()
|
|
doc = SimpleDocTemplate(buf, pagesize=landscape(A4), leftMargin=12 * mm, rightMargin=12 * mm)
|
|
styles, font = _styles()
|
|
story = []
|
|
story.append(Paragraph("Ведомость учёта расхода кормов (форма СП-20)", styles["Title"]))
|
|
story.append(Paragraph(f"ОКУД 0325020 · {month:02d}.{year}", styles["Normal"]))
|
|
story.append(Spacer(1, 6))
|
|
meta = [
|
|
["Организация", org.get("organization_name") or "—", "ОКПО", org.get("okpo") or "—"],
|
|
["Ферма", org.get("farm_name") or "—", "Бригада", org.get("brigade") or "—"],
|
|
["Вид скота", report.animal_type, "Ответственный", org.get("responsible_person") or "—"],
|
|
]
|
|
mt = Table(meta, colWidths=[35 * mm, 70 * mm, 35 * mm, 70 * mm])
|
|
mt.setStyle(
|
|
TableStyle(
|
|
[
|
|
("FONTNAME", (0, 0), (-1, -1), font),
|
|
("FONTSIZE", (0, 0), (-1, -1), 9),
|
|
("GRID", (0, 0), (-1, -1), 0.25, colors.grey),
|
|
]
|
|
)
|
|
)
|
|
story.append(mt)
|
|
story.append(Spacer(1, 8))
|
|
data = [["Дата", "Корм", "Голов", "Кг"]]
|
|
for line in report.lines[:120]:
|
|
data.append(
|
|
[
|
|
f"{line.day:02d}.{month:02d}.{year}",
|
|
line.feed_name,
|
|
str(line.heads or ""),
|
|
str(line.quantity_kg),
|
|
]
|
|
)
|
|
if len(data) == 1:
|
|
data.append(["—", "Нет данных за период", "", ""])
|
|
tbl = Table(data, repeatRows=1)
|
|
tbl.setStyle(
|
|
TableStyle(
|
|
[
|
|
("FONTNAME", (0, 0), (-1, -1), font),
|
|
("FONTSIZE", (0, 0), (-1, -1), 8),
|
|
("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey),
|
|
("GRID", (0, 0), (-1, -1), 0.25, colors.grey),
|
|
]
|
|
)
|
|
)
|
|
story.append(tbl)
|
|
story.append(Spacer(1, 12))
|
|
sig_row = []
|
|
for role, label in (
|
|
("zootechnician", org.get("zootechnician") or "Зоотехник"),
|
|
("warehouse_keeper", org.get("warehouse_keeper") or "Кладовщик"),
|
|
):
|
|
img = _signature_image_flowable(role)
|
|
sig_row.append([label, img if img else ""])
|
|
sig_tbl = Table(sig_row, colWidths=[100 * mm, 100 * mm])
|
|
sig_tbl.setStyle(TableStyle([("FONTNAME", (0, 0), (-1, -1), font), ("FONTSIZE", (0, 0), (-1, -1), 9)]))
|
|
story.append(sig_tbl)
|
|
doc.build(story)
|
|
return buf.getvalue()
|
|
|
|
|
|
def build_journal_pdf(app: Flask, year: int, month: int) -> bytes:
|
|
org = get_org_settings(app)
|
|
rows = build_journal_rows(app, year, month)
|
|
buf = io.BytesIO()
|
|
doc = SimpleDocTemplate(buf, pagesize=A4)
|
|
styles, font = _styles()
|
|
story = [
|
|
Paragraph("Журнал учёта расхода кормов", styles["Title"]),
|
|
Paragraph(org.get("organization_name") or "", styles["Normal"]),
|
|
Paragraph(f"Период: {month:02d}.{year}", styles["Normal"]),
|
|
Spacer(1, 8),
|
|
]
|
|
data = [["Вид корма", "Ед.", "За месяц, кг", "С начала года, кг"]]
|
|
for r in rows:
|
|
data.append([r["name"], r["unit"], r["month_kg"], r["ytd_kg"]])
|
|
if len(data) == 1:
|
|
data.append(["—", "кг", 0, 0])
|
|
tbl = Table(data, repeatRows=1)
|
|
tbl.setStyle(
|
|
TableStyle(
|
|
[
|
|
("FONTNAME", (0, 0), (-1, -1), font),
|
|
("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey),
|
|
("GRID", (0, 0), (-1, -1), 0.25, colors.grey),
|
|
]
|
|
)
|
|
)
|
|
story.append(tbl)
|
|
doc.build(story)
|
|
return buf.getvalue()
|