feat(portfolio): store and publish instrument sector and issuer (WealthLedger/main#48) (#148)
Backend verify / commits (push) Skipped
Backend verify / security (push) Successful in 1m4s
Backend verify / verify (push) Failing after 7m15s

This commit was merged in pull request #148.
This commit is contained in:
2026-11-01 19:13:32 +03:00
parent 5e84e1e5f1
commit 17432dfee3
16 changed files with 529 additions and 11 deletions
@@ -30,6 +30,7 @@ import {
} from '../infrastructure/portfolio.repository';
import { mapPortfolio } from '../infrastructure/tbank-portfolio.mapper';
import { PortfolioDto } from '../presentation/portfolio.dto';
import { type Classification, PositionClassificationService } from './position-classification.service';
const MISSING_DATA_REVISION = 0;
const NO_ACCOUNTS = 0;
@@ -46,6 +47,7 @@ export class PortfolioService {
private readonly access: TbankAccessService,
private readonly tbank: TbankClient,
private readonly metadata: InstrumentMetadataService,
private readonly classification: PositionClassificationService,
private readonly repository: PortfolioRepository,
private readonly membership: TbankMembershipService,
private readonly logger: StructuredLogger,
@@ -88,6 +90,11 @@ export class PortfolioService {
snapshot: account.portfolio
? {
...(account.portfolio.payload as unknown as PortfolioDto),
positions: (account.portfolio.payload as unknown as PortfolioDto).positions.map((position) => ({
...position,
sector: position.sector ?? null,
issuer: position.issuer ?? null,
})),
lastSuccessfulAt: account.portfolio.fetchedAt.toISOString(),
isStale:
Date.now() - account.portfolio.fetchedAt.valueOf() >=
@@ -231,8 +238,8 @@ export class PortfolioService {
};
}
request(userId: string, revision: string, accountId: string, force = false) {
return this.queueSnapshot(userId, revision, accountId, force);
request(userId: string, revision: string, accountId: string, force = false, walkStartedAt?: Date) {
return this.queueSnapshot(userId, revision, accountId, force, walkStartedAt);
}
private queueSnapshot(
@@ -240,6 +247,7 @@ export class PortfolioService {
revision: string,
accountId: string,
force = false,
walkStartedAt = new Date(),
) {
const reserved = this.reservations.then(async () => {
const requested = await this.repository.reserve(
@@ -268,9 +276,11 @@ export class PortfolioService {
this.tbank.getPortfolio(token, accountId),
this.tbank.getPositions(token, accountId),
]);
const classifications = await this.classification.refresh(token, portfolio.positions ?? [], walkStartedAt);
const snapshot = await this.normalize(
portfolio.positions ?? [],
positions.money ?? [],
classifications,
);
const published = await this.repository.publish(
attempt,
@@ -309,9 +319,10 @@ export class PortfolioService {
private async normalize(
upstream: NonNullable<TbankPortfolio['positions']>,
money: TbankMoney[],
classifications: ReadonlyMap<string, Classification>,
): Promise<PortfolioDto> {
const lastSuccessfulAt = new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
const savedMetadata = await this.metadata.savedMany(upstream);
return mapPortfolio(upstream, money, savedMetadata, lastSuccessfulAt);
return mapPortfolio(upstream, money, savedMetadata, lastSuccessfulAt, classifications);
}
}