You earned ₹2.8L last month. Bank shows ₹1.9L. Where did ₹90k go?
Sheet Expenses 2025 has 147 rows, 4 categories Tools | Travel | Food | Misc, no client or project link, amount as 2,000 text. You sum Misc ₹42k and still can't tell if Acme Revamp budget ₹1.2L was profitable after subcontractor ₹30k + hosting ₹2.4k.
You log spends, you don't learn profit.
Why Generic Expense Lists Hide Profit
Most freelancers track expenses like a diary, not a ledger:
- Flat categories hide margin.
Tools ₹12klumpsFigma for Acme(billable) withNotion personal ₹1k(overhead). NoclientId | projectIdlink → can't answer Did Acme profit? - No link to revenue.
transactions type expensesrc/app/api/transactions/route.ts:89lives apart frominvoices amount numeric(12,2)src/lib/db/schema.ts:69andprojects budget numeric(12,2)src/lib/db/schema.ts:35. SheetsVLOOKUPbreaks on insert. - Manual sums drift. Type
5000as50000— no servercomputedTotal Math.round((taxable+tax)*100)/100src/app/api/invoices/route.ts:127guard, chart lies for months.
Profit isn't Income - Expenses in one cell. It's per client/project - overhead over time. Flat lists can't do that.
The 7 Categories That Actually Reveal Profit
Forget 30 categories. Use 7 that map to how freelancers spend and how clients pay:
1. Client-Billable Tools
Figma, Webflow, Stock for a project. Link transactions clientId + projectId src/lib/db/schema.ts:97 + category Billable Tools. Recover via invoices items jsonb src/lib/db/schema.ts:74 line Tools ₹4k.
2. Subcontractors
Dev ₹25k, Content ₹12k per project. Link projectId → Budget ₹1.2L - Subcon ₹37k = Gross ₹83k. Sheets hides this as Freelancer payment without FK.
3. Hosting & Infra Pass-Through
assetsHosting costMonthly numeric(12,2) src/lib/db/schema.ts:196 ₹2,400/mo + assetsDomains expiryDate. Mark billableTo client vs personal — un-invoiced ₹28.8k/yr leak if missed.
4. SaaS Overhead
assetsSubscriptions cost billingCycle monthly|yearly src/lib/db/schema.ts:224 Notion ₹1k. Overhead SaaS billableTo personal src/lib/db/schema.ts:227 — overhead, not project.
5. Marketing & Acquisition
Ads ₹8k to get clients. category Marketing without clientId → CAC via clients status lead→active src/lib/db/schema.ts:5.
6. Professional Services
CA ₹12k/yr → category Professional — prevents March 15% expense surprise.
7. Owner Draw & Investments
investments type stock|sip|crypto|fd|other src/lib/db/schema.ts:267 amount numeric src/lib/db/schema.ts:273 + transactionId src/lib/db/schema.ts:275. Separates profit ₹2L → SIP ₹40k wealth, not cost.
With these 7, every transactions amount numeric(12,2) src/lib/db/schema.ts:92 has a profit question answered: Billable? Which project? Recurring overhead?
The Linked Graph: Expense → Client → Project → Invoice
In RunoSO tenant_${id} src/lib/db/tenant.ts:47, expense is not a row, it's a node:
clients(id, name)
projects(clientId fk notNull onDelete:cascade src/lib/db/schema.ts:25, budget numeric(12,2), advance numeric(12,2))
transactions(type income|expense, amount numeric, category varchar(100), clientId fk, projectId fk, invoiceId fk src/lib/db/schema.ts:97, date timestamp)
invoices(clientId fk, projectId fk src/lib/db/schema.ts:66, amount numeric, items jsonb, taxRate, taxType varchar(50) src/lib/db/schema.ts:77)
assetsSubscriptions(toolName, cost numeric, billingCycle, billableTo)
assetsHosting(costMonthly numeric src/lib/db/schema.ts:196)What linked gives you:
- Auto P&L per project.
GET /api/transactions?projectId=xxxsrc/app/api/transactions/route.ts:91→₹1.2L - ₹48k = ₹72k 60%noSUMIFS. - Client profitability.
transactions + clientId→Acme lifetime ₹4.2L - ₹73k = ₹3.47L. - Overdue-aware.
cron daily 30 3 * * *vercel.json:4flipssent→overduesrc/app/api/cron/daily/route.ts:46. Receivables excluded untilsyncInvoicePaidStatusconfirms.
Excel vs QuickBooks vs FreshBooks vs RunoSO
| Need | Excel/Sheets | QuickBooks | FreshBooks | RunoSO linked graph | |||
|---|---|---|---|---|---|---|---|
| Category depth | Text Tools — breaks on typo Tols | 50+ categories, US-centric | Simple Expense list | 7 typed + varchar(100) src/lib/db/schema.ts:94 + clientId/projectId FK | |||
| Auto compute | =SUM(D2:D90) manual, no guard | Auto but US tax only | Auto totals | Server subtotal - discount + tax src/app/api/invoices/route.ts:119 + transactions amount numeric typed — tamper-proof | |||
| Client/project link | VLOOKUP across tabs, fails | Client link, no project graph | Client link, weak project | clientId + projectId + invoiceId FKs in one query src/app/api/transactions/route.ts:91 | |||
| Overdue & recurring | Manual Overdue? YES | Overdue flag, no cron | Reminders only | Cron sent→overdue src/app/api/cron/daily/route.ts:46 + `recurringInterval none | monthly | quarterly | yearly src/lib/utils/recurring.ts:5` auto next invoice |
| India GST | You split 9+9 vs 18 manually | US sales tax | US | `taxType cgst_sgst | igst | gst src/app/api/invoices/route.ts:124 + taxRate numeric(5,2)` server computed | |
| Cost | Free but 3h/mo manual | ₹1,200/mo+ | ₹1,500/mo+ | Free 5 invoices/mo src/lib/plans/limits.ts:28 → Solo ∞ + finance_charts true src/lib/plans/limits.ts:59 |
Bottom line: Excel is free but lies by typo; QuickBooks/FreshBooks auto-compute but aren't built for Indian freelance graph client→project→invoice→transaction+assets. RunoSO auto-computes and links.
Setup in 30 Minutes
- Import 90 days.
POST /api/transactionssrc/app/api/transactions/route.ts:131withtype expense category Billable Tools|Subcontractors|Hosting|Overhead|Marketing|Professional+projectIdwhere billable. 40 rows enough. - Tag billable vs overhead.
assetsSubscriptions billableTosrc/lib/db/schema.ts:227+assetsHosting costMonthly→clientvspersonal— leaks visible. - Link invoices. Each
invoices projectIdsrc/lib/db/schema.ts:66→ matchingtransactions invoiceId Invoice Payment. Recharts shows true margin. - Weekly review. Filter
transactions datesrc/app/api/transactions/route.ts:88per project —Acme 58% vs Beta 32%→ price next quote.
Mistakes That Keep Expenses Blind
Misc >20% hides profit — keep Misc <5%. Not linking pass-throughs inflates overhead and hides true margin — always projectId + billableTo:client. SIP as expense destroys P&L — use investments type sip src/lib/db/schema.ts:269 linked transactionId.
RunoSO tracks every expense as a linked node — client, project and invoice aware — so profit per project is auto-computed, not spreadsheet-guessed.
→ Reveal Real Profit — Try RunoSO Free
By Gautam Parmar — solo founder building RunoSO in public. Finance that links beats finance that sums.
Keep Reading
- Budget before you quote: Budget Planning for Freelance Projects.
- Income without sheets: Track Freelance Income & Expenses Without Spreadsheets.
- Metrics that matter: 8 Metrics Solo Businesses Must Track.
FAQ
How many expense categories do freelancers really need? Seven. Billable Tools, Subcontractors, Hosting Pass-Through, Overhead SaaS, Marketing, Professional, Owner/Investments. Enough to reveal margin per project without 30-category fatigue. Each maps to transactions category varchar(100) src/lib/db/schema.ts:94 + FKs.
Can I see profit per client without manual pivots? Yes — filter GET /api/transactions?clientId=xxx src/app/api/transactions/route.ts:87 for income vs expense. RunoSO finance_charts true on Solo src/lib/plans/limits.ts:59 auto renders client P&L; Sheets needs SUMIFS each time.
What about GST on expenses? Log expense amount net, invoice taxType cgst_sgst|igst src/app/api/invoices/route.ts:124 handles GST on income side. For input GST, tag category Billable Tools with note GST 18% — future gst_invoicing Pro can automate input credit view.



