Skip to main content

Backend

The Statistics module manages financial and operational metrics by period, chart trends, and PDF export.

File Structure

app/
├── Http/
│ └── Controllers/Statistics/
│ └── StatisticsController.php
├── Queries/Statistics/
│ ├── StatisticsQuery.php
│ └── SubQueries/
│ ├── FinancialStatsQuery.php
│ ├── OperationalStatsQuery.php
│ ├── ChartDataQuery.php
│ ├── MonthlyBreakdownQuery.php
│ ├── MonthlyDetailQuery.php
│ └── TopProjectsQuery.php
└── Services/Statistics/
└── StatisticsPdfExporter.php

Routes

MethodURIActionDescription
GET/statisticsindexStatistics dashboard by year/month
GET/statistics/export-pdfexport-pdfPDF export of the current report

Controller

StatisticsController coordinates period input, aggregated query, and export.

Methods

  • index() - resolves year/month, calculates availableYears, invokes StatisticsQuery, and renders statistics.index
  • exportPdf() - uses StatisticsPdfExporter to generate and download the PDF report
  • getAvailableYears() - builds the list of available years (from the current year up to 2026)

Query Orchestrator

StatisticsQuery is the query orchestrator:

  • calculates the period range (startDate, endDate) based on year/month
  • composes a single output:
    • summary (financial + operational)
    • monthly (month-by-month breakdown, annual view only)
    • detail (per-row costs and payments, single month view only)
    • top_projects (top 10 most profitable projects, annual view only)
    • chart (monthly or daily chart dataset)

SubQueries

FinancialStatsQuery

Calculates financial KPIs for the selected period, filtered by the default currency from BusinessSettings::current()->default_currency:

  • payments (paid income)
  • costs
  • profit (payments - costs)
  • pending (uncollected payments)

Implementation notes:

  • uses dates on paid_at for income/costs
  • pending uses project.created_at within the selected range

OperationalStatsQuery

Calculates operational KPIs for the period:

  • projects_started
  • projects_completed
  • tasks_completed
  • meetings_held
  • new_clients

ChartDataQuery

Prepares the chart dataset:

  • annual view: monthly series (type=monthly)
  • single month view: daily series (type=daily)

Output:

  • labels
  • payments
  • costs
  • profit

Implementation notes:

  • SQL aggregations with strftime (SQLite-friendly)
  • filters paid payments + default currency from BusinessSettings

MonthlyDetailQuery

Loads the per-row detail of costs and payments for a single month (active only in single month view). Filters by default currency from BusinessSettings. Eager loads project.client.

Returns:

  • costsCollection of Cost with project.client, ordered by paid_at
  • paymentsCollection of Payment (paid only) with project.client, ordered by paid_at

Projects without a client are shown as "Internal" in the view.

TopProjectsQuery

Returns the top 10 most profitable projects for the selected year (annual view only). Filters by default currency from BusinessSettings.

For each project aggregates:

  • income — sum of paid payments (Payment::paid())
  • costs — sum of costs
  • profitincome - costs

Returns a Collection of 10 arrays sorted by profit descending, each containing project (with eager-loaded client), income, costs, profit.

Implementation: two separate selectRaw queries with SUM(amount) GROUP BY project_id, merged by project ID, then sorted and sliced in PHP to avoid a complex SQL join.

MonthlyBreakdownQuery

Builds the annual month-by-month table, filtering financial data by the default currency from BusinessSettings:

  • payments, costs, profit (filtered by currency)
  • projects, tasks, clients (absolute counts)

Returns a Collection with 12 normalized rows (months without data are included).

PDF Service

StatisticsPdfExporter encapsulates the export:

  • uses StatisticsQuery for the same data as the web page (includes detail when a month is selected)
  • in single month view, PDF includes two detail tables (costs + payments) followed by the month profit from $stats['summary']['profit']
  • resolves the currency symbol from BusinessSettings::default_currency (fallback EUR)
  • generates PDF via barryvdh/laravel-dompdf
  • dynamic filename based on period (Title-YYYY.pdf or Title-YYYY-MM.pdf)

Technical Notes

  • Statistics queries are centralized in dedicated classes (Query Classes + orchestrator pattern).
  • All financial queries read the currency from BusinessSettings::current()->default_currency (single source of truth).
  • The PDF exposes the currency symbol via the BusinessSettings::CURRENCIES constant.