Syntax
Francis formulas follow the pattern"ROW_NAME"[RELATIVE_PERIOD]. The period offset is always relative to the current cell:
[0]: current period[-1]: previous period[-12]: 12 months ago
Sign conventions
Income and balance sheet items are entered as positive values. Expenses are entered as negative values. This is how Francis presents actuals from your accounting system, so follow this convention to compare apples to apples. The practical effect: subtotals add rather than subtract. Gross profit looks like this:Fill right
Write the formula logic once, then fill right to apply it to every future period. You don’t need to repeat the formula cell by cell. The same formula carries forward, and because references are relative, each period resolves against its own neighbors. It’s a small mechanic that most users come to rely on heavily once it’s part of their workflow. Fill right applies to rows. A calculation already uses one formula across every period by definition, so there is nothing to fill right. Override individual cells whenever you need to. To make a step change at a point in time, write a new formula in that period and fill right again. Everything from that period forward picks up the new logic, and the periods before it keep the old. Fill right works within the layer you’re in. Fill right in the actuals layer and the formula applies to all future periods of the actuals layer, so when you close a new month the formula is already in place. Fill right in the forecast layer and it applies to future forecast periods. To run the same formula across both layers, enter it in each layer and fill right in both. In the actuals layer, start from far enough back, because fill right only carries forward, as the name says. Begin at the earliest period you want the formula to cover, otherwise the periods to its left stay untouched.Formula paths
A row name alone is enough when it is unique in the model. When the same row name appears in multiple sheets, groups, or breakdowns, Francis qualifies the reference with a formula path. The path keeps every cell reference unique even when row names repeat across entities, dimensions, or sections. Two separators can appear in the path:- Sheet (
!): the sheet name sits before!when the row lives on a different sheet. - Container (
.): a group or section name sits before.when the row lives inside a nested container.
"Cost VAT"[-1]: the"Cost VAT"row on the current sheet, previous period."VAT"!"VAT calculations"."Cost VAT addition"[0]: the"Cost VAT addition"row inside the"VAT calculations"group on the"VAT"sheet, current period.
Comments
Add# at the end of any formula to insert a comment. Everything after # is highlighted and ignored by the calculation.
Functions
Francis supports a set of built-in functions (avg(), sum(), if(), and others) for more complex logic.
avg("row_name"[range_start:range_end])
avg("row_name"[range_start:range_end])
avg_ytd("row_name")
avg_ytd("row_name")
avg_last_year("row_name")
avg_last_year("row_name")
avg_last("row_name", num_months)
avg_last("row_name", num_months)
median("row_name"[range_start:range_end])
median("row_name"[range_start:range_end])
median_ytd("row_name")
median_ytd("row_name")
median_last_year("row_name")
median_last_year("row_name")
median_last("row_name", num_months)
median_last("row_name", num_months)
sum("row_name"[range_start:range_end])
sum("row_name"[range_start:range_end])
sum_ytd("row_name")
sum_ytd("row_name")
sum_last_year("row_name")
sum_last_year("row_name")
sum_last("row_name", num_months)
sum_last("row_name", num_months)
receivables("row_name", payment_days)
receivables("row_name", payment_days)
payment_days argument must be hardcoded and cannot reference a row.ignore_div_zero(a/b)
ignore_div_zero(a/b)
#DIV/0 error. Use for margin and percentage calculations where the denominator may occasionally be zero.power(num, exponent)
power(num, exponent)
min(a, b)
min(a, b)
max(a, b)
max(a, b)
abs(num)
abs(num)
round(num, decimals)
round(num, decimals)
round_down(num)
round_down(num)
round_up(num)
round_up(num)
if(condition, value_if_true, value_if_false)
if(condition, value_if_true, value_if_false)
if_month(a, b, ...)
if_month(a, b, ...)
1 if the current period matches any of the specified month numbers, else 0.if_month(1)returns1in Januaryif_month(1, 6)returns1in January or Juneif_month(1, 6, 12)returns1in January, June, or December
if_quarter_month(num)
if_quarter_month(num)
1 if the current period is the nth month within its quarter.if_quarter_month(1)returns1in January, April, July, Octoberif_quarter_month(2)returns1in February, May, August, Novemberif_quarter_month(3)returns1in March, June, September, December
weekdays()
weekdays()
Errors
#DIV/0
#DIV/0
ignore_div_zero() to return zero instead.#NO_FRAC
#NO_FRAC
a/b).#ERR
#ERR
5+ throws #ERR because the expression is incomplete.#INF_LOOP
#INF_LOOP
#CIRCULAR
#CIRCULAR
#FUTURE_REF
#FUTURE_REF
[0] throws this error. "Revenue"[1] is invalid; "Revenue"[0] and "Revenue"[-1] are valid.#INVALID_REF
#INVALID_REF
#BAD_ARG
#BAD_ARG
#BAD_TYPE
#BAD_TYPE
#REF_ERR
#REF_ERR