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
| Method | URI | Action | Description |
|---|---|---|---|
| GET | /statistics | index | Statistics dashboard by year/month |
| GET | /statistics/export-pdf | export-pdf | PDF export of the current report |
Controller
StatisticsController coordinates period input, aggregated query, and export.
Methods
- index() - resolves
year/month, calculatesavailableYears, invokesStatisticsQuery, and rendersstatistics.index - exportPdf() - uses
StatisticsPdfExporterto 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)costsprofit(payments - costs)pending(uncollected payments)
Implementation notes:
- uses dates on
paid_atfor income/costs pendingusesproject.created_atwithin the selected range
OperationalStatsQuery
Calculates operational KPIs for the period:
projects_startedprojects_completedtasks_completedmeetings_heldnew_clients
ChartDataQuery
Prepares the chart dataset:
- annual view: monthly series (
type=monthly) - single month view: daily series (
type=daily)
Output:
labelspaymentscostsprofit
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:
costs—CollectionofCostwithproject.client, ordered bypaid_atpayments—CollectionofPayment(paid only) withproject.client, ordered bypaid_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 costsprofit—income - 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
StatisticsQueryfor the same data as the web page (includesdetailwhen 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(fallbackEUR) - generates PDF via
barryvdh/laravel-dompdf - dynamic filename based on period (
Title-YYYY.pdforTitle-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::CURRENCIESconstant.