expense-sharing
Expense sharing calculations, split types (EQUAL/PERCENTAGE/FIXED), participant state management, and settlement tracking. Use when working with shared expenses, split calculations, payment status transitions, or settlement balances.
Installation
Suivez les instructions du dépôt pour votre agent. Consultez les fichiers et dépendances avant l’installation.
Voir la source ↗GuidesInstructions
Expense Sharing Domain Knowledge
Schema Overview
SharedExpense Model
model SharedExpense {
id String @id @default(cuid())
transactionId String @unique
ownerId String
splitType SplitType @default(EQUAL)
totalAmount Decimal @db.Decimal(12, 2)
currency Currency @default(USD)
description String?
transaction Transaction @relation(...)
owner User @relation("SharedExpensesOwned", ...)
participants ExpenseParticipant[]
}
ExpenseParticipant Model
model ExpenseParticipant {
id String @id @default(cuid())
sharedExpenseId String
userId String
shareAmount Decimal @db.Decimal(12, 2)
sharePercentage Decimal? @db.Decimal(5, 2)
status PaymentStatus @default(PENDING)
paidAt DateTime?
reminderSentAt DateTime?
@@unique([sharedExpenseId, userId])
}
Split Types
enum SplitType {
EQUAL // Divide total equally among all participants + owner
PERCENTAGE // Each participant gets a specified percentage
FIXED // Each participant gets a fixed amount
}
calculateShares Function
Location: src/app/actions/expense-sharing.ts
export function calculateShares(
splitType: SplitType,
totalAmount: number,
participants: Array<{ email: string; shareAmount?: number; sharePercentage?: number }>,
validEmails: string[],
): Map<string, { amount: number; percentage: number | null }>
EQUAL Split Logic
// Divide among (numParticipants + 1) to include owner
const equalShare = Math.round((totalAmount / (numParticipants + 1)) * 100) / 100
PERCENTAGE Split Logic
// Validation: total percentage must not exceed 100%
const amount = Math.round(totalAmount * (percentage / 100) * 100) / 100
FIXED Split Logic
// Validation: total fixed amounts cannot exceed totalAmount
// Each participant gets their specified shareAmount
Payment Status Transitions
PENDING → PAID (owner marks as received)
PENDING → DECLINED (participant declines)
PAID → (terminal)
DECLINED → (terminal)
Transition Rules:
- Only owner can mark PENDING → PAID via
markSharePaidAction - Only participant can DECLINE their own share via
declineShareAction - Cannot mark DECLINED share as PAID
Action Pipeline
All expense sharing actions follow:
1. parseInput(schema, input) // Zod validation
2. requireCsrfToken(csrfToken) // CSRF protection
3. requireActiveSubscription() // Subscription gate
4. requireAuthUser() // Authentication
5. Authorization check // Ownership/participant verification
6. Business logic
7. revalidatePath('/') // Cache invalidation
8. return success/error
Email Notifications
-
sendExpenseSharedEmail: Sent to each participant when expense is shared -
sendPaymentReminderEmail: 24-hour cooldown (REMINDER_COOLDOWN_HOURS = 24)
Settlement Balance Calculation
Location: src/lib/finance.ts (getSettlementBalances)
Groups by user AND currency to avoid mixing currencies:
// Returns for each user:
{
userId: string
youOwe: number // What you owe them (PENDING expenses they shared with you)
theyOwe: number // What they owe you (PENDING expenses you shared with them)
netBalance: number // theyOwe - youOwe
}
Key Files
-
src/app/actions/expense-sharing.ts- All sharing actions -
src/lib/finance.ts- calculateShares, getSettlementBalances -
src/schemas/index.ts- shareExpenseSchema, participantSchema -
tests/calculate-shares.test.ts- Unit tests for split calculations
Exemples
Consultez les exemples du dépôt source.
Voir la source ↗Historique source
- Date d’indexation
- 2026-01-20
- Mise à jour de l’index
- 2026-01-26
- Branch
- main
Les dates concernent ce répertoire. Consultez le dépôt pour l’historique du code.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!