Skip to main content

Cash Flow Forecast

What it is

A projection of money in and money out over the next 30, 60, 90 or 180 days, for the entity selected in the header.

  • Inflows are open invoices from the invoice_balances view, bucketed on their due date.
  • Outflows are open purchase orders, assumed to be paid 30 days after the PO date, plus unpaid expenses on their expense date.

Who can open it

Needs the finance.pricing feature grant while FEATURE_GRANULAR_RBAC is on; the owner bypasses it. With the flag off, any signed-in account can reach it by URL.

Before you start

Pick an entity first. With none selected the screen says so and shows nothing — this forecast is always per-entity.

warning
fx_rates is empty in production, so anything not already in your chosen currency is excluded

Rows in another currency are converted using the closest-dated rate in fx_rates. That table currently holds zero rows, so today every cross-currency invoice, PO and expense is left out of the forecast entirely.

The screen tells you when this happens — a banner reads "Excluded N row(s) — no FX rate found for …". Read it. A forecast with that banner showing is a forecast of part of your business.

Nothing is guessed: a missing rate means the row is dropped, never converted at an invented rate.

The outflow dates are an assumption, not a schedule. A purchase order is assumed to be paid 30 days after it was raised. Nothing records an actual agreed payment date, so a supplier on different terms will be in the wrong bucket. The page states the assumption it used at the foot.

Only unpaid expenses count as outflows. An expense recorded as paid is history, not a projection — and Expenses always saves with status: 'paid', so expenses contribute nothing to this forecast in practice.

Every control on the page

ControlWhat it does
CurrencyUSD, INR, AED or LKR. Everything is converted to this, and rows that cannot be are excluded
30d / 60d / 90d / 180dThe horizon. Changing it refetches

The entity comes from the header switcher; there is no entity control on the page itself.

What it shows:

  • Summary cards — total inflow, total outflow, and net, in the chosen currency.
  • A chart of the net position across the horizon.
  • Expected Inflows — the invoices making up the money in.
  • Expected Outflows — the purchase orders and expenses making up the money out.
  • Warnings at the foot — the excluded-row count when there is one, and the outflow date assumption, always.

The header shows when the figures were computed ("Refreshed …"). Data is fetched with cache: 'no-store', so it is current each time the page loads.

What it writes

Nothing. Every part of this screen is a read through GET /api/analytics/cashflow. There is no control here that changes a record.

The API checks your entity access through get_my_entity_ids and returns 403 for an entity you are not assigned to, so the entity switcher cannot be used to see another company's forecast.

When it goes wrong

"Select an entity from the top bar…" No entity chosen.

"Excluded N row(s) — no FX rate found for …" Those rows are in a currency with no rate on or before their date. The forecast is incomplete by that many rows. Enter the auditor's rates in fx_rates, or switch Currency to the one those documents are already in.

"Request failed (403)" You are not assigned to the selected entity.

"Request failed (…)" for anything else — nothing was computed; the number is the HTTP status.

Outflows look far too small. Two reasons, both above: expenses are only counted while unpaid and the expense screen always saves them as paid; and purchase orders are dated by an assumption rather than by real terms.

The net line disagrees with the SOA. The SOA is what is owed today; this is what is expected to land by a date. They are different questions, and the SOA is not currency-converted while this is.