## Formula Syntax Reference CRITICAL: This application uses a CUSTOM formula language. It is NOT JavaScript. You must follow the syntax rules below exactly. ### Variables Variables are bare identifiers. There is NO var, let, or const keyword. CORRECT: myVar = 10 WRONG: var myVar = 10 You can optionally annotate types (decorative only): myVar:number = 10 ### Arithmetic Operations Standard arithmetic operators: +, -, *, / Division by zero returns 0 (not an error). Example: [Field 1] * [Field 2] + 10 Example: (10 + 10) * 2 / 5 String concatenation also uses the + operator: Example: "Hello " + [Field 1].GetText() + "!" IMPORTANT: There is NO ++ or -- operator. Use i = i + 1 instead. ### Field References - Reference fields using the format: [Field X] where X is the field ID number - Example: [Field 1], [Field 2], [Field 15] - Fixed fields use the format: [RNFX "Id" RNFX] for system values (see Fixed Fields section below) ### Fixed Fields (System Values) These are built-in system values you can use in formulas. Reference them with [RNFX "Id" RNFX]: - [RNFX "CurrentDate" RNFX] — The current date. Useful for date comparisons and calculations - [RNFX "FullUserName" RNFX] — The full name of the logged-in WordPress user - [RNFX "FirstName" RNFX] — The first name of the logged-in WordPress user - [RNFX "LastName" RNFX] — The last name of the logged-in WordPress user - [RNFX "Email" RNFX] — The email of the logged-in WordPress user - [RNFX "UserRole" RNFX] — The role of the logged-in WordPress user (e.g. "administrator", "subscriber") Example: RNAddDays([RNFX "CurrentDate" RNFX], 30) returns the date 30 days from today ### Methods (Dot Notation) Fields have methods you can call using dot notation: [Field X].MethodName Parentheses are optional for methods with no arguments. Example: [Field 5].GetTotal(10) Example: [Field 3].GetText() Example: [Field 3].GetText (same as above, parentheses optional) Example: [Field 1].IsReadonly Optional chaining is supported: Example: [Field 1]?.GetText() ### Comparison & Logical Operators - Comparison: ==, !=, >, >=, <, <= - Logical AND: && - Logical OR: || - Logical NOT: ! (unary) ### Conditionals if/else statements: if([Field 1] > 10) [Field 1] * 2 else [Field 1] if([Field 1] > 100) { discount = [Field 1] * 0.1 return [Field 1] - discount } else { return [Field 1] } Ternary expressions: [Field 1] > 10 ? "big" : "small" ### Blocks { } Use curly braces to group multiple statements. The last expression in a block is its return value. {1 2 3} — returns 3 {1 return 2 3} — returns 2 (early return) ### Return & Break - return VALUE — exits the entire formula early with the given value - break — exits the current for/foreach loop Without return, the last expression evaluated is the formula result. ### For Loop Syntax: for(init; condition; increment) statement IMPORTANT: No var keyword. No ++ operator. Use i = i + 1. total = 0 for(i = 0; i < 10; i = i + 1) total = total + i total With a block: total = 0 for(i = 1; i < 10; i = i + 1) { total = total + i if(total > 20) break } total ### ForEach Loop Syntax: foreach(expression as value) statement Syntax: foreach(expression as key => value) statement total = 0 foreach([1, 2, 3] as value) total = total + value total With block, return, and break: foreach([1, 2, 3] as value) { if(value == 2) return "found it" } foreach([1, 2, 3] as value) { if(value == 2) break } ### Arrays Array literals use square brackets: [1, 2, 3] Index access (0-based): [1, 2, 3][0] — returns 1 myArray[2] — returns third element Index assignment: a = [1, 2, 3] a[1] = "changed" ### Literal Values - Numbers: 10, 3.14, 0 - Strings: "hello", "world" (double quotes only) - Booleans: true, false - Null: null ### Built-in Functions #### Math Functions - RNRound(numberToRound, numberOfDecimals): Round a number to the specified decimal places Example: RNRound([Field 1] * 1.075, 2) rounds the result to 2 decimals - RNCeil(numberToRound): Round a number up to the next larger integer Example: RNCeil(4.2) returns 5 - RNFloor(numberToRound): Round a number down to the next smaller integer Example: RNFloor(4.8) returns 4 #### Date Functions - RNDateDiff(date1, date2, false): Get the number of days between two dates Example: RNDateDiff([Field 3], [Field 4], false) returns how many days between those dates - RNDatePart(date, part): Get a section of a date. Use "Y" for year, "M" for month, "D" for day, "H" for hour, "S" for second Example: RNDatePart([Field 5], "M") returns the month number - RNAddDays(date, numberOfDays): Return a date plus (or minus) X days Example: RNAddDays([Field 11], 7) returns the date 7 days after Field 11 #### Advanced Functions - RNGetParameter("ParameterName"): Get a query parameter value from the current page URL Example: RNGetParameter("coupon") returns the value of ?coupon=... in the URL #### Formatting Functions - RNFormatCurrency(numberToFormat): Format a number as currency using the form's configured currency settings Example: RNFormatCurrency([Field 1] * 0.9) formats the discounted price as currency - RNFormatTime(numberInMinutes): Format a number (in minutes) as time (e.g. 70 becomes "1:10") Example: RNFormatTime([Field 2]) converts minutes to a time string ### Repeater Fields A repeater is a field container that allows users to add multiple rows of the same set of fields. Fields inside a repeater have a "RepeaterParent" property in the field list indicating their parent repeater ID. When working with repeater child fields, use the parent repeater's methods to aggregate values across all rows: - `[Field REPEATER_ID].GetNumericalTotal(CHILD_FIELD_ID)` — Returns the sum of numeric values (GetNumber) of a child field across ALL repeater rows. Use this for summing quantities, counts, or any entered numeric values. - `[Field REPEATER_ID].GetTotal(CHILD_FIELD_ID)` — Returns the sum of prices (GetPrice) of a child field across ALL repeater rows. Use this only when summing prices. - `[Field REPEATER_ID].GetCount()` — Returns the number of repeater items (rows) - `[Field REPEATER_ID].GetField(CHILD_FIELD_ID, ROW_INDEX)` — Returns a specific child field at a given row index IMPORTANT: When the user asks about "sum", "total", or "combined value" of a field, prefer GetNumericalTotal unless they specifically mention prices. ### PROHIBITED Syntax NEVER use any of the following. They will cause parse errors: - var, let, const — just assign directly: x = 10 - i++, i-- — use i = i + 1 or i = i - 1 - .length — not available; use for loops with null checks or field methods - .push(), .pop(), .splice() — not available; build values with concatenation - .join(), .split(), .map(), .filter(), .reduce() — not available; use for/foreach loops - .indexOf(), .includes(), .toString() — not available - function keyword, arrow functions (=>) — not available - while loops — use for loops instead - switch/case — use if/else chains - try/catch/throw — not available - new, class, this — not available - typeof, instanceof — not available - console.log, alert — not available - Template literals (backticks) — use string concatenation with + - Single-quoted strings — use double quotes only: "text" - Semicolons as statement separators — statements are separated by whitespace/newlines ## Common Formula Patterns ### Concatenate text from all rows of a Repeater field To collect text from a child field across all repeater rows into a comma-separated string: result = "" for(i = 0; i < 100; i = i + 1) { child = [Field REPEATER_ID].GetField(CHILD_FIELD_ID, i) if(child == null) break if(i > 0) result = result + ", " result = result + child.GetText() } result ### Sum numeric values of a child field across all Repeater rows Use GetNumericalTotal for numeric values, GetTotal for prices: [Field REPEATER_ID].GetNumericalTotal(CHILD_FIELD_ID) [Field REPEATER_ID].GetTotal(CHILD_FIELD_ID) ### Conditional pricing if([Field 1].GetNumber() > 100) { return [Field 1].GetNumber() * 0.9 } else { return [Field 1].GetNumber() } ### Count selected options [Field 1].GetValue().length is NOT valid. Instead, use: count = 0 foreach([Field 1].GetSelectedOptions() as option) count = count + 1 count