frzxw
Resume
Selected work

Sakola

Student savings is other people's money, handled every school day. I'm building Sakola so that every rupiah deposited or withdrawn is recorded once, can be explained later, and adds up at closing time.

Sakola summary for a school principal: total student savings, this month's deposits, participation rate and cash difference, a balance chart by grade, and participation by class.Sakola dashboard for a school treasurer: total student funds, today’s deposits and deductions, cash in the safe, and a daily deposit chart.Sakola treasurer dashboard on a phone: a greeting, total student funds, and today's deposit chart.
Role
Design and build
Platform
Web
Timeline
Ongoing
Stack
  • TypeScript
  • Next.js
  • PostgreSQL
  • Tailwind CSS

The short version

Sakola is a school platform built as one system that many schools can share. Each school keeps its own data separate, and features switch on per school. The first feature I built is student savings: a digital version of the savings book a school keeps for each student. The project is ongoing.

The problem, in plain words

Student savings means real money changing hands every day: deposits collected in class, withdrawals that someone has to approve, and cash in a safe that has to match the records when the day ends.

If a balance can simply be typed over, nobody can tell later why it changed. So the design starts from one rule: money is never edited, only recorded.

How it works for the school

The treasurer's screen is organised around the day, not around tables: what came in, what went out, and what still has to be done before the cash is closed.

  • Deposits by QR code, or collected by the homeroom teacher for a whole class.
  • Withdrawals need two people: one records the request and another approves it.
  • At closing time the system compares its balance with the cash counted in the safe, note by note.
  • Passbooks are generated as PDFs, and parents receive notifications.

Why a ledger

Think of a bank statement you can never erase. Every deposit and every withdrawal is a new line; the balance is simply the sum of the lines. A mistake is fixed by adding a correcting line, never by changing an old one.

That one rule makes the rest easy. Balances, passbooks, daily closing and reports are all just different views of the same list.

Decisions and trade-offs

The choices that shaped it, and what each one cost.

  • A ledger instead of balances. Storing only entries makes every balance explainable and every mistake visible, at the cost of computing balances instead of reading one number.
  • Two people for every withdrawal. Maker-checker approval slows a withdrawal down a little, which is the point when the money belongs to children.
  • Many schools in one system. Each school is isolated, but students, users and roles are modelled once and shared by every module, so a new module does not need its own copy of the student list.
  • Features per school. A school that only needs savings gets only savings; modules switch on per school and connect automatically when combined.
  • Screens built around the day. A treasurer thinks in handovers and closing time, not in database tables, so the navigation separates today's work from the records.

For engineers

Multi-tenant architecture: each school is an isolated tenant over shared foundation data (students, users, roles) with role-based access, and modules activate per school and integrate when combined. The savings module is an append-only ledger in PostgreSQL; balances are derived, never stored as editable values. The frontend is Next.js, TypeScript and Tailwind CSS.

flowchart TB
  subgraph tenancy["Tenancy"]
    a["School A"]
    b["School B"]
    c["School C"]
  end
  shared[("Shared foundation data<br/>students, users, roles")]
  a --> shared
  b --> shared
  c --> shared
  subgraph savings["Student savings module"]
    deposit["Deposit<br/>QR code or homeroom"]
    withdraw["Withdrawal<br/>recorded, then approved"]
    ledger[("Ledger<br/>append-only")]
    deposit --> ledger
    withdraw --> ledger
    ledger --> balance["Balances"]
    ledger --> passbook["PDF passbooks"]
    ledger --> closing["Daily cash closing"]
    ledger --> notify["Parent notifications"]
  end
  shared --> savings
Two layers: schools as isolated tenants over shared data, and the savings module, where everything is derived from one ledger.

Where it is going

Savings is the first module, not the whole platform. Separately, my thesis explores a structured exam question bank, extracted and classified by agentic AI. That is an academic direction, not a shipped feature.

Screens

  • Sakola summary for a school principal: total student savings, this month's deposits, participation rate and cash difference, a balance chart by grade, and participation by class.
    Principal summary
  • Sakola treasurer dashboard on a phone: a greeting, total student funds, and today's deposit chart.
    Mobile
  • Sakola cash closing screen: counting notes by denomination, with a reconciliation of system balance against the physical count.
    Cash closing
  • Sakola withdrawals screen: two active requests and the detail of one, with amount, reason, and an approval trail.
    Withdrawals

What I took from it

  1. Model the money first

    Once every movement is an entry, reconciliation, passbooks, and balances are all views of the same table.

  2. A correction is an entry

    Nothing is edited in place, so the history always explains the balance.

  3. Design around the day

    A treasurer thinks in handovers and closing time, so the navigation does too.