DECCF · Cash Flow Modeler and Calculator

Business base forecast

The Business base forecast pack provides reusable forecast sections and calculation formulas. Add the sections you need to a businessBaseForecast() flow group. The group supplies a shared owner, schedule, and optional trigger; sections can set their own schedule or trigger when they need to.

The pack includes sales and operating-cost accruals plus opening-asset depreciation. Sections read the selected owner's compiled accounts and past postings through the engine context. The model author does not extract a history object or pass a source statement into a section.

Start with a whole-forecast template

businessForecast() creates a complete forecast component from a named starting template. It supplies common sales, goods-cost, premises, other-cost, and personnel sections, then lets the model replace or add sections as needed. Add its returned component to the model's components map.

Choose one of the whole-model templates:

  • straightforward uses six-month weighted averages for sales and costs.
  • growth applies positive annual sales and cost growth. Override growth and costGrowth with numbers or rate inputs.
  • downside starts with declining sales and rising costs; both annual rates can be overridden.
  • historical extends a recent linear trend when the fit is within the configured error limit, otherwise it uses a weighted average.
  • automatic picks between a fit-gated trend, a weighted average, and an annual growth fallback based on recent account history.
  • industry uses the input.sni() main group when available. When left blank, it infers a broad profile from account history.

The automatic template uses a 12-month observation window. It selects a linear regression with at least six non-zero months and normalized RMSE at or below 20%, a weighted average with at least three non-zero months, or the configured fallbackGrowth (2% annual by default) when history is sparse. These are transparent starting rules, not a substitute for reviewing the underlying accounts.

Industry template and SNI

The industry template creates an SNI 2025 dropdown input. It defaults to the company's attributes.sni main group when present. Otherwise it defaults to “Infer from history”; the forecast examines the owner's revenue, goods-cost, property-income, and personnel account mix. A model can also set the initial choice explicitly:

The industry profiles currently distinguish retail and wholesale trade, real estate, professional services, manufacturing, and general business. Each profile changes the formula for relevant sections; for example, property income can use same-month seasonality and retail goods costs use their own account history. Other main groups use the general profile until a sector rule is added.

The SNI dropdown uses the official SNI 2025 main-group classification. The pack accepts the two-digit main group, rather than a detailed five-digit activity code.

All default sections use owner-scoped account-plan roles and fall back to standard account codes when no matching account has been declared, so the example above does not need an accounts map. Declare accounts only when you need to provide opening balances or choose custom names. You may replace or extend sections using sections; see the section-specific APIs below. A template selects the starting methods and sections. The model author still controls the schedule, trigger, and final model composition.

Declare a forecast

sections accepts either one flow or flow group, or a named object. Named keys make it easy to include two instances of the same section with different assumptions. The returned declaration is an ordinary flowGroup; the model still decides which flows and closing entries to include.

The accountplan module is required. The forecast uses owner-scoped account roles to find depreciable assets, depreciation expenses, and accumulated depreciation. If a role matches multiple accounts, the historical account mix distributes the posting. A missing or unusable allocation basis is an error; select the accounts explicitly with accountChoices when the account plan cannot make a clear choice.

Use a formula from the collection

formulas groups ready-to-use methods by purpose. Configure a formula once; it returns a calculation function that receives one enriched calculation context. The context keeps the engine's usual methods such as get() and previous(), and adds context.forecast with the current owner, section, selected accounts, account history, period values, and trigger source.

The context can be extended with additional forecast facts without adding positional parameters to every method. Existing callbacks can continue to read history, time, context, assetAccounts, depreciationAccounts, and accumulatedDepreciationAccounts where those aliases apply.

The formula collection also includes gross-invoice VAT splitting; day-based working-capital balances and settlement; capital expenditure; useful-life estimation; facility draw and repayment; opening-balance interest; and tax on positive accumulated profit. Formula methods that need period-specific amounts read them from context.forecast.values. For example, invoice splitting reads grossSales; receivable settlement reads openingReceivables, invoices, and receivableTarget. These methods are plain synchronous calculations and can be reused by compatible forecast sections.

Monthly trend methods

The formulas.trend group forecasts an activity account role from its actual posted monthly history. history.monthlyActivityForRole(role) exposes one amount for each past month represented in the model timeline; income-account credits are normalized to positive amounts, and period-closing postings are left out. The calculation only uses account postings recorded in the model.

Available methods:

  • weightedMovingAverage({ role, periods, weights, minimumObservations }) weights newer months more heavily. The default weights rise linearly. If the selected window has too few non-zero observations, it uses the average over the covered history instead.
  • linearRegression({ role, periods, minimumObservations, floorAtZero }) fits a straight line through recent monthly amounts and extends it to the current forecast month. It floors negative projections at zero by default.
  • linearOrWeightedAverage({ role, periods, weights, minimumObservations, minimumWeightedObservations, maximumNormalizedRmse }) chooses the linear projection only when its in-window normalized root-mean-square error is within the configured limit; otherwise it returns the weighted moving average. The default fit limit is 20% of mean absolute activity. Regression requires six non-zero observations by default; the weighted fallback requires three.
  • exponentialSmoothing({ role, alpha, periods, minimumObservations }) applies simple exponential smoothing; alpha ranges above zero through one, with newer months receiving more weight.
  • seasonalSameMonthAverage({ role, years, minimumObservations }) averages observed values for the same calendar month in prior years.

Trend windows default to 12 months except the weighted moving average (6 months). A trend fit requires several non-zero monthly observations. This protects against treating a single annual balance snapshot as if it were recurring monthly activity: sparse history falls back to the covered-period average. These methods use the history available to the model; they do not infer missing monthly values or seasonality. Choose a method that fits the business and inspect its assumptions before relying on the result.

Use an opening-asset life assumption

openingAssets.remainingLifeYears accepts a number, numeric input reference, or calculation function. The function receives the same enriched context, including the selected asset accounts and opening balances.

The built-in estimator divides the balance of the selected depreciable asset accounts by historical depreciation expense annualized over the covered months. If no usable depreciation history exists, it uses fallbackYears; the result is bounded by the configured limits. This is a portfolio-level estimate, not an asset-register calculation.

To encode another method, pass a pure function instead of a string selector:

The section posts monthly depreciation as a balanced debit to depreciation expense and credit to accumulated depreciation. Depreciation has no cash movement. The forecast begins after its trigger period when triggeredBy is set.

Calculation functions

Forecast rules are functions so a model can replace one rule without choosing a text mode. The formulas collection offers ready-made growth, working-capital, investment, financing, invoice, and tax methods. Direct helpers such as annualGrowth({ annualRate }) and historicalMonthlyAverage() remain available. A custom function receives a single enriched context; use context.get(inputReference) for run inputs and context.forecast for forecast facts.

Methods that consume input references expose those references as preconditions, so the model's input schema can still be generated automatically.