Skip to content
Published
Platform

How we compute each finance indicator

For every figure: what it measures, its formula, the Odoo models read, the scope, the convention used and its known limits.

Why this page exists#

An accountant reviewing our figures does not ask whether the tool looks good. They ask where the amount comes from, over which period, under which convention, and what is not in it. Without those answers they cannot extend their trust, so they withhold it. This page exists so that nothing has to be guessed, and it is public: no account is needed to read it.

What keeps it current#

These definitions are not written alongside the product, they live in the code that computes them. One automated check refuses any change to an indicator's calculation that does not come with its definition, a second refuses this page drifting from the definitions, and a third refuses a definition without its translation. Unguarded documentation is wrong within three months; this one cannot be without blocking the release.

Two consequences worth stating:

  • A figure's scope is measured, not declared. When UpBoard reports "posted entries from 1 to 13 October, 1,240 lines read", those values come from the reads actually executed in your Odoo, not from a sentence written by hand.
  • Only posted entries are read, with one exception flagged as such: margin by product, which reads confirmed sales orders and is therefore not an accounting read.

The three rules that govern periods#

They apply to every dated indicator, and they are the ones that go wrong most silently elsewhere.

  1. A period is determined by the entry's accounting date, never by its creation date or its last technical modification. For receivables and payables it is the due date that counts, since what is measured is lateness.
  2. Cumulative figures start at the opening of your fiscal year, not on 1 January. If your year ends on 30 June, the comparison with the previous year and the corporate tax prepayments follow that year.
  3. The state of your books is read from Odoo. A month earlier than your lock date is reported as final; an open month states its day and the lock date. If UpBoard cannot read that date, it says the status is unknown, and never that the period is open.

What UpBoard does not cover in finance#

Stated plainly, because a gap discovered alone costs more than a gap announced:

  • closing with entries: UpBoard posts no adjusting entries and locks no period;
  • fixed assets: no tracking and no depreciation schedule. Only one anomaly is detected, assets left unamortised;
  • actual against budget: no budget comparison;
  • expense reports: neither approval nor reimbursement.

If your Odoo holds data on any of these, UpBoard tells you so rather than letting you believe its figures include them.

The reference#

19 documented indicators
O01

Accounting health score

Composite score 0-100 and grade A-F
What it measures
An overall mark for how well the books are kept, across five axes.
Calculation
Weighted average of the five axes (cash, receivables, payables, tax, performance). Each sub-score follows a `100 · exp(-penalty/100)` curve with a floor of 1, not a linear subtraction.
Odoo sources
  • No reads of its own: aggregates the outputs of O02 to O19.
Scope
Same scope as the analyses it aggregates.
Convention used
The exponential curve replaced a linear subtraction, which saturated at 0 from seven critical anomalies onwards and made 10 and 40 critical issues indistinguishable. The bottom of the scale is unchanged.
Known limits
  • An axis with no usable data is not scored: the score is then reported as partial, naming the unscored axes. Never read a partial score as a complete one.
  • An Odoo domain the customer does not have is excluded from the average rather than scored 100: zero anomalies for lack of a module is not everything-is-fine.
O02

Available cash and runway

Cash balance, burn rate, runway
What it measures
What is in the bank and in cash, and how long it lasts.
Calculation
Balance = sum of the balances of the liquidity accounts. Monthly burn rate = expenses of the last 90 days / 3. Runway = balance / monthly burn rate.
Odoo sources
  • `account.move.line.balance` on liquidity accounts
  • `account.account` to identify those accounts
Scope
Posted entries only (`state = posted`), current company.
Convention used
The burn rate is smoothed over 90 days: a single unusual month would give a fanciful runway.
Excluded from the figure
  • Undrawn credit lines, authorised overdrafts.
Known limits
  • Finding no cash entry does not yield a balance of 0 but an unknown balance: a false zero would read as bankruptcy.
  • Runway is only reported when the burn rate is positive.
O03

30, 60, 90 day cash forecast

