The modeled report, not the pipe

An Odoo Power BI Dashboard Modeled for Margin, Projects and Inventory Turns

Getting rows out of Odoo is a solved problem — we settle the route in the first conversation. The work worth paying for is the model underneath: real cost attached to each sale, timesheet hours attached to each project, and stock movement history behind every turns number. Senior US-based engineers build it, then hand it over.

  • Senior-led (20+ yrs)
  • US-based engineer
  • Hours never expire
  • 3–5 day turnaround
  • Full refund — first 4 hrs
Data Model Sales Date Product Region Customer

Representative dashboard — sample data

Where the native views run out

A pivot view answers today. It cannot answer the last three years.

Odoo's list, pivot, and graph views are genuinely good at what they are for: looking at live records inside the app, grouped the way you need them this morning. They are bound to one model at a time, they read the records as they stand right now, and they have no memory. So the questions that decide the quarter — which product lines actually earn once real cost is attached, which projects consumed more hours than they billed, how many times stock turned by warehouse — get answered in a spreadsheet instead, rebuilt by hand every month.

That is the gap we fill, and it is a modeling gap rather than a plumbing one. Most vendors selling into this space sell you a pipe: a module or a connector that copies Odoo tables somewhere. You still own the hard part afterwards. We treat the extraction route as a sub-step to be settled and documented in the first conversation, then spend the engagement where the value is — a star schema across sales, invoicing, stock valuation, projects, and timesheets, with measures your finance lead will stand behind.

What we build from Odoo

  • Margin by product line
  • Project profitability
  • Inventory turns & stock cover
  • Sales pipeline & win rate
  • AR aging & days sales outstanding
  • Purchasing & supplier spend

Try us risk-free for 4 hours

Buy a block of prepaid Flex hours and, if you're unhappy within the first 4 hours, we refund you in full — no questions asked. We scope each task and quote the hours before work starts, hours deduct only for work you approve, and your banked hours never expire.

Why these builds stall

Five things that break a first Odoo reporting build

None of them is a visual problem, and none of them is fixed by a better connector. Each one produces a number that looks plausible and is wrong — which is worse than a report that obviously fails.

Current cost is not historical cost

A product's cost field holds what the item costs today, and it is company-dependent. Multiply last year's quantities by it and margin quietly restates every time purchasing repricing runs. Real cost of goods lives in the valuation layers Odoo writes as stock moves, which is where margin has to come from.

Status fields store keys, not labels

Selection fields come back as technical values — a sale order's state, an invoice's move type — not the words shown in the interface. Read them raw and cancelled orders join the pipeline, credit notes add to revenue instead of reducing it, and draft entries land in a posted-ledger total.

Analytic distribution is a JSON map

Recent Odoo versions spread a journal item across analytic accounts as a JSON map of account to percentage, rather than one tidy foreign key. Ignore it and project cost either lands whole against one project or disappears entirely — and project profitability is usually the first thing anyone checks.

Record rules silently narrow the extract

Odoo filters what a user may read by company and by access rules, and the API honours that. An integration user with the wrong company set does not error — it returns fewer rows. Multi-company groups discover this when a consolidated total is short and nobody can say by how much.

The whole-history pull does not scale

Reading every sale line and journal item on every refresh works for the first demo and degrades from there. The API pages results, the database copy grows, and the refresh window closes. Incremental loading on a write date is the difference between a dashboard and a maintenance problem.

How does Power BI read Odoo data?

Through Odoo's external API or a database connection — never by pointing Power Query at Odoo directly. Power BI ships no first-party Odoo connector and Odoo publishes no OData endpoint, so a scheduled job reads the API (or a PostgreSQL replica), lands the records into a store you own, and Power BI models that copy on a refresh schedule.

The API route is the one that works everywhere, including Odoo Online. Odoo exposes its models over XML-RPC and JSON-RPC, and the calls are documented in Odoo's own external API reference (opens in new tab): authenticate a dedicated integration user with an API key, then read each model with explicit field lists and domain filters rather than pulling whole tables. That discipline matters more than it sounds — a narrow, named field list is what keeps a nightly pull predictable and makes an upgrade's schema change visible immediately instead of six weeks later.

Where you host Odoo yourself, or on a platform that exposes the database, a read-only PostgreSQL connection is usually available and is the cheapest route for long history: real SQL, proper incremental keys, and no load on the application workers. It is also the route that most needs a careful hand, because the physical schema is not the API. Many-to-one fields are bare integer columns, many-to-many relations live in join tables, and on recent versions translatable text such as a product name is stored as JSON, so a naive read returns a blob where you expected a string.

Downstream, the pattern is identical whichever route we take. We land a copy, refresh it incrementally so each run moves only records changed since the last one — Microsoft's incremental refresh reference (opens in new tab) explains how the partitioning works — and let Power BI query the modeled copy. A daily scheduled refresh is the usual cadence and more frequent runs are available; the ceilings are set out in Microsoft's data refresh reference (opens in new tab). Where Odoo runs inside your own network, an on-premises data gateway (opens in new tab) carries the connection; we configure it, document it, and fix breakages out of your banked hours.

