A mobile-friendly money tracking app for families. Kids earn money by completing tasks and log when they spend it; parents can manage all their kids' balances from one place. Both can add income or expense entries (free-form descriptions, money-only). The app runs on an existing Frappe v16 bench (kido1.test:8005), and the kido Frappe app is already scaffolded but has no doctypes yet.
All setup is done by an admin through Frappe Desk. Parents and kids never use Frappe Desk — their only interface is the mobile web UI.
- Create a User account for each family member (e.g. 4 accounts for 2 parents + 2 kids)
- Set User Type = Website User (not System User) — this alone blocks Frappe Desk access
- Assign Kido Parent role to parent accounts; Kido Kid role to kid accounts
- Create a Kid Profile record for each child, selecting:
- The kid's User account (
userfield) - One or more parent User accounts (rows in the
parentschild table) - An avatar emoji
- The kid's User account (
After this, parents and kids log in and are immediately routed to their mobile UI — no Frappe Desk access needed or expected.
Two layers ensure Kido users never land on Frappe Desk:
- Website User type (set by admin at account creation) — Frappe's built-in gate; Website Users cannot open
/appat all. role_home_pagehook — if a Kido role account somehow has System User type, Frappe will still redirect them to/kidoon login instead of/app.
# hooks.py
role_home_page = {
"Kido Parent": "/kido",
"Kido Kid": "/kido",
}- Kido Parent — can view/manage all kids linked to them
- Kido Kid — can view and manage only their own account
1. Kid Profile — one record per child
| Field | Type | Notes |
|---|---|---|
| kid_name | Data | Display name |
| user | Link → User | Kid's login |
| parents | Table → Kid Profile Parent | One or more parent logins (child table) |
| avatar_emoji | Data | Fun emoji avatar (optional, e.g. 🐱) |
| current_balance | Currency | Read-only; updated by transaction controller |
1a. Kid Profile Parent — child table row (one per parent)
| Field | Type | Notes |
|---|---|---|
| parent_user | Link → User | A parent's login |
A kid can have multiple parents; each row in the parents table is one parent. get_parent_kids() queries Kid Profile where any row in parents has parent_user == frappe.session.user.
2. Kid Transaction — each income/expense entry
| Field | Type | Notes |
|---|---|---|
| kid | Link → Kid Profile | Required |
| transaction_type | Select | Income / Expense |
| amount | Currency | Must be > 0 |
| description | Data | Free-form text |
| date | Date | Default today |
| added_by | Link → User | Auto-set to frappe.session.user |
current_balance on Kid Profile is maintained by the Kid Transaction controller: on save/cancel it recalculates SUM(Income) - SUM(Expense) for that kid.
- Kid Profile: Kido Parent → full CRUD on kids where they appear in the
parentschild table; Kido Kid → Read (own record only, viausermatch) - Kid Profile Parent: managed as part of Kid Profile (child table, no standalone permissions needed)
- Kid Transaction: Kido Parent → full CRUD on kids they are a parent of; Kido Kid → full CRUD on own kid record
Whitelisted methods for the frontend:
get_my_profile()— returns kid profile or parent's kid list based on roleget_kid_transactions(kid_name, limit=20, offset=0)— paginated transaction historyadd_transaction(kid_name, transaction_type, amount, description)— creates a Kid Transactionget_parent_kids()— returns all Kid Profiles linked to the current parent user
Three pages using vanilla HTML/CSS/JS + Frappe's fetch API:
- Splash/landing;
kido.pychecks session role and issues a client-side redirect to/kid-homeor/parent-home
┌─────────────────────────────┐
│ │
│ 🐣 Kido │
│ Kids Money Tracker │
│ │
│ Loading your │
│ account… │
│ ● ● ● │
│ │
└─────────────────────────────┘
- Large balance display (colorful, friendly)
- Recent transaction list (icon + description + amount)
- Floating
+button → sheet to add Income or Expense with amount and description - Pull-to-refresh via fetch
┌─────────────────────────────┐
│ 👋 Hi, Mia! ⚙️ │
├─────────────────────────────┤
│ My Balance │
│ ╔═══════════════════╗ │
│ ║ $ 47.50 ║ │
│ ╚═══════════════════╝ │
├─────────────────────────────┤
│ Recent Transactions │
│ ✅ Cleaned room +5.00 │
│ 🎂 Birthday gift +20.00 │
│ 🍦 Ice cream -3.50 │
│ ✅ Fed the dog +3.00 │
│ ╔═══╗ │
│ ║ + ║ ← FAB │
│ ╚═══╝ │
└─────────────────────────────┘
Add Transaction sheet (slides up on + tap):
┌─────────────────────────────┐
│ Add Money │
│ ┌──────────┐ ┌──────────┐ │
│ │ Income │ │ Expense │ │
│ └──────────┘ └──────────┘ │
│ Amount [ $ _______ ] │
│ What for [ __________ ] │
│ ╔═══════════════════╗ │
│ ║ Save ║ │
│ ╚═══════════════════╝ │
└─────────────────────────────┘
- Card grid of kids: avatar emoji, name, balance
- Tap a kid →
/kid-detail?kid=<name>page
┌─────────────────────────────┐
│ 👨👩👧 My Kids ⚙️ │
├─────────────────────────────┤
│ ┌──────────┐ ┌──────────┐ │
│ │ 🐱 │ │ 🐶 │ │
│ │ Mia │ │ Jake │ │
│ │ $47.50 │ │ $12.00 │ │
│ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ │
│ │ 🦊 │ │ + Add │ │
│ │ Lily │ │ Kid │ │
│ │ $8.25 │ │ │ │
│ └──────────┘ └──────────┘ │
└─────────────────────────────┘
- Same layout as
/kid-homebut viewed by parent for a specific kid - Parent can add income or expense on behalf of the kid
┌─────────────────────────────┐
│ ← Back 🐱 Mia │
├─────────────────────────────┤
│ Balance │
│ ╔═══════════════════╗ │
│ ║ $ 47.50 ║ │
│ ╚═══════════════════╝ │
├─────────────────────────────┤
│ Transactions │
│ ✅ Cleaned room +5.00 │
│ 🎂 Birthday gift +20.00 │
│ 🍦 Ice cream -3.50 │
│ ╔═══╗ │
│ ║ + ║ ← FAB │
│ ╚═══╝ │
└─────────────────────────────┘
- CSS variables for kid-friendly palette (bright, high-contrast)
- Large tap targets (min 48px)
- Card-based layout, max-width 480px centered
| Token | Value | Use |
|---|---|---|
--kido-green |
#4CAF50 |
Income amounts, balance |
--kido-red |
#F44336 |
Expense amounts |
--kido-blue |
#2196F3 |
Primary action buttons |
--kido-bg |
#FFF9F0 |
Page background |
--kido-card |
#FFFFFF |
Cards / sheets |
--kido-text |
#1A1A1A |
Body text |
kido/kido/
├── doctype/
│ ├── kid_profile/
│ │ ├── kid_profile.json ← doctype schema
│ │ ├── kid_profile.py ← no-op controller stub
│ │ ├── kid_profile.js
│ │ └── __init__.py
│ ├── kid_profile_parent/ ← child table doctype
│ │ ├── kid_profile_parent.json
│ │ ├── kid_profile_parent.py
│ │ └── __init__.py
│ └── kid_transaction/
│ ├── kid_transaction.json ← doctype schema
│ ├── kid_transaction.py ← updates Kid Profile balance on save/cancel
│ ├── kid_transaction.js
│ └── __init__.py
└── api.py ← whitelisted API methods
kido/www/
├── kido.html + kido.py ← role-based redirect
├── kid-home.html + kid-home.js
├── parent-home.html + parent-home.js
└── kid-detail.html + kid-detail.js
kido/public/css/kido.css ← shared mobile styles
kido/hooks.py ← add app_include_css, roles fixture path
app_include_css = "/assets/kido/css/kido.css"
fixtures = ["Role"]
role_home_page = {
"Kido Parent": "/kido",
"Kido Kid": "/kido",
}bench --site kido1.test migrate— applies new doctypes- Admin via Frappe Desk: create 4 user accounts (2 parents, 2 kids); assign Kido Parent / Kido Kid roles
- Admin via Frappe Desk: create 2 Kid Profile records, each linking a kid user + both parent users in the
parentstable - Open
http://kido1.test:8005/kidoon mobile viewport (Chrome DevTools or real phone):- Parent login →
/parent-homeshows both kid cards - Kid login →
/kid-homeshows balance
- Parent login →
- Add income and expense entries as both a kid and a parent; verify balance updates
- Confirm neither parent nor kid can reach any Frappe Desk page (
/app,/desk)