@@ -0,0 +1,130 @@
|
||||
"""PDF: отчёт «Потребление корма»."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
|
||||
from flask import Flask
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.pagesizes import A4, landscape
|
||||
from reportlab.lib.units import mm
|
||||
from reportlab.platypus import PageBreak, Paragraph, SimpleDocTemplate, Spacer, Table, TableStyle
|
||||
|
||||
from app.services.feed_accounting.consumption_report_builder import build_consumption_report
|
||||
from app.services.feed_accounting.pdf_feed_accounting import _styles
|
||||
|
||||
|
||||
def _table(data: list, font: str, *, font_size: int = 8, repeat_rows: int = 1) -> Table:
|
||||
tbl = Table(data, repeatRows=repeat_rows)
|
||||
tbl.setStyle(
|
||||
TableStyle(
|
||||
[
|
||||
("FONTNAME", (0, 0), (-1, -1), font),
|
||||
("FONTSIZE", (0, 0), (-1, -1), font_size),
|
||||
("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey),
|
||||
("GRID", (0, 0), (-1, -1), 0.25, colors.grey),
|
||||
("VALIGN", (0, 0), (-1, -1), "TOP"),
|
||||
]
|
||||
)
|
||||
)
|
||||
return tbl
|
||||
|
||||
|
||||
def build_consumption_pdf(app: Flask, date_from: str, date_to: str) -> bytes:
|
||||
report = build_consumption_report(app, date_from, date_to)
|
||||
buf = io.BytesIO()
|
||||
doc = SimpleDocTemplate(
|
||||
buf,
|
||||
pagesize=landscape(A4),
|
||||
leftMargin=10 * mm,
|
||||
rightMargin=10 * mm,
|
||||
topMargin=10 * mm,
|
||||
bottomMargin=10 * mm,
|
||||
)
|
||||
styles, font = _styles()
|
||||
story = [
|
||||
Paragraph("Потребление корма", styles["Title"]),
|
||||
Paragraph(f"Период: {date_from} — {date_to}", styles["Normal"]),
|
||||
Spacer(1, 6),
|
||||
Paragraph("Сводка", styles["Normal"]),
|
||||
Spacer(1, 4),
|
||||
]
|
||||
|
||||
sum_data = [
|
||||
[
|
||||
"Компонент",
|
||||
"Осталось, кг",
|
||||
"Расход за период, кг",
|
||||
"План кг/сут",
|
||||
"Осталось дней",
|
||||
"Поступление, кг",
|
||||
"Всего, кг",
|
||||
]
|
||||
]
|
||||
for row in report.summary:
|
||||
sum_data.append(
|
||||
[
|
||||
row["name"],
|
||||
row["remaining_kg"],
|
||||
row["consumed_kg"],
|
||||
row["planned_per_day_kg"] or "",
|
||||
row["days_left"] if row["days_left"] is not None else "",
|
||||
row["inflow_kg"],
|
||||
row["total_kg"],
|
||||
]
|
||||
)
|
||||
if len(sum_data) == 1:
|
||||
sum_data.append(["—", "", "", "", "", "", ""])
|
||||
story.append(_table(sum_data, font))
|
||||
story.append(PageBreak())
|
||||
|
||||
story.append(Paragraph("Расход по дням", styles["Normal"]))
|
||||
story.append(Spacer(1, 4))
|
||||
day_labels = [d.strftime("%d.%m") for d in report.days]
|
||||
daily_data = [["Компонент"] + day_labels + ["Итого"]]
|
||||
for row in report.daily:
|
||||
daily_data.append(
|
||||
[row["name"]]
|
||||
+ [row["days"].get(d, 0) or "" for d in report.days]
|
||||
+ [row["total_kg"]]
|
||||
)
|
||||
if len(daily_data) == 1:
|
||||
daily_data.append(["—"] + [""] * (len(report.days) + 1))
|
||||
story.append(_table(daily_data, font, font_size=6))
|
||||
story.append(PageBreak())
|
||||
|
||||
story.append(Paragraph("Детализация загрузок", styles["Normal"]))
|
||||
story.append(Spacer(1, 4))
|
||||
det_data = [
|
||||
[
|
||||
"Начало",
|
||||
"Окончание",
|
||||
"Рецепт",
|
||||
"Кормораздатчик",
|
||||
"Компонент",
|
||||
"План, кг",
|
||||
"Факт, кг",
|
||||
"Перегруз, кг",
|
||||
"№",
|
||||
]
|
||||
]
|
||||
for row in report.detail:
|
||||
det_data.append(
|
||||
[
|
||||
row["start"],
|
||||
row["end"],
|
||||
row["recipe"],
|
||||
row["dispenser"],
|
||||
row["component"],
|
||||
row["target_kg"],
|
||||
row["actual_kg"],
|
||||
row["overload_kg"],
|
||||
row["loading_order"],
|
||||
]
|
||||
)
|
||||
if len(det_data) == 1:
|
||||
det_data.append(["—", "", "", "", "", "", "", "", ""])
|
||||
story.append(_table(det_data, font, font_size=7))
|
||||
|
||||
doc.build(story)
|
||||
return buf.getvalue()
|
||||
Reference in New Issue
Block a user