@accounter/client
Version:
Accounter client application
99 lines (87 loc) • 3.16 kB
text/typescript
import type { AllSortCodesQuery, DynamicReportQuery } from '../../../../gql/graphql.js';
import type { CustomData, FlatNode } from './types.js';
export const BANK_ROOT = 'bank';
type BusinessSum = Extract<
NonNullable<DynamicReportQuery['businessTransactionsSumFromLedgerRecords']>,
{ __typename?: 'BusinessTransactionsSumFromLedgerRecordsSuccessfulResult' }
>['businessTransactionsSum'][number];
/**
* Builds the initial bank flat-tree from live GraphQL data.
*
* - Sort-code branches are ordered ascending by sortCode.key.
* - Only emits a sort-code branch when at least one visible entity belongs to it.
* - Entities with no sort code appear after all branches, sorted alphabetically.
* - Excluded entity ids (already placed in the report tree) are omitted.
* - If includeZeroed is false, entities with total.raw === 0 are omitted.
*/
export function buildInitialBankTree(
sortCodes: AllSortCodesQuery['allSortCodesByBusiness'],
businessSums: BusinessSum[],
excludedIds: Set<string>,
includeZeroed: boolean,
): FlatNode<CustomData>[] {
// 1. Filter
const filtered = businessSums.filter(b => {
if (excludedIds.has(b.business.id)) return false;
if (!includeZeroed && b.total.raw === 0) return false;
return true;
});
// 2. Group by sort code id
const bySortCodeId = new Map<
string,
{ key: number; name: string | null | undefined; entities: BusinessSum[] }
>();
const noSortCode: BusinessSum[] = [];
// Pre-index sort codes for O(1) lookup
const sortCodeMap = new Map(sortCodes.map(sc => [sc.id, sc]));
for (const b of filtered) {
const sc = b.business.sortCode;
if (sc) {
if (!bySortCodeId.has(sc.id)) {
// Prefer the richer name from the sortCodes param if available
const scParam = sortCodeMap.get(sc.id);
bySortCodeId.set(sc.id, {
key: sc.key,
name: scParam?.name ?? sc.name,
entities: [],
});
}
bySortCodeId.get(sc.id)!.entities.push(b);
} else {
noSortCode.push(b);
}
}
// 3. Sort branches ascending by key
const sortedBranches = [...bySortCodeId.entries()].sort((a, b) => a[1].key - b[1].key);
const result: FlatNode<CustomData>[] = [];
for (const [scId, { key, name, entities }] of sortedBranches) {
result.push({
id: scId,
parent: BANK_ROOT,
text: `${key} — ${name ?? ''}`,
droppable: true,
data: { nodeType: 'sort-code-branch', sortCode: key, isOpen: false },
});
for (const b of entities) {
result.push({
id: b.business.id,
parent: scId,
text: b.business.name,
droppable: false,
data: { nodeType: 'financial-entity', value: b.total.raw * -1, isOpen: false },
});
}
}
// 4. No-sort-code entities — alphabetical
noSortCode.sort((a, b) => a.business.name.localeCompare(b.business.name));
for (const b of noSortCode) {
result.push({
id: b.business.id,
parent: BANK_ROOT,
text: b.business.name,
droppable: false,
data: { nodeType: 'financial-entity', value: b.total.raw * -1, isOpen: false },
});
}
return result;
}