feat(accounts): merchants and payers as a league table
Expense and revenue accounts list this month per currency, share, visits, average, twelve months, typical and the change against it, last visit and category; quiet ones fold into one line behind i. 1 to 5 sort, ; groups by category with subtotals, x and s c set the category on all of a merchant's transactions in the period.
This commit is contained in:
1 parent
26903b0066
commit
fffb770cac
7 files changed
+1352
No files matched your search
@@ -0,0 +1,89 @@
|
||||
import { createQuery, keepPreviousData } from "@tanstack/svelte-query";
|
||||
import { lists } from "#lib/api/lists.js";
|
||||
import type { TransactionGroup } from "#lib/api/types.js";
|
||||
import { ratesQuery } from "#lib/dashboard/primary.svelte.js";
|
||||
import { today } from "#lib/dates.js";
|
||||
import { period } from "#lib/period.svelte.js";
|
||||
import { ledgerQuery, ledgerWindow, ownFlows } from "#lib/reports/ledger.js";
|
||||
import { pages } from "#lib/reports/queries.js";
|
||||
import { typicalMonths } from "#lib/reports/typical.js";
|
||||
import { session } from "#lib/session.svelte.js";
|
||||
import { board, type Kind, monthDay } from "./merchants.js";
|
||||
|
||||
export function useMerchants(kind: () => Kind) {
|
||||
const now = today();
|
||||
const current = ledgerWindow(now);
|
||||
const anchor = $derived(period.end >= current.start ? now : period.end);
|
||||
const window = $derived(ledgerWindow(anchor));
|
||||
const inside = $derived(
|
||||
period.start >= window.start && period.end <= window.end
|
||||
);
|
||||
|
||||
const accounts = createQuery(() => lists.accounts("all"));
|
||||
const ledger = createQuery(() => ({
|
||||
...ledgerQuery(anchor),
|
||||
placeholderData: keepPreviousData,
|
||||
}));
|
||||
const extra = createQuery(() => ({
|
||||
enabled: !inside,
|
||||
placeholderData: keepPreviousData,
|
||||
queryFn: () =>
|
||||
pages<TransactionGroup>("/v1/transactions", {
|
||||
end: period.end,
|
||||
start: period.start,
|
||||
type: "default",
|
||||
}),
|
||||
queryKey: ["merchants-period", period.start, period.end],
|
||||
}));
|
||||
const rates = createQuery(() => ({ ...ratesQuery(), enabled: true }));
|
||||
|
||||
const flows = $derived(
|
||||
ledger.data && accounts.data ? ownFlows(ledger.data, accounts.data) : []
|
||||
);
|
||||
const periodFlows = $derived.by(() => {
|
||||
if (inside) {
|
||||
return flows;
|
||||
}
|
||||
return extra.data && accounts.data
|
||||
? ownFlows(extra.data, accounts.data)
|
||||
: [];
|
||||
});
|
||||
const day = $derived(monthDay(period.range, now));
|
||||
const result = $derived.by(() => {
|
||||
if (!(accounts.data && ledger.data && (inside || extra.data))) {
|
||||
return null;
|
||||
}
|
||||
const month = period.end.slice(0, 7);
|
||||
const since = flows.reduce(
|
||||
(first, flow) => (flow.date < first ? flow.date : first),
|
||||
now
|
||||
);
|
||||
const primary = session.primary?.code ?? "";
|
||||
return board(accounts.data, flows, periodFlows, {
|
||||
day,
|
||||
kind: kind(),
|
||||
months: window.months.filter((m) => m <= month),
|
||||
past: typicalMonths(window.months, period.end, since),
|
||||
range: period.range,
|
||||
rate: (code) => rates.data?.[code]?.rate ?? (code === primary ? 1 : 0),
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
get board() {
|
||||
return result;
|
||||
},
|
||||
get day() {
|
||||
return day;
|
||||
},
|
||||
get error() {
|
||||
return accounts.error ?? ledger.error ?? extra.error ?? null;
|
||||
},
|
||||
get loading() {
|
||||
return result === null || rates.isPending;
|
||||
},
|
||||
get stale() {
|
||||
return ledger.isFetching || extra.isPlaceholderData;
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user