Probability-weighted inflows and outflows
What it measures
The likely path of the balance, and the date of any shortfall.
Calculation
Projected balance = current balance + weighted expected collections − expected payments. Collection weighting by horizon: 90% at 30 days, 75% at 60 days, 60% at 90 days. Supplier debts count at 100%.
Odoo sources
  • `account.move.amount_residual` and `invoice_date_due` (customers and suppliers)
  • Opening balance: same accounts as O02
Scope
Posted entries only (`state = posted`), current company.
Convention used
The asymmetry is deliberate: we discount what is due in, never what is due out. A forecast that is optimistic about outflows is of no use.
Excluded from the figure
  • Invoices not yet issued, orders not yet invoiced, payroll and tax deadlines that are not accounting entries.
Known limits
  • The collection rates are fixed assumptions, not a given customer's history: a habitually late payer is treated like everyone else.
  • Recurring outflows not yet in the books (payroll, rent) are not projected, so the projected balance leans optimistic.
O04

Cash alerts

Threshold crossings on balance and runway
What it measures
The cash situations that need a decision now.
Odoo sources
  • No reads of its own: reads the outputs of O02 and O03.
Scope
Same scope as O02 and O03.
Known limits
  • Unknown balance: no cash alert is raised at all, rather than a reassuring or alarming alert about a figure that is missing.
O05

Bank reconciliation

Unreconciled bank lines and proposed matches
What it measures
What is left to reconcile, and what reconciliation can propose on its own.
Odoo sources
  • `account.bank.statement.line` not reconciled
  • Open `account.move` (`amount_residual`) for matching
Scope
Posted entries only (`state = posted`), current company.
Convention used
A match is only proposed on a convergent set of clues; nothing is reconciled automatically.
Known limits
  • Lists are capped (2,000 rows read): on a very large database the count shown is a floor, and the truncation is stated.
  • A grouped payment covering several invoices is not matched.
O06

Net cash position

Cash net of short-term debt, quick ratio
What it measures
What is really left once the short term is paid.
Calculation
Net position = cash − short-term debt. Quick ratio = cash / short-term debt.
Odoo sources
  • Cash: same accounts as O02
  • `account.move.amount_residual` of open supplier invoices
Scope
Posted entries only (`state = posted`), current company.
Convention used
Short term is bounded to what is due within 30 days plus what is already overdue.
Excluded from the figure
  • Financial debt beyond one year, provisions.
Known limits
  • The quick ratio is not reported when there is no short-term debt: a division by zero is not an excellent ratio.
O07

Customer ageing balance

Receivables ageing by bucket
What it measures
Who owes how much, and for how long.
Calculation
`amount_residual` split into five buckets by the gap between today and `invoice_date_due`: not due, 1-30, 31-60, 61-90, over 90 days.
Odoo sources
  • Open customer `account.move`: `amount_residual`, `invoice_date_due`
  • `res.partner` for the customer name
Scope
Posted entries only (`state = posted`), current company.
Convention used
Age is counted from the due date, not the invoice date: what is measured is lateness, not the age of the document.
Excluded from the figure
  • Draft invoices, unallocated credit notes.
Known limits
  • A commercial dispute in progress is not distinguished from a plain payment delay: both appear as an overdue receivable.
O08

Customer credit risk

Default score 0-100 and exposure
What it measures
How likely a customer is not to pay, and how much is at stake.
Calculation
Score weighted over four factors: payment history 35%, current exposure 25%, length of relationship 20%, recent signals 20%. Thresholds: 75 critical, 50 high, 25 medium.
Odoo sources
  • Customer `account.move` and `account.payment` (history)
  • `res.partner` (length of relationship)
Scope
Posted entries only (`state = posted`), current company.
Convention used
The weights are fixed and owned as such: this is a ranking to prioritise chasing, not a credit rating.
Known limits
  • No external data (published solvency, declared default) enters the score: it only reads the history present in Odoo.
  • A new customer with no history gets a score that says little.
O09

Average collection period

DSO (days sales outstanding), countback method
What it measures
The average number of days between an invoice and its payment.
Calculation
Countback: revenue invoiced is walked back month by month until the receivable balance is exhausted, and the days covered are counted. Analysis window: 365 days (180 days per customer).
Odoo sources
  • Customer `account.move`: `invoice_date`, `invoice_date_due`, `amount_residual`
  • `account.payment` for settlement dates
