UNPKG

@accounter/server

Version:
209 lines • 10.1 kB
import { AdminContextProvider } from '../../../admin-context/providers/admin-context.provider.js'; import { ExchangeProvider } from '../../../exchange-rates/providers/exchange.provider.js'; import { TransactionsProvider } from '../../../transactions/providers/transactions.provider.js'; import { getEntriesFromFeeTransaction, splitFeeTransactions, } from '../../helpers/fee-transactions.js'; import { storeInitialGeneratedRecords } from '../../helpers/ledgrer-storage.helper.js'; import { generateMiscExpensesLedger } from '../../helpers/misc-expenses-ledger.helper.js'; import { getFinancialAccountTaxCategoryId, getLedgerBalanceInfo, isTransactionsOppositeSign, LedgerError, ledgerProtoToRecordsConverter, updateLedgerBalanceByEntry, validateTransactionBasicVariables, } from '../../helpers/utils.helper.js'; export const generateLedgerRecordsForInternalTransfer = async (charge, { insertLedgerRecordsIfNotExists }, { injector }) => { const { defaultLocalCurrency, general: { taxCategories: { exchangeRateTaxCategoryId }, }, } = await injector.get(AdminContextProvider).getVerifiedAdminContext(); const chargeId = charge.id; const errors = new Set(); try { // validate ledger records are balanced const ledgerBalance = new Map(); const dates = new Set(); const currencies = new Set(); // generate ledger from transactions const mainFinancialAccountLedgerEntries = []; const feeFinancialAccountLedgerEntries = []; let originEntry = undefined; let destinationEntry = undefined; // Get all transactions const transactions = await injector .get(TransactionsProvider) .transactionsByChargeIDLoader.load(chargeId); const { mainTransactions, feeTransactions } = splitFeeTransactions(transactions); if (mainTransactions.length !== 2) { errors.add(`Internal transfer Charge must include two main transactions`); } try { if (!isTransactionsOppositeSign(mainTransactions)) { errors.add(`Internal transfer Charge must include two main transactions with opposite sign`); } } catch (e) { if (e instanceof LedgerError) { errors.add(e.message); } else { throw e; } } // create a ledger record for main transactions const mainFinancialAccountLedgerEntriesPromises = mainTransactions.map(async (transaction) => { try { const { currency, valueDate } = validateTransactionBasicVariables(transaction); let amount = Number(transaction.amount); let foreignAmount = undefined; if (currency !== defaultLocalCurrency) { // get exchange rate for currency const exchangeRate = await injector .get(ExchangeProvider) .getExchangeRates(currency, defaultLocalCurrency, valueDate); foreignAmount = amount; // calculate amounts in ILS amount = exchangeRate * amount; } const financialAccountTaxCategoryId = await getFinancialAccountTaxCategoryId(injector, transaction); const isCreditorCounterparty = amount > 0; const ledgerEntry = { id: transaction.id, invoiceDate: transaction.event_date, valueDate, currency, ...(isCreditorCounterparty ? { debitAccountID1: financialAccountTaxCategoryId, } : { creditAccountID1: financialAccountTaxCategoryId, }), debitAmount1: foreignAmount ? Math.abs(foreignAmount) : undefined, localCurrencyDebitAmount1: Math.abs(amount), creditAmount1: foreignAmount ? Math.abs(foreignAmount) : undefined, localCurrencyCreditAmount1: Math.abs(amount), description: transaction.source_description ?? undefined, reference: transaction.origin_key, isCreditorCounterparty, ownerId: charge.owner_id, currencyRate: transaction.currency_rate ? Number(transaction.currency_rate) : undefined, chargeId, }; mainFinancialAccountLedgerEntries.push(ledgerEntry); updateLedgerBalanceByEntry(ledgerEntry, ledgerBalance, defaultLocalCurrency); dates.add(valueDate.getTime()); currencies.add(currency); } catch (e) { if (e instanceof LedgerError) { errors.add(e.message); } else { throw e; } } }); // create a ledger record for fee transactions const feeFinancialAccountLedgerEntriesPromises = feeTransactions.map(async (transaction) => { if (!transaction.is_fee) { return; } try { const ledgerEntries = await getEntriesFromFeeTransaction(transaction, charge, injector).catch(e => { if (e instanceof LedgerError) { errors.add(e.message); } else { throw e; } }); if (!ledgerEntries) { return; } feeFinancialAccountLedgerEntries.push(...ledgerEntries); ledgerEntries.map(ledgerEntry => { updateLedgerBalanceByEntry(ledgerEntry, ledgerBalance, defaultLocalCurrency); dates.add(ledgerEntry.valueDate.getTime()); currencies.add(ledgerEntry.currency); }); } catch (e) { if (e instanceof LedgerError) { errors.add(e.message); } else { throw e; } } }); // create ledger records for misc expenses const miscExpensesLedgerPromise = generateMiscExpensesLedger(charge, injector).then(entries => { entries.map(entry => { entry.ownerId = charge.owner_id; feeFinancialAccountLedgerEntries.push(entry); updateLedgerBalanceByEntry(entry, ledgerBalance, defaultLocalCurrency); dates.add(entry.valueDate.getTime()); currencies.add(entry.currency); }); }); await Promise.all([ ...feeFinancialAccountLedgerEntriesPromises, ...mainFinancialAccountLedgerEntriesPromises, miscExpensesLedgerPromise, ]); for (const ledgerEntry of mainFinancialAccountLedgerEntries) { if (ledgerEntry.isCreditorCounterparty) { destinationEntry = ledgerEntry; } else { originEntry = ledgerEntry; } } const { balanceSum } = await getLedgerBalanceInfo(injector, ledgerBalance); const miscLedgerEntries = []; if (!originEntry || !destinationEntry) { errors.add(`Internal transfer Charge must include two main transactions`); } else if (Math.abs(balanceSum) > 0.005) { // Add ledger completion entries const hasMultipleDates = dates.size > 1; const hasForeignCurrency = currencies.size > (currencies.has(defaultLocalCurrency) ? 1 : 0); if (hasMultipleDates && hasForeignCurrency) { const amount = Math.abs(balanceSum); const isCreditorCounterparty = balanceSum > 0; const latestDate = new Date(Math.max(originEntry.valueDate.getTime(), originEntry.invoiceDate.getTime(), destinationEntry.valueDate.getTime(), destinationEntry.invoiceDate.getTime())); const ledgerEntry = { id: destinationEntry.id, // NOTE: this field is dummy creditAccountID1: isCreditorCounterparty ? undefined : exchangeRateTaxCategoryId, creditAmount1: undefined, localCurrencyCreditAmount1: amount, debitAccountID1: isCreditorCounterparty ? exchangeRateTaxCategoryId : undefined, debitAmount1: undefined, localCurrencyDebitAmount1: amount, description: 'Exchange ledger record', isCreditorCounterparty, invoiceDate: latestDate, valueDate: latestDate, currency: defaultLocalCurrency, ownerId: destinationEntry.ownerId, chargeId, }; miscLedgerEntries.push(ledgerEntry); updateLedgerBalanceByEntry(ledgerEntry, ledgerBalance, defaultLocalCurrency); } } const ledgerBalanceInfo = await getLedgerBalanceInfo(injector, ledgerBalance, errors); const records = [ ...mainFinancialAccountLedgerEntries, ...feeFinancialAccountLedgerEntries, ...miscLedgerEntries, ]; if (insertLedgerRecordsIfNotExists) { await storeInitialGeneratedRecords(charge.id, records, injector); } return { records: ledgerProtoToRecordsConverter(records), charge, balance: ledgerBalanceInfo, errors: Array.from(errors), }; } catch (e) { return { __typename: 'CommonError', message: `Failed to generate ledger records for charge ID="${chargeId}"\n${e}`, }; } }; //# sourceMappingURL=internal-transfer-ledger-generation.resolver.js.map