Financial reporting build · Power BI on live Odoo accounting data
They did not need a new accounting system. They needed to see the one they had.
A manufacturing business running finance on Odoo, with multiple legal entities, a distributed sales team and a large base of trade partners buying on credit. Every invoice, receipt, refund and journal entry already existed. KoderXpert put a two page analytics layer above the ledger without touching the ERP.
Data modelling, DAX and dashboard build by KoderXpert Technologies Pvt. Ltd.
Power BI & data analytics · Ahmedabad & Gandhinagar, India
- Client
- Manufacturer, name withheld
- Domain
- Financial & receivables analytics
- Source
- Odoo accounting, invoicing, payments
- Platform
- Microsoft Power BI
- Headline result
- Month end rebuild replaced by a refresh
01 · Overview
The numbers existed. Getting to them cost a person and a day.
- A manufacturing business running finance on Odoo, across multiple legal entities, selling on credit into a large trade partner base.
- In manufacturing, cash is committed long before it is collected. Material is bought, production is run, goods are shipped, and only then does an invoice begin its wait. Working capital lives in that gap, and the gap is made of receivables.
- KoderXpert modelled the Odoo accounting tables into a star schema and delivered two pages: profit and loss performance, and receivables and collections. The connection is read only, so nothing inside Odoo changed.
02 · The challenge
Reporting was a manual export and rebuild cycle.
Everything below follows from one thing: an operational ledger is built to record a transaction correctly, not to answer an executive question quickly.
The same report, rebuilt every month
Figures were pulled out of Odoo and reassembled in spreadsheets each period, by hand, by someone whose job was supposed to be analysis.
The cost → a person and a day, every monthReceivables had no live view
Outstanding balances existed in the ledger but there was no ageing view and no per partner exposure report anyone could simply open.
The cost → collections worked from a ledgerCollections performance was invisible
Nobody could state the collection rate or days sales outstanding without building the calculation from scratch, so nobody stated them.
The cost → a cash cycle nobody was measuringManagement asked, finance produced
Every question from leadership became a request, a queue and a wait, so fewer questions got asked and decisions were made on older numbers.
The cost → curiosity rationed by capacityMove types are not intuitive
Invoices, refunds and receipts sit in one journal structure. Reading revenue out of it requires knowing exactly what to include, and two people rarely included the same things.
The cost → no number was trustedMulti entity, without double counting
Figures had to be readable per company and consolidated at once, which a spreadsheet built per entity cannot do without someone adding it up again.
The cost → consolidation by hand, monthlythe hard part was never the chart, it was agreeing what a number means
03 · Before vs after
Same ledger. A refresh instead of a rebuild.
Left is what financial reporting cost before. Right is what two modelled pages replaced it with.
- ✗Monthly export into spreadsheets, rebuilt by hand each period
- ✗No ageing view and no per partner exposure report
- ✗Collection rate and DSO calculated from scratch, or not at all
- ✗Every leadership question queued behind finance
- ✗Margin, DSO and collection rate meaning different things to different people
- ✗Consolidation across entities done manually
- ✓A scheduled refresh instead of a monthly rebuild
- ✓AR ageing in three bands with per partner exposure by quarter
- ✓Collection rate and a measured 38.8 day DSO as standing measures
- ✓Leadership self serving from the dashboard
- ✓Eleven governed measures, agreed with finance and coded once
- ✓Per company and consolidated, from the same model
drag the orange handle, or use the arrow keys ✨
04 · Goals
What the build had to achieve
Six goals agreed with finance before any DAX, because a dashboard is only as trustworthy as its definitions.
One version of every number. Definitions agreed once, encoded in DAX and reused everywhere, so the figure on the screen is the figure in the meeting.
Self serve, not request driven. Leadership opens the dashboard instead of queuing a request, so more questions get asked and earlier.
Working capital under control. Ageing, partner exposure and DSO visible without a rebuild, because cash decisions cannot wait for month end.
Sliceable by every dimension. Month, year, company, salesperson and move type, from one model rather than one report per cut.
Zero disruption to Odoo. Read from the ledger and change nothing inside it: no customisation, no upgrade risk, no performance cost.
Fast enough to be used. A dashboard that opens in seconds gets opened; one that takes a minute gets replaced by a spreadsheet.
05 · What we delivered
Two pages, built around the questions people actually ask
Profitability and cash are different questions with different readers, which is why they are separate pages rather than one crowded screen.
Executive KPI strip
Total revenue, total expenses, profit margin, collection rate, year on year growth and GST collected. The six numbers that frame every other question, on one line.
the frame, before the detailRevenue against expenses by month
A twelve month clustered view that exposes seasonality and the months where cost outran income, visible instantly rather than derived at quarter end.
the months that need explainingQuarterly roll up and P and L split
The same comparison at quarter grain for board cycles, with revenue, expense and profit as proportions so three absolute figures become a shape people remember.
a shape, not three numbersAR, AP and DSO
What is owed to the business, what the business owes, and how long cash actually takes to arrive, as standing measures rather than one off calculations.
38.8 days, finally measuredAR ageing buckets
Zero to thirty, thirty one to sixty and sixty one to ninety day bands, so overdue debt is grouped by risk rather than read as one undifferentiated number.
a prioritised call listPartner exposure matrix
Every trade partner ranked by exposure, quarter by quarter, with a consolidated total. The collections team's working list rather than a ledger to read.
who to call firstThe ageing ladder and the cash it implies
one overdue total, three very different risks
Receivables split into what is current and what is overdue. Move the slider to change the collected share and watch the outstanding balance and the implied DSO move with it. The build put a measured 38.8 days in front of a business that had never had one, and you cannot shorten a cash cycle you are not measuring.
At the measured collection rate, DSO stands at 38.8 days.
Ageing bands turn one overdue total into a prioritised call list06 · Functional deep dive
How each number gets its meaning
Four decisions that turned an operational ledger into figures finance will defend in a board meeting.
Reading revenue out of a journal structure
Invoices, refunds, receipts and entries share one structure in Odoo. Revenue is not a column; it is a decision about which move types count.
- Move types classified once, with finance, and encoded in the model
- Refunds netted against revenue rather than reported separately
- Amount by move type kept on the page so the composition stays auditable
DSO as a standing measure
Days sales outstanding is trivial to define and easy to define differently. The version that matters is the one finance will defend.
- Definition agreed and documented before it was coded
- Computed from receivables and credit sales over a rolling window
- The same measure feeds the KPI strip and the receivables page
Ageing bands that group by risk
One overdue total tells a collections team nothing. Grouping by age turns the same balance into an ordered list of calls.
- Zero to thirty, thirty one to sixty, sixty one to ninety day bands
- Bands computed from invoice due date, not posting date
- Partner exposure cross tabulated by quarter for the working list
Multi entity without double counting
Figures had to read per company and consolidated at once, from one model, without the consolidation being a second calculation.
- Company carried as a dimension rather than as separate models
- Intercompany moves identified so consolidation does not inflate
- Every measure tested at both entity and consolidated grain
A collections list, not a ledger
the team does not need the ledger, it needs who to call first
Trade partners ranked by total exposure, which is where most collections conversations start. Switch to the amount past ninety days and the order changes, because a large balance inside terms is not the same problem as a smaller one that has gone stale. Sort by days overdue to get the call list.
Ranked by total exposure, which is the list finance usually starts from.
Ageing grouped by risk rather than read as one number07 · How we built it
Six stages from ledger to dashboard
The workshop came first. Everything downstream depended on the definitions agreed in it.
Requirement and metric workshop week one
Agreed with finance exactly what collection rate, DSO and profit margin mean, in writing, before a line of DAX was written.
Data extraction and modelling week two
Pulled the Odoo accounting tables and shaped them into a clean analytical star schema with date and partner dimensions.
DAX measure development about a week
Built and unit checked every measure against the ledger, including the ageing bands and the collection rate over a rolling window.
Dashboard design four days
Two pages, one KPI strip each, laid out around the questions people actually ask rather than around the shape of the data.
Validation against Odoo four days
Reconciled every headline figure back to the source system, each within tolerance or explained, with the reconciliation kept in the report.
Deployment, training and tuning final week
Scheduled refresh and publication, then walked finance and leadership through slicers, drill paths and definitions, and tuned against real usage.
08 · Engineering notes
How the ERP stays untouched
A reporting layer that requires changes to a live accounting system does not get approved, and should not.
A read only path out of Odoo
Power BI connects through a dedicated role with SELECT rights, scoped to the accounting schema. No module is installed, no field added and nothing writes back.
- Reporting logic held in versioned views, not in the report file
- Column renames handled in one place rather than across visuals
- Gateway used where the instance is self hosted
A measure library finance signed off
Eleven governed measures, each defined once and reused, which is what makes the number on the screen the number in the meeting.
- Base measures for arithmetic, display measures for formatting
- DIVIDE with a blank fallback so an empty partner reads blank
- Definitions document handed over with the model
Access control
Financial detail is sensitive and the partner table names customers, so the report was built to be shared without exposing everything to everyone.
- Row level security by company for entity managers
- Salary and margin detail restricted to the finance role
- Aggregate views for anyone outside finance
Refresh and reconciliation
Scheduled refresh with a reconciliation view that compares headline figures back to Odoo every time it runs, so drift surfaces as a visible variance.
- Revenue, AR and AP checked on each refresh
- Tolerances agreed with finance rather than assumed
- Failures visible on the page, not silent
Revenue against expenses, month by month
two totals tell you little, twelve pairs tell you when
Twelve months of revenue, then the same axis redrawn as expenses. The months where cost outran income are visible instantly rather than derived at quarter end, which is the whole reason the P and L page leads with this chart.
Revenue by month, in one clustered view rather than a quarterly summary.
Definitions agreed once, encoded in DAX, reused everywhere09 · The impact
What changed for finance
All of it from a ledger that was already complete and already accurate.
- Finance stopped rebuilding the same report; the month end rebuild became a refresh
- Receivables became actionable, with a ranked list to work rather than a ledger to read
- Leadership self serves, so questions get asked earlier and answered on current data
- Margin, DSO and collection rate now mean the same thing in every meeting
the ledger was always right, now it is legible
10 · The stack
What this platform runs on
Six layers, none of them inside Odoo.
Odoo is excellent at recording a transaction and poor at cross period, cross entity analysis. Rather than bend the ERP into a reporting tool, we left it to do its job and put a purpose built analytics layer above it: no customisation, no upgrade risk, no performance cost.
11 · FAQ
Questions finance teams ask before commissioning a build like this
No. The connection is read only through a dedicated database role with SELECT rights, and any reporting logic sits in versioned views alongside your schema. No module is installed, no field is added and nothing writes back. That is deliberate: it keeps the ERP clean and the upgrade path clear.
Odoo records transactions correctly and reports well on one model at a time. This needs cross period, cross entity analysis over invoices, receipts, refunds and the partner master at once, with measures that exist nowhere as stored fields. That is modelling work, and a BI layer is the right place for it.
From receivables against credit sales over a rolling window, with the exact definition agreed with finance and documented before it was coded. DSO is easy to define and easy to define differently, so the version that matters is the one your finance team will defend in a board meeting.
Yes, from one model. Company is carried as a dimension rather than as separate models, intercompany moves are identified so consolidation does not inflate, and every measure was tested at both entity and consolidated grain before sign off.
Daily suits most finance reporting, scheduled around when your postings settle. Receivables do not change minute to minute, and a refresh that completes before the morning is worth more than a live connection loading your production database.
Row level security restricts entity managers to their own company, and margin and salary detail sit in a separate role so the operational pages can be shared widely. Distribution runs through your own workspace, inside your tenant, under your identity provider.
It surfaces immediately, which is useful. Misclassified move types, partners duplicated under slightly different names and entries posted to the wrong company all show up as visible anomalies in the first draft rather than being averaged away. We ship a data quality view alongside the two pages.
Odoo Online does not expose direct database access, so we connect through the external API or a scheduled replica instead of a live SQL connection. The model, the measures and the pages are identical. Odoo.sh and self hosted instances use the read only connection directly, which is simpler and faster.
Far less than a customisation would, because nothing inside Odoo was changed. The report reads standard accounting tables and the parts most likely to move between versions are isolated in versioned views, so a version change is usually a view level fix. We test against the new version on staging before you upgrade production.
Two to four weeks for a standard Odoo accounting setup: workshop and modelling first, then measures, the two pages and reconciliation. Multiple entities, a long history or extra domains such as inventory valuation extend it, and we scope that against your instance.
Yes, and receivables ageing is the natural foundation for it, because expected collection dates come from the same data. We build the ageing and DSO first, since a forecast built on definitions nobody has agreed is a forecast nobody will use.
Because we work on both sides of the join: we implement and support Odoo and we build the BI layers above it, so we know what the accounting tables actually contain rather than guessing from a schema diagram. See our data analytics services or talk to a consultant.
You know what you invoiced. Do you know when it arrives?
Tell us what your ERP already records and what leadership keeps asking finance for. We will agree the definitions, model the ledger and hand back a dashboard nobody has to rebuild.
Read next: a hospital that could record everything and report almost nothing
Financial reporting build · Power BI on live Odoo accounting data
They did not need a new accounting system. They needed to see the one they had.
A manufacturing business running finance on Odoo, with multiple legal entities, a distributed sales team and a large base of trade partners buying on credit. Every invoice, receipt, refund and journal entry already existed. KoderXpert put a two page analytics layer above the ledger without touching the ERP.
Data modelling, DAX and dashboard build by KoderXpert Technologies Pvt. Ltd.
Power BI & data analytics · Ahmedabad & Gandhinagar, India
- Client
- Manufacturer, name withheld
- Domain
- Financial & receivables analytics
- Source
- Odoo accounting, invoicing, payments
- Platform
- Microsoft Power BI
- Headline result
- Month end rebuild replaced by a refresh
01 · Overview
The numbers existed. Getting to them cost a person and a day.
- A manufacturing business running finance on Odoo, across multiple legal entities, selling on credit into a large trade partner base.
- In manufacturing, cash is committed long before it is collected. Material is bought, production is run, goods are shipped, and only then does an invoice begin its wait. Working capital lives in that gap, and the gap is made of receivables.
- KoderXpert modelled the Odoo accounting tables into a star schema and delivered two pages: profit and loss performance, and receivables and collections. The connection is read only, so nothing inside Odoo changed.
02 · The challenge
Reporting was a manual export and rebuild cycle.
Everything below follows from one thing: an operational ledger is built to record a transaction correctly, not to answer an executive question quickly.
The same report, rebuilt every month
Figures were pulled out of Odoo and reassembled in spreadsheets each period, by hand, by someone whose job was supposed to be analysis.
The cost → a person and a day, every monthReceivables had no live view
Outstanding balances existed in the ledger but there was no ageing view and no per partner exposure report anyone could simply open.
The cost → collections worked from a ledgerCollections performance was invisible
Nobody could state the collection rate or days sales outstanding without building the calculation from scratch, so nobody stated them.
The cost → a cash cycle nobody was measuringManagement asked, finance produced
Every question from leadership became a request, a queue and a wait, so fewer questions got asked and decisions were made on older numbers.
The cost → curiosity rationed by capacityMove types are not intuitive
Invoices, refunds and receipts sit in one journal structure. Reading revenue out of it requires knowing exactly what to include, and two people rarely included the same things.
The cost → no number was trustedMulti entity, without double counting
Figures had to be readable per company and consolidated at once, which a spreadsheet built per entity cannot do without someone adding it up again.
The cost → consolidation by hand, monthlythe hard part was never the chart, it was agreeing what a number means
03 · Before vs after
Same ledger. A refresh instead of a rebuild.
Left is what financial reporting cost before. Right is what two modelled pages replaced it with.
- ✗Monthly export into spreadsheets, rebuilt by hand each period
- ✗No ageing view and no per partner exposure report
- ✗Collection rate and DSO calculated from scratch, or not at all
- ✗Every leadership question queued behind finance
- ✗Margin, DSO and collection rate meaning different things to different people
- ✗Consolidation across entities done manually
- ✓A scheduled refresh instead of a monthly rebuild
- ✓AR ageing in three bands with per partner exposure by quarter
- ✓Collection rate and a measured 38.8 day DSO as standing measures
- ✓Leadership self serving from the dashboard
- ✓Eleven governed measures, agreed with finance and coded once
- ✓Per company and consolidated, from the same model
drag the orange handle, or use the arrow keys ✨
04 · Goals
What the build had to achieve
Six goals agreed with finance before any DAX, because a dashboard is only as trustworthy as its definitions.
One version of every number. Definitions agreed once, encoded in DAX and reused everywhere, so the figure on the screen is the figure in the meeting.
Self serve, not request driven. Leadership opens the dashboard instead of queuing a request, so more questions get asked and earlier.
Working capital under control. Ageing, partner exposure and DSO visible without a rebuild, because cash decisions cannot wait for month end.
Sliceable by every dimension. Month, year, company, salesperson and move type, from one model rather than one report per cut.
Zero disruption to Odoo. Read from the ledger and change nothing inside it: no customisation, no upgrade risk, no performance cost.
Fast enough to be used. A dashboard that opens in seconds gets opened; one that takes a minute gets replaced by a spreadsheet.
05 · What we delivered
Two pages, built around the questions people actually ask
Profitability and cash are different questions with different readers, which is why they are separate pages rather than one crowded screen.
Executive KPI strip
Total revenue, total expenses, profit margin, collection rate, year on year growth and GST collected. The six numbers that frame every other question, on one line.
the frame, before the detailRevenue against expenses by month
A twelve month clustered view that exposes seasonality and the months where cost outran income, visible instantly rather than derived at quarter end.
the months that need explainingQuarterly roll up and P and L split
The same comparison at quarter grain for board cycles, with revenue, expense and profit as proportions so three absolute figures become a shape people remember.
a shape, not three numbersAR, AP and DSO
What is owed to the business, what the business owes, and how long cash actually takes to arrive, as standing measures rather than one off calculations.
38.8 days, finally measuredAR ageing buckets
Zero to thirty, thirty one to sixty and sixty one to ninety day bands, so overdue debt is grouped by risk rather than read as one undifferentiated number.
a prioritised call listPartner exposure matrix
Every trade partner ranked by exposure, quarter by quarter, with a consolidated total. The collections team's working list rather than a ledger to read.
who to call firstThe ageing ladder and the cash it implies
one overdue total, three very different risks
Receivables split into what is current and what is overdue. Move the slider to change the collected share and watch the outstanding balance and the implied DSO move with it. The build put a measured 38.8 days in front of a business that had never had one, and you cannot shorten a cash cycle you are not measuring.
At the measured collection rate, DSO stands at 38.8 days.
Ageing bands turn one overdue total into a prioritised call list06 · Functional deep dive
How each number gets its meaning
Four decisions that turned an operational ledger into figures finance will defend in a board meeting.
Reading revenue out of a journal structure
Invoices, refunds, receipts and entries share one structure in Odoo. Revenue is not a column; it is a decision about which move types count.
- Move types classified once, with finance, and encoded in the model
- Refunds netted against revenue rather than reported separately
- Amount by move type kept on the page so the composition stays auditable
DSO as a standing measure
Days sales outstanding is trivial to define and easy to define differently. The version that matters is the one finance will defend.
- Definition agreed and documented before it was coded
- Computed from receivables and credit sales over a rolling window
- The same measure feeds the KPI strip and the receivables page
Ageing bands that group by risk
One overdue total tells a collections team nothing. Grouping by age turns the same balance into an ordered list of calls.
- Zero to thirty, thirty one to sixty, sixty one to ninety day bands
- Bands computed from invoice due date, not posting date
- Partner exposure cross tabulated by quarter for the working list
Multi entity without double counting
Figures had to read per company and consolidated at once, from one model, without the consolidation being a second calculation.
- Company carried as a dimension rather than as separate models
- Intercompany moves identified so consolidation does not inflate
- Every measure tested at both entity and consolidated grain
A collections list, not a ledger
the team does not need the ledger, it needs who to call first
Trade partners ranked by total exposure, which is where most collections conversations start. Switch to the amount past ninety days and the order changes, because a large balance inside terms is not the same problem as a smaller one that has gone stale. Sort by days overdue to get the call list.
Ranked by total exposure, which is the list finance usually starts from.
Ageing grouped by risk rather than read as one number07 · How we built it
Six stages from ledger to dashboard
The workshop came first. Everything downstream depended on the definitions agreed in it.
Requirement and metric workshop week one
Agreed with finance exactly what collection rate, DSO and profit margin mean, in writing, before a line of DAX was written.
Data extraction and modelling week two
Pulled the Odoo accounting tables and shaped them into a clean analytical star schema with date and partner dimensions.
DAX measure development about a week
Built and unit checked every measure against the ledger, including the ageing bands and the collection rate over a rolling window.
Dashboard design four days
Two pages, one KPI strip each, laid out around the questions people actually ask rather than around the shape of the data.
Validation against Odoo four days
Reconciled every headline figure back to the source system, each within tolerance or explained, with the reconciliation kept in the report.
Deployment, training and tuning final week
Scheduled refresh and publication, then walked finance and leadership through slicers, drill paths and definitions, and tuned against real usage.
08 · Engineering notes
How the ERP stays untouched
A reporting layer that requires changes to a live accounting system does not get approved, and should not.
A read only path out of Odoo
Power BI connects through a dedicated role with SELECT rights, scoped to the accounting schema. No module is installed, no field added and nothing writes back.
- Reporting logic held in versioned views, not in the report file
- Column renames handled in one place rather than across visuals
- Gateway used where the instance is self hosted
A measure library finance signed off
Eleven governed measures, each defined once and reused, which is what makes the number on the screen the number in the meeting.
- Base measures for arithmetic, display measures for formatting
- DIVIDE with a blank fallback so an empty partner reads blank
- Definitions document handed over with the model
Access control
Financial detail is sensitive and the partner table names customers, so the report was built to be shared without exposing everything to everyone.
- Row level security by company for entity managers
- Salary and margin detail restricted to the finance role
- Aggregate views for anyone outside finance
Refresh and reconciliation
Scheduled refresh with a reconciliation view that compares headline figures back to Odoo every time it runs, so drift surfaces as a visible variance.
- Revenue, AR and AP checked on each refresh
- Tolerances agreed with finance rather than assumed
- Failures visible on the page, not silent
Revenue against expenses, month by month
two totals tell you little, twelve pairs tell you when
Twelve months of revenue, then the same axis redrawn as expenses. The months where cost outran income are visible instantly rather than derived at quarter end, which is the whole reason the P and L page leads with this chart.
Revenue by month, in one clustered view rather than a quarterly summary.
Definitions agreed once, encoded in DAX, reused everywhere09 · The impact
What changed for finance
All of it from a ledger that was already complete and already accurate.
- Finance stopped rebuilding the same report; the month end rebuild became a refresh
- Receivables became actionable, with a ranked list to work rather than a ledger to read
- Leadership self serves, so questions get asked earlier and answered on current data
- Margin, DSO and collection rate now mean the same thing in every meeting
the ledger was always right, now it is legible
10 · The stack
What this platform runs on
Six layers, none of them inside Odoo.
Odoo is excellent at recording a transaction and poor at cross period, cross entity analysis. Rather than bend the ERP into a reporting tool, we left it to do its job and put a purpose built analytics layer above it: no customisation, no upgrade risk, no performance cost.
11 · FAQ
Questions finance teams ask before commissioning a build like this
No. The connection is read only through a dedicated database role with SELECT rights, and any reporting logic sits in versioned views alongside your schema. No module is installed, no field is added and nothing writes back. That is deliberate: it keeps the ERP clean and the upgrade path clear.
Odoo records transactions correctly and reports well on one model at a time. This needs cross period, cross entity analysis over invoices, receipts, refunds and the partner master at once, with measures that exist nowhere as stored fields. That is modelling work, and a BI layer is the right place for it.
From receivables against credit sales over a rolling window, with the exact definition agreed with finance and documented before it was coded. DSO is easy to define and easy to define differently, so the version that matters is the one your finance team will defend in a board meeting.
Yes, from one model. Company is carried as a dimension rather than as separate models, intercompany moves are identified so consolidation does not inflate, and every measure was tested at both entity and consolidated grain before sign off.
Daily suits most finance reporting, scheduled around when your postings settle. Receivables do not change minute to minute, and a refresh that completes before the morning is worth more than a live connection loading your production database.
Row level security restricts entity managers to their own company, and margin and salary detail sit in a separate role so the operational pages can be shared widely. Distribution runs through your own workspace, inside your tenant, under your identity provider.
It surfaces immediately, which is useful. Misclassified move types, partners duplicated under slightly different names and entries posted to the wrong company all show up as visible anomalies in the first draft rather than being averaged away. We ship a data quality view alongside the two pages.
Odoo Online does not expose direct database access, so we connect through the external API or a scheduled replica instead of a live SQL connection. The model, the measures and the pages are identical. Odoo.sh and self hosted instances use the read only connection directly, which is simpler and faster.
Far less than a customisation would, because nothing inside Odoo was changed. The report reads standard accounting tables and the parts most likely to move between versions are isolated in versioned views, so a version change is usually a view level fix. We test against the new version on staging before you upgrade production.
Two to four weeks for a standard Odoo accounting setup: workshop and modelling first, then measures, the two pages and reconciliation. Multiple entities, a long history or extra domains such as inventory valuation extend it, and we scope that against your instance.
Yes, and receivables ageing is the natural foundation for it, because expected collection dates come from the same data. We build the ageing and DSO first, since a forecast built on definitions nobody has agreed is a forecast nobody will use.
Because we work on both sides of the join: we implement and support Odoo and we build the BI layers above it, so we know what the accounting tables actually contain rather than guessing from a schema diagram. See our data analytics services or talk to a consultant.
You know what you invoiced. Do you know when it arrives?
Tell us what your ERP already records and what leadership keeps asking finance for. We will agree the definitions, model the ledger and hand back a dashboard nobody has to rebuild.
Read next: a hospital that could record everything and report almost nothing