Moula
Personal finance app for tracking income and expenses in DZD — mobile-first, with automated monthly email reports
What is this project?
Moula is a personal finance app for tracking income and expenses, built around the Algerian dinar (DZD). It's a mobile-first single-page application with a hand-drawn design language, where the browser talks directly to Firebase Auth and Firestore — there is no custom CRUD API. Instead, firestore.rules is the security model: per-user data isolation, field-level validation, and server-pinned audit timestamps, all unit-tested against the emulator. ▎ ▎ All financial math lives in a framework-agnostic shared/ layer used by both the browser and the server, so the numbers in an emailed monthly report are computed by exactly the same code as the on-screen dashboard. Serverless Vercel Functions exist only for what the browser must never do — sending email and deleting accounts — plus a daily cron that delivers monthly reports automatically. Money is stored as integer minor units end-to-end; floating-point never touches the ledger.
The Problem
Tracking personal finances in DZD means spreadsheets or foreign apps that mishandle the currency, store money as floating-point decimals, and offer no monthly summary without manual work. And when I moved off my earlier "Savings" app, its data had no transaction dates at all — importing it without corrupting the financial history was its own problem to solve.
The Solution
A mobile-first SPA where every amount is an integer in minor units (centimes), and every transaction carries two distinct timestamps: the financial date (what the user says happened, backdatable) and the audit timestamps pinned to the Firestore server clock, which clients cannot set or forge. The whole security model lives in Firestore security rules — per-user isolation, field-level validation, immutable audit fields — verified by emulator tests. Serverless functions handle only email delivery and account deletion, and a shared domain layer guarantees the emailed report and the on-screen dashboard compute identical numbers from the same ledger.
Feature Walkthrough
Integer money model — every amount stored as minor units; floating-point money never touches the ledger
Dual-date ledger: user-settable financial date vs. audit timestamps pinned to the Firestore server clock
Firestore security rules as the security boundary — per-user isolation, field-level validation, server-only collections, emulator-tested
Live dashboard: balance, monthly income/expense, savings-rate gauge, rule-based insight cards
Analytics: income vs. expense trend, spending-breakdown donut, savings trend
Monthly reports with month-over-month deltas, top categories, biggest expenses — export or email
Scheduled monthly email (Vercel Cron, daily 06:00 UTC) with a server-only double-send guard, sent only to verified addresses
Import from the old Savings app (JSON/CSV) with per-row date confirmation and duplicate detection; full CSV/JSON export
App lock: 4-digit PIN stored as salted SHA-256, idle auto-lock, cooldown after wrong attempts
Server-side account deletion (Auth user + Firestore data)
Email/password and Google sign-in, email verification, password reset
Light/dark themes with a hand-drawn design language
Technology Stack
Tooling
Framework: React 19 (SPA), React Router 7
Language: TypeScript (strict)
Styling/UI: Tailwind CSS v4, Recharts, Lucide icons
Backend: Firebase Auth, Cloud Firestore, firebase-admin 14 (server-side only)
Serverless: Vercel Functions (Node 22), Vercel Cron
Email: Resend
Build: Vite 8, esbuild (function bundling)
Testing: Vitest, @firebase/rules-unit-testing (Firestore emulator)
Deployment: Vercel (Git deploys)
Challenges & Solutions
Getting money right: representing every amount as integer minor units through storage, calculation, import, export, and email rendering, so rounding errors are structurally impossible. Making Firestore rules the actual security boundary — not just per-user isolation but field-level validation (types, bounds, date format, allowed enums), server-pinned timestamps, and server-only collections — and proving it with rule tests against the emulator. Keeping date semantics honest: the financial date and the creation date are different things, and imports from the old Savings app (which stored none) require the user to confirm every date rather than silently inferring one. And delivering email safely: reports go only to the verified address on the Firebase Auth record — never to a client-writable field — with a server-only delivery record acting as a double-send guard for the cron job.
What I Learned
Putting the entire authorization model in Firestore rules — and testing it like code — is what lets a client-only app be genuinely secure; the rules became the API boundary. Sharing one domain layer between browser and server removed an entire class of "the email says something different from the screen" bugs. Integer money and server-pinned audit timestamps are cheap decisions to make early and nearly impossible to retrofit.
Results & Impact
A deployed, working product — live at moulaa.vercel.app with email/password and Google sign-in, a full ledger with search and filtering, analytics, import from the old app's JSON/CSV exports with duplicate detection, CSV/JSON export, and automated monthly email reports. The financial core is covered by 204 unit tests, and the security rules are tested against the Firestore emulator. (No user or performance numbers — none exist in the repository.)
More Projects
StAffichage V2
Active production platform that replaced the original Moodle Notifier. Live at stbejaia.up.railway.app, with a dedicated install page and an Android APK. Source code kept private.
A mobile-first platform for university schedules, classroom info, and announcements — with search, dark mode, and real-time push notifications.
Forsati
FORSATI (فرصتي) — a lightweight, multilingual web app that connects Algerian youth to ODEJ establishments through personalized recommendations, event registration, and QR ticketing.
LhadjCAD
A polished portfolio-worthy project that demonstrates technical skill, UI organization, and full-stack thinking.
LhadjCAD is a technical web platform designed to help create, manage, and present structured CAD-style content with a clean and efficient workflow.