diff --git a/CHANGELOG.md b/CHANGELOG.md index a5a8297e1..97dcf4593 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added the transactions to the position detail dialog +- Added support for dividend + +### Todo + +- Apply data migration (`yarn database:migrate`) ## 1.96.0 - 27.12.2021 diff --git a/apps/api/src/app/order/order.service.ts b/apps/api/src/app/order/order.service.ts index eb58b889e..1654380ae 100644 --- a/apps/api/src/app/order/order.service.ts +++ b/apps/api/src/app/order/order.service.ts @@ -3,7 +3,7 @@ import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.se import { PrismaService } from '@ghostfolio/api/services/prisma.service'; import { OrderWithAccount } from '@ghostfolio/common/types'; import { Injectable } from '@nestjs/common'; -import { DataSource, Order, Prisma } from '@prisma/client'; +import { DataSource, Order, Prisma, Type as TypeOfOrder } from '@prisma/client'; import Big from 'big.js'; import { endOfToday, isAfter } from 'date-fns'; @@ -85,9 +85,11 @@ export class OrderService { public async getOrders({ includeDrafts = false, + types, userId }: { includeDrafts?: boolean; + types?: TypeOfOrder[]; userId: string; }) { const where: Prisma.OrderWhereInput = { userId }; @@ -96,6 +98,16 @@ export class OrderService { where.isDraft = false; } + if (types) { + where.OR = types.map((type) => { + return { + type: { + equals: type + } + }; + }); + } + return ( await this.orders({ where, diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index a0e93ea0c..ce178ee1c 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -330,6 +330,7 @@ export class PortfolioController { 'currentGrossPerformance', 'currentNetPerformance', 'currentValue', + 'dividend', 'fees', 'netWorth', 'totalBuy', diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 4bcf9565c..e9be3d0a2 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -401,17 +401,21 @@ export class PortfolioService { const positionCurrency = orders[0].currency; const name = orders[0].SymbolProfile?.name ?? ''; - const portfolioOrders: PortfolioOrder[] = orders.map((order) => ({ - currency: order.currency, - dataSource: order.dataSource, - date: format(order.date, DATE_FORMAT), - fee: new Big(order.fee), - name: order.SymbolProfile?.name, - quantity: new Big(order.quantity), - symbol: order.symbol, - type: order.type, - unitPrice: new Big(order.unitPrice) - })); + const portfolioOrders: PortfolioOrder[] = orders + .filter((order) => { + return order.type === 'BUY' || order.type === 'SELL'; + }) + .map((order) => ({ + currency: order.currency, + dataSource: order.dataSource, + date: format(order.date, DATE_FORMAT), + fee: new Big(order.fee), + name: order.SymbolProfile?.name, + quantity: new Big(order.quantity), + symbol: order.symbol, + type: order.type, + unitPrice: new Big(order.unitPrice) + })); const portfolioCalculator = new PortfolioCalculator( this.currentRateService, @@ -729,22 +733,6 @@ export class PortfolioService { }; } - public getFees(orders: OrderWithAccount[], date = new Date(0)) { - return orders - .filter((order) => { - // Filter out all orders before given date - return isBefore(date, new Date(order.date)); - }) - .map((order) => { - return this.exchangeRateDataService.toCurrency( - order.fee, - order.currency, - this.request.user.Settings.currency - ); - }) - .reduce((previous, current) => previous + current, 0); - } - public async getReport(impersonationId: string): Promise { const currency = this.request.user.Settings.currency; const userId = await this.getUserId(impersonationId, this.request.user.id); @@ -825,7 +813,7 @@ export class PortfolioService { new FeeRatioInitialInvestment( this.exchangeRateDataService, currentPositions.totalInvestment.toNumber(), - this.getFees(orders) + this.getFees(orders).toNumber() ) ], { baseCurrency: currency } @@ -844,8 +832,11 @@ export class PortfolioService { userId, currency ); - const orders = await this.orderService.getOrders({ userId }); - const fees = this.getFees(orders); + const orders = await this.orderService.getOrders({ + userId + }); + const dividend = this.getDividend(orders).toNumber(); + const fees = this.getFees(orders).toNumber(); const firstOrderDate = orders[0]?.date; const totalBuy = this.getTotalByType(orders, currency, 'BUY'); @@ -859,14 +850,17 @@ export class PortfolioService { return { ...performanceInformation.performance, + dividend, fees, firstOrderDate, netWorth, + totalBuy, + totalSell, cash: balance, committedFunds: committedFunds.toNumber(), - ordersCount: orders.length, - totalBuy: totalBuy, - totalSell: totalSell + ordersCount: orders.filter((order) => { + return order.type === 'BUY' || order.type === 'SELL'; + }).length }; } @@ -939,6 +933,47 @@ export class PortfolioService { return cashPositions; } + private getDividend(orders: OrderWithAccount[], date = new Date(0)) { + return orders + .filter((order) => { + // Filter out all orders before given date and type dividend + return ( + isBefore(date, new Date(order.date)) && + order.type === TypeOfOrder.DIVIDEND + ); + }) + .map((order) => { + return this.exchangeRateDataService.toCurrency( + new Big(order.quantity).mul(order.unitPrice).toNumber(), + order.currency, + this.request.user.Settings.currency + ); + }) + .reduce( + (previous, current) => new Big(previous).plus(current), + new Big(0) + ); + } + + private getFees(orders: OrderWithAccount[], date = new Date(0)) { + return orders + .filter((order) => { + // Filter out all orders before given date + return isBefore(date, new Date(order.date)); + }) + .map((order) => { + return this.exchangeRateDataService.toCurrency( + order.fee, + order.currency, + this.request.user.Settings.currency + ); + }) + .reduce( + (previous, current) => new Big(previous).plus(current), + new Big(0) + ); + } + private getStartDate(aDateRange: DateRange, portfolioStart: Date) { switch (aDateRange) { case '1d': @@ -967,7 +1002,11 @@ export class PortfolioService { transactionPoints: TransactionPoint[]; orders: OrderWithAccount[]; }> { - const orders = await this.orderService.getOrders({ includeDrafts, userId }); + const orders = await this.orderService.getOrders({ + includeDrafts, + userId, + types: ['BUY', 'SELL'] + }); if (orders.length <= 0) { return { transactionPoints: [], orders: [] }; diff --git a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html index 893718044..c9c34cd03 100644 --- a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html +++ b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html @@ -169,4 +169,18 @@ > +
+

+
+
+
Dividend
+
+ +
+
diff --git a/apps/client/src/app/components/transactions-table/transactions-table.component.html b/apps/client/src/app/components/transactions-table/transactions-table.component.html index 6f2626167..4564e6448 100644 --- a/apps/client/src/app/components/transactions-table/transactions-table.component.html +++ b/apps/client/src/app/components/transactions-table/transactions-table.component.html @@ -77,11 +77,15 @@
Type - BUY - SELL + BUY + DIVIDEND + SELL
@@ -141,7 +142,7 @@ [(ngModel)]="data.transaction.unitPrice" />