UNPKG

@accounter/server

Version:
226 lines • 9.3 kB
import { startOfDay } from 'date-fns'; import { EMPTY_UUID } from '../../../shared/constants.js'; import { formatCurrency } from '../../../shared/helpers/index.js'; import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js'; import { LedgerProvider } from '../providers/ledger.provider.js'; const comparisonKeys = [ 'credit_entity1', 'credit_entity2', 'credit_foreign_amount1', 'credit_foreign_amount2', 'credit_local_amount1', 'credit_local_amount2', 'currency', 'debit_entity1', 'debit_entity2', 'debit_foreign_amount1', 'debit_foreign_amount2', 'debit_local_amount1', 'debit_local_amount2', 'description', 'invoice_date', 'reference1', 'value_date', ]; const dateKeys = ['value_date', 'invoice_date']; export function ledgerRecordsGenerationFullMatchComparison(storageRecords, newRecords) { // compare existing records with new records const unmatchedStorageRecords = [...storageRecords]; const unmatchedNewRecords = []; const fullMatches = new Map(); newRecords.map((newRecord, index) => { const matchIndex = unmatchedStorageRecords.findIndex(storageRecord => { return isExactMatch(storageRecord, newRecord); }); if (matchIndex !== -1) { fullMatches.set(index, unmatchedStorageRecords[matchIndex].id); unmatchedStorageRecords.splice(matchIndex, 1); return; } unmatchedNewRecords.push(newRecord); }); return { unmatchedStorageRecords, unmatchedNewRecords, fullMatches, isFullyMatched: storageRecords.length === newRecords.length && newRecords.length === fullMatches.size, }; } export function ledgerRecordsGenerationPartialMatchComparison(storageRecords, newRecords) { const matches = new Map(); const unmatchedNewRecords = [...newRecords]; storageRecords.map(storageRecord => { let maxScore = 0; let bestMatch = undefined; unmatchedNewRecords.map(newRecord => { const score = getMatchScore(storageRecord, newRecord); if (score < 0) { return; } if (score > maxScore) { maxScore = score; bestMatch = newRecord; } }); if (bestMatch) { matches.set(storageRecord.id, bestMatch); unmatchedNewRecords.splice(unmatchedNewRecords.indexOf(bestMatch), 1); } }); const diffs = Array.from(matches.entries()).map(([storageId, newRecord]) => { const storageRecord = storageRecords.find(record => record.id === storageId); if (!storageRecord) { throw new Error('Storage record not found'); } const recordDiffs = { ...newRecord, id: storageRecord.id, }; return recordDiffs; }); return { toUpdate: [ ...diffs, ...unmatchedNewRecords.map(record => ({ ...record, id: EMPTY_UUID, })), ], toRemove: storageRecords.filter(record => !diffs.find(diff => diff.id === record.id)), }; } function isExactMatch(storageRecord, newRecord) { return comparisonKeys.reduce((isMatch, key) => { if (!isMatch) { return false; } if (dateKeys.includes(key)) { const newDate = newRecord[key] ? startOfDay(new Date(newRecord[key])) : undefined; return storageRecord[key]?.getTime() === newDate?.getTime(); } if (typeof storageRecord[key] === 'string' && !Number.isNaN(Number(storageRecord[key]))) { return Math.abs(Number(storageRecord[key]) - Number(newRecord[key])) < 0.005; } return storageRecord[key] === newRecord[key]; }, true); } export function convertToStorageInputRecord(record, ownerId) { return { chargeId: record.chargeId, ownerId: record.ownerId ?? ownerId, creditEntity1: record.creditAccountID1, creditEntity2: record.creditAccountID2, creditForeignAmount1: record.creditAmount1, creditForeignAmount2: record.creditAmount2, creditLocalAmount1: record.localCurrencyCreditAmount1, creditLocalAmount2: record.localCurrencyCreditAmount2, currency: record.currency, debitEntity1: record.debitAccountID1, debitEntity2: record.debitAccountID2, debitForeignAmount1: record.debitAmount1, debitForeignAmount2: record.debitAmount2, debitLocalAmount1: record.localCurrencyDebitAmount1, debitLocalAmount2: record.localCurrencyDebitAmount2, description: record.description, invoiceDate: record.invoiceDate, reference: record.reference, valueDate: record.valueDate, }; } function getMatchScore(storageRecord, newRecord) { return comparisonKeys.reduce((cumulativeScore, key) => { const factor = storageRecord[key] ? 1 : 0.5; let scoreDirection; if (dateKeys.includes(key)) { scoreDirection = storageRecord[key]?.getTime() === newRecord[key]?.getTime() ? 1 : -1; } else { scoreDirection = storageRecord[key] === newRecord[key] ? 1 : -1; } const addedScore = factor * scoreDirection; return cumulativeScore + addedScore; }, 0); } export function convertLedgerRecordToProto(record, ownerId) { return { id: record.id, creditAccountID1: record.credit_entity1 ?? undefined, creditAccountID2: record.credit_entity2 ?? undefined, debitAccountID1: record.debit_entity1 ?? undefined, debitAccountID2: record.debit_entity2 ?? undefined, creditAmount1: record.credit_foreign_amount1 ? Number(record.credit_foreign_amount1) : undefined, creditAmount2: record.credit_foreign_amount2 ? Number(record.credit_foreign_amount2) : undefined, debitAmount1: record.debit_foreign_amount1 ? Number(record.debit_foreign_amount1) : undefined, debitAmount2: record.debit_foreign_amount2 ? Number(record.debit_foreign_amount2) : undefined, localCurrencyCreditAmount1: Number(record.credit_local_amount1), localCurrencyCreditAmount2: record.credit_local_amount2 ? Number(record.credit_local_amount2) : undefined, localCurrencyDebitAmount1: Number(record.debit_local_amount1), localCurrencyDebitAmount2: record.debit_local_amount2 ? Number(record.debit_local_amount2) : undefined, description: record.description ?? undefined, invoiceDate: record.invoice_date, reference: record.reference1 ?? undefined, valueDate: record.value_date, currency: formatCurrency(record.currency), isCreditorCounterparty: false, // redundant value ownerId: record.owner_id ?? ownerId, currencyRate: undefined, chargeId: record.charge_id, }; } export function convertLedgerRecordToInput(record, ownerId) { return { chargeId: record.charge_id ?? undefined, ownerId: record.owner_id ?? ownerId, creditEntity1: record.credit_entity1 ?? undefined, creditEntity2: record.credit_entity2 ?? undefined, debitEntity1: record.debit_entity1 ?? undefined, debitEntity2: record.debit_entity2 ?? undefined, creditForeignAmount1: record.credit_foreign_amount1 ? Number(record.credit_foreign_amount1) : undefined, creditForeignAmount2: record.credit_foreign_amount2 ? Number(record.credit_foreign_amount2) : undefined, debitForeignAmount1: record.debit_foreign_amount1 ? Number(record.debit_foreign_amount1) : undefined, debitForeignAmount2: record.debit_foreign_amount2 ? Number(record.debit_foreign_amount2) : undefined, creditLocalAmount1: Number(record.credit_local_amount1), creditLocalAmount2: record.credit_local_amount2 ? Number(record.credit_local_amount2) : undefined, debitLocalAmount1: Number(record.debit_local_amount1), debitLocalAmount2: record.debit_local_amount2 ? Number(record.debit_local_amount2) : undefined, description: record.description ?? undefined, invoiceDate: record.invoice_date, reference: record.reference1 ?? undefined, valueDate: record.value_date, currency: formatCurrency(record.currency), ledgerId: record.id, }; } export async function storeInitialGeneratedRecords(chargeId, records, injector) { const ledgerRecords = await injector .get(LedgerProvider) .getLedgerRecordsByChargesIdLoader.load(chargeId); if (ledgerRecords.length === 0) { const { ownerId } = await injector.get(AdminContextProvider).getVerifiedAdminContext(); const ledgerRecords = records.map(record => convertToStorageInputRecord(record, ownerId)); if (ledgerRecords.length) { return injector.get(LedgerProvider).insertLedgerRecords({ ledgerRecords }); } } return void 0; } //# sourceMappingURL=ledgrer-storage.helper.js.map