WorkestraDocs
PlatformTime Tracking

Time Reports

Six tabs over the same dataset — Summary, Detailed, Weekly pivot, Profitability, Workload, Utilization. Plus the Custom Report Builder, saved views, scheduled email, and PDF / CSV export.

/time/reports is a six-tab surface, all sharing one filter row and one toolbar. Switch tabs without losing context: filters, date range, and rounding state carry through. Anything you can't answer in the six tabs, build in the Custom Report Builder.

Reports page

Screenshot needed — /time/reports with tabs across the top (Summary / Detailed / Weekly / Profitability / Workload / Utilization), filter row + toolbar, Summary tab content (KPI cards + duration bar + project donut).

Filter row + toolbar

Above every tab, the filter row:

FilterWhat it does
Date rangeToday / Yesterday / Last 7 days / This week / Last 30 days / This month / Last month / This quarter / This year / Custom
MemberOne or many users
ClientOne or many CRM contacts
ProjectOne or many projects
TagOne or many tags from time_entries.tags
DescriptionFree-text contains
+ Add filterBillable status, status (draft / submitted / approved), entity type

And the toolbar on the right:

ActionWhat it does
RoundingOff / nearest / up / down × 0 / 5 / 15 / 30 min — display-only, doesn't mutate entries
Saved viewsPopover of your saved filter+tab+widget snapshots. Click the Clock on any view to schedule email delivery; click the X to delete.
Custom builderJumps to /time/reports/builder to compose chart-by-chart reports
Schedule manager (mail icon)Opens /time/reports/schedules to pause / resume / delete every workspace email schedule
Create invoicePulls the currently filtered billable + approved hours into a new invoice draft (delegates to the existing invoicing bridge)
Export CSVDownloads the current tab's rows. Detailed exports row-for-row; the others export their aggregated shape.
Export PDFRenders the current tab to a print-ready A4 PDF via the shared Puppeteer stack. KPIs + tables included; charts are not embedded (the PDF is for hand-off, not for chart-by-chart browsing).
SettingsOpen /time/settings

All filter / tab / toolbar state lives in the URL, so deep-links and refreshes round-trip cleanly.

The six tabs

Summary

The default. Designed for the "where did my hours go this week?" question.

ElementWhat
KPI cardsTotal Hours · Billable Hours · Amount · Avg Daily
Duration by monthBar chart, hours per month over the selected range
Project distributionDonut chart with project legend + percentages
Project / description breakdownCollapsible table, rows = projects, expand to see descriptions

Detailed

A flat list of every entry that matches the filters, paginated. Columns: Date, User, Project, Description, Tags, Duration, Billable, Rate, Amount. Sortable on every column. CSV / PDF export from this tab uses the exact rows shown.

Admins and managers see an Add entry for… button on this tab — opens a quick form with a member picker so an admin can log time on behalf of someone else. The audit log records on_behalf_of so the trail stays transparent. See Timer → Logging time for someone else.

Weekly

Pivot grid: members × weeks, cells are hours. Heatmap-tinted (light → dark for higher hours), totals row + column. Useful for "who's been doing what" at a manager level.

Profitability (Pro)

Per-project rows showing revenue − cost − expenses → margin %:

ColumnSource
RevenueSum of hours × hourly_rate for billable + approved entries
CostSum of hours × cost_rate for entries where cost_rate IS NOT NULL
ExpensesSum of project-tagged expenses_expenses rows in the period
MarginRevenue − Cost − Expenses
Margin %Margin / Revenue

Sorted by margin desc by default. A warning chip surfaces when cost_rate is missing on N entries — those rows show "—" for cost and margin.

Requires Time Pro. Free workspaces see an upgrade card with a contextual explainer.

Workload

Same pivot shape as Weekly (members × weeks) but adds two columns: Total (sum across the range) and vs. capacity (Total ÷ default_weekly_hours × weeks-in-range). Cells get semantic heat tinting:

Cell colourMeaning
AmberUnder-tracked (< 80 % of expected weekly capacity)
GreenOn target (80–110 %)
RedOver-tracked (> 110 %)

Surfaces "who's stretched, who's coasting" at a glance.

Utilization

KPI strip + per-member bar chart + table. Reports billable hours as a percentage of contracted capacity per member.

