diff --git a/.env.example b/.env.example index abd0167b..56a62b67 100644 --- a/.env.example +++ b/.env.example @@ -57,4 +57,4 @@ NX_POSTMARK_API_TOKEN= ######################################################################## NX_PLAID_SECRET= NX_FINICITY_APP_KEY= -NX_FINICITY_PARTNER_SECRET= \ No newline at end of file +NX_FINICITY_PARTNER_SECRET= diff --git a/apps/workers/src/main.ts b/apps/workers/src/main.ts index 7db31bb0..a0cf5e82 100644 --- a/apps/workers/src/main.ts +++ b/apps/workers/src/main.ts @@ -140,7 +140,7 @@ syncInstitutionQueue.add( 'sync-teller-institutions', {}, { - repeat: { cron: '* */24 * * *' }, // Run every 24 hours + repeat: { cron: '0 0 */1 * *' }, // Run every 24 hours } ) diff --git a/libs/client/shared/src/api/useTellerApi.ts b/libs/client/shared/src/api/useTellerApi.ts index 2a323da7..a59287a1 100644 --- a/libs/client/shared/src/api/useTellerApi.ts +++ b/libs/client/shared/src/api/useTellerApi.ts @@ -6,6 +6,7 @@ import type { SharedType } from '@maybe-finance/shared' import { invalidateAccountQueries } from '../utils' import type { AxiosInstance } from 'axios' import type { TellerTypes } from '@maybe-finance/teller-api' +import { useAccountConnectionApi } from './useAccountConnectionApi' type TellerInstitution = { name: string @@ -30,6 +31,9 @@ export function useTellerApi() { const { axios } = useAxiosWithAuth() const api = useMemo(() => TellerApi(axios), [axios]) + const { useSyncConnection } = useAccountConnectionApi() + const syncConnection = useSyncConnection() + const addConnectionToState = (connection: SharedType.AccountConnection) => { const accountsData = queryClient.getQueryData(['accounts']) if (!accountsData) @@ -50,6 +54,7 @@ export function useTellerApi() { useMutation(api.handleEnrollment, { onSuccess: (_connection) => { addConnectionToState(_connection) + syncConnection.mutate(_connection.id) toast.success(`Account connection added!`) }, onSettled: () => { diff --git a/libs/client/shared/src/hooks/useTeller.ts b/libs/client/shared/src/hooks/useTeller.ts index b20c056b..6e09567f 100644 --- a/libs/client/shared/src/hooks/useTeller.ts +++ b/libs/client/shared/src/hooks/useTeller.ts @@ -58,7 +58,7 @@ export const useTellerConnect = (options: TellerConnectOptions, logger: Logger) { ...options, onSuccess: async (enrollment: TellerConnectEnrollment) => { - logger.debug(`User enrolled successfully`, enrollment) + logger.debug('User enrolled successfully') try { await handleEnrollment.mutateAsync({ institution: { diff --git a/libs/server/features/src/providers/teller/teller.etl.ts b/libs/server/features/src/providers/teller/teller.etl.ts index 241934bb..524659c9 100644 --- a/libs/server/features/src/providers/teller/teller.etl.ts +++ b/libs/server/features/src/providers/teller/teller.etl.ts @@ -1,6 +1,6 @@ import type { AccountConnection, PrismaClient } from '@prisma/client' import type { Logger } from 'winston' -import { SharedUtil, AccountUtil, type SharedType } from '@maybe-finance/shared' +import { SharedUtil, type SharedType } from '@maybe-finance/shared' import type { TellerApi, TellerTypes } from '@maybe-finance/teller-api' import { DbUtil, TellerUtil, type IETL, type ICryptoService } from '@maybe-finance/server/shared' import { Prisma } from '@prisma/client' @@ -117,8 +117,6 @@ export class TellerETL implements IETL { return [ // upsert accounts ...accounts.map((tellerAccount) => { - const type = TellerUtil.getType(tellerAccount.type) - const classification = AccountUtil.getClassification(type) return this.prisma.account.upsert({ where: { accountConnectionId_tellerAccountId: { @@ -132,12 +130,13 @@ export class TellerETL implements IETL { categoryProvider: TellerUtil.tellerTypesToCategory(tellerAccount.type), subcategoryProvider: tellerAccount.subtype ?? 'other', accountConnectionId: connection.id, + userId: connection.userId, tellerAccountId: tellerAccount.id, name: tellerAccount.name, tellerType: tellerAccount.type, tellerSubtype: tellerAccount.subtype, mask: tellerAccount.last_four, - ...TellerUtil.getAccountBalanceData(tellerAccount, classification), + ...TellerUtil.getAccountBalanceData(tellerAccount), }, update: { type: TellerUtil.getType(tellerAccount.type), @@ -145,7 +144,7 @@ export class TellerETL implements IETL { subcategoryProvider: tellerAccount.subtype ?? 'other', tellerType: tellerAccount.type, tellerSubtype: tellerAccount.subtype, - ..._.omit(TellerUtil.getAccountBalanceData(tellerAccount, classification), [ + ..._.omit(TellerUtil.getAccountBalanceData(tellerAccount), [ 'currentBalanceStrategy', 'availableBalanceStrategy', ]), @@ -226,13 +225,13 @@ export class TellerETL implements IETL { } AND teller_account_id = ${account_id.toString()}), ${id}, ${date}::date, - ${[description].filter(Boolean).join(' ')}, + ${description}, ${DbUtil.toDecimal(-amount)}, ${status === 'pending'}, ${'USD'}, ${details.counterparty.name ?? ''}, ${type}, - ${details.category ?? ''}, + ${details.category ?? ''} )` }) )} diff --git a/libs/server/features/src/providers/teller/teller.service.ts b/libs/server/features/src/providers/teller/teller.service.ts index 931e6721..7ce65280 100644 --- a/libs/server/features/src/providers/teller/teller.service.ts +++ b/libs/server/features/src/providers/teller/teller.service.ts @@ -45,6 +45,7 @@ export class TellerService implements IAccountConnectionProvider, IInstitutionPr where: { id: connection.id }, data: { status: 'OK', + syncStatus: 'IDLE', }, }) break @@ -157,21 +158,6 @@ export class TellerService implements IAccountConnectionProvider, IInstitutionPr throw new Error('USD_ONLY') } - // Create account connection on exchange; accounts + txns will sync later with webhook - const [accountConnection] = await this.prisma.$transaction([ - this.prisma.accountConnection.create({ - data: { - name: enrollment.enrollment.institution.name, - type: 'teller' as SharedType.AccountConnectionType, - tellerEnrollmentId: enrollment.enrollment.id, - tellerInstitutionId: institution.id, - tellerAccessToken: this.crypto.encrypt(enrollment.accessToken), - userId, - syncStatus: 'PENDING', - }, - }), - ]) - await this.prisma.user.update({ where: { id: userId }, data: { @@ -179,6 +165,28 @@ export class TellerService implements IAccountConnectionProvider, IInstitutionPr }, }) + const accountConnection = await this.prisma.accountConnection.create({ + data: { + name: enrollment.enrollment.institution.name, + type: 'teller' as SharedType.AccountConnectionType, + tellerEnrollmentId: enrollment.enrollment.id, + tellerInstitutionId: institution.id, + tellerAccessToken: this.crypto.encrypt(enrollment.accessToken), + userId, + syncStatus: 'PENDING', + }, + }) + + await this.sync(accountConnection, { type: 'teller', initialSync: true }) + + await this.prisma.accountConnection.update({ + where: { id: accountConnection.id }, + data: { + status: 'OK', + syncStatus: 'IDLE', + }, + }) + return accountConnection } } diff --git a/libs/server/shared/src/utils/teller-utils.ts b/libs/server/shared/src/utils/teller-utils.ts index a8e8e3ee..0b741583 100644 --- a/libs/server/shared/src/utils/teller-utils.ts +++ b/libs/server/shared/src/utils/teller-utils.ts @@ -1,10 +1,4 @@ -import { - Prisma, - AccountCategory, - AccountType, - type AccountClassification, - type Account, -} from '@prisma/client' +import { Prisma, AccountCategory, AccountType, type Account } from '@prisma/client' import type { TellerTypes } from '@maybe-finance/teller-api' import { Duration } from 'luxon' @@ -13,10 +7,10 @@ import { Duration } from 'luxon' */ export const TELLER_WINDOW_MAX = Duration.fromObject({ years: 1 }) -export function getAccountBalanceData( - { balances, currency }: Pick, - classification: AccountClassification -): Pick< +export function getAccountBalanceData({ + balance, + currency, +}: Pick): Pick< Account, | 'currentBalanceProvider' | 'currentBalanceStrategy' @@ -24,16 +18,11 @@ export function getAccountBalanceData( | 'availableBalanceStrategy' | 'currencyCode' > { - // Flip balance values to positive for liabilities - const sign = classification === 'liability' ? -1 : 1 - return { - currentBalanceProvider: new Prisma.Decimal( - balances.ledger ? sign * Number(balances.ledger) : 0 - ), + currentBalanceProvider: new Prisma.Decimal(balance.ledger ? Number(balance.ledger) : 0), currentBalanceStrategy: 'current', availableBalanceProvider: new Prisma.Decimal( - balances.available ? sign * Number(balances.available) : 0 + balance.available ? Number(balance.available) : 0 ), availableBalanceStrategy: 'available', currencyCode: currency, diff --git a/libs/teller-api/src/types/accounts.ts b/libs/teller-api/src/types/accounts.ts index 5df29953..d1ccbcc1 100644 --- a/libs/teller-api/src/types/accounts.ts +++ b/libs/teller-api/src/types/accounts.ts @@ -50,7 +50,7 @@ interface CreditAccount extends BaseAccount { export type Account = DepositoryAccount | CreditAccount export type AccountWithBalances = Account & { - balances: AccountBalance + balance: AccountBalance } export type GetAccountsResponse = Account[]