import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { PrismaService } from '../../prisma/prisma.service'; import type { BrokerOperation } from '../types/broker.types'; import { BrokerOperationsService } from './broker-operations.service'; type BrokerOperationSyncRange = { from: string; to: string; }; @Injectable() export class BrokerOperationSyncService { constructor( private readonly operationsService: BrokerOperationsService, private readonly prisma: PrismaService, ) {} async syncAccount( accountId: string, range: BrokerOperationSyncRange, ): Promise<{ upserted: number }> { let cursor: string | undefined; let lastCursor: string | null = null; let upserted = 0; do { const page = await this.operationsService.getOperations(accountId, { from: range.from, to: range.to, cursor, limit: 1000, state: 'OPERATION_STATE_EXECUTED', }); for (const operation of page.data.items) { await this.upsertOperation(accountId, operation); upserted++; } const nextCursor = page.data.nextCursor ?? undefined; if (page.data.hasNext && !nextCursor) { throw new InternalServerErrorException('T-Bank returned hasNext without nextCursor'); } if (nextCursor) { lastCursor = nextCursor; } cursor = nextCursor; if (!page.data.hasNext) { break; } } while (cursor); await this.prisma.brokerOperationSyncState.upsert({ where: { accountId }, create: { accountId, lastCursor, lastSyncedFrom: new Date(range.from), lastSyncedTo: new Date(range.to), }, update: { lastCursor, lastSyncedFrom: new Date(range.from), lastSyncedTo: new Date(range.to), syncedAt: new Date(), }, }); return { upserted }; } private async upsertOperation(accountId: string, operation: BrokerOperation): Promise { const cursor = operation.cursor || `${operation.id || 'operation'}:${operation.date || 'no-date'}`; const data = { accountId, cursor, operationId: operation.id, parentOperationId: operation.parentOperationId, date: operation.date ? new Date(operation.date) : null, type: operation.type, category: operation.category, state: operation.state, instrumentUid: operation.instrumentUid, figi: operation.figi, ticker: operation.ticker, classCode: operation.classCode, payment: operation.payment ? JSON.stringify(operation.payment) : null, price: operation.price ? JSON.stringify(operation.price) : null, commission: operation.commission ? JSON.stringify(operation.commission) : null, yield: operation.yield ? JSON.stringify(operation.yield) : null, accruedInt: operation.accruedInt ? JSON.stringify(operation.accruedInt) : null, quantity: operation.quantity, quantityDone: operation.quantityDone, raw: JSON.stringify(operation), }; await this.prisma.brokerOperation.upsert({ where: { accountId_cursor: { accountId, cursor } }, create: data, update: data, }); } }