@@ -0,0 +1,129 @@
|
||||
"""PDF плана на день (reportlab)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.pagesizes import A4
|
||||
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.pdf_feed_accounting import _register_cyrillic_font
|
||||
|
||||
|
||||
def _styles():
|
||||
font = _register_cyrillic_font()
|
||||
styles = getSampleStyleSheet()
|
||||
styles["Title"].fontName = font
|
||||
styles["Normal"].fontName = font
|
||||
styles["Heading2"].fontName = font
|
||||
return styles, font
|
||||
|
||||
|
||||
def build_daily_plan_pdf(plan: Dict[str, Any]) -> bytes:
|
||||
buf = io.BytesIO()
|
||||
doc = SimpleDocTemplate(buf, pagesize=A4, leftMargin=14 * mm, rightMargin=14 * mm)
|
||||
styles, font = _styles()
|
||||
story: List[Any] = []
|
||||
|
||||
story.append(Paragraph("План на день", styles["Title"]))
|
||||
story.append(
|
||||
Paragraph(
|
||||
f"Дата: {plan.get('date', '—')} · {plan.get('dispenserName', '—')} · {plan.get('farm', '')}",
|
||||
styles["Normal"],
|
||||
)
|
||||
)
|
||||
story.append(Spacer(1, 8))
|
||||
|
||||
periods = plan.get("periods") or []
|
||||
if not periods:
|
||||
story.append(Paragraph("Нет периодов или рейсов для выбранного кормораздатчика.", styles["Normal"]))
|
||||
else:
|
||||
for period in periods:
|
||||
story.append(Paragraph(str(period.get("name") or "Период"), styles["Heading2"]))
|
||||
for trip in period.get("trips") or []:
|
||||
story.append(
|
||||
Paragraph(
|
||||
f"Рейс {trip.get('order', '')}: {trip.get('recipeName', '—')} "
|
||||
f"({trip.get('headsPerTrip', 0)} гол., смеш. {trip.get('mixingTimeSec', 0)} с)",
|
||||
styles["Normal"],
|
||||
)
|
||||
)
|
||||
data = [["Компонент", "кг/гол", "Всего, кг"]]
|
||||
for ing in trip.get("ingredients") or []:
|
||||
data.append(
|
||||
[
|
||||
str(ing.get("name") or "—"),
|
||||
str(ing.get("weightPerHead") or ""),
|
||||
str(ing.get("totalKg") or ""),
|
||||
]
|
||||
)
|
||||
if len(data) == 1:
|
||||
data.append(["—", "", ""])
|
||||
tbl = Table(data, colWidths=[80 * mm, 35 * mm, 35 * mm])
|
||||
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)
|
||||
groups = trip.get("unloadingGroups") or []
|
||||
if groups:
|
||||
gdata = [["Группа", "кг", "Распределение"]]
|
||||
for g in groups:
|
||||
gdata.append(
|
||||
[
|
||||
str(g.get("name") or "—"),
|
||||
str(g.get("weightKg") or ""),
|
||||
str(
|
||||
g.get("distributionLabel")
|
||||
or g.get("distributionType")
|
||||
or ""
|
||||
),
|
||||
]
|
||||
)
|
||||
gtbl = Table(gdata, colWidths=[55 * mm, 30 * mm, 40 * mm])
|
||||
gtbl.setStyle(
|
||||
TableStyle(
|
||||
[
|
||||
("FONTNAME", (0, 0), (-1, -1), font),
|
||||
("FONTSIZE", (0, 0), (-1, -1), 8),
|
||||
("GRID", (0, 0), (-1, -1), 0.25, colors.grey),
|
||||
]
|
||||
)
|
||||
)
|
||||
story.append(gtbl)
|
||||
story.append(Spacer(1, 6))
|
||||
|
||||
totals = plan.get("ingredientTotals") or []
|
||||
if totals:
|
||||
story.append(Paragraph("Итого по компонентам", styles["Heading2"]))
|
||||
tdata = [["Компонент", "Всего, кг"]]
|
||||
for row in totals:
|
||||
tdata.append([str(row.get("name") or "—"), str(row.get("totalKg") or "")])
|
||||
grand = plan.get("ingredientGrandTotalKg")
|
||||
if grand is not None:
|
||||
tdata.append(["Итого", str(grand)])
|
||||
ttbl = Table(tdata, colWidths=[100 * mm, 40 * mm])
|
||||
ttbl.setStyle(
|
||||
TableStyle(
|
||||
[
|
||||
("FONTNAME", (0, 0), (-1, -1), font),
|
||||
("FONTSIZE", (0, 0), (-1, -1), 9),
|
||||
("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey),
|
||||
("GRID", (0, 0), (-1, -1), 0.25, colors.grey),
|
||||
]
|
||||
)
|
||||
)
|
||||
story.append(ttbl)
|
||||
|
||||
doc.build(story)
|
||||
return buf.getvalue()
|
||||
Reference in New Issue
Block a user