@accounter/server
Version:
Accounter GraphQL server
179 lines (162 loc) • 6.75 kB
text/typescript
import { GraphQLError } from 'graphql';
import { Currency } from '../../../shared/enums.js';
import {
formatFinancialAmount,
optionalDateToTimelessDateString,
} from '../../../shared/helpers/index.js';
import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js';
import { ChargesProvider } from '../../charges/providers/charges.provider.js';
import { IGetChargesByIdsResult } from '../../charges/types.js';
import { ExchangeProvider } from '../../exchange-rates/providers/exchange.provider.js';
import { BusinessesProvider } from '../../financial-entities/providers/businesses.provider.js';
import { validateTransactionBasicVariables } from '../../ledger/helpers/utils.helper.js';
import { getTransactionDebitDate } from '../../transactions/helpers/debit-date.helper.js';
import { TransactionsProvider } from '../../transactions/providers/transactions.provider.js';
import type { IGetTransactionsByIdsResult } from '../../transactions/types.js';
import { BusinessTripExpensesTransactionsMatchProvider } from '../providers/business-trips-expenses-transactions-match.provider.js';
import { BusinessTripsProvider } from '../providers/business-trips.provider.js';
import type { BusinessTripsModule } from '../types.js';
export const commonBusinessTripExpenseFields: BusinessTripsModule.BusinessTripExpenseResolvers = {
id: DbTripExpense => DbTripExpense.id!,
businessTrip: (DbTripExpense, _, { injector }) =>
injector
.get(BusinessTripsProvider)
.getBusinessTripsByIdLoader.load(DbTripExpense.business_trip_id!)
.then(res => {
if (!res) {
throw new GraphQLError(
`Business trip with id ${DbTripExpense.business_trip_id} not found`,
);
}
return res;
}),
date: DbTripExpense => optionalDateToTimelessDateString(DbTripExpense.date),
valueDate: DbTripExpense => optionalDateToTimelessDateString(DbTripExpense.value_date),
amount: async (DbTripExpense, _, { injector }) => {
const { defaultCryptoConversionFiatCurrency } = await injector
.get(AdminContextProvider)
.getVerifiedAdminContext();
if (!DbTripExpense.amount || !DbTripExpense.currency) {
if (!DbTripExpense.transaction_ids?.length) {
return null;
}
const transactionsPromise = injector
.get(TransactionsProvider)
.transactionByIdLoader.loadMany(DbTripExpense.transaction_ids);
const [transactions] = await Promise.all([transactionsPromise]);
const allTransactions = transactions.filter(
transaction => transaction && !(transaction instanceof Error),
) as IGetTransactionsByIdsResult[];
let amount = 0;
await Promise.all(
allTransactions.map(async transaction => {
if (!transaction || transaction instanceof Error) {
return;
}
const date = getTransactionDebitDate(transaction);
const exchangeRate = await injector
.get(ExchangeProvider)
.getExchangeRates(
transaction.currency as Currency,
defaultCryptoConversionFiatCurrency,
date,
);
const transactionAmount = Number(transaction.amount) * exchangeRate;
amount += transactionAmount;
}),
);
return formatFinancialAmount(amount, defaultCryptoConversionFiatCurrency);
}
if (DbTripExpense.currency === defaultCryptoConversionFiatCurrency) {
return formatFinancialAmount(DbTripExpense.amount, DbTripExpense.currency);
}
// handle payed by employee cases
if (DbTripExpense.payed_by_employee) {
const date = DbTripExpense.value_date || DbTripExpense.date;
if (!date) {
return null;
}
const exchangeRate = await injector
.get(ExchangeProvider)
.getExchangeRates(
DbTripExpense.currency as Currency,
defaultCryptoConversionFiatCurrency,
date,
);
return formatFinancialAmount(
Number(DbTripExpense.amount) * exchangeRate,
defaultCryptoConversionFiatCurrency,
);
}
if (!DbTripExpense.transaction_ids?.length) {
return null;
}
const transactionMatchesPromise = injector
.get(BusinessTripExpensesTransactionsMatchProvider)
.getBusinessTripsExpenseMatchesByExpenseIdLoader.load(DbTripExpense.id);
const transactionsPromise = injector
.get(TransactionsProvider)
.transactionByIdLoader.loadMany(DbTripExpense.transaction_ids)
.then(
res =>
res.filter(transaction => {
if (!transaction) {
return false;
}
if (transaction instanceof Error) {
throw transaction;
}
return transaction;
}) as IGetTransactionsByIdsResult[],
);
const [transactionMatches, transactions] = await Promise.all([
transactionMatchesPromise,
transactionsPromise,
]);
let totalAmount = 0;
await Promise.all(
transactions.map(async transaction => {
const match = transactionMatches.find(match => match.transaction_id === transaction.id);
if (!match) {
return;
}
const { currency, valueDate } = validateTransactionBasicVariables(transaction);
const exchangeRate = await injector
.get(ExchangeProvider)
.getExchangeRates(currency, defaultCryptoConversionFiatCurrency, valueDate);
const amount = Number(match.amount || transaction.amount);
totalAmount += amount * exchangeRate;
}),
);
return formatFinancialAmount(totalAmount, defaultCryptoConversionFiatCurrency);
},
employee: (DbTripExpense, _, { injector }) =>
DbTripExpense.employee_business_id
? injector
.get(BusinessesProvider)
.getBusinessByIdLoader.load(DbTripExpense.employee_business_id)
.then(res => {
if (!res) {
throw new GraphQLError(
`Employee with id ${DbTripExpense.employee_business_id} not found`,
);
}
return res;
})
: null,
transactions: DbTripTransaction =>
DbTripTransaction.transaction_ids?.length ? DbTripTransaction.transaction_ids : null,
charges: async (DbTripTransaction, _, { injector }) => {
if (!DbTripTransaction.charge_ids?.length) {
return null;
}
return injector
.get(ChargesProvider)
.getChargeByIdLoader.loadMany(DbTripTransaction.charge_ids)
.then(
res =>
res.filter(charge => charge && !(charge instanceof Error)) as IGetChargesByIdsResult[],
);
},
payedByEmployee: dbTransaction => dbTransaction.payed_by_employee,
};