All posts
Developer11 min read

Odoo analytic plans in v17 and v18: replacing analytic accounts with a multi-dimensional model

Odoo 17 replaced the single analytic account tree with analytic plans - a multi-dimensional system where each journal line can carry allocations across several independent plans simultaneously. Here is how the new model works, how to migrate your chart of analytic accounts, and how to report across plans.

Before Odoo 17, analytic accounting was a single-dimension system. Every journal line could carry one analytic account and one set of analytic tags. If you needed to track cost by project and by department simultaneously, you had to pick one dimension as the analytic account and encode the other in tags, which were non-hierarchical and could not aggregate into totals.

Odoo 17 introduced analytic plans: a formal multi-dimensional model where each journal line can carry allocations across any number of independent plans, each with its own account tree, allocation percentage, and enforcement rules. A single vendor bill line can now be 100% allocated to Project A, 60% to Department X and 40% to Department Y, and 100% to Cost Center North - simultaneously - with each axis reported independently.

This post explains the data model, the configuration steps, how to apply analytic allocations in practice, and how to query them for reporting.

The data model#

Three models form the analytic plan system:

account.analytic.plan - a named dimension (e.g., "Projects", "Departments", "Cost Centers"). Each plan has:

  • A list of analytic accounts belonging to it.
  • An applicability setting controlling where it appears (vendor bills, expenses, timesheets, all journal entries, or custom).
  • A mandatory/optional flag per document type.
  • Color coding for the UI column.

account.analytic.account - an individual account within a plan. Accounts can be hierarchical (parent-child) for grouping. Each account belongs to exactly one plan.

account.analytic.line - the allocation record. One line per (journal line, analytic account) pair. The line stores the amount, the account, and a percentage. Multiple analytic lines on a single journal line represent the multi-plan allocation.

Setting up plans#

Go to Accounting > Configuration > Analytic Plans (or in some versions, Accounting > Configuration > Analytic Accounting > Plans).

Create a plan:

Name: Projects
Applicability: All
Mandatory on: Vendor Bills, Customer Invoices

Then create analytic accounts within that plan at Accounting > Configuration > Analytic Accounts. Assign each account to the plan via the Plan field. Build the hierarchy using the Parent field.

Example Projects plan accounts:

  • Projects / 2026 / ERPeek Implementation
  • Projects / 2026 / ACME Migration
  • Projects / Internal / R&D

Example Departments plan accounts:

  • Departments / Engineering
  • Departments / Sales
  • Departments / Operations

Applying allocations on journal lines#

When entering a vendor bill or journal entry, each line in the "Analytic" column now shows a widget with one column per active plan. You fill in the account for each plan independently.

For split allocations (e.g., 60/40 between two departments), click the distribution icon on the analytic column. A dialog opens where you add multiple rows:

PlanAccountPercentage
DepartmentsEngineering60%
DepartmentsSales40%

Odoo stores these as two account.analytic.line records, each with the proportional amount.

If a plan is marked mandatory for vendor bills, Odoo blocks confirmation if any line is missing that plan's allocation. This enforces completeness across your analytic dimensions.

Analytic distribution templates#

For recurring expenses (rent, utilities, SaaS subscriptions) that always split the same way, create an Analytic Distribution Template at Accounting > Configuration > Analytic Distribution Templates.

A template captures a full allocation grid - multiple plans, multiple accounts, percentages - and can be applied to a line in one click. Templates appear as a dropdown on the analytic widget.

Example template "Office Rent":

  • Projects → Internal / Operations (100%)
  • Departments → Operations (70%), Engineering (30%)
  • Cost Centers → North (100%)

When you apply this template to a vendor bill line for the monthly rent, all three plans are populated simultaneously.

The Python data model for developers#

When building custom modules that integrate with analytic plans, the key field is analytic_distribution on account.move.line. In v17+ this is a JSON field, not a Many2many to analytic accounts:

python
# account.move.line in Odoo 17+
analytic_distribution = fields.Json(
    string='Analytic Distribution',
    # Format: {"analytic_account_id": percentage, ...}
    # Example: {"123": 60.0, "456": 40.0}
)

The JSON keys are analytic account IDs (as strings), and values are percentages summing to 100 per plan. Multiple plans appear as multiple keys - one per account.

To query which accounts a line is allocated to:

python
move_line = env['account.move.line'].browse(line_id)
distribution = move_line.analytic_distribution  # {'123': 60.0, '456': 40.0}
for account_id_str, percentage in distribution.items():
    account = env['account.analytic.account'].browse(int(account_id_str))
    print(account.plan_id.name, account.name, percentage)

The account.analytic.line records are still created as the expanded view of the distribution, and they are what the analytic reports query.

Reporting across plans#

Go to Accounting > Reporting > Analytic Report. The report lets you:

  • Filter by one or more plans (each plan is a column).
  • Drill down into account hierarchies within a plan.
  • Cross-filter: show entries that are in Project A AND in Department X simultaneously.
  • Group by period (month, quarter, year).
  • Compare actual vs. budget (if budget lines are set on analytic accounts).

The cross-filter is the key capability that the old single-dimension system lacked. You can now answer: "What did we spend in Q1 on Project A, broken down by department?" - in a single report.

Budget integration#

Analytic budgets are set directly on analytic accounts in the plan. Go to the analytic account record and set a Budget amount for the period. The analytic report then shows actual vs. budget with variance.

If you need more granular budgeting (budget by combination of project + department), use the Budget Lines model (account.budget.line) which supports multi-plan filters. This requires the Accounting > Budgets feature to be enabled.

Migrating from Odoo 16 to Odoo 17 analytic model#

The migration from single analytic accounts to analytic plans is semi-automatic. Odoo's upgrade script:

  1. Creates one default analytic plan named after your company.
  2. Moves all existing analytic accounts into that plan.
  3. Converts all account_analytic_account_id FK fields into analytic_distribution JSON with 100% allocation to the original account.

Post-migration, your existing reports continue to work because everything lands in the single default plan. To take advantage of multi-dimensional reporting, you then:

  1. Create additional plans (Departments, Cost Centers, etc.).
  2. Create analytic accounts within them.
  3. Retroactively update historical entries if needed (not required - new entries will use the full multi-plan model going forward).
  4. Configure applicability and mandatory rules on the new plans.

If you have custom modules that write to account_analytic_account_id directly, those fields are deprecated in v17. Update them to write to analytic_distribution instead, or use the compatibility layer (_prepare_analytic_distribution_vals helper method on account.move.line).

Common issues#

"Analytic plan not shown on bill lines" - check the plan's applicability. If set to "Timesheets only," it will not appear on accounting entries. Set applicability to "All" or "Vendor Bills."

"Allocations don't sum to 100%" warning - Odoo shows this warning per plan when percentages within a plan sum to anything other than 100. It is a warning, not a block, unless the plan is mandatory. Fix by adjusting percentages or adding a catch-all account at 0% to make the sum explicit.

"Analytic lines not appearing in reports" - account.analytic.line records are created asynchronously on some Odoo configurations. Wait a few seconds and refresh, or check the journal entry's chatter for any error in the analytic line creation.

Custom module writes the old account_analytic_account_id field - in v17 this field still exists on account.move.line but is a computed field derived from analytic_distribution. Writing to it directly sets only the first account in the distribution and clears multi-plan allocations. Switch to writing analytic_distribution directly.


For the original analytic accounting model (Odoo 16 and earlier), see the Odoo analytic accounting guide. For project-level budget tracking, see the Odoo project budget forecasting guide.

Try ERPeek on your own Odoo module - ask questions, scaffold tests, and explore your codebase in plain language.

Get started free