Scope
Posted entries only (`state = posted`), current company.
Convention used
Countback rather than the ratio formula (balance / revenue × days): the ratio flattens seasonality and yields a flattering DSO on irregular activity.
Known limits
  • An exceptional invoicing month distorts the following period's DSO, countback included.
  • Per-customer DSO over 180 days is not comparable to the overall DSO over 365 days: do not put them side by side without saying so.
O10

Customers to chase

Prioritised dunning queue, level and channel
What it measures
Who to chase this week, at which level and by which means.
Odoo sources
  • Overdue customer `account.move`
  • `mail.message` to know what has already been sent
  • `res.partner` for the available channel
Scope
Posted entries only (`state = posted`), current company.
Convention used
Three levels (reminder, formal notice, final demand) set by how old the delay is and by the dunning history, not by the amount alone.
Known limits
  • A case chased less than seven days ago and still unanswered is not flagged again: useful against harassment, but the case temporarily disappears from the queue.
  • A reply sent from an employee's mailbox rather than the customer's address may go unseen.
O11

Payment promises

Tracking of customer commitments, kept and broken
What it measures
Who promised to pay, and who kept their word.
Odoo sources
  • `mail.message` on invoices (a commitment is an exchange, not a field)
  • `account.move.amount_residual` and `invoice_date_due` (the expected payment)
Scope
Posted entries only (`state = posted`), current company.
Known limits
  • A commitment made over the phone and not recorded in Odoo is invisible: the indicator only measures what was written down.
O12

Supplier ageing balance

Payables ageing by bucket
What it measures
Who is owed how much, and since when.
Calculation
The same buckets as the customer ageing balance, applied to open supplier invoices.
Odoo sources
  • Open supplier `account.move`: `amount_residual`, `invoice_date_due`
  • `res.partner` for the supplier
Scope
Posted entries only (`state = posted`), current company.
Convention used
Age counted from the due date, as for customers.
Excluded from the figure
  • Draft invoices, unallocated credit notes.
Known limits
  • An available early-payment discount is not flagged here.
O13

Average payment period

DPO (days payable outstanding)
What it measures
The average number of days between a supplier invoice and its payment.
Calculation
The observed gap between `invoice_date` and the settlement date, matched through reconciliation. Analysis window: 365 days.
Odoo sources
  • Supplier `account.move` and `account.move.line` (reconciliation)
Scope
Posted entries only (`state = posted`), current company.
Convention used
The payment is dated by the reconciliation, not by the bank entry's date: reconciliation is what says which invoice was settled.
Known limits
  • The sector benchmark shown is indicative and internal; it is not an official statistic.
  • A partial payment spread over time yields an average that represents little.
O14

Recommended payment date

Cash against supplier relationship trade-off
What it measures
When to pay each invoice to keep cash without damaging the relationship.
Odoo sources
  • Open supplier `account.move`: `invoice_date_due`, `amount_residual`
Scope
Posted entries only (`state = posted`), current company.
Convention used
Three dates proposed per invoice (earliest, recommended, deadline); none is applied automatically.
Known limits
  • Payment terms negotiated outside Odoo, and early-payment discounts, are unknown to the indicator.
O15

VAT forecast

VAT balance for the current period and tax code anomalies
What it measures
What will be payable or recoverable at the next return.
Calculation
VAT collected − VAT deductible on the period's entries.
Odoo sources
  • `account.move.line` on VAT accounts: `balance`
  • `res.company` for the filing periodicity
  • `ir.module.module` to check the Belgian localisation
Scope
Posted entries only (`state = posted`), current company.
Convention used
Periodicity comes from the company setting when it exists; otherwise the quarterly filing of Belgian SMEs is assumed and said to be assumed, because a monthly deadline mistaken for a quarterly one is a missed deadline.
Excluded from the figure
  • Draft entries, hence VAT not yet posted.
Known limits
  • This is not a return: no carry-forward, no deduction pro rata, no special regime is applied.
  • A balance computed mid-period is not the final balance.
