Forecasts
Snapshot the deals forecast for a period, keep the deals behind each number, and compare two snapshots deal by deal.
The forecasts block freezes the deals forecast call
at a point in time. A snapshot stores the totals for a period and currency
(won, commit, best case, pipeline, not called, omitted, later, attainment and
the gap to the target) and a copy of every deal behind them, so next week's
review can show what moved: which deals were won, lost, added or changed.
better-supabase add forecasts # adds tenant, access and deals as well| Permission | Lets a member | Default roles |
|---|---|---|
forecasts.read | list and open snapshots | member, admin |
forecasts.snapshot | take a snapshot of the forecast | admin (forecasts.*) |
The service role can always take a snapshot, so a weekly cron job works without a member session.
Take a snapshot
import {
createForecasts,
rpcTransport,
} from "better-supabase/blocks/forecasts";
import { money } from "better-supabase/commerce";
const forecasts = createForecasts({ transport: rpcTransport(supabase) });
const snapshot = await forecasts
.snapshot(organizationId, {
since: Temporal.PlainDate.from("2026-10-01"),
until: Temporal.PlainDate.from("2026-12-31"),
currency: "EUR",
target: money(25_000_000n, "EUR"),
})
.orThrow();
snapshot.totals.commit; // a Money value, as deals.forecast returns it
snapshot.dealCount; // the deals copied into the snapshotA snapshot sums every deal of the organization in the currency, whoever owns
it, so the numbers match for everyone who opens it later. It copies the open
deals that close in the period and the deals won or lost in it. Pass a
pipelineId to snapshot one pipeline. A period runs at most 366 days.
Compare two snapshots
const [latest, previous] = await forecasts
.list(organizationId, { limit: 2 })
.orThrow();
const { deals } = await forecasts.changes(previous.id, latest.id).orThrow();
for (const deal of deals) {
deal.change; // "added", "removed", "won", "lost" or "changed"
deal.before?.amount;
deal.after?.amount;
}list returns snapshots newest first and filters by pipelineId, since,
until and limit. get(snapshotId) returns one snapshot with its deals.
changes lists only the deals whose stage, owner, forecast category,
outcome, amount or close date differ between the two snapshots, and needs
two snapshots of the same organization and currency.
Events
Every snapshot writes forecast.snapshotted to the
outbox with the snapshot id, the pipeline, the period,
the currency and the deal count. See Events.
| Error code | When |
|---|---|
FORECAST_FORBIDDEN | the caller lacks forecasts.snapshot |
FORECAST_INVALID | a bad period, currency or target, or a period over 366 days |
FORECAST_PIPELINE_UNKNOWN | the pipeline is not one of the organization's |
FORECAST_NOT_FOUND | no snapshot you may read has the id |
FORECAST_MISMATCH | the two snapshots differ in organization or currency |
Last updated on