Coverage Summary for Class: SavingsGoalDetails (com.georgeci.moneysurfer.domain.model)
| Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| SavingsGoalDetails |
100%
(1/1)
|
100%
(1/1)
|
|
100%
(4/4)
|
100%
(17/17)
|
package com.georgeci.moneysurfer.domain.model
import com.georgeci.moneysurfer.domain.primitives.Money
/**
* How far a [SavingsGoal] has come. [saved] is always `SUM(contributions.amount)` —
* see the goal model for why it is never stored.
*
* [fraction] is uncapped so logic can tell "exactly on target" from "overshot";
* [percent] is clamped to `0..1` because the ring and the bar cannot draw more.
*/
data class GoalProgress(
val saved: Money,
val target: Money,
) {
/** Never negative: a goal past its target has nothing left to save. */
val remaining: Money = if (saved >= target) Money.zero() else target - saved
/** Uncapped ratio of saved to target. `0.0` when the target is zero. */
val fraction: Double =
if (target.minor <= 0L) 0.0 else saved.minor.toDouble() / target.minor.toDouble()
/** [fraction] clamped to `0.0..1.0`, for progress bars and rings. */
val percent: Double = fraction.coerceIn(0.0, 1.0)
val isReached: Boolean = target.minor > 0L && saved >= target
companion object {
fun of(goal: SavingsGoal, saved: Money): GoalProgress =
GoalProgress(saved = saved, target = goal.target)
}
}
/** A goal plus its derived progress — what the goals list renders. */
data class SavingsGoalSummary(
val goal: SavingsGoal,
val progress: GoalProgress,
)
/** A goal, its progress and its full contribution history — the details screen. */
data class SavingsGoalDetails(
val goal: SavingsGoal,
val progress: GoalProgress,
val contributions: List<GoalContribution>,
)