[PR #5315] [CLOSED] feat: add spreadsheet report #5873

Closed
opened 2026-02-28 21:20:13 -06:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/actualbudget/actual/pull/5315
Author: @SaiPratyush
Created: 7/11/2025
Status: Closed

Base: masterHead: spreadsheet-report


📝 Commits (10+)

  • 18336b9 feat: add spreadsheet report
  • 0a1290f docs: add release notes
  • 0f1db46 Merge branch 'master' into spreadsheet-report
  • e7bbea7 fix: fix ci errors
  • fa9c3f2 Merge branch 'master' into spreadsheet-report
  • 9d537be fix: fix lint errors
  • 1b08007 feat: improve user experience
  • bd53234 fix: safeguard regex queries
  • e3261d4 [autofix.ci] apply automated fixes
  • 10e489c fix: safeguard regex queries

📊 Changes

31 files changed (+5755 additions, -3 deletions)

View changed files

📝 packages/desktop-client/src/components/reports/Overview.tsx (+54 -1)
📝 packages/desktop-client/src/components/reports/ReportOptions.ts (+7 -0)
📝 packages/desktop-client/src/components/reports/ReportRouter.tsx (+3 -0)
packages/desktop-client/src/components/reports/SaveSpreadsheetReport.tsx (+310 -0)
packages/desktop-client/src/components/reports/reports/SheetRow.tsx (+608 -0)
packages/desktop-client/src/components/reports/reports/Spreadsheet.tsx (+722 -0)
packages/desktop-client/src/components/reports/reports/SpreadsheetCard.tsx (+613 -0)
packages/desktop-client/src/components/reports/reports/spreadsheetReport/BalanceQueryForm.tsx (+50 -0)
packages/desktop-client/src/components/reports/reports/spreadsheetReport/CostQueryForm.tsx (+309 -0)
packages/desktop-client/src/components/reports/reports/spreadsheetReport/FormulaPreview.tsx (+109 -0)
packages/desktop-client/src/components/reports/reports/spreadsheetReport/FormulaQueryForm.tsx (+133 -0)
packages/desktop-client/src/components/reports/reports/spreadsheetReport/QueryTypeSelector.tsx (+50 -0)
packages/desktop-client/src/components/reports/reports/spreadsheetReport/RowOperationHelpers.tsx (+168 -0)
packages/desktop-client/src/components/reports/spreadsheets/QueryBuilder.tsx (+684 -0)
packages/desktop-client/src/components/reports/spreadsheets/queryParser.ts (+338 -0)
packages/desktop-client/src/components/reports/spreadsheets/sheetEngine.ts (+877 -0)
packages/desktop-client/src/components/reports/spreadsheets/useSheetCalculation.ts (+432 -0)
📝 packages/desktop-client/src/components/settings/Experimental.tsx (+6 -0)
📝 packages/desktop-client/src/hooks/useFeatureFlag.ts (+1 -0)
packages/desktop-client/src/hooks/useSpreadsheetReport.ts (+15 -0)

...and 11 more files

📄 Description

This PR adds a new spreadsheet report as an experimental feature. The following is the documentation on how to use this feature along with (slightly outdated) screenshots of what the report looks like

Available Filters

Category Filters
cost({ category: 'Food' });
Account Filters
cost({ account: 'Checking Account' });
Payee Filters
cost({ payee: 'Grocery Store' });
Notes Filters
cost({ notes: 'groceries' }); // Exact match
cost({ notes: contains('groceries') }); // Contains text
cost({ notes: hasTags('#food #groceries') }); // Has specific hashtags
Transaction Status Filters
cost({ cleared: true }); // Only cleared transactions
cost({ cleared: false }); // Only uncleared transactions
cost({ reconciled: true }); // Only reconciled transactions
cost({ reconciled: false }); // Only unreconciled transactions
cost({ transfer: true }); // Only transfer transactions
cost({ transfer: false }); // Only non-transfer transactions
Date Filters
cost({ date: thisMonth }); // Current month
cost({ date: lastMonth }); // Previous month
cost({ date: thisYear }); // Current year
cost({ date: lastYear }); // Previous year
cost({ date: between('2024-01-01', '2024-12-31') }); // Custom range
cost({ date: gte('2024-01-01') }); // From date onwards
cost({ date: lte('2024-12-31') }); // Up to date
Amount Filters
cost({ amount: gte(1000) }); // Amount >= $10.00 (in cents)
cost({ amount: lte(5000) }); // Amount <= $50.00 (in cents)
Combined Filters
cost({
  category: 'Food',
  date: thisMonth,
  amount: gte(1000),
  cleared: true,
});

balance() - Account Balance

Get the current balance of any account.

Syntax

balance('Account Name');

Examples

balance('Checking Account');
balance('Savings Account');
balance('Credit Card');

Mathematical Functions

Aggregation Functions

sum()

Sum values in a range or individual values.

sum(row-1:row-5)     // Sum of rows 1-5
sum(row-1, row-3, row-5)  // Sum of specific rows
sum(100, 200, 300)   // Sum of numbers

average()

Calculate the average of values.

average(row-1:row-5)     // Average of rows 1-5
average(row-1, row-3, row-5)  // Average of specific rows
average(100, 200, 300)   // Average of numbers

min()

Find the minimum value.

min(row-1:row-5)     // Minimum of rows 1-5
min(100, 200, 300)   // Minimum of numbers

max()

Find the maximum value.

max(row-1:row-5)     // Maximum of rows 1-5
max(100, 200, 300)   // Maximum of numbers

count()

Count numeric values.

count(row-1:row-5)     // Count of numeric values in rows 1-5
count(100, 200, 300)   // Count of numbers

Mathematical Functions

abs()

Absolute value.

abs(-100); // Returns 100
abs(row - 1); // Absolute value of row 1

round()

Round to specified decimal places.

round(3.14159, 2); // Returns 3.14
round(row - 1, 0); // Round row 1 to whole number

floor()

Round down to nearest integer.

floor(3.7); // Returns 3
floor(row - 1); // Floor of row 1

ceil()

Round up to nearest integer.

ceil(3.2); // Returns 4
ceil(row - 1); // Ceiling of row 1

sqrt()

Square root.

sqrt(16); // Returns 4
sqrt(row - 1); // Square root of row 1

pow()

Power function.

pow(2, 3); // Returns 8 (2^3)
pow(row - 1, 2); // Square of row 1

Logical Functions

if()

Conditional logic.

if(condition, true_value, false_value)

Examples

if(row-1 > 0, row-1, 0)                    // If row 1 > 0, use row 1, else 0
if(cost({category:"Food"}) > 500, "High", "Low")  // Conditional text
if(row-1 > row-2, "Greater", "Less")       // Compare two rows

and()

Logical AND - returns 1 if all arguments are truthy, 0 otherwise.

and(row - 1 > 0, row - 2 > 0); // Returns 1 if both rows > 0
and(true, true, false); // Returns 0
and(1, 2, 3); // Returns 1

or()

Logical OR - returns 1 if any argument is truthy, 0 otherwise.

or(row - 1 > 0, row - 2 > 0); // Returns 1 if either row > 0
or(false, false, true); // Returns 1
or(0, 0, 0); // Returns 0

not()

Logical NOT.

not(0); // Returns 1
not(1); // Returns 0
not(row - 1 > 0); // Returns 1 if row 1 <= 0

Date and Time Functions

today()

Returns today's date as days since epoch.

today(); // Returns current date as number

Text Functions

concatenate()

Join multiple text values together.

concatenate('Hello', ' ', 'World'); // Returns "Hello World"
concatenate('Row 1: ', row - 1); // Combine text with row value

len()

Get the length of a string.

len('Hello'); // Returns 5
len('Row 1: ' + row - 1); // Length of concatenated string

Cell References

Individual Row References

Reference values from other rows using row-X format.

row - 1; // Value from row 1
row - 2; // Value from row 2
row - 10; // Value from row 10

Row Ranges

Reference a range of rows using row-X:row-Y format.

row-1:row-5    // Values from rows 1 through 5
row-3:row-7    // Values from rows 3 through 7

Using Row References

row-1 + row-2                    // Add two row values
sum(row-1:row-5)                 // Sum of rows 1-5
average(row-1:row-10)            // Average of rows 1-10
if(row-1 > row-2, row-1, row-2)  // Conditional row reference

Operators

Arithmetic Operators

+    // Addition
-    // Subtraction
*    // Multiplication
/    // Division
^    // Exponentiation

Comparison Operators

>    // Greater than
<    // Less than
>=   // Greater than or equal
<=   // Less than or equal
==   // Equal to
!=   // Not equal to

Examples

row - 1 + row - 2; // Add two rows
cost({ category: 'Food' }) * 1.1; // Add 10% to food cost
if ((row - 1 > 1000, 'High', 'Low'))
  // Conditional based on comparison
  row - 1 / row - 2; // Divide row 1 by row 2

Query Builder

Query Builder

Spreadsheet Report

Spreadsheet Base

Spreadsheet Report with Hidden Formulas

Spreadsheet Hide Formulae

Spreadsheet Report with Hidden Rows

Spreadsheet Hide Rows

Spreadsheet Report Card

Spreadsheet Card

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/actualbudget/actual/pull/5315 **Author:** [@SaiPratyush](https://github.com/SaiPratyush) **Created:** 7/11/2025 **Status:** ❌ Closed **Base:** `master` ← **Head:** `spreadsheet-report` --- ### 📝 Commits (10+) - [`18336b9`](https://github.com/actualbudget/actual/commit/18336b9ece129929b18005a2883f915c24c8d938) feat: add spreadsheet report - [`0a1290f`](https://github.com/actualbudget/actual/commit/0a1290f9ed8c90916c4f3a9aebec0dce4c182de6) docs: add release notes - [`0f1db46`](https://github.com/actualbudget/actual/commit/0f1db4638b7a6369a79968c6507c6dc300921432) Merge branch 'master' into spreadsheet-report - [`e7bbea7`](https://github.com/actualbudget/actual/commit/e7bbea734d4099a7bb586d27e1e9ac24e1b3b9ea) fix: fix ci errors - [`fa9c3f2`](https://github.com/actualbudget/actual/commit/fa9c3f2b1712d46bcf50808845ff6c1509235c3f) Merge branch 'master' into spreadsheet-report - [`9d537be`](https://github.com/actualbudget/actual/commit/9d537bec2ff9735f969d3e6060f4dfbb326dd31b) fix: fix lint errors - [`1b08007`](https://github.com/actualbudget/actual/commit/1b08007ad98c1348fc50b96398dfa5b47586d045) feat: improve user experience - [`bd53234`](https://github.com/actualbudget/actual/commit/bd5323403c52842fc2474d2ae7530244a7f6ce57) fix: safeguard regex queries - [`e3261d4`](https://github.com/actualbudget/actual/commit/e3261d45801cc5f0f5c14f7c30703e67e9231db0) [autofix.ci] apply automated fixes - [`10e489c`](https://github.com/actualbudget/actual/commit/10e489c1a15cf8ea9a1249cabb0684c2c328c188) fix: safeguard regex queries ### 📊 Changes **31 files changed** (+5755 additions, -3 deletions) <details> <summary>View changed files</summary> 📝 `packages/desktop-client/src/components/reports/Overview.tsx` (+54 -1) 📝 `packages/desktop-client/src/components/reports/ReportOptions.ts` (+7 -0) 📝 `packages/desktop-client/src/components/reports/ReportRouter.tsx` (+3 -0) ➕ `packages/desktop-client/src/components/reports/SaveSpreadsheetReport.tsx` (+310 -0) ➕ `packages/desktop-client/src/components/reports/reports/SheetRow.tsx` (+608 -0) ➕ `packages/desktop-client/src/components/reports/reports/Spreadsheet.tsx` (+722 -0) ➕ `packages/desktop-client/src/components/reports/reports/SpreadsheetCard.tsx` (+613 -0) ➕ `packages/desktop-client/src/components/reports/reports/spreadsheetReport/BalanceQueryForm.tsx` (+50 -0) ➕ `packages/desktop-client/src/components/reports/reports/spreadsheetReport/CostQueryForm.tsx` (+309 -0) ➕ `packages/desktop-client/src/components/reports/reports/spreadsheetReport/FormulaPreview.tsx` (+109 -0) ➕ `packages/desktop-client/src/components/reports/reports/spreadsheetReport/FormulaQueryForm.tsx` (+133 -0) ➕ `packages/desktop-client/src/components/reports/reports/spreadsheetReport/QueryTypeSelector.tsx` (+50 -0) ➕ `packages/desktop-client/src/components/reports/reports/spreadsheetReport/RowOperationHelpers.tsx` (+168 -0) ➕ `packages/desktop-client/src/components/reports/spreadsheets/QueryBuilder.tsx` (+684 -0) ➕ `packages/desktop-client/src/components/reports/spreadsheets/queryParser.ts` (+338 -0) ➕ `packages/desktop-client/src/components/reports/spreadsheets/sheetEngine.ts` (+877 -0) ➕ `packages/desktop-client/src/components/reports/spreadsheets/useSheetCalculation.ts` (+432 -0) 📝 `packages/desktop-client/src/components/settings/Experimental.tsx` (+6 -0) 📝 `packages/desktop-client/src/hooks/useFeatureFlag.ts` (+1 -0) ➕ `packages/desktop-client/src/hooks/useSpreadsheetReport.ts` (+15 -0) _...and 11 more files_ </details> ### 📄 Description This PR adds a new spreadsheet report as an experimental feature. The following is the documentation on how to use this feature along with (slightly outdated) screenshots of what the report looks like #### Available Filters ##### Category Filters ```javascript cost({ category: 'Food' }); ``` ##### Account Filters ```javascript cost({ account: 'Checking Account' }); ``` ##### Payee Filters ```javascript cost({ payee: 'Grocery Store' }); ``` ##### Notes Filters ```javascript cost({ notes: 'groceries' }); // Exact match cost({ notes: contains('groceries') }); // Contains text cost({ notes: hasTags('#food #groceries') }); // Has specific hashtags ``` ##### Transaction Status Filters ```javascript cost({ cleared: true }); // Only cleared transactions cost({ cleared: false }); // Only uncleared transactions cost({ reconciled: true }); // Only reconciled transactions cost({ reconciled: false }); // Only unreconciled transactions cost({ transfer: true }); // Only transfer transactions cost({ transfer: false }); // Only non-transfer transactions ``` ##### Date Filters ```javascript cost({ date: thisMonth }); // Current month cost({ date: lastMonth }); // Previous month cost({ date: thisYear }); // Current year cost({ date: lastYear }); // Previous year cost({ date: between('2024-01-01', '2024-12-31') }); // Custom range cost({ date: gte('2024-01-01') }); // From date onwards cost({ date: lte('2024-12-31') }); // Up to date ``` ##### Amount Filters ```javascript cost({ amount: gte(1000) }); // Amount >= $10.00 (in cents) cost({ amount: lte(5000) }); // Amount <= $50.00 (in cents) ``` ##### Combined Filters ```javascript cost({ category: 'Food', date: thisMonth, amount: gte(1000), cleared: true, }); ``` ### balance() - Account Balance Get the current balance of any account. #### Syntax ```javascript balance('Account Name'); ``` #### Examples ```javascript balance('Checking Account'); balance('Savings Account'); balance('Credit Card'); ``` ## Mathematical Functions ### Aggregation Functions #### sum() Sum values in a range or individual values. ```javascript sum(row-1:row-5) // Sum of rows 1-5 sum(row-1, row-3, row-5) // Sum of specific rows sum(100, 200, 300) // Sum of numbers ``` #### average() Calculate the average of values. ```javascript average(row-1:row-5) // Average of rows 1-5 average(row-1, row-3, row-5) // Average of specific rows average(100, 200, 300) // Average of numbers ``` #### min() Find the minimum value. ```javascript min(row-1:row-5) // Minimum of rows 1-5 min(100, 200, 300) // Minimum of numbers ``` #### max() Find the maximum value. ```javascript max(row-1:row-5) // Maximum of rows 1-5 max(100, 200, 300) // Maximum of numbers ``` #### count() Count numeric values. ```javascript count(row-1:row-5) // Count of numeric values in rows 1-5 count(100, 200, 300) // Count of numbers ``` ### Mathematical Functions #### abs() Absolute value. ```javascript abs(-100); // Returns 100 abs(row - 1); // Absolute value of row 1 ``` #### round() Round to specified decimal places. ```javascript round(3.14159, 2); // Returns 3.14 round(row - 1, 0); // Round row 1 to whole number ``` #### floor() Round down to nearest integer. ```javascript floor(3.7); // Returns 3 floor(row - 1); // Floor of row 1 ``` #### ceil() Round up to nearest integer. ```javascript ceil(3.2); // Returns 4 ceil(row - 1); // Ceiling of row 1 ``` #### sqrt() Square root. ```javascript sqrt(16); // Returns 4 sqrt(row - 1); // Square root of row 1 ``` #### pow() Power function. ```javascript pow(2, 3); // Returns 8 (2^3) pow(row - 1, 2); // Square of row 1 ``` ## Logical Functions ### if() Conditional logic. ```javascript if(condition, true_value, false_value) ``` #### Examples ```javascript if(row-1 > 0, row-1, 0) // If row 1 > 0, use row 1, else 0 if(cost({category:"Food"}) > 500, "High", "Low") // Conditional text if(row-1 > row-2, "Greater", "Less") // Compare two rows ``` ### and() Logical AND - returns 1 if all arguments are truthy, 0 otherwise. ```javascript and(row - 1 > 0, row - 2 > 0); // Returns 1 if both rows > 0 and(true, true, false); // Returns 0 and(1, 2, 3); // Returns 1 ``` ### or() Logical OR - returns 1 if any argument is truthy, 0 otherwise. ```javascript or(row - 1 > 0, row - 2 > 0); // Returns 1 if either row > 0 or(false, false, true); // Returns 1 or(0, 0, 0); // Returns 0 ``` ### not() Logical NOT. ```javascript not(0); // Returns 1 not(1); // Returns 0 not(row - 1 > 0); // Returns 1 if row 1 <= 0 ``` ## Date and Time Functions ### today() Returns today's date as days since epoch. ```javascript today(); // Returns current date as number ``` ## Text Functions ### concatenate() Join multiple text values together. ```javascript concatenate('Hello', ' ', 'World'); // Returns "Hello World" concatenate('Row 1: ', row - 1); // Combine text with row value ``` ### len() Get the length of a string. ```javascript len('Hello'); // Returns 5 len('Row 1: ' + row - 1); // Length of concatenated string ``` ## Cell References ### Individual Row References Reference values from other rows using `row-X` format. ```javascript row - 1; // Value from row 1 row - 2; // Value from row 2 row - 10; // Value from row 10 ``` ### Row Ranges Reference a range of rows using `row-X:row-Y` format. ```javascript row-1:row-5 // Values from rows 1 through 5 row-3:row-7 // Values from rows 3 through 7 ``` ### Using Row References ```javascript row-1 + row-2 // Add two row values sum(row-1:row-5) // Sum of rows 1-5 average(row-1:row-10) // Average of rows 1-10 if(row-1 > row-2, row-1, row-2) // Conditional row reference ``` ## Operators ### Arithmetic Operators ```javascript + // Addition - // Subtraction * // Multiplication / // Division ^ // Exponentiation ``` ### Comparison Operators ```javascript > // Greater than < // Less than >= // Greater than or equal <= // Less than or equal == // Equal to != // Not equal to ``` ### Examples ```javascript row - 1 + row - 2; // Add two rows cost({ category: 'Food' }) * 1.1; // Add 10% to food cost if ((row - 1 > 1000, 'High', 'Low')) // Conditional based on comparison row - 1 / row - 2; // Divide row 1 by row 2 ``` ## Query Builder <img width="546" height="593" alt="Query Builder" src="https://github.com/user-attachments/assets/71c0f0eb-974c-4e5e-acd4-a75103f2c349" /> ## Spreadsheet Report <img width="1470" height="953" alt="Spreadsheet Base" src="https://github.com/user-attachments/assets/494124b5-5868-4818-9948-a02041e85b4a" /> ## Spreadsheet Report with Hidden Formulas <img width="1470" height="953" alt="Spreadsheet Hide Formulae" src="https://github.com/user-attachments/assets/b729f4bc-0fac-43d7-af4e-d7cf9f97a14d" /> ## Spreadsheet Report with Hidden Rows <img width="1470" height="953" alt="Spreadsheet Hide Rows" src="https://github.com/user-attachments/assets/d1497c0e-0d73-46d9-a66b-7cae05b652c2" /> ## Spreadsheet Report Card <img width="710" height="429" alt="Spreadsheet Card" src="https://github.com/user-attachments/assets/71eda1cc-1501-4074-a269-58013c440326" /> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-02-28 21:20:13 -06:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/actual#5873