Skip to content

Implement partial payments and reservation flow with sub-expenses #1519

Description

@pedrazamiguez

Problem / Motivation

Currently, the app assumes expenses are paid entirely at once. However, for many travel activities (e.g., booking a boat in Bacalar), a reservation fee (e.g., 50%) is paid in advance, often via an international transfer with its own currency, exchange rate, and bank fees. The remaining balance is typically paid on the day of the activity, often in cash and possibly including a tip.

Users cannot currently break down an expense into multiple sub-payments, each with its own currency, exchange rate, add-ons (fees, discounts, tips), and paid status. This makes it impossible to accurately log complex, real-world travel expenses.

Proposed Solution

Introduce the concept of "Sub-expenses" (or "Payment Tranches") for a single Expense.

  • Update the Expense domain model to support a list of sub-payments.
  • A sub-payment should encapsulate its own:
    • Amount (exact amount or a percentage of the total).
    • Currency and exchange rate (which can differ from the parent expense or group default).
    • Add-ons applied to that specific payment (e.g., transfer fee, tip).
    • Funding source (the pocket/pool the money is withdrawn from).
  • Introduce a new PaymentStatus.PARTIAL or PaymentStatus.RESERVED to the PaymentStatus enum in the :domain layer, indicating that the expense has started being paid but is not fully settled.
  • Ensure the sum of all sub-expenses accurately rolls up to the total parent expense amount.
  • Modify the presentation layer (:features:expenses) to allow adding, editing, and managing these sub-payments inside the Add/Edit Expense wizard.

Acceptance Criteria

  • Users can specify an exact amount or a percentage of the total for a sub-expense.
  • Users can assign a specific currency and exchange rate to each sub-expense.
  • Users can attach add-ons (fees, tips, surcharges) independently to each sub-expense.
  • Users can mark sub-expenses as contributed/paid, specifying which pocket/pool the money comes from.
  • Expenses with incomplete payments are marked with a new PaymentStatus (e.g. PARTIAL or RESERVED).
  • The parent expense's total amount reflects the exact sum of all its sub-expenses.
  • All existing tests pass; new tests added where behaviour changes.
  • make fast-check passes during iterative development (~15–30s).
  • make check > build.log 2>&1 && echo "Check passed successfully" || (tail -n 100 build.log && exit 1) passes with 0 failures before merging.
  • No new detekt findings introduced.

Out of Scope

  • Automatic/recurring payment schedules.
  • Refunds for partial payments (handled in full refunds).

Related Issues / PRs

  • N/A

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureNew app features

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions