From df5b8f91e94e688df42010c558c7a61f91a80613 Mon Sep 17 00:00:00 2001 From: Thomas <4159106+dtslvr@users.noreply.github.com> Date: Wed, 2 Feb 2022 21:55:11 +0100 Subject: [PATCH] Add support for wealth items --- apps/api/src/app/import/import.service.ts | 35 +++++++++------ .../src/services/data-gathering.service.ts | 11 +++++ .../data-provider/data-provider.module.ts | 5 +++ .../data-provider/data-provider.service.ts | 1 + .../data-provider/manual/manual.service.ts | 43 +++++++++++++++++++ .../create-or-update-transaction-dialog.html | 1 + .../services/import-transactions.service.ts | 2 + .../activities-table.component.html | 24 +++++++---- .../activities-table.component.scss | 4 ++ .../activities-table.component.ts | 2 +- prisma/schema.prisma | 2 + test/import/ok.csv | 1 + 12 files changed, 108 insertions(+), 23 deletions(-) create mode 100644 apps/api/src/services/data-provider/manual/manual.service.ts diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index 92ffcca2c..c365c22f5 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -21,8 +21,13 @@ export class ImportService { userId: string; }): Promise { for (const order of orders) { - order.dataSource = - order.dataSource ?? this.dataProviderService.getPrimaryDataSource(); + if (!order.dataSource) { + if (order.type === 'ITEM') { + order.dataSource = 'MANUAL'; + } else { + order.dataSource = this.dataProviderService.getPrimaryDataSource(); + } + } } await this.validateOrders({ orders, userId }); @@ -111,20 +116,22 @@ export class ImportService { throw new Error(`orders.${index} is a duplicate transaction`); } - const result = await this.dataProviderService.get([ - { dataSource, symbol } - ]); + if (dataSource !== 'MANUAL') { + const result = await this.dataProviderService.get([ + { dataSource, symbol } + ]); - if (result[symbol] === undefined) { - throw new Error( - `orders.${index}.symbol ("${symbol}") is not valid for the specified data source ("${dataSource}")` - ); - } + if (result[symbol] === undefined) { + throw new Error( + `orders.${index}.symbol ("${symbol}") is not valid for the specified data source ("${dataSource}")` + ); + } - if (result[symbol].currency !== currency) { - throw new Error( - `orders.${index}.currency ("${currency}") does not match with "${result[symbol].currency}"` - ); + if (result[symbol].currency !== currency) { + throw new Error( + `orders.${index}.currency ("${currency}") does not match with "${result[symbol].currency}"` + ); + } } } } diff --git a/apps/api/src/services/data-gathering.service.ts b/apps/api/src/services/data-gathering.service.ts index 8fe2c4835..81c9c884d 100644 --- a/apps/api/src/services/data-gathering.service.ts +++ b/apps/api/src/services/data-gathering.service.ts @@ -445,6 +445,11 @@ export class DataGatheringService { }, scraperConfiguration: true, symbol: true + }, + where: { + dataSource: { + not: 'MANUAL' + } } }) ).map((symbolProfile) => { @@ -479,6 +484,11 @@ export class DataGatheringService { dataSource: true, scraperConfiguration: true, symbol: true + }, + where: { + dataSource: { + not: 'MANUAL' + } } }); @@ -537,6 +547,7 @@ export class DataGatheringService { return distinctOrders.filter((distinctOrder) => { return ( distinctOrder.dataSource !== DataSource.GHOSTFOLIO && + distinctOrder.dataSource !== DataSource.MANUAL && distinctOrder.dataSource !== DataSource.RAKUTEN ); }); diff --git a/apps/api/src/services/data-provider/data-provider.module.ts b/apps/api/src/services/data-provider/data-provider.module.ts index c05570932..e2a77af4a 100644 --- a/apps/api/src/services/data-provider/data-provider.module.ts +++ b/apps/api/src/services/data-provider/data-provider.module.ts @@ -2,6 +2,7 @@ import { ConfigurationModule } from '@ghostfolio/api/services/configuration.modu import { CryptocurrencyModule } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.module'; import { GhostfolioScraperApiService } from '@ghostfolio/api/services/data-provider/ghostfolio-scraper-api/ghostfolio-scraper-api.service'; import { GoogleSheetsService } from '@ghostfolio/api/services/data-provider/google-sheets/google-sheets.service'; +import { ManualService } from '@ghostfolio/api/services/data-provider/manual/manual.service'; import { RakutenRapidApiService } from '@ghostfolio/api/services/data-provider/rakuten-rapid-api/rakuten-rapid-api.service'; import { YahooFinanceService } from '@ghostfolio/api/services/data-provider/yahoo-finance/yahoo-finance.service'; import { PrismaModule } from '@ghostfolio/api/services/prisma.module'; @@ -23,6 +24,7 @@ import { DataProviderService } from './data-provider.service'; DataProviderService, GhostfolioScraperApiService, GoogleSheetsService, + ManualService, RakutenRapidApiService, YahooFinanceService, { @@ -30,6 +32,7 @@ import { DataProviderService } from './data-provider.service'; AlphaVantageService, GhostfolioScraperApiService, GoogleSheetsService, + ManualService, RakutenRapidApiService, YahooFinanceService ], @@ -38,12 +41,14 @@ import { DataProviderService } from './data-provider.service'; alphaVantageService, ghostfolioScraperApiService, googleSheetsService, + manualService, rakutenRapidApiService, yahooFinanceService ) => [ alphaVantageService, ghostfolioScraperApiService, googleSheetsService, + manualService, rakutenRapidApiService, yahooFinanceService ] diff --git a/apps/api/src/services/data-provider/data-provider.service.ts b/apps/api/src/services/data-provider/data-provider.service.ts index f051927f9..6ffd5b2dd 100644 --- a/apps/api/src/services/data-provider/data-provider.service.ts +++ b/apps/api/src/services/data-provider/data-provider.service.ts @@ -194,6 +194,7 @@ export class DataProviderService { return dataProviderInterface; } } + throw new Error('No data provider has been found.'); } } diff --git a/apps/api/src/services/data-provider/manual/manual.service.ts b/apps/api/src/services/data-provider/manual/manual.service.ts new file mode 100644 index 000000000..3a486f897 --- /dev/null +++ b/apps/api/src/services/data-provider/manual/manual.service.ts @@ -0,0 +1,43 @@ +import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; +import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; +import { + IDataProviderHistoricalResponse, + IDataProviderResponse +} from '@ghostfolio/api/services/interfaces/interfaces'; +import { Granularity } from '@ghostfolio/common/types'; +import { Injectable } from '@nestjs/common'; +import { DataSource } from '@prisma/client'; + +@Injectable() +export class ManualService implements DataProviderInterface { + public constructor() {} + + public canHandle(symbol: string) { + return false; + } + + public async get( + aSymbols: string[] + ): Promise<{ [symbol: string]: IDataProviderResponse }> { + return {}; + } + + public async getHistorical( + aSymbols: string[], + aGranularity: Granularity = 'day', + from: Date, + to: Date + ): Promise<{ + [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; + }> { + return {}; + } + + public getName(): DataSource { + return DataSource.MANUAL; + } + + public async search(aQuery: string): Promise<{ items: LookupItem[] }> { + return { items: [] }; + } +} diff --git a/apps/client/src/app/pages/portfolio/transactions/create-or-update-transaction-dialog/create-or-update-transaction-dialog.html b/apps/client/src/app/pages/portfolio/transactions/create-or-update-transaction-dialog/create-or-update-transaction-dialog.html index 11f7415fb..1cf1401b3 100644 --- a/apps/client/src/app/pages/portfolio/transactions/create-or-update-transaction-dialog/create-or-update-transaction-dialog.html +++ b/apps/client/src/app/pages/portfolio/transactions/create-or-update-transaction-dialog/create-or-update-transaction-dialog.html @@ -54,6 +54,7 @@ BUY DIVIDEND + ITEM SELL diff --git a/apps/client/src/app/services/import-transactions.service.ts b/apps/client/src/app/services/import-transactions.service.ts index b39d4ac65..8fe829d55 100644 --- a/apps/client/src/app/services/import-transactions.service.ts +++ b/apps/client/src/app/services/import-transactions.service.ts @@ -245,6 +245,8 @@ export class ImportTransactionsService { return Type.BUY; case 'dividend': return Type.DIVIDEND; + case 'item': + return Type.ITEM; case 'sell': return Type.SELL; default: diff --git a/libs/ui/src/lib/activities-table/activities-table.component.html b/libs/ui/src/lib/activities-table/activities-table.component.html index 5fd343a0a..ccf2e6a85 100644 --- a/libs/ui/src/lib/activities-table/activities-table.component.html +++ b/libs/ui/src/lib/activities-table/activities-table.component.html @@ -87,15 +87,21 @@ [ngClass]="{ buy: element.type === 'BUY', dividend: element.type === 'DIVIDEND', + item: element.type === 'ITEM', sell: element.type === 'SELL' }" > + + {{ element.type }} @@ -109,7 +115,7 @@
- {{ element.symbol | gfSymbol }} + {{ element.SymbolProfile.symbol | gfSymbol }} Draft @@ -349,13 +355,15 @@ (click)=" hasPermissionToOpenDetails && !row.isDraft && + row.type !== 'ITEM' && onOpenPositionDialog({ dataSource: row.dataSource, - symbol: row.symbol + symbol: row.SymbolProfile.symbol }) " [ngClass]="{ - 'cursor-pointer': hasPermissionToOpenDetails && !row.isDraft + 'cursor-pointer': + hasPermissionToOpenDetails && !row.isDraft && row.type !== 'ITEM' }" >