Browse Source

Task/refactor dividend import (#6150)

* Refactor dividend import

* Update changelog
pull/6158/head^2
Thomas Kaul 1 week ago
committed by GitHub
parent
commit
5101c406b4
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 1
      apps/api/src/app/import/import.controller.ts
  3. 2
      apps/api/src/app/import/import.module.ts
  4. 73
      apps/api/src/app/import/import.service.ts

1
CHANGELOG.md

@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Deprecated `activities` in the endpoint `GET api/v1/portfolio/holding/:dataSource/:symbol` - Deprecated `activities` in the endpoint `GET api/v1/portfolio/holding/:dataSource/:symbol`
- Moved the data service to `@ghostfolio/ui/services` - Moved the data service to `@ghostfolio/ui/services`
- Refactored the dividend import
## 2.228.0 - 2026-01-03 ## 2.228.0 - 2026-01-03

1
apps/api/src/app/import/import.controller.ts

@ -103,6 +103,7 @@ export class ImportController {
const activities = await this.importService.getDividends({ const activities = await this.importService.getDividends({
dataSource, dataSource,
symbol, symbol,
userCurrency: this.request.user.settings.settings.baseCurrency,
userId: this.request.user.id userId: this.request.user.id
}); });

2
apps/api/src/app/import/import.module.ts

@ -6,6 +6,7 @@ import { PortfolioModule } from '@ghostfolio/api/app/portfolio/portfolio.module'
import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module';
import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module';
import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module'; import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module';
import { ApiModule } from '@ghostfolio/api/services/api/api.module';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module';
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module';
@ -24,6 +25,7 @@ import { ImportService } from './import.service';
controllers: [ImportController], controllers: [ImportController],
imports: [ imports: [
AccountModule, AccountModule,
ApiModule,
CacheModule, CacheModule,
ConfigurationModule, ConfigurationModule,
DataGatheringModule, DataGatheringModule,

73
apps/api/src/app/import/import.service.ts

@ -2,6 +2,7 @@ import { AccountService } from '@ghostfolio/api/app/account/account.service';
import { OrderService } from '@ghostfolio/api/app/order/order.service'; import { OrderService } from '@ghostfolio/api/app/order/order.service';
import { PlatformService } from '@ghostfolio/api/app/platform/platform.service'; import { PlatformService } from '@ghostfolio/api/app/platform/platform.service';
import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service';
import { ApiService } from '@ghostfolio/api/services/api/api.service';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
@ -25,7 +26,7 @@ import {
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { import {
AccountWithPlatform, AccountWithValue,
OrderWithAccount, OrderWithAccount,
UserWithSettings UserWithSettings
} from '@ghostfolio/common/types'; } from '@ghostfolio/common/types';
@ -43,6 +44,7 @@ import { ImportDataDto } from './import-data.dto';
export class ImportService { export class ImportService {
public constructor( public constructor(
private readonly accountService: AccountService, private readonly accountService: AccountService,
private readonly apiService: ApiService,
private readonly configurationService: ConfigurationService, private readonly configurationService: ConfigurationService,
private readonly dataGatheringService: DataGatheringService, private readonly dataGatheringService: DataGatheringService,
private readonly dataProviderService: DataProviderService, private readonly dataProviderService: DataProviderService,
@ -57,8 +59,12 @@ export class ImportService {
public async getDividends({ public async getDividends({
dataSource, dataSource,
symbol, symbol,
userCurrency,
userId userId
}: AssetProfileIdentifier & { userId: string }): Promise<Activity[]> { }: AssetProfileIdentifier & {
userCurrency: string;
userId: string;
}): Promise<Activity[]> {
try { try {
const holding = await this.portfolioService.getHolding({ const holding = await this.portfolioService.getHolding({
dataSource, dataSource,
@ -71,36 +77,45 @@ export class ImportService {
return []; return [];
} }
const { activities, firstBuyDate, historicalData } = holding; const filters = this.apiService.buildFiltersFromQueryParams({
filterByDataSource: dataSource,
filterBySymbol: symbol
});
const [[assetProfile], dividends] = await Promise.all([ const { firstBuyDate, historicalData } = holding;
this.symbolProfileService.getSymbolProfiles([
{
dataSource,
symbol
}
]),
await this.dataProviderService.getDividends({
dataSource,
symbol,
from: parseDate(firstBuyDate),
granularity: 'day',
to: new Date()
})
]);
const accounts = activities const [{ accounts }, { activities }, [assetProfile], dividends] =
.filter(({ account }) => { await Promise.all([
return !!account; this.portfolioService.getAccountsWithAggregations({
}) filters,
.map(({ account }) => { userId,
return account; withExcludedAccounts: true
}); }),
this.orderService.getOrders({
filters,
userCurrency,
userId,
startDate: parseDate(firstBuyDate)
}),
this.symbolProfileService.getSymbolProfiles([
{
dataSource,
symbol
}
]),
await this.dataProviderService.getDividends({
dataSource,
symbol,
from: parseDate(firstBuyDate),
granularity: 'day',
to: new Date()
})
]);
const account = this.isUniqueAccount(accounts) ? accounts[0] : undefined; const account = this.isUniqueAccount(accounts) ? accounts[0] : undefined;
return await Promise.all( return await Promise.all(
Object.entries(dividends).map(async ([dateString, { marketPrice }]) => { Object.entries(dividends).map(([dateString, { marketPrice }]) => {
const quantity = const quantity =
historicalData.find((historicalDataItem) => { historicalData.find((historicalDataItem) => {
return historicalDataItem.date === dateString; return historicalDataItem.date === dateString;
@ -695,11 +710,11 @@ export class ImportService {
); );
} }
private isUniqueAccount(accounts: AccountWithPlatform[]) { private isUniqueAccount(accounts: AccountWithValue[]) {
const uniqueAccountIds = new Set<string>(); const uniqueAccountIds = new Set<string>();
for (const account of accounts) { for (const { id } of accounts) {
uniqueAccountIds.add(account.id); uniqueAccountIds.add(id);
} }
return uniqueAccountIds.size === 1; return uniqueAccountIds.size === 1;

Loading…
Cancel
Save