UNPKG

@accounter/server

Version:
178 lines (169 loc) • 6.28 kB
import { CommentaryProto } from '../types.js'; import { DecoratedLedgerRecord } from './profit-and-loss.helper.js'; type EntityAmountAccumulator = { amount: number; // ledger records are keyed by ID, so a record hitting the same entity on multiple sides is listed once ledgerRecords: Map<string, DecoratedLedgerRecord>; }; type AmountsBySortCode = Map< number, { amount: number; records: Map<string, EntityAmountAccumulator> } >; export function updateRecords( amountsByEntity: AmountsBySortCode, amount: number, sortCode: number, financialEntityId: string, ledgerRecord: DecoratedLedgerRecord, ) { const currentRecord = amountsByEntity.get(sortCode); if (currentRecord) { currentRecord.amount += amount; const record = currentRecord.records.get(financialEntityId); if (record) { record.amount += amount; record.ledgerRecords.set(ledgerRecord.id, ledgerRecord); } else { currentRecord.records.set(financialEntityId, { amount, ledgerRecords: new Map([[ledgerRecord.id, ledgerRecord]]), }); } } else { amountsByEntity.set(sortCode, { amount, records: new Map([ [financialEntityId, { amount, ledgerRecords: new Map([[ledgerRecord.id, ledgerRecord]]) }], ]), }); } } export type FilteringRule = (financialEntityId: string, sortCode: number) => boolean; export type FilteringOptions = { rule: FilteringRule; negate?: boolean; }; export function amountByFinancialEntityIdAndSortCodeValidations< T extends FilteringOptions[], R extends { [K in keyof T]: number }, >(unfilteredRecords: DecoratedLedgerRecord[], filteringRules: T): R { const amounts = filteringRules.map(() => 0) as R; unfilteredRecords.map(record => { if (record.credit_entity1) { for (let i = 0; i < filteringRules.length; i++) { if (filteringRules[i].rule(record.credit_entity1, record.credit_entity_sort_code1!)) { amounts[i] += Number(record.credit_local_amount1) * (filteringRules[i].negate ? -1 : 1); } } } if (record.credit_entity2) { for (let i = 0; i < filteringRules.length; i++) { if (filteringRules[i].rule(record.credit_entity2, record.credit_entity_sort_code2!)) { amounts[i] += Number(record.credit_local_amount2) * (filteringRules[i].negate ? -1 : 1); } } } if (record.debit_entity1) { for (let i = 0; i < filteringRules.length; i++) { if (filteringRules[i].rule(record.debit_entity1, record.debit_entity_sort_code1!)) { amounts[i] += Number(record.debit_local_amount1) * (filteringRules[i].negate ? 1 : -1); } } } if (record.debit_entity2) { for (let i = 0; i < filteringRules.length; i++) { if (filteringRules[i].rule(record.debit_entity2, record.debit_entity_sort_code2!)) { amounts[i] += Number(record.debit_local_amount2) * (filteringRules[i].negate ? 1 : -1); } } } }); return amounts; } export function recordsByFinancialEntityIdAndSortCodeValidations< T extends FilteringOptions[], R extends { [K in keyof T]: CommentaryProto }, >(unfilteredRecords: DecoratedLedgerRecord[], filteringRules: T): R { const commentaryProtos = filteringRules.map(() => ({ amount: 0, amountsByEntity: new Map() as AmountsBySortCode, })); unfilteredRecords.map(record => { if (record.credit_entity1) { for (let i = 0; i < filteringRules.length; i++) { if (filteringRules[i].rule(record.credit_entity1, record.credit_entity_sort_code1!)) { const sign = filteringRules[i].negate ? -1 : 1; const recordAmount = Number(record.credit_local_amount1) * sign; updateRecords( commentaryProtos[i].amountsByEntity, recordAmount, record.credit_entity_sort_code1!, record.credit_entity1, record, ); commentaryProtos[i].amount += recordAmount; } } } if (record.credit_entity2) { for (let i = 0; i < filteringRules.length; i++) { if (filteringRules[i].rule(record.credit_entity2, record.credit_entity_sort_code2!)) { const sign = filteringRules[i].negate ? -1 : 1; const recordAmount = Number(record.credit_local_amount2) * sign; updateRecords( commentaryProtos[i].amountsByEntity, recordAmount, record.credit_entity_sort_code2!, record.credit_entity2, record, ); commentaryProtos[i].amount += recordAmount; } } } if (record.debit_entity1) { for (let i = 0; i < filteringRules.length; i++) { if (filteringRules[i].rule(record.debit_entity1, record.debit_entity_sort_code1!)) { const sign = filteringRules[i].negate ? -1 : 1; const recordAmount = Number(record.debit_local_amount1) * sign * -1; updateRecords( commentaryProtos[i].amountsByEntity, recordAmount, record.debit_entity_sort_code1!, record.debit_entity1, record, ); commentaryProtos[i].amount += recordAmount; } } } if (record.debit_entity2) { for (let i = 0; i < filteringRules.length; i++) { if (filteringRules[i].rule(record.debit_entity2, record.debit_entity_sort_code2!)) { const sign = filteringRules[i].negate ? -1 : 1; const recordAmount = Number(record.debit_local_amount2) * sign * -1; updateRecords( commentaryProtos[i].amountsByEntity, recordAmount, record.debit_entity_sort_code2!, record.debit_entity2, record, ); commentaryProtos[i].amount += recordAmount; } } } }); return commentaryProtos.map(proto => ({ amount: proto.amount, records: Array.from(proto.amountsByEntity.entries()).map(([sortCode, data]) => ({ sortCode, amount: data.amount, records: Array.from(data.records.entries()).map(([financialEntityId, entityData]) => ({ financialEntityId, amount: entityData.amount, ledgerRecords: Array.from(entityData.ledgerRecords.values()), })), })), })) as R; }