Part of our Data Analytics & BI series
Read the complete guideOdoo + Power BI: Complete Analytics Integration Guide
Odoo 19 Enterprise includes solid built-in reporting, but for organizations that need self-service analytics, cross-system data modeling, and enterprise-grade visualization, Power BI is the natural complement. Connecting Odoo's operational data to Power BI's analytical engine unlocks insights that Odoo's native reports can't provide.
This guide covers every aspect of the Odoo-Power BI integration: connection architecture, data modeling best practices, building key business dashboards, DAX measure creation, incremental refresh configuration, and deployment in Microsoft Fabric for enterprise scale.
Key Takeaways
- Three connection methods: PostgreSQL direct, Odoo REST API, and ODBC via Odoo's export
- DirectQuery mode provides real-time data; Import mode provides better performance for large datasets
- Odoo's PostgreSQL schema requires denormalization for efficient Power BI data models
- Incremental refresh reduces load time for large tables (account.move, stock.move)
- Row-Level Security in Power BI mirrors Odoo's company-level access control
- Gateway deployment required for on-premises Odoo; cloud Odoo connects directly
- Microsoft Fabric (Power BI Premium) enables Odoo data in enterprise lakehouse
- Key measures: revenue, gross margin, inventory turnover, receivables aging, OEE
Integration Architecture Options
Choose the right connection architecture based on your Odoo deployment and reporting needs.
Option 1: Direct PostgreSQL Connection (Recommended)
Connect Power BI directly to Odoo's PostgreSQL database using the PostgreSQL connector:
Pros:
- Full access to all Odoo tables and raw data
- Best performance for complex joins
- Supports both Import and DirectQuery modes
Cons:
- Requires network access from Power BI Gateway to PostgreSQL
- Changes in Odoo's data model require updating Power BI queries
- Direct database access bypasses Odoo's access control
Option 2: Odoo REST API
Connect via Power BI's Web connector using Odoo's REST API:
Pros:
- Works without network access to PostgreSQL
- Respects Odoo's access rights per user
- No database credentials required
Cons:
- Slower than direct PostgreSQL (one API call per table)
- Rate limiting affects large data pulls
- Difficult to paginate efficiently for large datasets
Option 3: Export to Data Warehouse
ETL Odoo data into a dedicated data warehouse (Azure Synapse, Snowflake, BigQuery):
Pros:
- Maximum performance at scale
- Decouples BI from ERP
- Can integrate multiple source systems
Cons:
- Highest infrastructure cost and complexity
- Data latency depends on ETL schedule (typically 1 hour to 24 hours)
- Requires ETL pipeline maintenance
Recommended architecture for most organizations: Direct PostgreSQL with Power BI Gateway (on-premises) or direct connection (cloud Odoo), Import mode with incremental refresh, scheduled refresh every 1-4 hours.
Setting Up the PostgreSQL Connection
Step 1: Network access
For on-premises Odoo:
- Install the On-Premises Data Gateway on a server with network access to PostgreSQL
- Configure the gateway with your Microsoft 365 credentials
- Open PostgreSQL port (5432 or 5433) from the gateway server to the Odoo DB server
For cloud Odoo (AWS, Azure, GCP):
- Configure security group / firewall to allow inbound from Power BI's IP ranges
- Or: use the on-premises gateway on a cloud VM in the same VPC
Step 2: Create a read-only database user
Never connect Power BI with your main Odoo database user. Create a dedicated read-only user:
-- Create read-only user for Power BI
CREATE USER powerbi_reader WITH PASSWORD 'strong_password_here';
-- Grant connection to database
GRANT CONNECT ON DATABASE your_odoo_db TO powerbi_reader;
-- Grant schema usage
GRANT USAGE ON SCHEMA public TO powerbi_reader;
-- Grant SELECT on all current and future tables
GRANT SELECT ON ALL TABLES IN SCHEMA public TO powerbi_reader;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO powerbi_reader;
Step 3: Configure in Power BI Desktop
- Open Power BI Desktop → Get Data → PostgreSQL Database
- Enter:
- Server: your PostgreSQL host (and port if not 5432)
- Database: your Odoo database name
- Username: powerbi_reader
- Password: the read-only user password
- Select connection mode: Import (recommended) or DirectQuery
Key Odoo Tables for Power BI
Understanding Odoo's PostgreSQL schema is essential for building accurate data models.
Financial tables:
| Table | Description | Key Fields |
|---|---|---|
account_move | Invoices, bills, journal entries | move_type, state, invoice_date, amount_total, currency_id |
account_move_line | Line items on journal entries | move_id, account_id, debit, credit, quantity, price_subtotal |
account_account | Chart of accounts | code, name, account_type |
account_payment | Customer/vendor payments | amount, payment_date, state, partner_id |
Sales tables:
| Table | Description | Key Fields |
|---|---|---|
sale_order | Sales orders | name, state, date_order, amount_total, partner_id, user_id |
sale_order_line | Sale order lines | order_id, product_id, product_uom_qty, price_subtotal |
crm_lead | CRM opportunities | name, stage_id, expected_revenue, probability, user_id |
Inventory tables:
| Table | Description | Key Fields |
|---|---|---|
stock_quant | Current inventory levels | product_id, location_id, quantity |
stock_move | All inventory movements | product_id, state, date, quantity_done |
stock_picking | Delivery/receipt documents | picking_type_id, state, scheduled_date |
product_template | Product master data | name, list_price, categ_id, type |
HR and Payroll:
| Table | Description | Key Fields |
|---|---|---|
hr_employee | Employees | name, department_id, job_id, company_id |
hr_attendance | Time and attendance | employee_id, check_in, check_out |
hr_payslip | Payslips | employee_id, date_from, date_to, state |
Power BI Data Model Design
Star schema design for Odoo data:
Convert Odoo's normalized schema to a star schema for optimal Power BI performance:
[Date Table] (dimension)
↓
[Sales Fact Table]
↓
[Product Dimension] ← [Product Category Dimension]
↓
[Customer Dimension] ← [Country Dimension]
↓
[Salesperson Dimension]
↓
[Company Dimension]
Power Query M code — Sales Fact Table:
let
Source = PostgreSQL.Database("your-odoo-server:5433", "your_db"),
SaleOrderLine = Source{[Schema="public", Item="sale_order_line"]}[Data],
SaleOrder = Source{[Schema="public", Item="sale_order"]}[Data],
ProductTemplate = Source{[Schema="public", Item="product_template"]}[Data],
ProductProduct = Source{[Schema="public", Item="product_product"]}[Data],
// Join order lines with orders
JoinWithOrder = Table.NestedJoin(
SaleOrderLine, {"order_id"},
SaleOrder, {"id"},
"Order", JoinKind.Inner
),
// Expand order columns needed
ExpandOrder = Table.ExpandTableColumn(
JoinWithOrder, "Order",
{"name", "state", "date_order", "partner_id", "user_id", "company_id"},
{"order_name", "order_state", "date_order", "partner_id", "user_id", "company_id"}
),
// Filter: confirmed and done orders only
FilterState = Table.SelectRows(
ExpandOrder,
each [order_state] = "sale" or [order_state] = "done"
),
// Select and rename final columns
SelectColumns = Table.SelectColumns(FilterState, {
"id", "order_id", "product_id", "date_order", "partner_id",
"user_id", "company_id", "product_uom_qty", "price_unit",
"price_subtotal", "price_tax", "price_total"
}),
// Change types
ChangedTypes = Table.TransformColumnTypes(SelectColumns, {
{"date_order", type datetime},
{"price_subtotal", type number},
{"product_uom_qty", type number}
})
in
ChangedTypes
Essential DAX Measures
Revenue and margin:
// Total Revenue (Net)
Revenue = SUMX(SalesFact, SalesFact[price_subtotal])
// Revenue MTD
Revenue MTD =
CALCULATE([Revenue], DATESMTD(DateTable[Date]))
// Revenue YTD
Revenue YTD =
CALCULATE([Revenue], DATESYTD(DateTable[Date]))
// Revenue vs Prior Period
Revenue vs PY =
VAR CurrentRevenue = [Revenue]
VAR PriorYearRevenue =
CALCULATE([Revenue], SAMEPERIODLASTYEAR(DateTable[Date]))
RETURN
DIVIDE(CurrentRevenue - PriorYearRevenue, PriorYearRevenue, 0)
// Gross Margin
Gross Margin =
SUMX(SalesFact,
SalesFact[price_subtotal] -
(RELATED(ProductDim[standard_price]) * SalesFact[product_uom_qty])
)
// Gross Margin %
Gross Margin % =
DIVIDE([Gross Margin], [Revenue], 0)
Inventory measures:
// Current Stock Value
Stock Value =
SUMX(
StockQuant,
StockQuant[quantity] * RELATED(ProductDim[standard_price])
)
// Inventory Turnover (annualized)
Inventory Turnover =
DIVIDE(
[COGS Annualized],
[Average Inventory Value],
0
)
// Days of Inventory Outstanding
DIO =
DIVIDE(365, [Inventory Turnover], 0)
// Stockout % (products with zero stock)
Stockout Rate =
DIVIDE(
COUNTROWS(FILTER(StockQuant, StockQuant[quantity] <= 0)),
COUNTROWS(StockQuant),
0
)
Receivables aging:
// Current (0-30 days)
AR Current =
CALCULATE(
SUM(ARFact[amount_residual]),
ARFact[days_overdue] <= 0
)
// 1-30 days overdue
AR 1-30 Days =
CALCULATE(
SUM(ARFact[amount_residual]),
ARFact[days_overdue] >= 1 && ARFact[days_overdue] <= 30
)
// Days Sales Outstanding
DSO =
DIVIDE(
SUM(ARFact[amount_residual]),
[Revenue] / 365,
0
)
Key Dashboard Pages
1. Executive Dashboard
- Revenue vs budget (gauge chart)
- Revenue trend (line chart, 13 months rolling)
- Gross margin % (KPI card with trend)
- Top 10 customers by revenue (bar chart)
- Top 10 products by revenue (horizontal bar)
- Revenue by region (filled map)
2. Sales Pipeline (CRM)
- Pipeline by stage (funnel chart)
- Weighted pipeline value (KPI)
- Win/loss rate (donut chart)
- Average deal size trend
- Sales rep performance (matrix table)
- Forecast vs actual (line + bar combo)
3. Financial Overview
- P&L summary (table with YTD, YoY)
- Cash position (KPI)
- Receivables aging (stacked bar)
- Payables aging (stacked bar)
- DSO trend (line chart)
4. Inventory Dashboard
- Stock value by category (treemap)
- Inventory turnover by warehouse (bar)
- Slow-moving inventory (table: stock > 90 days)
- Stockout risk items (table: days coverage < 7)
- Reorder point alerts (cards)
5. HR Dashboard
- Headcount by department (bar)
- Attendance vs. scheduled hours (gauge)
- Leave balance utilization (matrix)
- Turnover rate trend (line)
Incremental Refresh for Large Tables
Odoo's account_move_line, stock_move, and mail_message tables grow to millions of rows. Incremental refresh prevents full-table reloads on every refresh.
Configure incremental refresh:
- In Power Query, add parameters
RangeStartandRangeEnd(DateTime type) - Filter your date column:
Table.SelectRows(Source, each [write_date] >= RangeStart and [write_date] < RangeEnd) - Right-click the table in the Fields pane → Incremental Refresh
- Set: store last 12 months, refresh last 3 days
Odoo tables that benefit most from incremental refresh:
account_move_line: filter bydatestock_move: filter bydatesale_order: filter bydate_ordermail_message: filter bydate
Row-Level Security
Implement Row-Level Security (RLS) in Power BI to mirror Odoo's company-level access control.
// RLS filter: user sees only their assigned companies
[company_id] IN
CALCULATETABLE(
VALUES(UserCompanyMapping[company_id]),
UserCompanyMapping[user_email] = USERPRINCIPALNAME()
)
Create a UserCompanyMapping table (maintained in Power BI or synced from Odoo) mapping email addresses to authorized company IDs.
Frequently Asked Questions
Can I use DirectQuery with Odoo's PostgreSQL database for real-time data?
Yes, but with caveats. DirectQuery on Odoo's PostgreSQL is feasible for dashboards with simple queries. Complex dashboards with many measures will be slow because each visual triggers new SQL queries against your production database. For most use cases, Import mode with 1-hour refresh is the better trade-off between freshness and performance.
How do I handle Odoo's multi-currency data in Power BI?
Odoo stores amounts in both the transaction currency and the company currency. Use the amount_currency field for the original currency and debit/credit (or price_subtotal) for the company currency equivalent. For group-level consolidation in Power BI, use Odoo's company currency amounts and apply a separate currency conversion dimension table for consistent reporting.
What is the performance impact on Odoo's PostgreSQL database when Power BI refreshes?
A full Power BI dataset refresh runs multiple analytical queries simultaneously against PostgreSQL. For large Odoo databases (>50GB), this can consume significant I/O and CPU during the refresh window. Best practices: schedule refreshes during off-peak hours (e.g., 2:00-4:00 AM), use a read replica of PostgreSQL for Power BI queries, and implement incremental refresh to minimize query scope.
Can I connect Power BI to Odoo Community (free version) via PostgreSQL?
Yes. Power BI connects to any PostgreSQL database regardless of which application manages it. Odoo Community's PostgreSQL schema is nearly identical to Enterprise (minus some Enterprise-only tables). The connection method is the same; just ensure the read-only database user has access to the Community database.
How do I keep my Power BI data model in sync when Odoo is upgraded to a new version?
Odoo version upgrades can rename or restructure database tables, especially for modules that underwent significant refactoring. After any Odoo upgrade: run a comparison of table schemas between old and new versions, update Power Query queries to reference renamed columns, and validate all DAX measures against the new schema. Build a schema change check into your migration runbook.
Next Steps
Building a production-grade Odoo + Power BI integration requires data modeling expertise, PostgreSQL knowledge, and deep understanding of Odoo's schema. Done correctly, it delivers a unified analytics platform that transforms how your leadership team makes decisions.
ECOSIRE delivers end-to-end Odoo + Power BI analytics solutions — from database architecture and data modeling through dashboard design, DAX development, and deployment. Our team bridges Odoo expertise and Power BI specialization.
Talk to ECOSIRE About Your Odoo Analytics Integration →
Explore ECOSIRE's Power BI Services →
Share your reporting requirements and we'll design a Power BI architecture that gives your leadership team real-time visibility into every dimension of your Odoo operations.
Written by
ECOSIRE Research and Development Team
Building enterprise-grade digital products at ECOSIRE. Sharing insights on Odoo integrations, e-commerce automation, and AI-powered business solutions.
Related Articles
Odoo Accounting vs QuickBooks: Detailed Comparison 2026
In-depth 2026 comparison of Odoo Accounting vs QuickBooks covering features, pricing, integrations, scalability, and which platform fits your business needs.
Building Financial Dashboards with Power BI
Step-by-step guide to building financial dashboards in Power BI covering data connections to accounting systems, DAX measures for KPIs, P&L visualisations, and best practices.
AI + ERP Integration: How AI is Transforming Enterprise Resource Planning
Learn how AI is transforming ERP systems in 2026—from intelligent automation and predictive analytics to natural language interfaces and autonomous operations.
More from Data Analytics & BI
Building Financial Dashboards with Power BI
Step-by-step guide to building financial dashboards in Power BI covering data connections to accounting systems, DAX measures for KPIs, P&L visualisations, and best practices.
Case Study: Power BI Analytics for Multi-Location Retail
How a 14-location retail chain unified their reporting in Power BI connected to Odoo, replacing 40 spreadsheets with one dashboard and cutting reporting time by 78%.
GoHighLevel + Power BI: Advanced Reporting and Analytics
Connect GoHighLevel to Power BI for advanced marketing analytics. Build executive dashboards, track multi-channel ROI, and create automated reports that go beyond GHL's native reporting.
GoHighLevel Reporting and Analytics: Measuring What Matters
Master GoHighLevel reporting and analytics. Learn to build custom dashboards, track ROI across channels, measure funnel conversion, and make data-driven marketing decisions.
Odoo Events Module: Planning, Registration, and Analytics
Complete guide to Odoo 19 Events: create events, manage registrations, sell tickets, track attendance, and analyze event ROI with native ERP integration.
Using OpenClaw AI Agents to Automate Power BI Reports
How OpenClaw AI agents automate Power BI report generation, distribution, and data preparation — delivering analytics at scale without manual BI developer intervention.