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