O16

Tax deadlines

90 day calendar (VAT, corporate tax, EC sales list)
What it measures
What falls due within three months, and what is critical.
Odoo sources
  • Internal Belgian catalogue, dated by the code
  • `res.company` for the fiscal year and the company registration number
Scope
Current company: only the fiscal-year setting and the registration number are read. No accounting entry is read: the dates come from the legal catalogue, not from the books. Obligations are never consolidated at group level, each company files its own.
Convention used
Corporate tax prepayments are counted from the opening of the taxable period, not from calendar quarters: for a year ending 30 June, the four dates shift accordingly. VAT keeps its calendar rhythm.
Known limits
  • Belgian catalogue only, and maintained by hand: a legal date that changes requires a code change.
  • Without a readable registration number on the company, company-level deadlines are shown by default rather than hidden.
  • This calendar does not replace the one kept by your tax agent.
O17

Profit and loss for the month

Monthly P&L with expected recurring charges
What it measures
The result for the current month, and what is still missing to close it.
Calculation
Aggregation of the month's entries by account category (Belgian chart of accounts): revenue, gross margin, result before depreciation, operating result. Recurring charges from the previous three months that are absent from the current month are flagged as expected.
Odoo sources
  • `account.move.line.balance` and `account.account.code`
Scope
Posted entries only (`state = posted`), current company. Calendar month.
Convention used
The month stays the calendar month, even on a shifted fiscal year: a monthly profit and loss statement is monthly. What was missing is the state of the books: it now comes from Odoo, so a month earlier than the lock date is reported as final, and an open month states its day and the lock date.
Known limits
  • Expected recurring charges are an estimate based on the previous three months, not an entry: they are shown separately and must not be copied into a return.
  • A month in progress is not a closed month, however it is presented.
O18

Comparison with the previous year

Fiscal year to date against the same period of the previous year
What it measures
How eight indicators evolve against the same period last year.
Calculation
Cumulative figures from the opening of the fiscal year to today, compared with the previous fiscal year stopped at the same date one year earlier.
Odoo sources
  • `account.move.line.balance` and `account.account.code`
Scope
Posted entries only (`state = posted`), current company.
Convention used
The cumulative figure starts at the opening of the company's fiscal year, not on 1 January: on a shifted year, 1 January falls in the middle of the period and the two cumulative figures would not correspond. The equivalent date in the previous year is the same date one year earlier, trimmed to the real month (29 February becomes 28 February).
Known limits
  • A change of scope between the two years (an acquisition, a change of chart of accounts) is not detected: the comparison is made on the accounts as they stand.
  • If either end falls in a period that is not locked, the cumulative figure is reported as not final.
O19

Margin by product and by customer

Estimated gross margin on confirmed orders
What it measures
Where the margin is made, and what is sold at a loss.
Calculation
Margin = line sale price − estimated unit cost × quantity. The cost used is the product's cost price, cross-checked against purchase lines from the last six months.
Odoo sources
  • `sale.order.line` of confirmed orders (`state in sale, done`)
  • `purchase.order.line` of the last six months (`state in purchase, done`)
  • `product.product.standard_price`
Scope
Confirmed sales orders only, current company. This is not an accounting read: unconfirmed quotations and invoicing are not read.
Convention used
Margin is computed on the order, not the invoice: that is the commercial view, available earlier, and it may differ from the accounting result.
Excluded from the figure
  • Unconfirmed quotations, cancelled orders.
Known limits
  • The cost is an estimated cost price, not an accounting cost of goods sold: shipping, supplier rebates and differently valued stock are not included. A product shown as sold at a loss here is a signal to check, not an accounting conclusion.
  • A product never purchased in the last six months keeps its cost price from the product record, which may be out of date.
  • Lines without a product (manually entered fees) are outside the margin.

Does a figure look wrong?#

Two cases, handled differently. If the figure does not match the definition above, it is a bug: write to info@upboard.ai naming the indicator. If the figure matches the definition but not your expectation, it is a convention to discuss, and that is a conversation we welcome: these choices are documented precisely so they can be challenged.