Which extraction route fits your deployment?

This is the honest comparison behind the scope estimate you approve before any hours are spent. We confirm the route with whoever administers your Odoo instance first, because the wrong assumption here is what turns a five-day build into a five-week one.

Routes out of Odoo into Power BI, compared
Route out of Odoo Best for Tradeoff
External API (XML-RPC or JSON-RPC) under an API key Odoo Online and any deployment where the database is closed; works on Community and Enterprise alike Paged reads and per-model field lists to maintain; access rules on the integration user decide what you can see
Read-only PostgreSQL replica Self-hosted and platform deployments with a database connection; long history at low cost You read the physical schema — integer keys, join tables, JSON text fields — and it can change on upgrade
Scheduled extracts into a store you own Teams that want the pull to survive an Odoo upgrade with no live coupling at all Freshness is bounded by the export schedule; needs somewhere to land the files
Third-party connector module Getting a first look at a handful of tables quickly A recurring licence for the pipe only; the model, the cost logic, and the measures are still yours to build
Landed copy modeled as a star schema Every production build, on top of whichever route above applies Adds a modeling layer — which is the point, not a cost

Which Odoo records sit behind each dashboard?

Every view below maps to records your instance already holds. The models named are Odoo's standard ones; your version, the apps you have installed, and any custom fields your team or integrator has added change the detail, so we confirm the mapping against your own instance before quoting the work.

Dashboard area, the Odoo data behind it, and the question it settles
Dashboard area Odoo records it reads Question it answers
Margin by product line Invoice lines (account.move.line) joined to product and category (product.product, product.template) with cost taken from inventory valuation layers Which lines and items actually earn once real cost of goods is attached?
Project profitability Projects and tasks (project.project, project.task) with timesheet and analytic entries (account.analytic.line) and the billed side from sales and invoices Which projects consumed more hours and cost than they recovered?
Inventory turns & stock cover Stock moves (stock.move) and valuation history against current on-hand quantities (stock.quant) by warehouse How fast does each item turn, what is sitting still, and where are we short?
Sales pipeline & orders CRM opportunities (crm.lead) and sale orders with their lines (sale.order, sale.order.line), filtered on real states What is genuinely open, what converted, and what is on order but unshipped?
AR aging & cash collection Posted customer entries in account.move and account.move.line with their reconciliations and credit notes What is actually open, how old is it, and who owns the oldest balances?
Purchasing & supplier spend Purchase orders and lines (purchase.order) against vendor bills and the partner master (res.partner) What are we committed to, with whom, and how does receipt timing compare to promise?

Which Odoo data traps break a first build?

The same handful, on nearly every engagement. None is exotic, and each one is cheap to handle at modeling time and expensive to discover after finance has quoted a number from the dashboard in a board meeting.

  • Cost of goods comes from valuation, not from the product form. The cost field on a product is current and company-dependent; historical margin has to read the valuation layers written as stock moved.
  • Draft is not posted, and a credit note is not a sale. Ledger measures have to filter on posted state and respect the entry type, or revenue includes refunds and entries nobody has approved.
  • Variants and templates are different grains. Reporting by product template hides variant-level movement; reporting only by variant makes a product-line total impossible. The model needs both, related properly.
  • Document currency and company currency are both true. On multi-currency instances a measure must declare which one it reports, or two departments will quote different revenue forever.
  • Units of measure are not interchangeable. Odoo converts between a product's stocking and purchase units; sum a quantity column across them and inventory turns becomes a number with no meaning.
  • Company is a dimension, not a filter. Multi-company groups share partners and products; a group total built without an explicit company key double counts, and inter-company documents land twice.
  • Records change after you read them. Invoices get reconciled and orders get amended weeks later, so an incremental load keyed on a write date — not a creation date — is what keeps the copy honest.

What does the modeling actually change?

It turns a pile of copied Odoo tables into one governed semantic model: fact tables at a declared grain for invoice lines, order lines, stock movements, and analytic entries, surrounded by conformed dimensions for date, company, product and category, partner, warehouse, project, and salesperson.

That structure is what lets one filter move across subject areas — pick a company and a quarter, and revenue, margin, stock, and project cost all respond together, because they share dimensions instead of each carrying a private copy of the calendar. It is also what makes measures reusable: gross margin, days sales outstanding, and inventory turns get defined once and agree everywhere they appear. If you want the reasoning in full, our guide to star schema modeling in Power BI walks through why a wide table underperforms, and the broader discipline is described on our Power BI data modeling and DAX page.

Who is this for, and what does it cost?

Distributors, manufacturers, and services firms running Odoo — Community or Enterprise, hosted by Odoo or by you — usually where a founder, controller, or operations lead rebuilds the same workbook every month. You buy prepaid Flex hours that never expire: Starter at $2,500, Growth at $4,600, or Power at $8,000, with hourly rates from $100 on the largest block.

