@accounter/server
Version:
217 lines (202 loc) • 7.94 kB
text/typescript
import { GraphQLError } from 'graphql';
import { deleteCharges } from '@modules/charges/helpers/delete-charges.helper.js';
import { ChargesProvider } from '@modules/charges/providers/charges.provider.js';
import { ExchangeProvider } from '@modules/exchange-rates/providers/exchange.provider.js';
import { EMPTY_UUID } from '@shared/constants';
import type { Resolvers } from '@shared/gql-types';
import { formatCurrency } from '@shared/helpers';
import { effectiveDateSupplement } from '../helpers/effective-date.helper.js';
import { FeeTransactionsProvider } from '../providers/fee-transactions.provider.js';
import { TransactionsProvider } from '../providers/transactions.provider.js';
import type {
IGetTransactionsByIdsResult,
IUpdateTransactionParams,
TransactionsModule,
} from '../types.js';
import { commonChargeFields, commonTransactionFields } from './common.js';
export const transactionsResolvers: TransactionsModule.Resolvers &
Pick<Resolvers, 'UpdateTransactionResult'> = {
Query: {
transactionsByIDs: async (_, { transactionIDs }, { injector }) => {
if (transactionIDs.length === 0) {
return [];
}
const dbTransactions = await injector
.get(TransactionsProvider)
.getTransactionByIdLoader.loadMany(transactionIDs);
if (!dbTransactions) {
if (transactionIDs.length === 1) {
throw new GraphQLError(`Transaction ID="${transactionIDs[0]}" not found`);
} else {
throw new GraphQLError(`Couldn't find any transactions`);
}
}
const transactions = transactionIDs.map(id => {
const transaction = dbTransactions.find(
transaction => transaction && 'id' in transaction && transaction.id === id,
);
if (!transaction) {
throw new GraphQLError(`Transaction ID="${id}" not found`);
}
return transaction as IGetTransactionsByIdsResult;
});
return transactions;
},
},
Mutation: {
updateTransaction: async (_, { transactionId, fields }, { injector }) => {
let postUpdateActions = async (): Promise<void> => void 0;
try {
// let charge: ChargesTypes.IGetChargesByIdsResult | undefined;
const existingChargePromise = async () => {
const charge = await injector
.get(ChargesProvider)
.getChargeByIdLoader.load(fields.chargeId ?? '');
if (!charge) {
throw new GraphQLError(`Charge ID="${chargeId}" not valid`);
}
};
const emptyChargePromise = async () => {
// case unlinked from charge
const transaction = await injector
.get(TransactionsProvider)
.getTransactionByIdLoader.load(transactionId);
if (!transaction) {
throw new GraphQLError(`Transaction ID="${transactionId}" not valid`);
}
if (transaction.charge_id) {
const charge = await injector
.get(ChargesProvider)
.getChargeByIdLoader.load(transaction.charge_id);
if (!charge) {
throw new GraphQLError(`Former transaction's charge ID ("${chargeId}") not valid`);
}
// generate new charge
const newCharge = await injector.get(ChargesProvider).generateCharge({
ownerId: charge.owner_id,
userDescription: 'Transaction unlinked from charge',
});
if (!newCharge || newCharge.length === 0) {
throw new GraphQLError(
`Failed to generate new charge for transaction ID="${transactionId}"`,
);
}
chargeId = newCharge?.[0]?.id;
if (
Number(charge.documents_count ?? 0) === 0 &&
Number(charge.transactions_count ?? 1) === 1
) {
postUpdateActions = async () =>
deleteCharges([charge.id], injector)
.catch(e => {
console.error(e);
throw new GraphQLError(
`Failed to delete the empty former charge ID="${charge.id}"`,
);
})
.then(postUpdateActions);
}
}
};
let chargeId = fields.chargeId;
const chargePromise =
chargeId && chargeId !== EMPTY_UUID
? existingChargePromise()
: chargeId === EMPTY_UUID
? emptyChargePromise()
: Promise.resolve();
const feePromise = async () => {
if (fields.isFee === true) {
return injector.get(FeeTransactionsProvider).addFeeTransaction({
feeTransactions: [
{
id: transactionId,
isRecurring: false,
},
],
});
}
if (fields.isFee === false) {
return injector.get(FeeTransactionsProvider).deleteFeeTransactionsByIds({
transactionIds: [transactionId],
});
}
return Promise.resolve();
};
await Promise.all([chargePromise, feePromise()]);
const adjustedFields: IUpdateTransactionParams = {
transactionId,
businessId: fields.counterpartyId,
chargeId: chargeId ?? null,
debitDate: fields.effectiveDate ?? null,
};
injector.get(TransactionsProvider).getTransactionByIdLoader.clear(transactionId);
const res = await injector
.get(TransactionsProvider)
.updateTransaction({ ...adjustedFields });
const transaction = await injector
.get(TransactionsProvider)
.getTransactionByIdLoader.load(res[0].id);
if (!transaction) {
throw new GraphQLError(`Transaction ID="${res[0].id}" not found`);
}
await postUpdateActions();
return transaction as IGetTransactionsByIdsResult;
} catch (e) {
return {
__typename: 'CommonError',
message: (e as Error)?.message ?? 'Unknown error',
};
}
},
},
UpdateTransactionResult: {
__resolveType: (obj, _context, _info) => {
if ('__typename' in obj && obj.__typename === 'CommonError') return 'CommonError';
return 'type' in obj && obj.type === 'CONVERSION'
? 'ConversionTransaction'
: 'CommonTransaction';
},
},
CommonCharge: commonChargeFields,
FinancialCharge: commonChargeFields,
ConversionCharge: commonChargeFields,
SalaryCharge: commonChargeFields,
InternalTransferCharge: commonChargeFields,
DividendCharge: commonChargeFields,
BusinessTripCharge: commonChargeFields,
MonthlyVatCharge: commonChargeFields,
BankDepositCharge: commonChargeFields,
CreditcardBankCharge: commonChargeFields,
ConversionTransaction: {
__isTypeOf: DbTransaction => DbTransaction.charge_type === 'CONVERSION',
...commonTransactionFields,
effectiveDate: DbTransaction => {
const date = effectiveDateSupplement(DbTransaction);
if (!date) {
console.error(`Conversion transaction ID="${DbTransaction.id}" has no effective date`);
throw new GraphQLError('Conversion transaction must have effective date');
}
return date;
},
type: DbTransaction => (Number(DbTransaction.amount) > 0 ? 'QUOTE' : 'BASE'),
bankRate: DbTransaction => DbTransaction.currency_rate,
officialRateToLocal: async (
DbTransaction,
_,
{ injector, adminContext: { defaultLocalCurrency } },
) => {
return injector
.get(ExchangeProvider)
.getExchangeRates(
defaultLocalCurrency,
formatCurrency(DbTransaction.currency),
DbTransaction.event_date,
);
},
},
CommonTransaction: {
__isTypeOf: DbTransaction => DbTransaction.charge_type !== 'CONVERSION',
...commonTransactionFields,
},
};