fix(views): keep a post visible when its only figure is an unconfirmed occurrence (I4)

hasActivity only checked carriedIn/allocation/charges, so an archived post
whose sole activity this month was an unconfirmed recurring occurrence
(zero remaining, nonzero expected/projected) was dropped from
monthView.rows — its Projected figure never reached the month view even
though ExpectedBand listed the same occurrence. remaining and projected
are both fully derived from five raw fields; the check now covers all
five.
This commit is contained in:
Lucas Winther
2026-09-04 07:57:56 +02:00
parent 5a047f53de
commit 093521b2c8
2 changed files with 45 additions and 1 deletions
+34
View File
@@ -122,6 +122,40 @@ describe("monthView", () => {
"old",
]);
});
test("an archived post with only an unconfirmed occurrence still appears, with its projected figure (I4)", () => {
// "old" has a zero-percent rule (no allocation) and no purchases at all,
// so carriedIn/allocation/charges are all zero this month — its only
// figure is the unconfirmed recurring occurrence's `expected`/`projected`.
// Before I4's fix, hasActivity ignored those two fields and dropped the
// row, even though ExpectedBand lists the same occurrence.
const withRecurring: Dataset = {
...data,
posts: [...data.posts, post("old", 2, 0, true)],
recurring: [
{
id: "r1",
name: "Storage unit",
order: 0,
archived: false,
amount: { amount: 400, currency: "DKK" },
startDate: "2026-01",
recurrence: { kind: "everyNMonths", n: 1 },
anchoring: "calendar",
splitMode: "percent",
splits: [{ postId: "old", value: 100, absorbsRemainder: true }],
},
],
};
const view = monthView(withRecurring, "2026-01");
const row = view.rows.find((r) => r.post.id === "old");
expect(row).toBeDefined();
expect(row!.figures.carriedIn).toBe(0);
expect(row!.figures.allocation).toBe(0);
expect(row!.figures.charges).toBe(0);
expect(row!.figures.expected).toBe(400);
expect(row!.figures.projected).toBe(-400);
});
});
describe("yearView", () => {
+11 -1
View File
@@ -28,8 +28,18 @@ export interface MonthViewModel {
}
function hasActivity(figures: PostMonthFigures): boolean {
// `remaining` and `projected` are both fully derived from these five raw
// fields, so checking all five is what keeps a post visible whenever either
// column would show a nonzero figure. `expected` and `projectedCarriedIn`
// are the two the recurring-costs feature added: an archived post whose
// only activity this month is an unconfirmed occurrence has a zero
// `remaining` but a nonzero `projected`, and must not be dropped.
return (
figures.carriedIn !== 0 || figures.allocation !== 0 || figures.charges !== 0
figures.carriedIn !== 0 ||
figures.allocation !== 0 ||
figures.charges !== 0 ||
figures.expected !== 0 ||
figures.projectedCarriedIn !== 0
);
}