Split bills, track balances, and settle up—built for roommates who don't want spreadsheets.
Overview • Demo • Features • Getting Started • Testing
DivvyDo is a production-ready expense management app designed for roommates and shared households. No more awkward conversations about who owes what—track expenses, split bills using 5 different methods, and settle balances with confidence.
- Split a shared bill: create expense → choose split method → review balances
- Track household chores: add tasks → assign owners → mark done
- Settle up: review pairwise balances → record a settlement
- Manual expense tracking in spreadsheets is error-prone
- Calculating fair splits across multiple people is tedious
- Tracking who owes whom becomes messy with multiple transactions
- Existing apps are overcomplicated or charge fees
DivvyDo provides:
- 5 split methods for any scenario (equal, exact, percentage, shares, adjustment)
- Penny-perfect rounding ensuring cents never disappear or duplicate
- Automatic balance calculation showing net and pairwise balances
- Receipt uploads with Supabase Storage integration
- Admin tooling for managing household members and exporting data
Expense Split Interface
5 split methods: equal, exact, percentage, shares, and adjustment
Balance Dashboard
Net and pairwise balances with settlement tracking
Admin Tools
CSV exports, people merging, and invite management
Follow the Getting Started guide below to run locally.
5 Split Methods — Handle any splitting scenario:
- Equal: Split evenly among N people (e.g., $100 ÷ 4 = $25 each)
- Exact: Specify exact amounts per person (e.g., Alice $30, Bob $70)
- Percentage: Split by percentages (e.g., Alice 60%, Bob 40%)
- Shares: Weight-based splitting (e.g., Alice 2 shares, Bob 1 share)
- Adjustment: Fixed adjustments on top of equal split (e.g., +$5 for Alice, -$5 for Bob)
Penny-Perfect Rounding:
- Ensures total split amounts always equal the original expense
- Distributes rounding errors fairly (largest-remainder method)
- Example: $100 split 3 ways → $33.34, $33.33, $33.33 (not $33.33 × 3 = $99.99)
Receipt Uploads:
- Upload photos via Supabase Storage
- Public bucket with row-level security
- Automatic file validation and size limits
Recurring Templates:
- Save frequently used expenses (rent, utilities, subscriptions)
- Manual generation (automated cron coming soon)
Net Balances:
- See who owes money overall vs. who is owed
- Visual indicators (red for owes, green for owed)
Pairwise Balances:
- Detailed breakdown of who owes whom
- Example: "Alice owes Bob $45.67"
Settlement Recording:
- Mark balances as settled when paid
- Keeps audit trail of all transactions
Groups:
- Personal group for individual expenses
- Household groups for shared expenses
- Quick group switcher
- Rename, leave, or delete groups (admin only)
Invite System:
- Generate invite codes with expiration
- Email-based placeholder creation
- Automatic placeholder claim on signup
Admin Tools:
- CSV Exports: Download all expenses with full details
- Merge People: Combine duplicate entries with audit logs
- Placeholder Management: Convert email placeholders to real users
Basic Task Tracking:
- Create, edit, and delete tasks
- Assign to household members
- Set status (todo, in progress, done) and priority
- Filters and search
- Recurring task templates
Frontend:
- React 19 with TypeScript
- Vite for build tooling
- React Router for navigation
- Vitest + React Testing Library for testing
Backend:
- Supabase (PostgreSQL + Auth + Storage + Edge Functions)
- Row-Level Security for data protection
- Edge Functions for server-side logic
Testing:
- 25 test files covering:
- Financial calculation logic
- Balance computation
- Component rendering
- Split method accuracy
users
├── id (uuid, PK)
├── email (text)
├── name (text)
└── created_at (timestamp)
groups
├── id (uuid, PK)
├── name (text)
├── created_by (uuid, FK → users)
└── created_at (timestamp)
group_members
├── group_id (uuid, FK → groups)
├── user_id (uuid, FK → users)
├── role (text: 'admin' | 'member')
└── joined_at (timestamp)
expenses
├── id (uuid, PK)
├── group_id (uuid, FK → groups)
├── description (text)
├── amount (numeric)
├── payer_id (uuid, FK → users)
├── split_method (text)
├── split_details (jsonb)
├── receipt_url (text)
└── created_at (timestamp)
balances
├── group_id (uuid, FK → groups)
├── user_from (uuid, FK → users)
├── user_to (uuid, FK → users)
├── amount (numeric)
└── updated_at (timestamp)
All split calculations are in /src/utils/splitCalculations.ts:
export function calculateSplit(
amount: number,
method: SplitMethod,
participants: Participant[]
): SplitResult[] {
// Returns array of {userId, amount} ensuring sum equals original amount
}Rounding Algorithm (Largest Remainder Method):
- Calculate ideal amounts (may have fractions)
- Round down all amounts to nearest cent
- Calculate total shortage
- Distribute shortage (1 cent each) to participants with largest remainders
- Node.js 18+
- npm or yarn
- Supabase account
# Clone the repository
git clone https://github.com/harishm17/task-manager.git
cd task-manager
# Install dependencies
npm installCreate a .env file based on .env.example:
VITE_SUPABASE_URL=your_supabase_url
VITE_SUPABASE_ANON_KEY=your_supabase_anon_key- Create a Supabase project
- Apply migrations:
# Install Supabase CLI
npm install -g supabase
# Link to your project
supabase link --project-ref your-project-ref
# Apply migrations
supabase db push- Create storage bucket:
- Go to Supabase Dashboard → Storage
- Create a public bucket named
receipts - Enable RLS policies
Deploy Edge Functions for admin operations:
# Deploy accept-invite function
supabase functions deploy accept-invite
# Deploy merge-people function
supabase functions deploy merge-peoplenpm run devVisit http://localhost:5173
Test Suite: 25 test files with comprehensive coverage
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverageUnit Tests:
splitCalculations.test.ts— All 5 split methodsbalanceCalculations.test.ts— Net and pairwise balance logicroundingAlgorithm.test.ts— Penny-perfect rounding
Component Tests:
ExpenseForm.test.tsx— Form validation and submissionBalanceCard.test.tsx— Balance display renderingSplitMethodSelector.test.tsx— Split method UI
Integration Tests:
ExpenseFlow.test.tsx— Full expense creation and balance updateSettlementFlow.test.tsx— Settlement recording workflow
describe('Equal Split Method', () => {
it('should split $100 equally among 3 people with correct rounding', () => {
const result = calculateSplit(100, 'equal', [
{ id: '1', name: 'Alice' },
{ id: '2', name: 'Bob' },
{ id: '3', name: 'Charlie' }
]);
expect(result).toEqual([
{ userId: '1', amount: 33.34 },
{ userId: '2', amount: 33.33 },
{ userId: '3', amount: 33.33 }
]);
// Verify total equals original amount
const total = result.reduce((sum, r) => sum + r.amount, 0);
expect(total).toBe(100);
});
});npm run dev # Start dev server (Vite)
npm run build # Build for production
npm run preview # Preview production build
npm run lint # Run ESLint
npm run test # Run tests
npm run test:watch # Run tests in watch mode
npm run test:coverage # Generate coverage reportDivvyDo is production-ready with Docker containerization and automated deployment to GCP Cloud Run.
- gcloud CLI installed and configured
- Docker installed
- Google Cloud Platform project with billing enabled
- Supabase project set up
- Set your GCP project:
gcloud config set project YOUR_PROJECT_ID- Set environment variables:
export VITE_SUPABASE_URL="https://your-project.supabase.co"
export VITE_SUPABASE_ANON_KEY="your-anon-key"- Deploy:
./deploy.shThe script will:
- Build the Docker image
- Push to Google Container Registry
- Deploy to Cloud Run with optimized settings (512Mi memory, 1 CPU, auto-scaling)
- Configure environment variables
- Output your live URL
Test the Docker build locally before deploying:
# Copy environment variables
cp .env.example .env
# Edit .env with your Supabase credentials
# Run locally
./deploy-local.sh
# Access at http://localhost:8080
# Health check: http://localhost:8080/healthAutomatic deployment on git push using Cloud Build:
- Connect repository:
gcloud builds connect --region=us-central1-
Create trigger:
- Go to Cloud Build → Triggers in GCP Console
- Create trigger from
cloudbuild.yaml - Add substitution variables:
_SUPABASE_URL: Your Supabase URL_SUPABASE_ANON_KEY: Your Supabase anon key
-
Push to deploy:
git push origin mainUpdate environment variables:
gcloud run services update divvydo \
--region us-central1 \
--update-env-vars VITE_SUPABASE_URL=...,VITE_SUPABASE_ANON_KEY=...Get service URL:
gcloud run services describe divvydo \
--region us-central1 \
--format 'value(status.url)'View logs:
gcloud run services logs read divvydo --region us-central1Multi-stage Build:
- Node.js builder stage compiles Vite application
- Nginx production stage serves static files
Runtime Environment Injection:
- Environment variables injected at container startup
- No rebuild needed for config changes
- Creates
env-config.jsand injects intoindex.html
Health Checks:
/healthendpoint for Cloud Run health monitoring- Automatic container restart on failures
Optimizations:
- Gzip compression for all text assets
- 1-year cache headers for immutable assets
- Security headers (X-Frame-Options, X-Content-Type-Options, etc.)
For simpler deployment without Docker:
- Connect GitHub repository to Netlify
- Configure environment variables in Netlify dashboard
- Build command:
npm run build - Publish directory:
dist
npm run buildOutput: dist/ directory
Auth + profile (signup, signin, password reset) Groups (personal + household, switcher, admin controls) Tasks (create/edit/delete, assignees, filters, recurring) Expenses (5 split methods, receipts, recurring, reports) Balances (net + pairwise, settlement recording) Admin tools (invites, placeholders, merge, CSV exports) Test suite (25 test files)
- Real-time updates (Supabase Realtime)
- Notifications (in-app + email)
- Automated recurring generation (cron jobs)
- Mobile app (React Native)
- Budget tracking
- Expense categories with budgets
This project is licensed under the MIT License - see the LICENSE file for details.
Harish Manoharan
- GitHub: @harishm17
- LinkedIn: linkedin.com/in/harishm17
- Email: harish.manoharan@utdallas.edu
- Portfolio: harishm17.github.io