A client sends you a .pbix that already has its tables, relationships and report pages. The request sounds small: add gross pay, headcount, cost per employee and overtime percentage. The risky part is not typing DAX. It is understanding what each number means in the model the client already has.
Start by tracing the existing model, then build a small set of reusable measures and test each one against the source. This keeps a quick enhancement from turning into a quiet reporting error.
Read the model before writing measures
Open Model view and identify the payroll fact table, employee and department dimensions, date table, and the relationships between them. Check which relationships are active, their filter direction, and whether the fact table has one row per employee, pay period, earning code, or pay transaction.
That last detail is the grain. If a person appears on several earning rows in one pay period, counting fact rows will overstate headcount. Find the employee key and decide whether the requested headcount means distinct employees with payroll activity, employees on the roster, or an end-of-period snapshot. Those are different business definitions.
Check column names, data types, hidden fields, existing measures and format strings before adding anything. Reuse a shared measure table or naming convention already in the file. Do not create a second date or department table just because it is easier than understanding the current one.
Build base measures first
Keep additive measures close to the source columns. For example, if Payroll contains one row per earning transaction and Payroll[GrossAmount] is the approved gross-pay amount:
Gross Pay =
SUM ( Payroll[GrossAmount] )
For headcount, if the request means distinct employees with rows in the current filter context:
Headcount =
DISTINCTCOUNT ( Payroll[EmployeeKey] )
Confirm the definition with the client when the model includes inactive employees, multiple jobs, retroactive adjustments, or employees without a payroll transaction. A distinct count of the fact table answers “who appears in these rows,” not necessarily “who was employed on the last day of the period.”
Build derived measures from those base measures. Use DIVIDE so zero or missing denominators return a controlled result instead of an error:
Cost per Employee =
DIVIDE ( [Gross Pay], [Headcount] )
Overtime % =
DIVIDE ( [Overtime Hours], [Total Hours] )
Define Overtime Hours and Total Hours as their own base measures, using the model’s earning-code or hours columns. Format the percentage as a percentage in the model; do not multiply by 100 and then format it as a percentage again.
Filter context and row context in a payroll example
A measure is evaluated in the filter context created by the visual, slicers and relationships. Put [Gross Pay] in a card and it returns the total for the current page filters. Put Department on a matrix row and the same measure is recalculated for each department. The department row filters the related payroll rows through the model.
Row context is different: it exists while an expression iterates over rows, such as inside SUMX. A normal SUM measure does not need a calculated column or an iterator just to total gross pay. For a weighted cost per employee, divide the filtered gross-pay total by the filtered employee count. Do not average department-level costs to calculate an organization-wide cost; departments can have very different headcounts.
A useful check is to add Department, [Gross Pay], [Headcount], [Cost per Employee] and [Overtime %] to a table. If the measures show the same value on every row, inspect the relationship path and the field used for Department. If only overtime fails, verify the earning-code filter and its relationship to the fact table.
Add time intelligence after the date table is ready
Before writing month-over-month or year-to-date measures, confirm that the model has a complete, continuous date table, related to the correct payroll date, and marked as a date table in Power BI. If payroll has separate pay-period-end and transaction dates, decide which one the report should use; an inactive relationship may require USERELATIONSHIP in specific measures.
Once the date table and relationship are correct, a prior-month measure can use the existing date dimension:
Gross Pay Previous Month =
CALCULATE (
[Gross Pay],
DATEADD ( 'Date'[Date], -1, MONTH )
)
For a year-to-date total:
Gross Pay YTD =
TOTALYTD ( [Gross Pay], 'Date'[Date] )
Test these with months that cross a year boundary and with the date slicers used by the report. Time intelligence added to an unmarked or incomplete date table can look plausible while returning gaps or unexpected totals.
Validate each measure against the source
For a fixed test period and department, export the same aggregation from the source system or run a SQL query against the payroll view. Compare gross pay, distinct employee count, overtime hours and total hours with the Power BI results. Use the same date column, earning codes, employee status rules, currency and rounding on both sides.
Then repeat the comparison for a single department, multiple departments, a month, a full year and a period with no overtime. Document the test filters and expected totals. A grand total that matches is not enough if the measure breaks when someone slices by department or pay period.
Common mistakes in an existing .pbix
- Summing a calculated column for a value that should be a measure. Use a measure for totals that must respond to report filters; keep row-level attributes in columns only when the model needs them.
- Counting payroll rows as people. Multiple earning codes and pay transactions can create several rows per employee. Use the agreed employee definition and key.
- Ignoring an inactive relationship. A date or department field may not filter the fact table through the relationship you expect. Check the active path and use
USERELATIONSHIPonly when that is the intended date or key. - A measure that breaks by department. Check relationship direction, dimension keys, and whether the department field comes from the dimension that actually filters payroll.
- A ratio that adds up incorrectly. Recalculate the numerator and denominator in the current context with
DIVIDE; do not sum row-level or department-level percentages. - Time intelligence before fixing dates. Mark and validate the date table first, then add previous-period and YTD calculations.
The safest extension is usually small: understand the grain and relationships, add base measures, derive ratios, then reconcile the results with the source. That gives the client the requested numbers while preserving the behavior of the report they already rely on.