UNPKG

@accounter/server

Version:
351 lines • 17.3 kB
import { GraphQLError } from 'graphql'; 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 { ScopeProvider } from '../../auth/providers/scope.provider.js'; import { deleteCharges } from '../../charges/helpers/delete-charges.helper.js'; import { ChargesProvider } from '../../charges/providers/charges.provider.js'; import { DocumentsProvider } from '../../documents/providers/documents.provider.js'; import { ExchangeProvider } from '../../exchange-rates/providers/exchange.provider.js'; import { effectiveDateSupplement } from '../helpers/effective-date.helper.js'; import { TransactionsProvider } from '../providers/transactions.provider.js'; import { commonChargeFields, commonTransactionFields } from './common.js'; export const transactionsResolvers = { Query: { transactionsByIDs: async (_, { transactionIDs }, { injector }) => { if (transactionIDs.length === 0) { return []; } const transactions = await injector .get(TransactionsProvider) .transactionByIdLoader.loadMany(transactionIDs); if (!transactions) { if (transactionIDs.length === 1) { throw new GraphQLError(`Transaction ID="${transactionIDs[0]}" not found`); } else { throw new GraphQLError(`Couldn't find any transactions`); } } transactionIDs.map(id => { const transaction = transactions.find(transaction => !(transaction instanceof Error) && transaction.id === id); if (!transaction) { throw new GraphQLError(`Transaction ID="${id}" not found`); } }); return transactionIDs; }, transactionsByFinancialEntity: async (_, { financialEntityID }, { injector }) => { const ownerIDs = await injector.get(ScopeProvider).getReadScope(); const transactions = await injector .get(TransactionsProvider) .getTransactionsByFilters({ businessIDs: [financialEntityID], ownerIDs, }) .then(res => res.map(t => t.id)); return transactions; }, }, Mutation: { updateTransaction: async (_, { transactionId, fields }, { injector }) => { let postUpdateActions = async () => 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) .transactionByIdLoader.load(transactionId); if (!transaction) { throw new GraphQLError(`Transaction ID="${transactionId}" not valid`); } if (transaction.charge_id) { const [charge, transactions, documents] = await Promise.all([ injector.get(ChargesProvider).getChargeByIdLoader.load(transaction.charge_id), injector .get(TransactionsProvider) .transactionsByChargeIDLoader.load(transaction.charge_id), injector .get(DocumentsProvider) .getDocumentsByChargeIdLoader.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) { throw new GraphQLError(`Failed to generate new charge for transaction ID="${transactionId}"`); } chargeId = newCharge.id; if (documents.length === 0 && transactions.length === 1) { postUpdateActions = async () => deleteCharges([charge.id], injector) .catch(e => { if (e instanceof GraphQLError) { throw 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(); await chargePromise; const adjustedFields = { transactionIds: [transactionId], businessId: fields.counterpartyId, chargeId: chargeId ?? null, debitDate: fields.effectiveDate ?? null, isFee: fields.isFee, }; const res = await injector .get(TransactionsProvider) .updateTransactions({ ...adjustedFields }); if (!res[0]?.id) { throw new GraphQLError('Transaction update failed'); } await postUpdateActions(); return res[0].id; } catch (e) { if (e instanceof GraphQLError) { throw e; } console.error(e); return { __typename: 'CommonError', message: e?.message ?? 'Unknown error', }; } }, updateTransactions: async (_, { transactionIds, fields }, { injector }) => { let postUpdateActions = async () => void 0; try { const { ownerId } = await injector.get(AdminContextProvider).getVerifiedAdminContext(); const existingChargePromise = async () => { // verify charge exists 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 transactions = await injector .get(TransactionsProvider) .transactionByIdLoader.loadMany(transactionIds) .then(res => res.filter((t, i) => { if (t instanceof Error) { throw new GraphQLError(`Transaction ID="${transactionIds[i]}" not valid`); } return true; })); const transactionsWithChargeId = transactions.filter(t => t.charge_id); if (transactionsWithChargeId.length > 0) { const chargesToDelete = new Set(); await Promise.all(transactionsWithChargeId.map(async (transaction) => { if (!transaction.charge_id) { return; } const [charge, transactions, documents] = await Promise.all([ injector.get(ChargesProvider).getChargeByIdLoader.load(transaction.charge_id), injector .get(TransactionsProvider) .transactionsByChargeIDLoader.load(transaction.charge_id), injector .get(DocumentsProvider) .getDocumentsByChargeIdLoader.load(transaction.charge_id), ]); if (!charge) { throw new GraphQLError(`Former transaction's charge ID ("${transaction.charge_id}") not valid`); } if (documents.length === 0 && transactions.length === 1) { chargesToDelete.add(charge.id); } })); if (chargesToDelete.size > 0) { const oldPostUpdateActions = postUpdateActions; postUpdateActions = async () => { await oldPostUpdateActions(); await deleteCharges(Array.from(chargesToDelete), injector).catch(e => { if (e instanceof GraphQLError) { throw e; } console.error(e); throw new GraphQLError(`Failed to delete empty former charges: ${Array.from(chargesToDelete).join(', ')}`); }); }; } // generate new charge const newCharge = await injector.get(ChargesProvider).generateCharge({ ownerId, userDescription: 'Transactions unlinked from charge', }); if (!newCharge) { throw new GraphQLError(`Failed to generate new charge for transactions update`); } chargeId = newCharge.id; } }; let chargeId = fields.chargeId; const chargePromise = chargeId && chargeId !== EMPTY_UUID ? existingChargePromise() : chargeId === EMPTY_UUID ? emptyChargePromise() : Promise.resolve(); await chargePromise; const adjustedFields = { transactionIds, businessId: fields.counterpartyId, chargeId: chargeId ?? null, debitDate: fields.effectiveDate ?? null, isFee: fields.isFee, }; const res = await injector.get(TransactionsProvider).updateTransactions(adjustedFields); if (!res[0]?.id) { throw new GraphQLError('Transactions update failed'); } await postUpdateActions(); return { transactions: res.map(transaction => transaction.id) }; } catch (e) { if (e instanceof GraphQLError) { throw e; } console.error(e); return { __typename: 'CommonError', message: e?.message ?? 'Unknown error', }; } }, }, UpdateTransactionResult: { __resolveType: async (raw, { injector }, _info) => { if (typeof raw === 'string') { const transaction = await injector .get(TransactionsProvider) .transactionByIdLoader.load(raw); const charge = await injector .get(ChargesProvider) .getChargeByIdLoader.load(transaction.charge_id); if (!charge) { throw new GraphQLError(`Charge with ID "${transaction.charge_id}" not found`); } return charge.type === 'CONVERSION' ? 'ConversionTransaction' : 'CommonTransaction'; } if (typeof raw === 'object' && '__typename' in raw && raw.__typename === 'CommonError') { return 'CommonError'; } throw new GraphQLError('Unexpected type for UpdateTransactionResult'); }, }, UpdateTransactionsResult: { __resolveType: (obj, _context, _info) => { if (('__typename' in obj && obj.__typename === 'CommonError') || 'message' in obj) return 'CommonError'; return 'UpdatedTransactionsSuccessfulResult'; }, }, BankDepositCharge: commonChargeFields, BusinessTripCharge: commonChargeFields, CommonCharge: commonChargeFields, ConversionCharge: commonChargeFields, CreditcardBankCharge: commonChargeFields, DividendCharge: commonChargeFields, FinancialCharge: commonChargeFields, ForeignSecuritiesCharge: commonChargeFields, InternalTransferCharge: commonChargeFields, MonthlyVatCharge: commonChargeFields, SalaryCharge: commonChargeFields, ConversionTransaction: { __isTypeOf: async (transactionId, { injector }) => { const transaction = await injector .get(TransactionsProvider) .transactionByIdLoader.load(transactionId); const charge = await injector .get(ChargesProvider) .getChargeByIdLoader.load(transaction.charge_id); if (!charge) { throw new GraphQLError(`Charge ID="${transaction.charge_id}" not found`); } return charge.type === 'CONVERSION'; }, ...commonTransactionFields, effectiveDate: async (transactionId, __dirname, { injector }) => { const transaction = await injector .get(TransactionsProvider) .transactionByIdLoader.load(transactionId); const date = effectiveDateSupplement(transaction); if (!date) { console.error(`Conversion transaction ID="${transactionId}" has no effective date`); throw new GraphQLError('Conversion transaction must have effective date'); } return date; }, type: async (transactionId, __dirname, { injector }) => { const transaction = await injector .get(TransactionsProvider) .transactionByIdLoader.load(transactionId); return Number(transaction.amount) > 0 ? 'QUOTE' : 'BASE'; }, bankRate: async (transactionId, __dirname, { injector }) => { const transaction = await injector .get(TransactionsProvider) .transactionByIdLoader.load(transactionId); return transaction.currency_rate; }, officialRateToLocal: async (transactionId, _, { injector }) => { const { defaultLocalCurrency } = await injector .get(AdminContextProvider) .getVerifiedAdminContext(); const transaction = await injector .get(TransactionsProvider) .transactionByIdLoader.load(transactionId); return injector .get(ExchangeProvider) .getExchangeRates(defaultLocalCurrency, formatCurrency(transaction.currency), transaction.event_date); }, }, CommonTransaction: { __isTypeOf: async (transactionId, { injector }) => { const transaction = await injector .get(TransactionsProvider) .transactionByIdLoader.load(transactionId); const charge = await injector .get(ChargesProvider) .getChargeByIdLoader.load(transaction.charge_id); if (!charge) { throw new GraphQLError(`Charge ID="${transaction.charge_id}" not found`); } if (charge instanceof GraphQLError) { console.error(charge); throw new GraphQLError(`Charge ID="${transaction.charge_id}" not found`); } return charge.type !== 'CONVERSION'; }, ...commonTransactionFields, }, }; //# sourceMappingURL=transactions.resolver.js.map