KaruniaAndrianSaputra
All projects

Full Stack · 2026

Tosm Finance

A full stack personal finance platform — React PWA, React Native app, and its own Express/MongoDB API — for multi-wallet budgeting and an investment ledger, with an LLM assistant that turns chat messages and receipt photos into transactions.

tosm-finance.tosm.my.id

Gallery

A closer look

Dashboard overview

Catat Cepat — AI transaction entry

Reports & cash flow

Investment portfolio

Overview

What it is, and why it exists

Tosm Finance (TosmFi) is a money-tracking product I built end to end across three repos: a React/Vite PWA, a React Native app for Android and iOS, and the Express/MongoDB API both clients share. It covers everyday budgeting (wallets, categories, transactions, recurring schedules, budgets) alongside an investment ledger that tracks invested vs. current value per account, plus a dedicated reporting layer so the dashboard stays fast without shipping full transaction history to the client. On top of that sits Catat Cepat, an LLM-powered assistant: type something casual like "beli warteg 12rb sama kopi 8rb" or snap a receipt, and it drafts the transactions, asks for anything missing, and saves only after you confirm the preview.

My Role

Full stack developer — product architecture, REST API design, database modeling, LLM integration, and the web and mobile clients.

Problem

The problem

Most personal-finance trackers are single-user, store data only in local storage, and treat investments as an afterthought (if they support them at all) — and logging every coffee by filling in a form is tedious enough that people stop doing it. There was no lightweight tool that let a household track shared wallets and an investment portfolio side by side, backed by a real API, that was also quick enough to keep up with day to day.

Solution

The solution

I built TosmFi as a full stack product with its own backend and two clients: wallets, categories, and transactions share one atomic ledger — every mutation adjusts wallet balance and category counts inside the same database transaction as the write itself — while a separate investment-transaction ledger tracks each account's invested and current value independently. To make logging fast, Catat Cepat sends the conversation to an LLM that returns structured transaction drafts; the backend validates every draft against the user's live data and commits the confirmed batch through the same code paths as a manual entry. New accounts stay pending until an admin approves them, and five dedicated report endpoints keep the dashboard and charts fast on both web and mobile.

Key Features

What it does

  • Catat Cepat (AI): log transactions by chatting in casual Indonesian, English, or Japanese — the LLM drafts them, asks for missing details, and saves only after a confirmed preview
  • Receipt scanning: photograph a receipt and the LLM reads the merchant, total, date, and items into an expense draft
  • Multi-wallet balance tracking with drag-to-reorder and a primary wallet
  • Income, expense, transfer, and balance-correction transactions with live filtering and search
  • Recurring schedules with bill reminders, and category-scoped budgets pinned to the dashboard
  • Investment ledger tracking invested vs. current value per account, with backdatable top-ups, withdrawals, transfers, and value updates
  • Dedicated report endpoints — summary, wallet usage, top spending, cash flow, monthly trend — exportable to PDF/Excel
  • Native Android/iOS app sharing the same API, with an optional PIN/fingerprint app lock

Tech Stack

Built with

Technology choices made to fit the product's real constraints, not novelty.

ReactReact NativeTypeScriptViteRedux ToolkitTailwind CSSExpress.jsMongoDBLLM (Gemini API)Recharts
Architecture

System architecture

Web (React + Vite PWA)
Mobile (React Native, Android/iOS)
REST API (Express.js)
LLM assistant (OpenAI-compatible API, Gemini)
Mongoose / MongoDB
JWT Auth Middleware

Challenges

Challenges & solutions

Trusting an LLM with structured financial data

The model has to turn free-form chat (and receipt photos) into transactions without ever writing something wrong to the ledger. It never sees database ids — wallets, categories, and accounts are passed as short refs (W1, C2.1, A3) that the server maps back — and its output is constrained to a strict JSON schema. Every draft is then re-validated server-side against the user's live data (ownership, category type, withdrawals over the current value), missing details always become a follow-up question with quick-reply chips instead of a broken preview, and nothing is saved until the user confirms; the confirmed batch commits in one database session.

Keeping the assistant responsive on an overloaded free-tier model

The free tier of the LLM API regularly returns 503s, hangs, or hits its daily quota. The backend's LLM client caps each attempt, gives the whole call a fixed time budget, hedges a slow model by starting a fallback model in parallel (first answer wins), and temporarily deprioritises a model that just failed or was rate-limited — so a busy day degrades to a fallback model instead of a stuck chat. Plain confirmations and cancellations ("ok, catat", "batal") are answered without an LLM call at all.

Keeping wallet balances and category counts consistent

Every transaction create/edit/delete has to adjust wallet balance and category/subcategory counts atomically, and an edit or delete must reverse the exact prior effect instead of trusting a client-supplied snapshot. I moved this into Mongoose session transactions that reload the existing document server-side, revert its effect, then apply the new one — so a mutation and its side effects always commit or roll back together.

Result

Outcome

  • Ships as three coordinated repos — React PWA, React Native app (v1.2.0), and REST API — sharing one backend and one data model
  • Catat Cepat turns a chat message or a receipt photo into confirmed transactions, including investment entries, in a single atomic commit
  • Light/dark theme and 3 languages (Indonesian, English, Japanese) on both web and mobile
  • Every balance-affecting mutation runs inside a database transaction, keeping wallet/category/investment totals consistent even under concurrent edits