This guide covers all common customization tasks for Cashew-Reports, including widget management, category configuration, CSV format, PDF export, and currency changes.
Set visible: false in its CONFIG.WIDGETS entry:
{ id: "rent_trend", visible: false, size: "third" },Section titles auto-hide when all their content widgets are hidden. You do not need to hide them manually.
Move the entry in the CONFIG.WIDGETS array. Widgets render in array order, top to bottom, left to right within the 12-column grid.
Change the size field:
| Size | Grid columns | Fits per row |
|---|---|---|
"small" |
3 of 12 | 4 widgets |
"third" |
4 of 12 | 3 widgets |
"medium" |
6 of 12 | 2 widgets |
"large" |
12 of 12 | 1 widget |
Use the optional col field for explicit grid-column start (1-based):
{ id: "disc_split", visible: true, size: "medium", col: 1 },
{ id: "lifestyle_tax", visible: true, size: "medium", col: 7 },Valid positions depend on size: small → 1,4,7,10 / third → 1,5,9 / medium → 1,7 / large → 1. The col field is ignored on tablet and mobile breakpoints.
Insert a section title entry before the first widget in the section:
{ id: "section_title", visible: true, title: "My New Section" },
{ id: "my_widget_1", visible: true, size: "medium" },
{ id: "my_widget_2", visible: true, size: "medium" },Use pageBreak: true on the section title if you want a page break before it in PDF exports.
Edit the three CONFIG arrays:
DISC_CATEGORIES: ["Shopping", "Dining Out", "Entertainment"],
NON_DISC_CATEGORIES: ["Rent", "Utilities", "Insurance", "Healthcare"],
SEMI_DISC_CATEGORIES: ["Groceries", "Transport", "Subscriptions"],Categories not in any of these three lists (and not in BURN_RATE_EXCLUDED) will fall into semi-discretionary by default.
The disc_split widget also has two special-case overrides in computeDiscSplit_():
- Food > Eating Out is routed to discretionary (even though "Food" is in semi-disc)
- Services > Household Staff is routed to non-discretionary
To change these, edit the if conditions in computeDiscSplit_().
Replace the EXPECTED_UTILITY_BILLS array:
EXPECTED_UTILITY_BILLS: [
"Electricity", "Water", "Internet", "Phone", "Gas", "Streaming"
],These must match subcategory names under your "Utility Bills" category in Cashew exactly. The widget checks for paid/upcoming/missing status for each entry.
Edit BURN_RATE_EXCLUDED:
BURN_RATE_EXCLUDED: ["Investment", "Balance Correction", "Payroll Deductions"],This affects burn_rate, burn_trend, savings_rate, cat_breakdown, top_subcats, biggest_increases, and disc_split.
Edit CORE_INCOME_CATEGORIES:
CORE_INCOME_CATEGORIES: ["Salary", "Passive Income", "Capital Gains", "Ad Hoc Income"],The savings rate formula is: (sum of core income - burn) / sum of core income, trailing 3 months.
Edit computeLifestyleTax_() in Code.gs. Replace the hardcoded references:
function computeLifestyleTax_(ctx) {
// Change these to your own "lifestyle" categories/subcategories
var eo = sumFilter_(ctx.expenses, ctx.currentMonth, "Food", "Eating Out");
var pp = sumCat_(ctx.expenses, ctx.currentMonth, "Personal Purchases");
// ...
}Edit computeGroceryTrend_():
// Change "Home Essentials" and "Groceries" to your category/subcategory:
var amt = sumFilter_(ctx.expenses, m, "Home Essentials", "Groceries");Edit PEOPLE_PAYMENT_FILTERS:
PEOPLE_PAYMENT_FILTERS: [
{ category: "Services", subcategory: "Household Staff" },
{ category: "Services", subcategory: "Family Transfer" },
{ category: "Occasion", subcategory: "Gifts" },
{ category: "Occasion", subcategory: "Donations" }
]Add or remove entries as needed. Each entry is a { category, subcategory } pair.
The dashboard reads Cashew's standard Outbox CSV export. The key columns used are:
| Column | Used for | Notes |
|---|---|---|
date |
Month grouping, date display | Parsed with new Date() |
amount |
Transaction amount | Filled = settled/actual transaction |
amount unpaid |
Expected amount | Used as fallback when amount is empty |
type |
Transaction type | upcoming and subscription with empty amount → upcoming; everything else → actual |
title |
Transaction description | Used by rent_trend for property matching (AJH/NJH in title) |
income |
true / false |
Separates income from expenses |
category name |
Primary grouping | Must match CONFIG category names exactly |
subcategory name |
Secondary grouping | Must match CONFIG subcategory names exactly |
color |
Category color | 0XFFRRGGBB format → converted to #RRGGBB for bar colors |
Unused columns (present in CSV but ignored): account, currency, note, icon, emoji, budget, objective, transaction id, last modified.
amountfilled → actual transaction (settled, paid)amountempty +typeis "upcoming" or "subscription" → upcoming (expected, not yet paid)amountempty +typeis anything else (e.g., "default") → actual (unsettled but real, e.g., salary/deductions that appear before settlement)- Upcoming transactions are used only by the utility_bills widget (to show "upcoming" status for expected bills)
The CSV filename and Drive folder are configured at the top of Code.gs:
CSV_FOLDER_NAME: "Cashew",
CSV_FILENAME: "outbox.csv",The script searches for a folder named "Cashew" on your Drive, then finds the file named "outbox.csv" inside it. If not found, it falls back to searching for cashew_outbox.csv in the root of Drive.
Click "Save as PDF" in the dashboard header. The flow is:
- Client calls
google.script.run.exportPdfToSnapshots(month) - Server re-runs
loadAndCompute_()withpdfMode=true - The
static-renderCSS class is applied, which:- Hides all
<canvas>elements and shows.chart-fallbackbars instead - Expands all drill-down panels
- Shows all hidden table rows
- Hides expand buttons and toggle hints
- Hides all
- The HTML is converted to PDF via
Utilities.newBlob().getAs("application/pdf") - The PDF is saved to
/Cashew/Snapshots/on Drive (folder is auto-created) - A link to the saved file opens in a new tab
Section titles with pageBreak: true in CONFIG get page-break-before: always in the PDF. The default config has page breaks before "Spending Patterns" and "Income & Savings".
The dashboard also supports Ctrl+P / Cmd+P for direct browser printing. The @media print CSS rules apply the same static-render behavior as PDF mode.
The fmt_() function in Code.gs formats amounts with the ₹ symbol and Indian comma grouping (1,23,456). To change:
function fmt_(n) {
n = Math.round(n || 0);
var neg = n < 0; if (neg) n = -n;
var s = n.toString();
// Standard grouping (1,234,567):
s = s.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
// Change "$" to your currency symbol:
return (neg ? "-" : "") + "$" + s;
}Also update fK_() if you want a currency prefix on abbreviated chart labels:
function fK_(n) {
n = Math.round(n || 0);
return n >= 1000 ? "$" + (n / 1000).toFixed(1) + "k" : "$" + n.toString();
}And update the client-side fK() function in Dashboard.html (used in chart tooltips and value labels).
The dashboard supports month navigation via URL parameter or the picker in the header:
| URL | What it shows |
|---|---|
.../exec |
Latest month in the CSV (default) |
.../exec?month=2026-03 |
March 2026 specifically |
.../exec?month=2025-11 |
November 2025 specifically |
The month picker arrows navigate to adjacent months. Each navigation is a full page reload (GAS re-reads the CSV and re-computes for the requested month).
If the requested month has no data in the CSV, the dashboard shows zeros for that month.
| What changed | What to do | Re-deploy needed? |
|---|---|---|
| Transaction data (new CSV) | Replace the file in Drive | No -- dashboard reads the latest on every load |
| Widget visibility/order/size | Edit CONFIG.WIDGETS in Code.gs |
Yes (new version) |
| Category classifications | Edit CONFIG arrays in Code.gs | Yes (new version) |
| Colors, layout, CSS | Edit Dashboard.html | Yes (new version) |
| Add a new widget | Edit Code.gs (+ Dashboard.html if chart) | Yes (new version) |
Re-deploy steps: Deploy → Manage deployments → pencil icon → Version: "New version" → Deploy. The same URL serves the updated code. Do not create a new deployment (that generates a different URL).