Because we scope each task and quote the hours before any work begins, you agree the cost of a build up front rather than watching a meter run. The usual pattern is a Growth or Power block: the extraction route, the model, and the first pages are front-loaded, and the remaining hours go to extra views, measure changes, and refresh care over the following months. Most individual tasks ship in 3–5 business days after approval; a first full ERP build is larger and gets a clear timeline up front. There is no project minimum and no retainer — the mechanics are on how prepaid Flex hours work.

If the group runs more than one system, we build the same reporting on the neighbouring mid-market ERPs: Acumatica Power BI dashboards covers the Generic Inquiry and OData route, Business Central Power BI dashboards covers the Dynamics side, and SAP Business One Power BI dashboards covers HANA and SQL instances. Distributors will recognise most of the stock and margin reporting on our Power BI for wholesale distributors page, and where Odoo handles operations while the books stay elsewhere, our QuickBooks Power BI dashboards page covers joining the two.

What do you own when we finish?

Everything. The Power BI file, the semantic model and DAX, the extraction logic and field lists, the staging store, the refresh schedule, and the integration user all live in your tenant and your Odoo instance. There is no black box, no licence to us, and no lock-in — if you never send another request, the dashboards keep running.

We publish to your workspace, document the access route and every model we pull, and walk your team through the semantic model so someone internal can maintain it. Most clients then keep a few banked hours for what follows: a page for a newly added company, a measure that has to track a changed costing method, or a refresh that broke after an Odoo version upgrade moved a field. That is post-launch support without a retainer — the same hours, spent when you need them. Contact is form-only: send your request and we reply with a scope estimate.

What we build from Odoo

  • Margin by product line and item
  • Project profit with timesheet cost
  • Inventory turns by warehouse
  • Pipeline, orders & backlog
  • AR aging and collection trend
3–5

business days, typical

Prepaid

hours, never expire

You

own everything

Extraction, modeling, DAX and refresh care all draw from the same banked hours. See Flex pricing →

How it works

From Odoo access to live reporting in three steps

  1. 1

    Settle the route in one call

    Tell us your Odoo version and edition, whether it is hosted by Odoo or by you, how many companies you report on, and the views you need first. We confirm what your administrator can open up, then reply with a scope and an hours estimate before any work begins.

  2. 2

    Model, cost and reconcile

    A senior US-based engineer builds the extraction, lands the records incrementally, shapes the star schema on date, company, product, partner, warehouse and project, attaches real cost from the valuation history, and writes the DAX — then ties every total back to the Odoo reports your team already trusts.

  3. 3

    Refresh and hand over

    We publish to your workspace, configure the gateway and a daily scheduled refresh, apply row-level security by company or warehouse where you need it, document everything, and hand it over. You own it outright; we keep the refresh healthy from your banked hours.

Representative perspective

We had already paid for a connector and still had no margin number anyone believed. The fix was attaching real cost from stock valuation rather than the product form — nothing to do with how the data got out.
Controller, mid-market distributor

Representative perspective

Project profitability was the whole reason we asked. Getting timesheet hours and vendor bills onto the same project, with the analytic split handled properly, was the part we could not do ourselves.
Operations director, professional services firm

Skip the connector debate. Start with the report you need.

Tell us your Odoo edition, your company structure, and the three numbers you want first. We'll confirm the extraction route, scope the task and quote the hours before any work begins — drawn from prepaid Flex hours that never expire, with no project minimums.

FAQ

Odoo and Power BI questions, answered

No. Power BI ships no first-party Odoo connector, and Odoo exposes no OData endpoint, so Power Query cannot simply point at your database. Something has to read Odoo's external API, or a PostgreSQL replica, and land the rows where Power BI can model them. Choosing that route is a sub-step of the build, not the build.

Yes. On Odoo Online we read the external API with a dedicated user and an API key, pulling each model with search_read against explicit field lists, and land the records into a store you own. Self-hosted and Odoo.sh deployments usually also allow a read-only PostgreSQL connection, which is cheaper for long history.

Usually because the report multiplies quantity by the product's current cost field. That field is today's cost, not the cost at the time of the sale, and it is company-dependent. Real historical margin comes from the inventory valuation layers Odoo writes as stock moves, reconciled to posted journal items.

Yes, and they stay the right tool for live operational questions on a record you are working in. Power BI answers a different class of question: multi-year trend, project profitability with timesheet cost attached, inventory turns, and sales next to invoicing next to stock in a single model with one set of agreed measures.

No. We are an independent, US-based Power BI practice and claim no Odoo or Microsoft partnership. Our engineers are Microsoft-certified, and we build only with documented, supported tooling: Odoo's published external API or a database connection your administrator approves, plus Power BI's gateway and incremental refresh features.

Book a free demo

Get senior Power BI help, on demand

Tell us the task. We scope it, you approve, and a senior consultant delivers — typically in 3–5 business days. Prepaid hours that never expire.

  • Prepaid hours from $100/hour — and they never expire
  • Senior-led delivery — 20+ years on the Microsoft platform
  • Typical 3–5 business day turnaround
  • Full refund if you’re unhappy in your first 4 hours

The fastest way to reach us is the form — tell us the task and we'll reply within one business day.

All fields are required.

We reply within one business day. No newsletters, no drip campaigns.

Book a Free Demo