Finance Data Schema
This document describes the data schema requirements for the Finance tab features at the project level.
Overview
The Finance tab tracks:
- Budget Versions - Multiple versions of budget allocations over time
- Monthly Allocations - Resource allocations (roles and counts) per month
- Expenses - Additional expenses (claims, equipment, software, etc.)
- Actual vs Estimated - Comparison of estimated costs vs actual spending
Current Schema (TypeScript Types)
The schema is already defined in /apps/web/src/types/project.ts:
BudgetVersion
interface BudgetVersion {
id: string;
version: string; // e.g., "v1.0", "v1.1"
createdBy: string; // Person ID or name
createdDate: Date;
isCurrent: boolean; // Only one version should be current
totalEstimated: number; // Total estimated cost (can be calculated or stored)
status: 'draft' | 'approved' | 'archived';
monthlyAllocations: MonthlyAllocation[];
}
MonthlyAllocation
interface MonthlyAllocation {
month: MonthString; // Format: "YYYY-MM" (e.g., "2025-10")
allocations: RoleAllocation[];
}
interface RoleAllocation {
role: string; // e.g., "senior-engineer", "engineer", "product-owner", "designer"
count: number; // Number of people in this role for this month
}
BudgetExpense
interface BudgetExpense {
id: string;
type: 'claim' | 'equipment' | 'software' | 'other';
description: string;
amount: number; // In MYR
date: Date;
submittedBy: string; // Person ID or name
status: 'pending' | 'approved' | 'rejected';
}
ProjectBudget
interface ProjectBudget {
versions: BudgetVersion[];
expenses: BudgetExpense[];
}
Storage Location
The budget data is stored as part of the Project entity in Firestore:
- Collection:
projects - Document ID:
projectKey(e.g., "PROJ") - Field:
budget(optionalProjectBudgetobject)
Data Flow
1. Budget Version Creation
Source: Excel sheet upload via "Ingest New Version" dialog
Process:
- User uploads Excel file (
.xlsxor.xls) - Backend parses Excel file to extract:
- Monthly allocations (month, role, count)
- Calculates total estimated cost
- Creates new
BudgetVersion:- Sets
isCurrent: falsefor all existing versions - Sets
isCurrent: truefor new version - Generates version number (increment from latest)
- Sets
status: 'draft'initially
- Sets
- Updates project document:
project.budget.versions.push(newVersion)
API Endpoint (to be implemented):
POST /api/projects/[projectKey]/budget/ingest
Content-Type: multipart/form-data
Body: { file: File }
2. Budget Version Management
Actions:
- Download: Export current version to Excel format
- Preview: Show budget allocations in table format
- Set Current: Mark a version as current (archives others)
- Approve: Change status from 'draft' to 'approved'
- Archive: Change status to 'archived'
API Endpoints (to be implemented):
GET /api/projects/[projectKey]/budget/versions
GET /api/projects/[projectKey]/budget/versions/[versionId]/download
PUT /api/projects/[projectKey]/budget/versions/[versionId]
DELETE /api/projects/[projectKey]/budget/versions/[versionId]
3. Expense Management
Create Expense:
- User fills form: type, description, amount, date
- Backend creates
BudgetExpense:- Generates unique ID
- Sets
submittedByfrom authenticated user - Sets
status: 'pending'initially
- Updates project document:
project.budget.expenses.push(newExpense)
Update Expense Status:
- Admin/Manager can approve/reject expenses
- Updates
statusfield
API Endpoints (to be implemented):
POST /api/projects/[projectKey]/budget/expenses
GET /api/projects/[projectKey]/budget/expenses
PUT /api/projects/[projectKey]/budget/expenses/[expenseId]
DELETE /api/projects/[projectKey]/budget/expenses/[expenseId]
PUT /api/projects/[projectKey]/budget/expenses/[expenseId]/status
4. Actual Cost Calculation
Current Implementation: Placeholder calculation
Future Implementation:
- Calculate actual resource costs from
worklogscollection:- Filter worklogs by project
- Group by month
- Calculate hours per role
- Multiply by role rates
- Combine with approved expenses
- Compare with estimated costs
Data Sources:
worklogscollection: Time tracking dataproject.budget.expenses: Additional expensesproject.budget.versions[].monthlyAllocations: Estimated costs
Role Rates Configuration
Current: Hardcoded in component (ROLE_RATES constant)
Future: Should be stored in:
- Option 1: Configuration collection (singleton document)
- Option 2: Per-project configuration
- Option 3: Global settings with project overrides
Recommended Structure:
interface RoleRate {
role: string;
label: string;
rate: number; // Monthly rate in MYR
effectiveDate?: Date; // When this rate becomes effective
}
Calculations
Total Estimated Cost
totalEstimated = sum(monthlyAllocations.map(month =>
sum(month.allocations.map(alloc =>
ROLE_RATES[alloc.role].rate * alloc.count
))
))
Actual Spent
actualResourceCost = calculateFromWorklogs(projectKey, months)
approvedExpenses = sum(expenses.filter(e => e.status === 'approved').map(e => e.amount))
totalActualSpent = actualResourceCost + approvedExpenses
Budget Utilization
budgetUtilization = (totalActualSpent / totalEstimated) * 100
isOverBudget = totalActualSpent > totalEstimated
variance = totalActualSpent - totalEstimated
Monthly Variance
monthlyEstimated = calculateMonthlyTotal(month)
monthlyActual = calculateFromWorklogs(projectKey, month.month)
monthlyVariance = monthlyActual - monthlyEstimated
Data Validation
BudgetVersion
versionmust be unique within project- Only one version can have
isCurrent: true monthlyAllocationsmust have validMonthStringformatmonthlyAllocations[].allocations[].countmust be >= 0
BudgetExpense
amountmust be > 0datemust be valid datetypemust be one of: 'claim', 'equipment', 'software', 'other'statusmust be one of: 'pending', 'approved', 'rejected'
Migration Considerations
Existing Projects
- Projects without
budgetfield should initialize with emptyProjectBudget:budget: {
versions: [],
expenses: []
}
Version History
- Keep all versions (don't delete archived versions)
- Use
isCurrentflag to identify active version - Use
statusto filter/display versions
Security & Permissions
Read Access
- All authenticated users can view budget and expenses
Write Access
- Budget Versions: Admin, Manager, Project Owner
- Expenses: All authenticated users (can create their own)
- Expense Status: Admin, Manager (can approve/reject)
Firestore Rules
match /projects/{projectKey} {
// Budget read access
allow read: if request.auth != null;
// Budget version write access
allow write: if request.auth != null &&
(get(/databases/$(database)/documents/people/$(request.auth.uid)).data.role in ['admin', 'manager']);
// Expense create access
allow create: if request.auth != null;
// Expense update (status change) access
allow update: if request.auth != null &&
(get(/databases/$(database)/documents/people/$(request.auth.uid)).data.role in ['admin', 'manager']);
}
Future Enhancements
- Budget Templates: Pre-defined budget templates for common project types
- Budget Approval Workflow: Multi-step approval process
- Budget Alerts: Notifications when approaching budget limits
- Budget Forecasting: Predict future spending based on trends
- Currency Support: Multi-currency budgets
- Budget Categories: Categorize expenses (e.g., personnel, infrastructure, tools)
- Integration with Accounting Systems: Sync with external accounting software
- Budget Reports: Generate PDF/Excel reports with charts and analysis
Related Files
- Type Definitions:
/apps/web/src/types/project.ts - UI Component:
/apps/web/src/app/projects/[projectKey]/components/finance-tab.tsx - API Routes: (to be implemented in
/apps/web/src/app/api/projects/[projectKey]/budget/) - Database structure: Database structure
- Data overview: Data overview