feat: enrich portfolio list with total value and position breakdown from MOEX #10
@ -60,10 +60,55 @@ export class PortfolioService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async findAll(userId: number) {
|
async findAll(userId: number) {
|
||||||
return this.prisma.portfolio.findMany({
|
const portfolios = await this.prisma.portfolio.findMany({
|
||||||
where: { userId },
|
where: { userId },
|
||||||
|
include: { positions: true },
|
||||||
orderBy: { updatedAt: 'desc' },
|
orderBy: { updatedAt: 'desc' },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const allPositions = portfolios.flatMap((p) => p.positions);
|
||||||
|
if (allPositions.length === 0) {
|
||||||
|
return portfolios.map((p) => ({
|
||||||
|
id: p.id,
|
||||||
|
name: p.name,
|
||||||
|
description: p.description,
|
||||||
|
currency: p.currency,
|
||||||
|
createdAt: p.createdAt.toISOString(),
|
||||||
|
updatedAt: p.updatedAt.toISOString(),
|
||||||
|
totalValue: 0,
|
||||||
|
positionCount: 0,
|
||||||
|
shareCount: 0,
|
||||||
|
bondCount: 0,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
const enrichedPositions = await this.enrichPositions(allPositions);
|
||||||
|
|
||||||
|
const posByPortfolioId = new Map<number, (typeof enrichedPositions)[number][]>();
|
||||||
|
for (let i = 0; i < enrichedPositions.length; i++) {
|
||||||
|
const pfId = allPositions[i].portfolioId;
|
||||||
|
if (!posByPortfolioId.has(pfId)) {
|
||||||
|
posByPortfolioId.set(pfId, []);
|
||||||
|
}
|
||||||
|
posByPortfolioId.get(pfId)!.push(enrichedPositions[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return portfolios.map((p) => {
|
||||||
|
const positions = posByPortfolioId.get(p.id) ?? [];
|
||||||
|
const totalValue = positions.reduce((sum, pos) => sum + (pos.currentValue ?? 0), 0);
|
||||||
|
return {
|
||||||
|
id: p.id,
|
||||||
|
name: p.name,
|
||||||
|
description: p.description,
|
||||||
|
currency: p.currency,
|
||||||
|
createdAt: p.createdAt.toISOString(),
|
||||||
|
updatedAt: p.updatedAt.toISOString(),
|
||||||
|
totalValue: Math.round(totalValue * 100) / 100,
|
||||||
|
positionCount: positions.length,
|
||||||
|
shareCount: positions.filter((pos) => pos.type === 'share').length,
|
||||||
|
bondCount: positions.filter((pos) => pos.type === 'bond').length,
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOne(userId: number, id: number) {
|
async findOne(userId: number, id: number) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user