UNPKG

@accounter/server

Version:
284 lines (265 loc) • 10.8 kB
import { GraphQLError } from 'graphql'; import type { Resolvers } from '../../../__generated__/types.js'; import { Currency } from '../../../shared/enums.js'; import { dateToTimelessDateString, formatFinancialAmount } from '../../../shared/helpers/index.js'; import type { BusinessTransactionProto } from '../../../shared/types/index.js'; import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js'; import { ChargesProvider } from '../../charges/providers/charges.provider.js'; import { LedgerProvider } from '../../ledger/providers/ledger.provider.js'; import { handleBusinessTransaction } from '../helpers/business-transactions.helper.js'; import { FinancialEntitiesProvider } from '../providers/financial-entities.provider.js'; import type { FinancialEntitiesModule, IGetBusinessesByIdsResult } from '../types.js'; import { businessTransactionsSumFromLedgerRecords } from './business-transactions-sum-from-ledger-records.resolver.js'; export const businessTransactionsResolvers: FinancialEntitiesModule.Resolvers & Pick< Resolvers, 'BusinessTransactionsSumFromLedgerRecordsResult' | 'BusinessTransactionsFromLedgerRecordsResult' > = { Query: { businessTransactionsSumFromLedgerRecords, businessTransactionsFromLedgerRecords: async (_, { filters }, context, _info) => { const { injector } = context; const { ownerIds, businessIDs, fromDate, toDate, type } = filters || {}; const shouldFetchAllFinancialEntities = !businessIDs?.length && !!type; const financialEntities = await (shouldFetchAllFinancialEntities ? injector.get(FinancialEntitiesProvider).getAllFinancialEntities() : injector .get(FinancialEntitiesProvider) .getFinancialEntityByIdLoader.loadMany(businessIDs ?? [])); const isFilteredByFinancialEntities = !!filters?.businessIDs?.length; const financialEntitiesIDs = financialEntities ?.filter(fe => { if (!fe || !('id' in fe)) { return false; } if (type) { return type.toLocaleLowerCase() === fe.type; } return true; }) .map(business => (business as IGetBusinessesByIdsResult).id); try { const charges = await injector.get(ChargesProvider).getChargesByFilters({ ownerIds: ownerIds ?? undefined, businessIds: businessIDs, fromAnyDate: fromDate, toAnyDate: toDate, }); const ledgerRecordSets = await Promise.all( charges.map(charge => injector.get(LedgerProvider).getLedgerRecordsByChargesIdLoader.load(charge.id), ), ); const ledgerRecords = ledgerRecordSets.flat(); const rawTransactions: BusinessTransactionProto[] = []; for (const record of ledgerRecords) { // re-filter ledger records by date (to prevent charge's out-of-range dates from affecting the sum) if (!!fromDate && dateToTimelessDateString(record.invoice_date) < fromDate) { continue; } if (!!toDate && dateToTimelessDateString(record.invoice_date) > toDate) { continue; } if ( record.credit_entity1 && (!isFilteredByFinancialEntities || financialEntitiesIDs.includes(record.credit_entity1)) ) { const transaction = handleBusinessTransaction( record, record.credit_entity1, record.debit_entity1, true, record.credit_local_amount1, record.credit_foreign_amount1, ); rawTransactions.push(transaction); } if ( record.credit_entity2 && (!isFilteredByFinancialEntities || financialEntitiesIDs.includes(record.credit_entity2)) ) { const transaction = handleBusinessTransaction( record, record.credit_entity2, record.debit_entity2 ?? record.debit_entity2, true, record.credit_local_amount2, record.credit_foreign_amount2, ); rawTransactions.push(transaction); } if ( record.debit_entity1 && (!isFilteredByFinancialEntities || financialEntitiesIDs.includes(record.debit_entity1)) ) { const transaction = handleBusinessTransaction( record, record.debit_entity1, record.credit_entity1, false, record.debit_local_amount1, record.debit_foreign_amount1, ); rawTransactions.push(transaction); } if ( record.debit_entity2 && (!isFilteredByFinancialEntities || financialEntitiesIDs.includes(record.debit_entity2)) ) { const transaction = handleBusinessTransaction( record, record.debit_entity2, record.credit_entity2 ?? record.credit_entity1, false, record.debit_local_amount2, record.debit_foreign_amount2, ); rawTransactions.push(transaction); } } return { businessTransactions: rawTransactions.sort((a, b) => { const chargeA = charges.find(charge => charge.id === a.chargeId); const chargeB = charges.find(charge => charge.id === b.chargeId); const dateA = Math.min( ...([ chargeA?.documents_min_date?.getTime(), chargeA?.transactions_min_event_date?.getTime(), ].filter(Boolean) as number[]), ); const dateB = Math.min( ...([ chargeB?.documents_min_date?.getTime(), chargeB?.transactions_min_event_date?.getTime(), ].filter(Boolean) as number[]), ); if (dateA < dateB) return -1; if (dateA > dateB) return 1; if (a.chargeId < b.chargeId) return -1; if (a.chargeId > b.chargeId) return 1; if (a.date.getTime() < b.date.getTime()) return -1; if (a.date.getTime() > b.date.getTime()) return 1; return 0; }), }; } catch (e) { console.error(e); return { __typename: 'CommonError', message: 'Error fetching business transactions from ledger records', }; } }, }, BusinessTransactionsSumFromLedgerRecordsResult: { __resolveType: (obj, _context, _info) => { if ('__typename' in obj && obj.__typename === 'CommonError') return 'CommonError'; return 'BusinessTransactionsSumFromLedgerRecordsSuccessfulResult'; }, }, BusinessTransactionSum: { business: (rawSum, _, { injector }) => injector .get(FinancialEntitiesProvider) .getFinancialEntityByIdLoader.load(rawSum.businessId) .then(res => { if (!res) { throw new GraphQLError(`Business with id ${rawSum.businessId} not found`); } return res; }), credit: async (rawSum, _, { injector }) => { const { defaultLocalCurrency } = await injector .get(AdminContextProvider) .getVerifiedAdminContext(); return formatFinancialAmount(rawSum[defaultLocalCurrency].credit, defaultLocalCurrency); }, debit: async (rawSum, _, { injector }) => { const { defaultLocalCurrency } = await injector .get(AdminContextProvider) .getVerifiedAdminContext(); return formatFinancialAmount(rawSum[defaultLocalCurrency].debit, defaultLocalCurrency); }, total: async (rawSum, _, { injector }) => { const { defaultLocalCurrency } = await injector .get(AdminContextProvider) .getVerifiedAdminContext(); return formatFinancialAmount(rawSum[defaultLocalCurrency].total, defaultLocalCurrency); }, foreignCurrenciesSum: async (rawSum, _, { injector }) => { const { defaultLocalCurrency } = await injector .get(AdminContextProvider) .getVerifiedAdminContext(); const currencies = []; for (const key in rawSum) { if (!Object.values(Currency).includes(key as Currency)) continue; const currency = key as Currency; if (currency === defaultLocalCurrency) continue; const currencySum = rawSum[currency]; if (currencySum.credit || currencySum.debit) { currencies.push({ credit: formatFinancialAmount(currencySum.credit, currency), debit: formatFinancialAmount(currencySum.debit, currency), total: formatFinancialAmount(currencySum.total, currency), currency, }); } } return currencies; }, }, BusinessTransactionsFromLedgerRecordsResult: { __resolveType: parent => 'businessTransactions' in parent ? 'BusinessTransactionsFromLedgerRecordsSuccessfulResult' : 'CommonError', }, BusinessTransaction: { amount: async (parent, _, { injector }) => { const { defaultLocalCurrency } = await injector .get(AdminContextProvider) .getVerifiedAdminContext(); return formatFinancialAmount( Number.isNaN(parent.foreignAmount) ? parent.amount : Number(parent.amount) * (parent.isCredit ? 1 : -1), defaultLocalCurrency, ); }, business: (parent, _, { injector }) => injector .get(FinancialEntitiesProvider) .getFinancialEntityByIdLoader.load(parent.businessId) .then(res => { if (!res) { throw new GraphQLError(`Financial entity with id ${parent.businessId} not found`); } return res; }), foreignAmount: parent => formatFinancialAmount( Number.isNaN(parent.foreignAmount) ? parent.foreignAmount : Number(parent.foreignAmount) * (parent.isCredit ? 1 : -1), parent.currency, ), invoiceDate: parent => dateToTimelessDateString(parent.date!), reference: parent => parent.reference ?? null, details: parent => parent.details ?? null, counterAccount: (parent, _, { injector }) => parent.counterAccountId ? injector .get(FinancialEntitiesProvider) .getFinancialEntityByIdLoader.load(parent.counterAccountId) .then(res => { if (!res) { throw new GraphQLError( `Financial entity with id ${parent.counterAccountId} not found`, ); } return res; }) : null, chargeId: parent => parent.chargeId, }, };