KPIFormula
Avg utilizationAverage of all members' utilization %
Billable hoursSum of billable hours in range
Capacity hoursdefault_weekly_hours × weeks-in-range × member-count

The table breaks each member down: Total / Billable / Capacity / Utilization %. Bar colour: green ≥ 80 %, amber 50–79 %, red < 50 %.

The capacity denominator falls back to the workspace's default_weekly_hours when People isn't subscribed; with People, each employee's individual weekly_hours is used.

Saved views

Take any combination of filters + tab and click Save current view in the Saved views popover. It's persisted in the cross-cutting views table with a 'time_report' marker. Click any saved view to re-apply the snapshot.

Saved views can be:

  • Private (per-user, the default) — only the creator sees them
  • Shared (workspace-wide) — every workspace member sees them

For more advanced reports — charts beyond the six tabs — save a configured custom report from /time/reports/builder. See Custom Reports.

Scheduled email delivery

Every saved view can be emailed on a recurring schedule.

  1. Open the Saved views popover.
  2. Click the Clock icon on any view (the icon is highlighted when a schedule already exists).
  3. Pick frequency: daily (yesterday's data), weekly (Monday, last 7 days), monthly (1st, prior month).
  4. Enter up to 20 recipient emails, comma- or space-separated.
  5. Enabled toggle lets you pause delivery without losing the recipient list. Re-enable any time.
  6. Schedule to save.

What recipients receive: a clean HTML digest with the report's KPIs (Total / Billable / Revenue), top 5 projects, top 10 recent entries, and a link to the live report at the right tab and date range. Subject line = view name + period (e.g. "Q2 billable summary — last 7 days").

The cron fires at 06:20 UTC daily, decides per-schedule whether to fire today (always for daily, only Mondays for weekly, only the 1st for monthly), and stamps last_sent_at so it doesn't double-send if it retries.

Schedule manager

/time/reports/schedules (mail-icon link in the reports toolbar) lists every active and paused schedule in the workspace. Per row:

  • View name (links back to the builder for custom reports, or to the tab for built-in views)
  • Frequency badge (daily / weekly / monthly / paused)
  • Recipient list
  • Last sent timestamp + next-send projection
  • Pause / Resume toggle (preserves recipient + frequency config)
  • Delete

This is the surface for "we have 15 schedules, who's on the Q2 deliveries?" — a single pane vs. opening each saved view's clock icon.

Export

FormatWhere it worksNotes
CSVDetailed (always wired); other tabs as their aggregated shapeBrowser-side download via Blob URL
PDFAll six tabsServer-rendered via Puppeteer; toast-tracked progress (5–15 s); no charts in the PDF — KPIs and tables only

PDF generation runs the same Chrome stack as Finance invoices and Sales quotations. The exported HTML has JavaScript disabled in Puppeteer, so client-supplied content cannot execute scripts on the server.

Where the data flows

time_entries (filtered by query params)

    ├── TimeReportService.summaryRollup(filters)
    │     → KPI cards, project donut, breakdown table

    ├── TimeReportService.detailedList(filters, page, limit)
    │     → flat list, paginated

    ├── TimeReportService.weeklyPivot(filters)
    │     → members × weeks heatmap

    ├── TimeReportService.profitabilityByProject(filters)
    │     → revenue / cost / margin per project

    ├── TimeReportService.utilizationReport(filters, weeklyCapacity)
    │     → utilization % per member

    └── TimeCustomReportService.rollup(filters, primary, secondary?)
          → flexible 2-D map for the Custom Report Builder

Every method shares the same filter shape and respects URL filters + approved-only semantics where applicable.

Where else reports live

  • /projects/[id] Budget tab — single-project actuals (uses the same rollup service).
  • /people/employees/[id] Utilization tab — single-employee read-only view (lights up when People is subscribed).
  • /dashboard widgets — workspace billable % can be added as a dashboard widget.
  • CRM deal detail — projected cost + projected margin in the right rail (uses the same cost-rate snapshot).
  • /api/v1/time/reports/summary — the Summary tab payload over REST, useful for the browser extension and external dashboards.

Next steps

  • Custom Reports — drag-drop builder for reports beyond the six tabs
  • Time Pro — what unlocks Profitability + advanced approvals
  • Rates & Billing — how bill rate + cost rate flow into reports
  • Integrations — pull report data over the public API