chore: strengthen backend eslint profile

This commit is contained in:
2026-09-28 16:37:01 +03:00
committed by ksv741
parent 53b2794963
commit a970eaaf7a
179 changed files with 4485 additions and 2716 deletions
@@ -1,8 +1,10 @@
import { CatalogDiscoveryService } from '../../application/catalog-discovery.service';
import { Injectable } from '@nestjs/common';
import { formatDecimal } from '../../../../../common/decimal';
import type { TbankBond } from '../../../../../integrations/tbank/tbank.api';
import { formatDecimal } from '../../../../../common/decimal';
import { decimal } from '../../../../../integrations/tbank/decimal';
import { CatalogDiscoveryService } from '../../application/catalog-discovery.service';
import { timestamp } from '../../application/instrument-market';
import {
BondDto,
@@ -32,23 +34,24 @@ export class BondCatalogService {
data:
result.data?.map((item) => {
const short = toBond(item);
const detail = item.detail
? toBond({ ...item.detail, uid: item.uid })
: short;
const detail = item.detail ?
toBond({ ...item.detail, uid: item.uid }) :
short;
const last = item.price;
return {
...detail,
classCode: detail.classCode ?? short.classCode,
isin: detail.isin ?? short.isin,
apiTradeAvailable: short.apiTradeAvailable,
pricePercent: last?.price
? formatDecimal(decimal(last.price))
: null,
pricePercent: last?.price ?
formatDecimal(decimal(last.price)) :
null,
priceAt: timestamp(last?.time),
};
}) ?? null,
};
}
async get(
userId: string,
uid: string,
@@ -57,11 +60,12 @@ export class BondCatalogService {
const result = await this.discovery.detail(userId, 'bond', uid, observe);
return {
...result,
data: result.data
? { ...toBond(result.data), slug: result.data.slug }
: null,
data: result.data ?
{ ...toBond(result.data), slug: result.data.slug } :
null,
};
}
async getByTicker(
userId: string,
ticker: string,
@@ -75,9 +79,9 @@ export class BondCatalogService {
);
return {
...result,
data: result.data
? { ...toBond(result.data), slug: result.data.slug }
: null,
data: result.data ?
{ ...toBond(result.data), slug: result.data.slug } :
null,
};
}
}
@@ -1,18 +1,19 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import {
decimalFromString,
divide,
formatDecimal,
multiply,
divide,
} from '../../../../../common/decimal';
import {
TbankAccessService,
type TbankCoupon,
type TbankBond,
type TbankCoupon,
} from '../../../../../integrations/tbank/tbank.api';
import {
PortfolioService,
ForecastService,
PortfolioService,
} from '../../../portfolio/portfolio.api';
import {
CatalogEnrichmentService,
@@ -20,14 +21,14 @@ import {
} from '../../application/catalog-enrichment.service';
import { moscowDay, timestamp } from '../../application/instrument-market';
import { instrumentPosition } from '../../application/instrument-position';
import { money } from '../infrastructure/bond-money.mapper';
import {
BondCouponsDto,
BondCouponsResourceDto,
BondPositionDto,
BondMetricsDto,
BondMetricsResourceDto,
BondPositionDto,
} from '../presentation/bond.dto';
import { money } from '../infrastructure/bond-money.mapper';
import { bondQuote } from './bond-market.service';
const ISO_YEAR_LENGTH = 4;
const INCOME_HISTORY_YEARS = 5;
@@ -40,6 +41,7 @@ export class BondIncomeService {
private readonly access: TbankAccessService,
private readonly forecasts: ForecastService,
) {}
async metrics(
userId: string,
uid: string,
@@ -53,9 +55,9 @@ export class BondIncomeService {
observe,
);
return {
data: result.values.instrument
? bondMetrics(result.values, result.unavailable, result.observedAt)
: null,
data: result.values.instrument ?
bondMetrics(result.values, result.unavailable, result.observedAt) :
null,
meta: result.meta,
};
}
@@ -67,6 +69,7 @@ export class BondIncomeService {
meta: { ...saved.meta, resourceKey: `bond/position/${uid}` },
};
}
async coupons(
userId: string,
uid: string,
@@ -85,21 +88,22 @@ export class BondIncomeService {
['detail', 'coupons'],
{ from, to },
);
if (sources[0].meta.availability === 'complete' && !sources[0].instrument)
if (sources[0].meta.availability === 'complete' && !sources[0].instrument) {
throw new NotFoundException();
}
const events = sources[1].payload?.coupons;
const position = portfolio.data
? instrumentPosition(portfolio.data, uid, 'BOND')
: null;
const projected = events
? bondCoupons(
const position = portfolio.data ?
instrumentPosition(portfolio.data, uid, 'BOND') :
null;
const projected = events ?
bondCoupons(
events,
position,
{ from, to, today },
sources[1].meta.lastSuccessfulAt ?? new Date().toISOString(),
!portfolio.data,
)
: undefined;
) :
undefined;
const result = await this.forecasts.result({
userId,
revision: connection.revision,
@@ -110,9 +114,9 @@ export class BondIncomeService {
candidate:
portfolio.data && sources[0].instrument ? projected : undefined,
});
return result.data || !projected
? result
: {
return result.data || !projected ?
result :
{
data: bondCoupons(
events ?? [],
null,
@@ -135,19 +139,19 @@ export function bondCoupons(
const { from, to, today } = window;
const quantity =
position?.accounts.length &&
position.accounts.every((p) => decimalFromString(p.quantity).value > 0n)
? decimalFromString(position.quantity)
: null;
position.accounts.every((p) => decimalFromString(p.quantity).value > 0n) ?
decimalFromString(position.quantity) :
null;
const coupons = events
.map((event) => {
const paymentDate = timestamp(event.couponDate)?.slice(0, 10) ?? null;
const recordDate = timestamp(event.fixDate)?.slice(0, 10) ?? null;
const perBond = money(event.payOneBond);
const timing = !paymentDate
? ('undated' as const)
: paymentDate >= today
? ('upcoming' as const)
: ('past' as const);
const timing = !paymentDate ?
('undated' as const) :
paymentDate >= today ?
('upcoming' as const) :
('past' as const);
return {
number: event.couponNumber ?? null,
paymentDate,
@@ -161,14 +165,14 @@ export function bondCoupons(
perBond &&
timing === 'upcoming' &&
recordDate &&
recordDate >= today
? {
recordDate >= today ?
{
currency: perBond.currency,
value: formatDecimal(
multiply(quantity, decimalFromString(perBond.value)),
),
}
: null,
} :
null,
};
})
.sort((a, b) => (a.paymentDate ?? '').localeCompare(b.paymentDate ?? ''));
@@ -214,8 +218,8 @@ export function bondMetrics(
clean &&
coupon.currency === clean.currency &&
decimalFromString(coupon.value).value >= 0n &&
decimalFromString(clean.value).value > 0n
? formatDecimal(
decimalFromString(clean.value).value > 0n ?
formatDecimal(
divide(
multiply(
decimalFromString(coupon.value),
@@ -227,14 +231,14 @@ export function bondMetrics(
),
COUPON_YIELD_DECIMAL_PLACES,
),
)
: null;
) :
null;
const couponType =
next?.couponType && next.couponType !== 'COUPON_TYPE_UNSPECIFIED'
? next.couponType
: instrument.floatingCouponFlag === true
? 'COUPON_TYPE_FLOATING'
: null;
next?.couponType && next.couponType !== 'COUPON_TYPE_UNSPECIFIED' ?
next.couponType :
instrument.floatingCouponFlag === true ?
'COUPON_TYPE_FLOATING' :
null;
return {
creditRating,
coupon,
@@ -1,10 +1,7 @@
import { CandleService } from '../../application/candle.service';
import type { TbankBond } from '../../../../../integrations/tbank/tbank.api';
import {
CatalogEnrichmentService,
type CatalogPayload,
} from '../../application/catalog-enrichment.service';
import { Injectable } from '@nestjs/common';
import type { TbankBond } from '../../../../../integrations/tbank/tbank.api';
import {
add,
decimalFromString,
@@ -14,15 +11,19 @@ import {
subtract,
} from '../../../../../common/decimal';
import { decimal } from '../../../../../integrations/tbank/decimal';
import { CandleService } from '../../application/candle.service';
import {
CatalogEnrichmentService,
type CatalogPayload,
} from '../../application/catalog-enrichment.service';
import { timestamp } from '../../application/instrument-market';
import { money } from '../infrastructure/bond-money.mapper';
import {
BondCandlesQueryDto,
BondQuoteDto,
BondQuoteResourceDto,
} from '../presentation/bond.dto';
import { money } from '../infrastructure/bond-money.mapper';
const PRICE_DECIMAL_PLACES = 9;
@Injectable()
export class BondMarketService {
@@ -30,6 +31,7 @@ export class BondMarketService {
private readonly history: CandleService,
private readonly sources: CatalogEnrichmentService,
) {}
async quote(
userId: string,
uid: string,
@@ -43,12 +45,13 @@ export class BondMarketService {
observe,
);
return {
data: result.values.instrument
? bondQuote(result.values, result.unavailable, result.observedAt)
: null,
data: result.values.instrument ?
bondQuote(result.values, result.unavailable, result.observedAt) :
null,
meta: result.meta,
};
}
candles(
userId: string,
uid: string,
@@ -77,8 +80,8 @@ export function bondQuote(
const cleanPrice =
pricePercent !== null &&
nominal &&
decimalFromString(nominal.value).value > 0n
? {
decimalFromString(nominal.value).value > 0n ?
{
currency: nominal.currency,
value: formatDecimal(
divide(
@@ -90,11 +93,11 @@ export function bondQuote(
PRICE_DECIMAL_PLACES,
),
),
}
: null;
} :
null;
const dirtyPrice =
cleanPrice && aci && aci.currency === cleanPrice.currency
? {
cleanPrice && aci && aci.currency === cleanPrice.currency ?
{
currency: cleanPrice.currency,
value: formatDecimal(
add(
@@ -102,8 +105,8 @@ export function bondQuote(
decimalFromString(aci.value),
),
),
}
: null;
} :
null;
return {
pricePercent,
cleanPrice,
@@ -112,9 +115,9 @@ export function bondQuote(
priceAt,
tradingStatus,
changePercentagePoints:
last?.price && previous?.close
? formatDecimal(subtract(decimal(last.price), decimal(previous.close)))
: null,
last?.price && previous?.close ?
formatDecimal(subtract(decimal(last.price), decimal(previous.close))) :
null,
previousCloseDate: timestamp(previous?.time)?.slice(0, 10) ?? null,
unavailable,
observedAt,
@@ -1,7 +1,8 @@
import type { PortfolioDto } from '../../../portfolio/portfolio.api';
import { instrumentPosition } from '../../application/instrument-position';
import { bondCoupons, bondMetrics } from './bond-income.service';
import { bondQuote } from './bond-market.service';
import { instrumentPosition } from '../../application/instrument-position';
import type { PortfolioDto } from '../../../portfolio/portfolio.api';
jest.mock('../../../../../prisma/prisma.service', () => ({
PrismaService: class {},
}));
@@ -122,9 +123,9 @@ describe('bond financial projections', () => {
{
couponDate: time('2026-10-10'),
payOneBond:
scenario === 'unknown'
? undefined
: money('30', scenario === 'currency' ? 'usd' : 'rub'),
scenario === 'unknown' ?
undefined :
money('30', scenario === 'currency' ? 'usd' : 'rub'),
couponPeriod: scenario === 'period' ? 0 : 90,
},
],