feat: group all-account rows by instrument and day (WealthLedger/main#47) (#126)
Frontend verify / commits (push) Skipped
Frontend verify / security (push) Successful in 9s
Frontend verify / verify (push) Failing after 6m1s

This commit was merged in pull request #126.
This commit is contained in:
2026-10-30 07:28:19 +03:00
parent 6792f5b70a
commit 71126c91f0
40 changed files with 1564 additions and 102 deletions
@@ -100,7 +100,10 @@ function money(currency: string, value: string) {
return { currency, value };
}
function renderPage(search = "from=2026-08-01&to=2026-08-27") {
function renderPage(
search = "from=2026-08-01&to=2026-08-27",
accountId = "account-1",
) {
configureAuthClient({
accessToken: () => "token",
clear: vi.fn(),
@@ -113,7 +116,7 @@ function renderPage(search = "from=2026-08-01&to=2026-08-27") {
<DatesProvider settings={{ locale: "ru" }}>
<QueryClientProvider client={queryClient}>
<MemoryRouter
initialEntries={[`/accounts/account-1/income?${search}`]}
initialEntries={[`/accounts/${accountId}/income?${search}`]}
>
<LocationProbe />
@@ -680,4 +683,110 @@ describe("income period and snapshot boundary", () => {
expect(screen.queryByText(/Итого:|Выплат:/)).not.toBeInTheDocument();
expect(screen.queryByRole("button")).not.toBeInTheDocument();
});
it("groups all-account payouts by instrument with an exact total", async () => {
server.use(
http.get(
"*/api/v1/brokerage-accounts/:accountId/income-analytics",
() => HttpResponse.json(resource({
...analytics,
accountId: "all",
entries: [
{
...entry(),
account: { id: "broker", name: "Брокерский" },
payment: money("RUB", "10"),
},
{
...entry(),
account: { id: "iis", name: "ИИС" },
id: "coupon-2",
payment: money("RUB", "20"),
},
],
})),
),
);
renderPage(undefined, "all");
expect(await screen.findByText("Купон · BOND")).toBeInTheDocument();
expect(screen.getByText("+30 ₽")).toBeInTheDocument();
const accounts = within(screen.getByRole("list", {
name: /Купон · BOND, Исполнено/,
}));
expect(accounts.getByText("Брокерский")).toBeInTheDocument();
expect(accounts.getByText("ИИС")).toBeInTheDocument();
expect(accounts.queryByRole("link")).not.toBeInTheDocument();
});
it("keeps repeated payout ids inside one group as separate cards", async () => {
server.use(
http.get(
"*/api/v1/brokerage-accounts/:accountId/income-analytics",
() => HttpResponse.json(resource({
...analytics,
accountId: "all",
entries: [
{
...entry(),
account: { id: "broker", name: "Брокерский" },
payment: money("RUB", "10"),
},
{
...entry(),
account: { id: "iis", name: "ИИС" },
payment: money("RUB", "20"),
},
],
})),
),
);
renderPage(undefined, "all");
expect(await screen.findByText("+30 ₽")).toBeInTheDocument();
const accounts = within(screen.getByRole("list", {
name: /Купон · BOND/,
}));
expect(accounts.getAllByRole("listitem")).toHaveLength(2);
});
it("shows the group total after completeness changes to complete", async () => {
let completeness: "complete" | "incomplete" = "incomplete";
const entries = [
{
...entry(),
account: { id: "broker", name: "Брокерский" },
payment: money("RUB", "10"),
},
{
...entry(),
account: { id: "iis", name: "ИИС" },
id: "coupon-2",
payment: money("RUB", "20"),
},
];
server.use(
http.get(
"*/api/v1/brokerage-accounts/:accountId/income-analytics",
() => HttpResponse.json(
resource({ ...analytics, accountId: "all", entries }, { completeness }),
),
),
);
const view = renderPage(undefined, "all");
await screen.findByText("Купон · BOND");
expect(screen.queryByText("+30 ₽")).not.toBeInTheDocument();
view.unmount();
completeness = "complete";
renderPage(undefined, "all");
expect(await screen.findByText("+30 ₽")).toBeInTheDocument();
});
});