Files

67 lines
2.3 KiB
Python

"""Groups a year's lines by category with computed figures; shared by the API and every report."""
from dataclasses import dataclass, field
from sqlalchemy import select
from sqlalchemy.orm import Session, selectinload
from .calc import LineFigures, Totals, line_figures
from .models import BudgetYear, Category, CostLine
@dataclass
class LineView:
line: CostLine
figures: LineFigures
@dataclass
class CategoryView:
category: Category
lines: list[LineView] = field(default_factory=list)
totals: Totals = field(default_factory=Totals)
def name(self, lang: str) -> str:
return (self.category.name_pt if lang == "pt" else "") or self.category.name_en
@dataclass
class YearView:
year: BudgetYear
categories: list[CategoryView]
revenue: Totals
costs: Totals
def with_lines(self, section: str | None = None) -> list[CategoryView]:
return [c for c in self.categories if c.lines and (section is None or c.category.section == section)]
def build_year_view(db: Session, year: BudgetYear, site_id: int | None = None) -> YearView:
categories = db.scalars(select(Category).where(Category.department_id == year.department_id)
.order_by(Category.sort_order, Category.id)).all()
lines = db.scalars(
select(CostLine)
.where(CostLine.year_id == year.id)
.options(selectinload(CostLine.site), selectinload(CostLine.category))
.order_by(CostLine.sort_order, CostLine.id)
).all()
views = {c.id: CategoryView(c) for c in categories}
for line in lines:
if site_id is not None and line.site_id != site_id:
continue
lv = LineView(line, line_figures(line, year.default_rate))
cv = views.get(line.category_id)
if cv is None: # a line whose category was moved to another department
cv = views[line.category_id] = CategoryView(line.category)
cv.lines.append(lv)
cv.totals.add(lv.figures)
revenue, costs = Totals(), Totals()
shown = []
for cv in views.values():
if not cv.lines and not (cv.category.active and cv.category.show_when_empty):
continue
shown.append(cv)
target = revenue if cv.category.section == "revenue" else costs
for lv in cv.lines:
target.add(lv.figures)
return YearView(year, shown, revenue, costs)