Autotask stores purchase order references across charges, contracts, projects, and service call tasks. This report maps those fields, shows the full charge structure from the demo dataset, and gives you working DAX queries to filter by PO number in your own Power BI environment.
Autotask stores purchase order references across charges, contracts, projects, and service call tasks. This report maps those fields, shows the full charge structure from the demo dataset, and gives you working DAX queries to filter by PO number in your own Power BI environment.
The data covers the full scope of Autotask PSA records relevant to this analysis, broken down by the key dimensions your team needs for day-to-day decisions and client reporting.
Who should use this: Operations managers, service delivery leads, and MSP owners managing capacity
How often: Weekly for scheduling, monthly for utilization reviews, quarterly for staffing decisions
Autotask stores purchase order references across charges, contracts, projects, and service call tasks. This report maps those fields, shows the full charge structure from the demo dataset, and gives you working DAX queries to filter by PO number in your own Power BI environment.
The 0% coverage shown here is a direct reflection of the demo dataset, where purchase_order_number and internal_po_number have not been populated. This is not an error — it mirrors what many MSPs look like before they start enforcing PO tracking. In a live Autotask environment where clients have PO requirements, these fields carry the authorization reference and this report would show coverage rates per client, per PO, and per charge category.
EVALUATE
ROW(
"Total_Charge_Records",
COUNTROWS('BI_Autotask_Charges'),
"Total_Billable",
SUM('BI_Autotask_Charges'[billable_amount]),
"Charges_With_PO",
CALCULATE(
COUNTROWS('BI_Autotask_Charges'),
NOT(ISBLANK('BI_Autotask_Charges'[purchase_order_number]))
),
"Charges_Without_PO",
CALCULATE(
COUNTROWS('BI_Autotask_Charges'),
ISBLANK('BI_Autotask_Charges'[purchase_order_number])
),
"Unique_PO_Numbers",
DISTINCTCOUNT('BI_Autotask_Charges'[purchase_order_number])
)
Even without PO numbers assigned, this data shows the full scope of what would need to be covered once PO enforcement starts. SLA charges and hardware together make up over 94% of total billable volume. If a client requires POs, these two categories are where the tracking gap creates the biggest financial exposure.
| Metric | Value |
|---|---|
| Charges | 7,155 |
| Revenue | $6,697,517 |
| PO Numbers | 1 |
EVALUATE ROW("TotalCharges", COUNTROWS('BI_Autotask_Charges'), "TotalRevenue", SUM('BI_Autotask_Charges'[billable_amount]), "DistinctPOs", DISTINCTCOUNT('BI_Autotask_Charges'[purchase_order_number]))
Autotask does not have a single "PO tracking" screen. Instead, purchase order references sit on four different objects depending on the type of work: charge records, contracts, projects, and tasks. Understanding which field to populate — and when — is the first step toward consistent PO tracking.
-- Check PO field coverage across all four Autotask objects
EVALUATE
UNION(
ROW(
"Object", "Charges (BI_Autotask_Charges)",
"Total_Records", COUNTROWS('BI_Autotask_Charges'),
"With_PO", CALCULATE(
COUNTROWS('BI_Autotask_Charges'),
NOT(ISBLANK('BI_Autotask_Charges'[purchase_order_number]))
)
),
ROW(
"Object", "Contracts (BI_Autotask_Contracts)",
"Total_Records", COUNTROWS('BI_Autotask_Contracts'),
"With_PO", CALCULATE(
COUNTROWS('BI_Autotask_Contracts'),
NOT(ISBLANK('BI_Autotask_Contracts'[purchase_order_number]))
)
),
ROW(
"Object", "Projects (BI_Autotask_Projects)",
"Total_Records", COUNTROWS('BI_Autotask_Projects'),
"With_PO", CALCULATE(
COUNTROWS('BI_Autotask_Projects'),
NOT(ISBLANK('BI_Autotask_Projects'[purchase_order_number]))
)
),
ROW(
"Object", "Tasks (BI_Autotask_Tasks)",
"Total_Records", COUNTROWS('BI_Autotask_Tasks'),
"With_PO", CALCULATE(
COUNTROWS('BI_Autotask_Tasks'),
NOT(ISBLANK('BI_Autotask_Tasks'[purchase_order_number]))
)
)
)
These are the three queries you are most likely to need. The first pulls all charges against a specific PO. The second lists all charges where no PO is assigned — your billing exposure report. The third cross-references charges with time entries to show actual hours worked per PO. Swap PO-2026-001 for your client's actual PO number.
-- All charge records for a specific client PO number
-- Replace "PO-2026-001" with the actual PO number
EVALUATE
CALCULATETABLE(
SELECTCOLUMNS(
'BI_Autotask_Charges',
"Company", RELATED('BI_Autotask_Companies'[company_name]),
"PO_Number", 'BI_Autotask_Charges'[purchase_order_number],
"Internal_PO", 'BI_Autotask_Charges'[internal_po_number],
"Material_Code", 'BI_Autotask_Charges'[material_code_name],
"Description", 'BI_Autotask_Charges'[description],
"Quantity", 'BI_Autotask_Charges'[quantity],
"Billable_Amount", 'BI_Autotask_Charges'[billable_amount],
"Cost", 'BI_Autotask_Charges'[extended_cost],
"Invoice_Number", 'BI_Autotask_Charges'[invoice_number]
),
'BI_Autotask_Charges'[purchase_order_number] = "PO-2026-001"
)
ORDER BY 'BI_Autotask_Charges'[billable_amount] DESC
-- Charges with no PO number assigned — your billing exposure
-- Run this before each invoice cycle to catch uncovered items
EVALUATE
SUMMARIZECOLUMNS(
'BI_Autotask_Charges'[company_id],
RELATED('BI_Autotask_Companies'[company_name]),
'BI_Autotask_Charges'[material_code_name],
FILTER(
ALL('BI_Autotask_Charges'),
ISBLANK('BI_Autotask_Charges'[purchase_order_number])
&& ISBLANK('BI_Autotask_Charges'[invoice_number])
),
"Uninvoiced_Lines_No_PO",
COUNTROWS('BI_Autotask_Charges'),
"Total_Billable_Exposed",
SUM('BI_Autotask_Charges'[billable_amount])
)
ORDER BY [Total_Billable_Exposed] DESC
-- Hours logged against a PO number via the contract relationship
-- Contracts carry the PO; time entries link to contracts
EVALUATE
SUMMARIZECOLUMNS(
'BI_Autotask_Contracts'[purchase_order_number],
FILTER(
ALL('BI_Autotask_Contracts'),
NOT(ISBLANK('BI_Autotask_Contracts'[purchase_order_number]))
),
"Contract_Name",
CALCULATE(VALUES('BI_Autotask_Contracts'[contract_name])),
"Company",
CALCULATE(VALUES(RELATED('BI_Autotask_Companies'[company_name]))),
"Total_Hours_Worked",
CALCULATE(
SUM('BI_Autotask_TimeEntries'[hours_worked]),
USERELATIONSHIP(
'BI_Autotask_TimeEntries'[contract_id],
'BI_Autotask_Contracts'[id]
)
),
"Total_Billable_Hours",
CALCULATE(
SUM('BI_Autotask_TimeEntries'[billing_amount]),
USERELATIONSHIP(
'BI_Autotask_TimeEntries'[contract_id],
'BI_Autotask_Contracts'[id]
)
)
)
ORDER BY [Total_Hours_Worked] DESC
Most MSPs start using Autotask to track time and generate invoices. PO fields are optional, so they get left blank. Six months in, one of your larger clients comes back to say they cannot process an invoice because their procurement system requires a PO number that was never captured. The invoice sits in accounts payable limbo until someone chases the client's finance team and manually traces back what authorization was in place at the time the work was done.
The charge categories in this dataset tell you where the risk concentrates. SLA charges make up €3.9M of the €6.7M total. These usually get covered by a contract-level PO for managed services clients. Hardware charges at €2.6M are the bigger gap: most hardware purchases need a client PO before the order is placed, let alone before billing. If those charges reach the invoice without a PO reference, payment delays follow.
Consulting charges at €106K and cloud costs at €47K are smaller by volume but often the most contested at invoice time. Clients frequently dispute professional services charges when there is no written authorization. A PO number on each consult line item serves as that authorization and eliminates the dispute before it starts.
The good news: Autotask makes it easy to require PO fields at the account level. Under Account settings > Billing preferences, you can flag specific clients as requiring PO numbers before charges can be invoiced. This surfaces as a validation warning in Autotask and prevents charges from being invoiced until the field is populated. The setup takes about ten minutes per client.
Start with your largest accounts by billable volume. Hardware-heavy clients and enterprise accounts with procurement departments almost always have a PO requirement. Pull a list of your top 10 accounts by charges value and check each one. Flag the accounts that have ever disputed an invoice over a missing PO — those are your first priority for enforcement.
In Autotask, go to the account record and enable the “Require PO Number” setting under billing preferences. Once enabled, Autotask will prompt for a PO number before a charge can be marked billable or included in an invoice. This turns the tracking gap into a process enforcement point rather than a reporting afterthought.
For clients on monthly managed services agreements, assign the PO number at the contract level rather than requiring a new PO per charge line. The contract PO pre-authorizes all charges generated under that agreement for the contract period. When a new PO number is issued for the next period, update the contract record. This reduces admin burden while maintaining coverage.
Use Query 2 from section 4.0 to pull all uninvoiced charges with no PO number before each billing cycle. This gives your finance team a pre-invoice checklist: contact the client for any missing PO references, update the charge records, then run the invoice. Building this into your monthly billing workflow prevents the awkward post-invoice call and keeps cash flow predictable.
Yes. When the PO number is set at the contract level, you can join the contracts table to the time entries table on contract_id to see all hours worked under that contract — and by extension, under that PO authorization. Use Query 3 in section 4.0 for this. The join works through the BI_Autotask_Contracts table, which holds the PO reference, linked to BI_Autotask_TimeEntries via the contract_id foreign key.
purchase_order_number holds the client's own PO reference — the number their procurement team issued. This is what goes on the invoice and what their finance team uses to match your invoice against their internal approval. internal_po_number is your own reference, used for internal job costing, cross-referencing with your finance system, or linking to a specific project budget. Both fields are optional in Autotask; the client-facing one is what matters for invoice disputes.
Autotask invoice templates support the purchase_order_number field as a merge field. In your invoice template settings, add the PO number field to the invoice header or line item section. Once the template includes the field, any charge with a PO number will show that reference on the generated invoice PDF. For charges with no PO assigned, the field will be blank — which is your signal that the pre-invoice check found a gap.
Autotask does not store a PO budget amount natively, so you cannot pull a budget figure from the data model automatically. The practical approach is to maintain a reference table with PO numbers and their authorized amounts, load that table into Power BI, and join it to the charges or time entries on purchase_order_number. From there you can build a utilization visual showing charges or hours consumed versus the authorized amount per PO. This setup takes about an hour in Power BI and gives you a full PO burn-down view per client.
Connect Proxuma Power BI to your PSA, RMM, and M365 environment, use an MCP-compatible AI to ask questions, and generate custom reports - in minutes, not days.
See more reports Get started