feat(ui): add finance plan editor
Plans default to an equal split, each slice is individually editable, and plans compose with splits. Cancelling keeps past slices. planBalance delegates to domain slicesBalance rather than recomputing the same total-minus-slices arithmetic, per the project's one-implementation rule for shared math.
This commit is contained in:
1 parent
5a11941d39
commit
5b879809d2
4 files changed
+235
-1
No files matched your search
@@ -1,4 +1,5 @@
|
||||
import { roundMoney } from "../domain/money.ts";
|
||||
import { equalSlices, slicesBalance } from "../domain/plans.ts";
|
||||
import type {
|
||||
Currency,
|
||||
IsoDate,
|
||||
@@ -94,6 +95,51 @@ export function toPurchase(draft: PurchaseDraft): Omit<Purchase, "id"> {
|
||||
};
|
||||
}
|
||||
|
||||
export function withPlan(
|
||||
draft: PurchaseDraft,
|
||||
startMonth: MonthId,
|
||||
months: number,
|
||||
): PurchaseDraft {
|
||||
return {
|
||||
...draft,
|
||||
plan: {
|
||||
startMonth,
|
||||
slices: equalSlices({ amount: draft.amount, currency: draft.currency }, startMonth, months),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function withoutPlan(draft: PurchaseDraft): PurchaseDraft {
|
||||
return { ...draft, plan: null };
|
||||
}
|
||||
|
||||
export function setSliceAmount(
|
||||
draft: PurchaseDraft,
|
||||
index: number,
|
||||
amount: number,
|
||||
): PurchaseDraft {
|
||||
if (!draft.plan) return draft;
|
||||
return {
|
||||
...draft,
|
||||
plan: {
|
||||
...draft.plan,
|
||||
slices: draft.plan.slices.map((slice, i) =>
|
||||
i === index ? { ...slice, amount: { ...slice.amount, amount } } : slice,
|
||||
),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* slices - total, delegated to the domain's slicesBalance so the "sum the
|
||||
* slices and compare to the total" arithmetic has exactly one implementation.
|
||||
* Positive means the slices fall short of the purchase total.
|
||||
*/
|
||||
export function planBalance(draft: PurchaseDraft): number {
|
||||
if (!draft.plan) return 0;
|
||||
return slicesBalance({ amount: draft.amount, currency: draft.currency }, draft.plan.slices);
|
||||
}
|
||||
|
||||
export function fromPurchase(purchase: Purchase): PurchaseDraft {
|
||||
return {
|
||||
date: purchase.date,
|
||||
|
||||
Reference in new issue
Block a user