Administration
Report builder guide
How to build, filter, group, save and share a report — including Excel-style formula columns and period-on-period comparisons. The builder is self-service: you compose a report against a data source, preview it live, and keep it to run again later.
This is the guide HumanR users read inside the product, published as-is. It is written for someone with the screen in front of them, so it describes buttons you cannot click from here — which is rather the point: you can check how the product behaves before you commit to it.
Overview
A report is built in four steps: pick a source, tick the columns you want, add filters to narrow the rows, and — optionally — group the results to get totals and a chart. The preview on the right updates as you go.
When it looks right, give it a name and Save it. Saved reports run live against current records every time you open them, so the numbers are never stale — and they always re-apply your own permissions, so a report shared with you never shows more than your role allows.
Sources & permissions
A source is the table a report draws from — Employees, Leave requests, Payroll lines, Disciplinary cases, and so on. You only see the sources your role can view: each one requires the View permission of the module it comes from (e.g. the Payroll sources need Payroll · View). If a source isn't in the dropdown, your role doesn't have access to it.
The full source → permission map is on the Roles & permissions page.
Columns
Columns are grouped by area (Staff, Issue, Pay…). Tick a box to add that column to the report; untick to remove it. The order in the report follows the catalog, not the order you tick them.
Columns you're not cleared to see (pay or personal data) simply aren't offered, and they can't be added by editing a saved report either — the check happens on the server, not in the browser.
Filters
A filter keeps only the rows that match. Add as many as you like with + add; rows must match all of them (they are combined with AND). Each filter is a column, an operator, and usually a value. The operators offered depend on the column's type:
| Column type | Operator | Matches |
|---|---|---|
| Text (names, references, remarks…) | ||
| Text | contains | the value appears anywhere in the text — a LIKE '%value%' match, case-insensitive. “ali” finds “Ahmed Ali”. |
| Text | equals | the whole value matches exactly (case-insensitive). |
| Text | does not equal | anything except that exact value — including blanks. |
| Text | is empty | the field is blank or unset. No value needed. |
| Text | is not empty | the field has some value. No value needed. |
| Number | ||
| Number | = ≠ > < | compare against the value you type. |
| Number | between | within a low–high range (inclusive). |
| Date | ||
| Date | on before after | relative to a date you pick. |
| Date | between | within a date range (inclusive). |
| Date | in last N days | a rolling window ending today — type the number of days. |
| Choice & lookup (status, department, site…) | ||
| Choice / Lookup | is / is not | equals (or doesn't) the option you pick. |
| Yes / No | ||
| Yes/No | is | true or false. |
Does “contains” work like SQL LIKE? Yes — it's a substring match
(LIKE '%value%'), run case-insensitively, so partial words match and there's no need
to type wildcards yourself.
The value box hides itself for is empty / is not empty since those need
no value. A filter you leave without a value (where one is required) is simply ignored, so a
half-finished filter never breaks the report.
Formula columns
A formula column computes a new number for every row, Excel-style, from the source's number
columns. Reference a column by its key in square brackets and combine with
+ - * / ( ):
[gross] - [deductions]— what net should be, to cross-check it.round([gross] * 0.07, 2)— a 7% pension figure to sanity-check the payslip item.max([basic], 5000)— a floor for a what-if comparison.
Four functions are available: round(x, places) (places optional, rounds halves away
from zero like Excel), abs(x), min(…) and max(…). A blank
cell counts as 0, and dividing by zero leaves the cell empty rather than erroring. The column keys
are shown in the builder; a typo gets a message under the formula, not a broken page.
Every formula you define appears as a column in the report, and can be summed or averaged in a grouped report like any other number. Formulas follow the same permission rules as the columns they reference — a viewer who can't see a referenced column doesn't get the formula either.
Group by & totals
Leave Group by on “none (flat list)” and you get a normal row-by-row list. Choose a column instead and the report collapses into one row per distinct value of that column, with a total beside it — for example, count of employees per department, or sum of penalty amount per status.
- Date bucket — when you group by a date column, roll it up by month or by year instead of every individual day.
- Aggregate — pick what to compute for each group: Count (how many rows), or Sum / Average / Min / Max of a numeric column.
Grouped reports are computed over up to 3,000 matching rows. If a group could exceed that, tighten the filters first so the totals stay exact.
Comparisons
Three toggles under the group-by turn a grouped report into a comparison:
- Change vs previous period — with a month or year bucket, each row gains a Δ and Δ% against the period before it. A period with no predecessor in the data shows “—”: the comparison is unknowable, and pretending it's zero would read as “no change”.
- % of total — each row's share of the column total, for Sum and Count (an average's share of summed averages means nothing, so it stays off those).
- Top N groups by value — sort groups by the first total, largest first, and keep the top N. The note under the table says how many groups were cut.
The extra columns export to CSV and PDF like any others.
Groups as columns (pivot)
A grouped report answers one number per group, down the page. Tick the pivot checkbox and the same report turns sideways: the columns you ticked become the row — typically Staff no and Name, up to four — each group becomes a column, and the cells hold the Sum or Count for that person and group, with a Total at the end of every row. Group by pay period for the classic gross per staff member per month matrix; group by item on payslip items for one column per deduction.
- It needs a calendar or a category. The checkbox lights up when you group by a date column with a month or year bucket, or by a text column like item or department — and the cell must be a Sum or Count: a row of averages has no honest total.
- “—” means no data, not zero. A month with no payslip keeps its column and shows a dash; months with nothing at all anywhere still appear, so the timeline reads whole. (A category nobody has simply isn't a column.) The row Total adds up the columns you can see.
- Wide answers stay readable. Past 24 columns, a calendar keeps the most recent 24 and a category keeps the 24 largest by total — either way a note says so; narrow the filters to see the rest.
- The comparison toggles and charts sit this one out. Δ, % of total, top-N and the chart grey out while the pivot is on; untick it and they come back exactly as you left them.
Three ready-made ones on the Reports page: Gross by staff and month; Deductions by staff and month — open that one in the builder and add an item is … filter to follow a single deduction, like one loan; and Deductions by staff and type, the year's deductions with one column per item.
Payroll reports
The payroll sources are built for cross-month comparison. Payroll lines has one row per person per run, and its Run columns — pay period, run status, run kind — are what let a single report span months: group by pay period with a month bucket and you have cost-by-month; switch the bucket to year for the annual view. Payroll items goes one level deeper — one row per payslip component (“PENSION”, “LATE FINE”…) — which is the source for deductions by type or deductions per staff member.
- Filter to finalized runs. A draft run's figures are still moving, so a total that includes one is the wrong number. The ready-made payroll reports already filter run status is Finalized — keep that filter when you build your own, and remove it only when you deliberately want a work-in-progress run included.
- Sum across currencies with the “(base)” columns. Gross, deductions and net also exist as Gross (base), Deductions (base) and Net (base), restated into the base currency at the rate each run was built at — the only columns that are safe to add across currencies. The plain money columns stay in each line's own currency, exactly as the payslip register shows them.
Put together: Sum of Gross (base) grouped by pay period per month, with
change vs previous period switched on, is the month-on-month payroll cost report —
and a formula like [gross] - [deductions] cross-checks net on every payslip line.
Charts
A grouped report can also draw a bar, line or pie chart of its totals — pick one from the chart dropdown that appears once you've chosen a group-by. The chart uses the group values along one axis and the aggregate as the height/slice. Flat lists have nothing to chart, so the option only shows for grouped reports.
Saving & sharing
Give the report a name (and an optional description) and press Save. It lands on your Saved reports page, where you can open, edit or delete it. Editing re-opens it in the builder with everything as you left it.
Sharing: only Admin and HR can tick Shared to make a report visible to other users; everyone else's reports stay private to them. A shared report still re-applies each viewer's own permissions, so sharing never leaks columns a viewer isn't cleared for. Only the owner (or an Admin) can edit or delete a report.
Running, sorting & export
Opening a saved report runs it against current data and shows it as a paged list. Click a column header to sort by it, and use the pager at the bottom to move through pages — exactly like every other list in the app.
On a quick report or a saved report's page, a grouped or pivoted table also closes with a bold Total row, and headline cards appear above it — the grand total, an average per row, and (on a matrix) an average per period. Totals cover exactly the rows and columns on screen, only where adding is honest: averages, minima and Δ columns show a dash, and a gap month stays "—". The builder's live preview keeps things lean and skips them.
Export CSV downloads the full result (not just the page on screen), honouring the current sort. Exports are audited, and any pay/personal columns you can't see are left out of the file just as they are on screen. The PDF prints the Total row; the CSV deliberately doesn't carry one, so re-pivoting the file in a spreadsheet never double-counts.
Emailing it on a schedule
A saved report can mail itself. Open it and press Schedule (the owner or an Admin only — being able to read a shared report doesn't let you subscribe people to it), pick daily, weekly or monthly and an hour, and tick the recipients. Each send arrives as a CSV attached to a short covering email. Send now delivers to the same list without moving the schedule, which is the way to see what everyone's copy actually contains before it starts going out on its own.
Every recipient's copy is produced separately, under their own permissions. Someone without pay clearance receives the same report with the pay columns absent — the report isn't run once and forwarded. Recipients are re-checked at every send too, so moving someone off a role, or un-sharing the report, ends their subscription by itself; nobody has to remember to come back and remove them. Monthly schedules stop at day 28 — “the 31st” is a schedule that skips February. Times follow the instance's timezone, not your own.
Sensitive columns
Pay and personal columns (salaries, bank accounts, addresses, dates of birth…) are gated by the Sensitive · Pay and Sensitive · Personal permissions. If you don't hold them, those columns are dropped entirely server-side — never shown blank — from the preview, the saved report, and the CSV alike. The same applies to filters and sorts on those columns, so there's no way to infer a hidden value by filtering on it.