Browse Source

Eliminate data source from order model (#730)

* Eliminate currency, data source and symbol from order model

* Remove prefix for symbols with data source GHOSTFOLIO

* Update changelog
pull/739/head
Thomas Kaul 3 years ago
committed by GitHub
parent
commit
c216ab1d76
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      CHANGELOG.md
  2. 5
      apps/api/src/app/export/export.service.ts
  3. 3
      apps/api/src/app/import/import-data.dto.ts
  4. 17
      apps/api/src/app/import/import.service.ts
  5. 9
      apps/api/src/app/order/order.controller.ts
  6. 71
      apps/api/src/app/order/order.service.ts
  7. 29
      apps/api/src/app/portfolio/portfolio.service-new.ts
  8. 29
      apps/api/src/app/portfolio/portfolio.service.ts
  9. 8
      apps/api/src/interceptors/transform-data-source-in-response.interceptor.ts
  10. 27
      apps/api/src/services/data-gathering.service.ts
  11. 8
      apps/api/src/services/data-provider/ghostfolio-scraper-api/ghostfolio-scraper-api.service.ts
  12. 7
      apps/api/src/services/exchange-rate-data.service.ts
  13. 14
      apps/client/src/app/pages/portfolio/transactions/create-or-update-transaction-dialog/create-or-update-transaction-dialog.component.ts
  14. 4
      libs/common/src/lib/helper.ts
  15. 4
      libs/ui/src/lib/activities-table/activities-table.component.html
  16. 2
      prisma/migrations/20220302184222_removed_data_source_from_order/migration.sql
  17. 2
      prisma/migrations/20220302191841_removed_currency_from_order/migration.sql
  18. 2
      prisma/migrations/20220302193633_removed_symbol_from_order/migration.sql
  19. 5
      prisma/migrations/20220302200727_changed_currency_to_required_in_symbol_profile/migration.sql
  20. 5
      prisma/schema.prisma
  21. 40
      prisma/seed.js

9
CHANGELOG.md

@ -11,10 +11,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Included data provider errors in API response - Included data provider errors in API response
### Changed
- Removed the redundant attributes (`currency`, `dataSource`, `symbol`) of the activity model
- Removed the prefix for symbols with the data source `GHOSTFOLIO`
### Fixed ### Fixed
- Improved the account calculations - Improved the account calculations
### Todo
- Apply data migration (`yarn database:migrate`)
## 1.122.0 - 01.03.2022 ## 1.122.0 - 01.03.2022
### Added ### Added

5
apps/api/src/app/export/export.service.ts

@ -18,8 +18,6 @@ export class ExportService {
orderBy: { date: 'desc' }, orderBy: { date: 'desc' },
select: { select: {
accountId: true, accountId: true,
currency: true,
dataSource: true,
date: true, date: true,
fee: true, fee: true,
id: true, id: true,
@ -42,7 +40,6 @@ export class ExportService {
orders: orders.map( orders: orders.map(
({ ({
accountId, accountId,
currency,
date, date,
fee, fee,
quantity, quantity,
@ -52,12 +49,12 @@ export class ExportService {
}) => { }) => {
return { return {
accountId, accountId,
currency,
date, date,
fee, fee,
quantity, quantity,
type, type,
unitPrice, unitPrice,
currency: SymbolProfile.currency,
dataSource: SymbolProfile.dataSource, dataSource: SymbolProfile.dataSource,
symbol: type === 'ITEM' ? SymbolProfile.name : SymbolProfile.symbol symbol: type === 'ITEM' ? SymbolProfile.name : SymbolProfile.symbol
}; };

3
apps/api/src/app/import/import-data.dto.ts

@ -1,5 +1,4 @@
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { Order } from '@prisma/client';
import { Type } from 'class-transformer'; import { Type } from 'class-transformer';
import { IsArray, ValidateNested } from 'class-validator'; import { IsArray, ValidateNested } from 'class-validator';
@ -7,5 +6,5 @@ export class ImportDataDto {
@IsArray() @IsArray()
@Type(() => CreateOrderDto) @Type(() => CreateOrderDto)
@ValidateNested({ each: true }) @ValidateNested({ each: true })
orders: Order[]; orders: CreateOrderDto[];
} }

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

@ -3,8 +3,8 @@ import { OrderService } from '@ghostfolio/api/app/order/order.service';
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; import { ConfigurationService } from '@ghostfolio/api/services/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 { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Order } from '@prisma/client';
import { isSameDay, parseISO } from 'date-fns'; import { isSameDay, parseISO } from 'date-fns';
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
@Injectable() @Injectable()
export class ImportService { export class ImportService {
@ -19,7 +19,7 @@ export class ImportService {
orders, orders,
userId userId
}: { }: {
orders: Partial<Order>[]; orders: Partial<CreateOrderDto>[];
userId: string; userId: string;
}): Promise<void> { }): Promise<void> {
for (const order of orders) { for (const order of orders) {
@ -52,11 +52,8 @@ export class ImportService {
unitPrice unitPrice
} of orders) { } of orders) {
await this.orderService.createOrder({ await this.orderService.createOrder({
currency,
dataSource,
fee, fee,
quantity, quantity,
symbol,
type, type,
unitPrice, unitPrice,
userId, userId,
@ -65,6 +62,7 @@ export class ImportService {
SymbolProfile: { SymbolProfile: {
connectOrCreate: { connectOrCreate: {
create: { create: {
currency,
dataSource, dataSource,
symbol symbol
}, },
@ -85,7 +83,7 @@ export class ImportService {
orders, orders,
userId userId
}: { }: {
orders: Partial<Order>[]; orders: Partial<CreateOrderDto>[];
userId: string; userId: string;
}) { }) {
if ( if (
@ -99,6 +97,7 @@ export class ImportService {
} }
const existingOrders = await this.orderService.orders({ const existingOrders = await this.orderService.orders({
include: { SymbolProfile: true },
orderBy: { date: 'desc' }, orderBy: { date: 'desc' },
where: { userId } where: { userId }
}); });
@ -109,12 +108,12 @@ export class ImportService {
] of orders.entries()) { ] of orders.entries()) {
const duplicateOrder = existingOrders.find((order) => { const duplicateOrder = existingOrders.find((order) => {
return ( return (
order.currency === currency && order.SymbolProfile.currency === currency &&
order.dataSource === dataSource && order.SymbolProfile.dataSource === dataSource &&
isSameDay(order.date, parseISO(<string>(<unknown>date))) && isSameDay(order.date, parseISO(<string>(<unknown>date))) &&
order.fee === fee && order.fee === fee &&
order.quantity === quantity && order.quantity === quantity &&
order.symbol === symbol && order.SymbolProfile.symbol === symbol &&
order.type === type && order.type === type &&
order.unitPrice === unitPrice order.unitPrice === unitPrice
); );

9
apps/api/src/app/order/order.controller.ts

@ -114,6 +114,7 @@ export class OrderController {
SymbolProfile: { SymbolProfile: {
connectOrCreate: { connectOrCreate: {
create: { create: {
currency: data.currency,
dataSource: data.dataSource, dataSource: data.dataSource,
symbol: data.symbol symbol: data.symbol
}, },
@ -171,6 +172,14 @@ export class OrderController {
id_userId: { id: accountId, userId: this.request.user.id } id_userId: { id: accountId, userId: this.request.user.id }
} }
}, },
SymbolProfile: {
connect: {
dataSource_symbol: {
dataSource: data.dataSource,
symbol: data.symbol
}
}
},
User: { connect: { id: this.request.user.id } } User: { connect: { id: this.request.user.id } }
}, },
where: { where: {

71
apps/api/src/app/order/order.service.ts

@ -53,7 +53,13 @@ export class OrderService {
} }
public async createOrder( public async createOrder(
data: Prisma.OrderCreateInput & { accountId?: string; userId: string } data: Prisma.OrderCreateInput & {
accountId?: string;
currency?: string;
dataSource?: DataSource;
symbol?: string;
userId: string;
}
): Promise<Order> { ): Promise<Order> {
const defaultAccount = ( const defaultAccount = (
await this.accountService.getAccounts(data.userId) await this.accountService.getAccounts(data.userId)
@ -71,15 +77,13 @@ export class OrderService {
}; };
if (data.type === 'ITEM') { if (data.type === 'ITEM') {
const currency = data.currency; const currency = data.SymbolProfile.connectOrCreate.create.currency;
const dataSource: DataSource = 'MANUAL'; const dataSource: DataSource = 'MANUAL';
const id = uuidv4(); const id = uuidv4();
const name = data.SymbolProfile.connectOrCreate.create.symbol; const name = data.SymbolProfile.connectOrCreate.create.symbol;
Account = undefined; Account = undefined;
data.dataSource = dataSource;
data.id = id; data.id = id;
data.symbol = null;
data.SymbolProfile.connectOrCreate.create.currency = currency; data.SymbolProfile.connectOrCreate.create.currency = currency;
data.SymbolProfile.connectOrCreate.create.dataSource = dataSource; data.SymbolProfile.connectOrCreate.create.dataSource = dataSource;
data.SymbolProfile.connectOrCreate.create.name = name; data.SymbolProfile.connectOrCreate.create.name = name;
@ -95,7 +99,7 @@ export class OrderService {
await this.dataGatheringService.gatherProfileData([ await this.dataGatheringService.gatherProfileData([
{ {
dataSource: data.dataSource, dataSource: data.SymbolProfile.connectOrCreate.create.dataSource,
symbol: data.SymbolProfile.connectOrCreate.create.symbol symbol: data.SymbolProfile.connectOrCreate.create.symbol
} }
]); ]);
@ -106,7 +110,7 @@ export class OrderService {
// Gather symbol data of order in the background, if not draft // Gather symbol data of order in the background, if not draft
this.dataGatheringService.gatherSymbols([ this.dataGatheringService.gatherSymbols([
{ {
dataSource: data.dataSource, dataSource: data.SymbolProfile.connectOrCreate.create.dataSource,
date: <Date>data.date, date: <Date>data.date,
symbol: data.SymbolProfile.connectOrCreate.create.symbol symbol: data.SymbolProfile.connectOrCreate.create.symbol
} }
@ -116,6 +120,9 @@ export class OrderService {
await this.cacheService.flush(); await this.cacheService.flush();
delete data.accountId; delete data.accountId;
delete data.currency;
delete data.dataSource;
delete data.symbol;
delete data.userId; delete data.userId;
const orderData: Prisma.OrderCreateInput = data; const orderData: Prisma.OrderCreateInput = data;
@ -193,50 +200,60 @@ export class OrderService {
value, value,
feeInBaseCurrency: this.exchangeRateDataService.toCurrency( feeInBaseCurrency: this.exchangeRateDataService.toCurrency(
order.fee, order.fee,
order.currency, order.SymbolProfile.currency,
userCurrency userCurrency
), ),
valueInBaseCurrency: this.exchangeRateDataService.toCurrency( valueInBaseCurrency: this.exchangeRateDataService.toCurrency(
value, value,
order.currency, order.SymbolProfile.currency,
userCurrency userCurrency
) )
}; };
}); });
} }
public async updateOrder(params: { public async updateOrder({
data,
where
}: {
data: Prisma.OrderUpdateInput & {
currency?: string;
dataSource?: DataSource;
symbol?: string;
};
where: Prisma.OrderWhereUniqueInput; where: Prisma.OrderWhereUniqueInput;
data: Prisma.OrderUpdateInput;
}): Promise<Order> { }): Promise<Order> {
const { data, where } = params;
if (data.Account.connect.id_userId.id === null) { if (data.Account.connect.id_userId.id === null) {
delete data.Account; delete data.Account;
} }
let isDraft = false;
if (data.type === 'ITEM') { if (data.type === 'ITEM') {
const name = data.symbol; const name = data.SymbolProfile.connect.dataSource_symbol.symbol;
data.symbol = null;
data.SymbolProfile = { update: { name } }; data.SymbolProfile = { update: { name } };
} } else {
isDraft = isAfter(data.date as Date, endOfToday());
const isDraft = isAfter(data.date as Date, endOfToday());
if (!isDraft) {
if (!isDraft) { // Gather symbol data of order in the background, if not draft
// Gather symbol data of order in the background, if not draft this.dataGatheringService.gatherSymbols([
this.dataGatheringService.gatherSymbols([ {
{ dataSource: data.SymbolProfile.connect.dataSource_symbol.dataSource,
dataSource: <DataSource>data.dataSource, date: <Date>data.date,
date: <Date>data.date, symbol: data.SymbolProfile.connect.dataSource_symbol.symbol
symbol: <string>data.symbol }
} ]);
]); }
} }
await this.cacheService.flush(); await this.cacheService.flush();
delete data.currency;
delete data.dataSource;
delete data.symbol;
return this.prismaService.order.update({ return this.prismaService.order.update({
data: { data: {
...data, ...data,

29
apps/api/src/app/portfolio/portfolio.service-new.ts

@ -450,7 +450,7 @@ export class PortfolioServiceNew {
}; };
} }
const positionCurrency = orders[0].currency; const positionCurrency = orders[0].SymbolProfile.currency;
const [SymbolProfile] = await this.symbolProfileService.getSymbolProfiles([ const [SymbolProfile] = await this.symbolProfileService.getSymbolProfiles([
aSymbol aSymbol
]); ]);
@ -460,13 +460,13 @@ export class PortfolioServiceNew {
return order.type === 'BUY' || order.type === 'SELL'; return order.type === 'BUY' || order.type === 'SELL';
}) })
.map((order) => ({ .map((order) => ({
currency: order.currency, currency: order.SymbolProfile.currency,
dataSource: order.SymbolProfile?.dataSource ?? order.dataSource, dataSource: order.SymbolProfile.dataSource,
date: format(order.date, DATE_FORMAT), date: format(order.date, DATE_FORMAT),
fee: new Big(order.fee), fee: new Big(order.fee),
name: order.SymbolProfile?.name, name: order.SymbolProfile?.name,
quantity: new Big(order.quantity), quantity: new Big(order.quantity),
symbol: order.symbol, symbol: order.SymbolProfile.symbol,
type: order.type, type: order.type,
unitPrice: new Big(order.unitPrice) unitPrice: new Big(order.unitPrice)
})); }));
@ -1023,7 +1023,7 @@ export class PortfolioServiceNew {
.map((order) => { .map((order) => {
return this.exchangeRateDataService.toCurrency( return this.exchangeRateDataService.toCurrency(
new Big(order.quantity).mul(order.unitPrice).toNumber(), new Big(order.quantity).mul(order.unitPrice).toNumber(),
order.currency, order.SymbolProfile.currency,
this.request.user.Settings.currency this.request.user.Settings.currency
); );
}) })
@ -1042,7 +1042,7 @@ export class PortfolioServiceNew {
.map((order) => { .map((order) => {
return this.exchangeRateDataService.toCurrency( return this.exchangeRateDataService.toCurrency(
order.fee, order.fee,
order.currency, order.SymbolProfile.currency,
this.request.user.Settings.currency this.request.user.Settings.currency
); );
}) })
@ -1064,7 +1064,7 @@ export class PortfolioServiceNew {
.map((order) => { .map((order) => {
return this.exchangeRateDataService.toCurrency( return this.exchangeRateDataService.toCurrency(
new Big(order.quantity).mul(order.unitPrice).toNumber(), new Big(order.quantity).mul(order.unitPrice).toNumber(),
order.currency, order.SymbolProfile.currency,
this.request.user.Settings.currency this.request.user.Settings.currency
); );
}) })
@ -1117,24 +1117,24 @@ export class PortfolioServiceNew {
} }
const portfolioOrders: PortfolioOrder[] = orders.map((order) => ({ const portfolioOrders: PortfolioOrder[] = orders.map((order) => ({
currency: order.currency, currency: order.SymbolProfile.currency,
dataSource: order.SymbolProfile?.dataSource ?? order.dataSource, dataSource: order.SymbolProfile.dataSource,
date: format(order.date, DATE_FORMAT), date: format(order.date, DATE_FORMAT),
fee: new Big( fee: new Big(
this.exchangeRateDataService.toCurrency( this.exchangeRateDataService.toCurrency(
order.fee, order.fee,
order.currency, order.SymbolProfile.currency,
userCurrency userCurrency
) )
), ),
name: order.SymbolProfile?.name, name: order.SymbolProfile?.name,
quantity: new Big(order.quantity), quantity: new Big(order.quantity),
symbol: order.symbol, symbol: order.SymbolProfile.symbol,
type: order.type, type: order.type,
unitPrice: new Big( unitPrice: new Big(
this.exchangeRateDataService.toCurrency( this.exchangeRateDataService.toCurrency(
order.unitPrice, order.unitPrice,
order.currency, order.SymbolProfile.currency,
userCurrency userCurrency
) )
) )
@ -1180,7 +1180,8 @@ export class PortfolioServiceNew {
for (const order of ordersByAccount) { for (const order of ordersByAccount) {
let currentValueOfSymbol = let currentValueOfSymbol =
order.quantity * portfolioItemsNow[order.symbol].marketPrice; order.quantity *
portfolioItemsNow[order.SymbolProfile.symbol].marketPrice;
let originalValueOfSymbol = order.quantity * order.unitPrice; let originalValueOfSymbol = order.quantity * order.unitPrice;
if (order.type === 'SELL') { if (order.type === 'SELL') {
@ -1230,7 +1231,7 @@ export class PortfolioServiceNew {
.map((order) => { .map((order) => {
return this.exchangeRateDataService.toCurrency( return this.exchangeRateDataService.toCurrency(
order.quantity * order.unitPrice, order.quantity * order.unitPrice,
order.currency, order.SymbolProfile.currency,
currency currency
); );
}) })

29
apps/api/src/app/portfolio/portfolio.service.ts

@ -438,7 +438,7 @@ export class PortfolioService {
}; };
} }
const positionCurrency = orders[0].currency; const positionCurrency = orders[0].SymbolProfile.currency;
const [SymbolProfile] = await this.symbolProfileService.getSymbolProfiles([ const [SymbolProfile] = await this.symbolProfileService.getSymbolProfiles([
aSymbol aSymbol
]); ]);
@ -448,13 +448,13 @@ export class PortfolioService {
return order.type === 'BUY' || order.type === 'SELL'; return order.type === 'BUY' || order.type === 'SELL';
}) })
.map((order) => ({ .map((order) => ({
currency: order.currency, currency: order.SymbolProfile.currency,
dataSource: order.SymbolProfile?.dataSource ?? order.dataSource, dataSource: order.SymbolProfile.dataSource,
date: format(order.date, DATE_FORMAT), date: format(order.date, DATE_FORMAT),
fee: new Big(order.fee), fee: new Big(order.fee),
name: order.SymbolProfile?.name, name: order.SymbolProfile?.name,
quantity: new Big(order.quantity), quantity: new Big(order.quantity),
symbol: order.symbol, symbol: order.SymbolProfile.symbol,
type: order.type, type: order.type,
unitPrice: new Big(order.unitPrice) unitPrice: new Big(order.unitPrice)
})); }));
@ -987,7 +987,7 @@ export class PortfolioService {
.map((order) => { .map((order) => {
return this.exchangeRateDataService.toCurrency( return this.exchangeRateDataService.toCurrency(
new Big(order.quantity).mul(order.unitPrice).toNumber(), new Big(order.quantity).mul(order.unitPrice).toNumber(),
order.currency, order.SymbolProfile.currency,
this.request.user.Settings.currency this.request.user.Settings.currency
); );
}) })
@ -1006,7 +1006,7 @@ export class PortfolioService {
.map((order) => { .map((order) => {
return this.exchangeRateDataService.toCurrency( return this.exchangeRateDataService.toCurrency(
order.fee, order.fee,
order.currency, order.SymbolProfile.currency,
this.request.user.Settings.currency this.request.user.Settings.currency
); );
}) })
@ -1028,7 +1028,7 @@ export class PortfolioService {
.map((order) => { .map((order) => {
return this.exchangeRateDataService.toCurrency( return this.exchangeRateDataService.toCurrency(
new Big(order.quantity).mul(order.unitPrice).toNumber(), new Big(order.quantity).mul(order.unitPrice).toNumber(),
order.currency, order.SymbolProfile.currency,
this.request.user.Settings.currency this.request.user.Settings.currency
); );
}) })
@ -1080,24 +1080,24 @@ export class PortfolioService {
} }
const portfolioOrders: PortfolioOrder[] = orders.map((order) => ({ const portfolioOrders: PortfolioOrder[] = orders.map((order) => ({
currency: order.currency, currency: order.SymbolProfile.currency,
dataSource: order.SymbolProfile?.dataSource ?? order.dataSource, dataSource: order.SymbolProfile.dataSource,
date: format(order.date, DATE_FORMAT), date: format(order.date, DATE_FORMAT),
fee: new Big( fee: new Big(
this.exchangeRateDataService.toCurrency( this.exchangeRateDataService.toCurrency(
order.fee, order.fee,
order.currency, order.SymbolProfile.currency,
userCurrency userCurrency
) )
), ),
name: order.SymbolProfile?.name, name: order.SymbolProfile?.name,
quantity: new Big(order.quantity), quantity: new Big(order.quantity),
symbol: order.symbol, symbol: order.SymbolProfile.symbol,
type: order.type, type: order.type,
unitPrice: new Big( unitPrice: new Big(
this.exchangeRateDataService.toCurrency( this.exchangeRateDataService.toCurrency(
order.unitPrice, order.unitPrice,
order.currency, order.SymbolProfile.currency,
userCurrency userCurrency
) )
) )
@ -1139,7 +1139,8 @@ export class PortfolioService {
for (const order of ordersByAccount) { for (const order of ordersByAccount) {
let currentValueOfSymbol = let currentValueOfSymbol =
order.quantity * portfolioItemsNow[order.symbol].marketPrice; order.quantity *
portfolioItemsNow[order.SymbolProfile.symbol].marketPrice;
let originalValueOfSymbol = order.quantity * order.unitPrice; let originalValueOfSymbol = order.quantity * order.unitPrice;
if (order.type === 'SELL') { if (order.type === 'SELL') {
@ -1189,7 +1190,7 @@ export class PortfolioService {
.map((order) => { .map((order) => {
return this.exchangeRateDataService.toCurrency( return this.exchangeRateDataService.toCurrency(
order.quantity * order.unitPrice, order.quantity * order.unitPrice,
order.currency, order.SymbolProfile.currency,
currency currency
); );
}) })

8
apps/api/src/interceptors/transform-data-source-in-response.interceptor.ts

@ -32,7 +32,6 @@ export class TransformDataSourceInResponseInterceptor<T>
activity.SymbolProfile.dataSource = encodeDataSource( activity.SymbolProfile.dataSource = encodeDataSource(
activity.SymbolProfile.dataSource activity.SymbolProfile.dataSource
); );
activity.dataSource = encodeDataSource(activity.dataSource);
return activity; return activity;
}); });
} }
@ -66,13 +65,6 @@ export class TransformDataSourceInResponseInterceptor<T>
}); });
} }
if (data.orders) {
data.orders.map((order) => {
order.dataSource = encodeDataSource(order.dataSource);
return order;
});
}
if (data.positions) { if (data.positions) {
data.positions.map((position) => { data.positions.map((position) => {
position.dataSource = encodeDataSource(position.dataSource); position.dataSource = encodeDataSource(position.dataSource);

27
apps/api/src/services/data-gathering.service.ts

@ -549,19 +549,24 @@ export class DataGatheringService {
} }
private async getSymbolsProfileData(): Promise<IDataGatheringItem[]> { private async getSymbolsProfileData(): Promise<IDataGatheringItem[]> {
const distinctOrders = await this.prismaService.order.findMany({ const symbolProfiles = await this.prismaService.symbolProfile.findMany({
distinct: ['symbol'], orderBy: [{ symbol: 'asc' }]
orderBy: [{ symbol: 'asc' }],
select: { dataSource: true, symbol: true }
}); });
return distinctOrders.filter((distinctOrder) => { return symbolProfiles
return ( .filter((symbolProfile) => {
distinctOrder.dataSource !== DataSource.GHOSTFOLIO && return (
distinctOrder.dataSource !== DataSource.MANUAL && symbolProfile.dataSource !== DataSource.GHOSTFOLIO &&
distinctOrder.dataSource !== DataSource.RAKUTEN symbolProfile.dataSource !== DataSource.MANUAL &&
); symbolProfile.dataSource !== DataSource.RAKUTEN
}); );
})
.map((symbolProfile) => {
return {
dataSource: symbolProfile.dataSource,
symbol: symbolProfile.symbol
};
});
} }
private async isDataGatheringNeeded() { private async isDataGatheringNeeded() {

8
apps/api/src/services/data-provider/ghostfolio-scraper-api/ghostfolio-scraper-api.service.ts

@ -7,11 +7,7 @@ import {
} from '@ghostfolio/api/services/interfaces/interfaces'; } from '@ghostfolio/api/services/interfaces/interfaces';
import { PrismaService } from '@ghostfolio/api/services/prisma.service'; import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile.service';
import { import { DATE_FORMAT, getYesterday } from '@ghostfolio/common/helper';
DATE_FORMAT,
getYesterday,
isGhostfolioScraperApiSymbol
} from '@ghostfolio/common/helper';
import { Granularity } from '@ghostfolio/common/types'; import { Granularity } from '@ghostfolio/common/types';
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { DataSource, SymbolProfile } from '@prisma/client'; import { DataSource, SymbolProfile } from '@prisma/client';
@ -29,7 +25,7 @@ export class GhostfolioScraperApiService implements DataProviderInterface {
) {} ) {}
public canHandle(symbol: string) { public canHandle(symbol: string) {
return isGhostfolioScraperApiSymbol(symbol); return true;
} }
public async getAssetProfile( public async getAssetProfile(

7
apps/api/src/services/exchange-rate-data.service.ts

@ -191,12 +191,7 @@ export class ExchangeRateDataService {
await this.prismaService.symbolProfile.findMany({ await this.prismaService.symbolProfile.findMany({
distinct: ['currency'], distinct: ['currency'],
orderBy: [{ currency: 'asc' }], orderBy: [{ currency: 'asc' }],
select: { currency: true }, select: { currency: true }
where: {
currency: {
not: null
}
}
}) })
).forEach((symbolProfile) => { ).forEach((symbolProfile) => {
currencies.push(symbolProfile.currency); currencies.push(symbolProfile.currency);

14
apps/client/src/app/pages/portfolio/transactions/create-or-update-transaction-dialog/create-or-update-transaction-dialog.component.ts

@ -158,11 +158,11 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy {
this.activityForm.controls['type'].disable(); this.activityForm.controls['type'].disable();
} }
if (this.data.activity?.symbol) { if (this.data.activity?.SymbolProfile?.symbol) {
this.dataService this.dataService
.fetchSymbolItem({ .fetchSymbolItem({
dataSource: this.data.activity?.dataSource, dataSource: this.data.activity?.SymbolProfile?.dataSource,
symbol: this.data.activity?.symbol symbol: this.data.activity?.SymbolProfile?.symbol
}) })
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ marketPrice }) => { .subscribe(({ marketPrice }) => {
@ -196,9 +196,7 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy {
} else { } else {
this.activityForm.controls['searchSymbol'].setErrors({ incorrect: true }); this.activityForm.controls['searchSymbol'].setErrors({ incorrect: true });
this.data.activity.currency = null; this.data.activity.SymbolProfile = null;
this.data.activity.dataSource = null;
this.data.activity.symbol = null;
} }
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
@ -259,9 +257,7 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy {
}) })
.pipe( .pipe(
catchError(() => { catchError(() => {
this.data.activity.currency = null; this.data.activity.SymbolProfile = null;
this.data.activity.dataSource = null;
this.data.activity.unitPrice = null;
this.isLoading = false; this.isLoading = false;

4
libs/common/src/lib/helper.ts

@ -102,10 +102,6 @@ export function isCurrency(aSymbol = '') {
return currencies[aSymbol]; return currencies[aSymbol];
} }
export function isGhostfolioScraperApiSymbol(aSymbol = '') {
return aSymbol.startsWith(ghostfolioScraperApiSymbolPrefix);
}
export function resetHours(aDate: Date) { export function resetHours(aDate: Date) {
const year = getYear(aDate); const year = getYear(aDate);
const month = getMonth(aDate); const month = getMonth(aDate);

4
libs/ui/src/lib/activities-table/activities-table.component.html

@ -144,7 +144,7 @@
class="d-none d-lg-table-cell px-1" class="d-none d-lg-table-cell px-1"
mat-cell mat-cell
> >
{{ element.currency }} {{ element.SymbolProfile.currency }}
</td> </td>
<td *matFooterCellDef class="d-none d-lg-table-cell px-1" mat-footer-cell> <td *matFooterCellDef class="d-none d-lg-table-cell px-1" mat-footer-cell>
{{ baseCurrency }} {{ baseCurrency }}
@ -362,7 +362,7 @@
!row.isDraft && !row.isDraft &&
row.type !== 'ITEM' && row.type !== 'ITEM' &&
onOpenPositionDialog({ onOpenPositionDialog({
dataSource: row.dataSource, dataSource: row.SymbolProfile.dataSource,
symbol: row.SymbolProfile.symbol symbol: row.SymbolProfile.symbol
}) })
" "

2
prisma/migrations/20220302184222_removed_data_source_from_order/migration.sql

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Order" DROP COLUMN "dataSource";

2
prisma/migrations/20220302191841_removed_currency_from_order/migration.sql

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Order" DROP COLUMN "currency";

2
prisma/migrations/20220302193633_removed_symbol_from_order/migration.sql

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Order" DROP COLUMN "symbol";

5
prisma/migrations/20220302200727_changed_currency_to_required_in_symbol_profile/migration.sql

@ -0,0 +1,5 @@
-- Set default value
UPDATE "SymbolProfile" SET "currency" = 'USD' WHERE "currency" IS NULL;
-- AlterTable
ALTER TABLE "SymbolProfile" ALTER COLUMN "currency" SET NOT NULL;

5
prisma/schema.prisma

@ -74,14 +74,11 @@ model Order {
accountId String? accountId String?
accountUserId String? accountUserId String?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
currency String?
dataSource DataSource?
date DateTime date DateTime
fee Float fee Float
id String @default(uuid()) id String @default(uuid())
isDraft Boolean @default(false) isDraft Boolean @default(false)
quantity Float quantity Float
symbol String?
SymbolProfile SymbolProfile @relation(fields: [symbolProfileId], references: [id]) SymbolProfile SymbolProfile @relation(fields: [symbolProfileId], references: [id])
symbolProfileId String symbolProfileId String
type Type type Type
@ -119,7 +116,7 @@ model SymbolProfile {
assetSubClass AssetSubClass? assetSubClass AssetSubClass?
countries Json? countries Json?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
currency String? currency String
dataSource DataSource dataSource DataSource
id String @id @default(uuid()) id String @id @default(uuid())
name String? name String?

40
prisma/seed.js

@ -192,14 +192,11 @@ async function main() {
{ {
accountId: '65cfb79d-b6c7-4591-9d46-73426bc62094', accountId: '65cfb79d-b6c7-4591-9d46-73426bc62094',
accountUserId: userDemo.id, accountUserId: userDemo.id,
currency: 'USD',
dataSource: DataSource.YAHOO,
date: new Date(Date.UTC(2017, 0, 3, 0, 0, 0)), date: new Date(Date.UTC(2017, 0, 3, 0, 0, 0)),
fee: 30, fee: 30,
id: 'cf7c0418-8535-4089-ae3d-5dbfa0aec2e1', id: 'cf7c0418-8535-4089-ae3d-5dbfa0aec2e1',
quantity: 50, quantity: 50,
symbol: 'TSLA', symbolProfileId: 'd1ee9681-fb21-4f99-a3b7-afd4fc04df2e', // TSLA
symbolProfileId: 'd1ee9681-fb21-4f99-a3b7-afd4fc04df2e',
type: Type.BUY, type: Type.BUY,
unitPrice: 42.97, unitPrice: 42.97,
userId: userDemo.id userId: userDemo.id
@ -207,14 +204,11 @@ async function main() {
{ {
accountId: 'd804de69-0429-42dc-b6ca-b308fd7dd926', accountId: 'd804de69-0429-42dc-b6ca-b308fd7dd926',
accountUserId: userDemo.id, accountUserId: userDemo.id,
currency: 'USD',
dataSource: DataSource.YAHOO,
date: new Date(Date.UTC(2017, 7, 16, 0, 0, 0)), date: new Date(Date.UTC(2017, 7, 16, 0, 0, 0)),
fee: 29.9, fee: 29.9,
id: 'a1c5d73a-8631-44e5-ac44-356827a5212c', id: 'a1c5d73a-8631-44e5-ac44-356827a5212c',
quantity: 0.5614682, quantity: 0.5614682,
symbol: 'BTCUSD', symbolProfileId: 'fdc42ea6-1321-44f5-9fb0-d7f1f2cf9b1e', // BTCUSD
symbolProfileId: 'fdc42ea6-1321-44f5-9fb0-d7f1f2cf9b1e',
type: Type.BUY, type: Type.BUY,
unitPrice: 3562.089535970158, unitPrice: 3562.089535970158,
userId: userDemo.id userId: userDemo.id
@ -222,14 +216,11 @@ async function main() {
{ {
accountId: '480269ce-e12a-4fd1-ac88-c4b0ff3f899c', accountId: '480269ce-e12a-4fd1-ac88-c4b0ff3f899c',
accountUserId: userDemo.id, accountUserId: userDemo.id,
currency: 'USD',
dataSource: DataSource.YAHOO,
date: new Date(Date.UTC(2018, 9, 1, 0, 0, 0)), date: new Date(Date.UTC(2018, 9, 1, 0, 0, 0)),
fee: 80.79, fee: 80.79,
id: '71c08e2a-4a86-44ae-a890-c337de5d5f9b', id: '71c08e2a-4a86-44ae-a890-c337de5d5f9b',
quantity: 5, quantity: 5,
symbol: 'AMZN', symbolProfileId: '2bd26362-136e-411c-b578-334084b4cdcc', // AMZN
symbolProfileId: '2bd26362-136e-411c-b578-334084b4cdcc',
type: Type.BUY, type: Type.BUY,
unitPrice: 2021.99, unitPrice: 2021.99,
userId: userDemo.id userId: userDemo.id
@ -237,14 +228,11 @@ async function main() {
{ {
accountId: '480269ce-e12a-4fd1-ac88-c4b0ff3f899c', accountId: '480269ce-e12a-4fd1-ac88-c4b0ff3f899c',
accountUserId: userDemo.id, accountUserId: userDemo.id,
currency: 'USD',
dataSource: DataSource.YAHOO,
date: new Date(Date.UTC(2019, 2, 1, 0, 0, 0)), date: new Date(Date.UTC(2019, 2, 1, 0, 0, 0)),
fee: 19.9, fee: 19.9,
id: '385f2c2c-d53e-4937-b0e5-e92ef6020d4e', id: '385f2c2c-d53e-4937-b0e5-e92ef6020d4e',
quantity: 10, quantity: 10,
symbol: 'VTI', symbolProfileId: '7d9c8540-061e-4e7e-b019-0d0f4a84e796', // VTI
symbolProfileId: '7d9c8540-061e-4e7e-b019-0d0f4a84e796',
type: Type.BUY, type: Type.BUY,
unitPrice: 144.38, unitPrice: 144.38,
userId: userDemo.id userId: userDemo.id
@ -252,14 +240,11 @@ async function main() {
{ {
accountId: '480269ce-e12a-4fd1-ac88-c4b0ff3f899c', accountId: '480269ce-e12a-4fd1-ac88-c4b0ff3f899c',
accountUserId: userDemo.id, accountUserId: userDemo.id,
currency: 'USD',
dataSource: DataSource.YAHOO,
date: new Date(Date.UTC(2019, 8, 3, 0, 0, 0)), date: new Date(Date.UTC(2019, 8, 3, 0, 0, 0)),
fee: 19.9, fee: 19.9,
id: '185f2c2c-d53e-4937-b0e5-a93ef6020d4e', id: '185f2c2c-d53e-4937-b0e5-a93ef6020d4e',
quantity: 10, quantity: 10,
symbol: 'VTI', symbolProfileId: '7d9c8540-061e-4e7e-b019-0d0f4a84e796', // VTI
symbolProfileId: '7d9c8540-061e-4e7e-b019-0d0f4a84e796',
type: Type.BUY, type: Type.BUY,
unitPrice: 147.99, unitPrice: 147.99,
userId: userDemo.id userId: userDemo.id
@ -267,14 +252,11 @@ async function main() {
{ {
accountId: '480269ce-e12a-4fd1-ac88-c4b0ff3f899c', accountId: '480269ce-e12a-4fd1-ac88-c4b0ff3f899c',
accountUserId: userDemo.id, accountUserId: userDemo.id,
currency: 'USD',
dataSource: DataSource.YAHOO,
date: new Date(Date.UTC(2020, 2, 2, 0, 0, 0)), date: new Date(Date.UTC(2020, 2, 2, 0, 0, 0)),
fee: 19.9, fee: 19.9,
id: '347b0430-a84f-4031-a0f9-390399066ad6', id: '347b0430-a84f-4031-a0f9-390399066ad6',
quantity: 10, quantity: 10,
symbol: 'VTI', symbolProfileId: '7d9c8540-061e-4e7e-b019-0d0f4a84e796', // VTI
symbolProfileId: '7d9c8540-061e-4e7e-b019-0d0f4a84e796',
type: Type.BUY, type: Type.BUY,
unitPrice: 151.41, unitPrice: 151.41,
userId: userDemo.id userId: userDemo.id
@ -282,14 +264,11 @@ async function main() {
{ {
accountId: '480269ce-e12a-4fd1-ac88-c4b0ff3f899c', accountId: '480269ce-e12a-4fd1-ac88-c4b0ff3f899c',
accountUserId: userDemo.id, accountUserId: userDemo.id,
currency: 'USD',
dataSource: DataSource.YAHOO,
date: new Date(Date.UTC(2020, 8, 1, 0, 0, 0)), date: new Date(Date.UTC(2020, 8, 1, 0, 0, 0)),
fee: 19.9, fee: 19.9,
id: '67ec3f47-3189-4b63-ba05-60d3a06b302f', id: '67ec3f47-3189-4b63-ba05-60d3a06b302f',
quantity: 10, quantity: 10,
symbol: 'VTI', symbolProfileId: '7d9c8540-061e-4e7e-b019-0d0f4a84e796', // VTI
symbolProfileId: '7d9c8540-061e-4e7e-b019-0d0f4a84e796',
type: Type.BUY, type: Type.BUY,
unitPrice: 177.69, unitPrice: 177.69,
userId: userDemo.id userId: userDemo.id
@ -297,14 +276,11 @@ async function main() {
{ {
accountId: '480269ce-e12a-4fd1-ac88-c4b0ff3f899c', accountId: '480269ce-e12a-4fd1-ac88-c4b0ff3f899c',
accountUserId: userDemo.id, accountUserId: userDemo.id,
currency: 'USD',
dataSource: DataSource.YAHOO,
date: new Date(Date.UTC(2020, 2, 1, 0, 0, 0)), date: new Date(Date.UTC(2020, 2, 1, 0, 0, 0)),
fee: 19.9, fee: 19.9,
id: 'd01c6fbc-fa8d-47e6-8e80-66f882d2bfd2', id: 'd01c6fbc-fa8d-47e6-8e80-66f882d2bfd2',
quantity: 10, quantity: 10,
symbol: 'VTI', symbolProfileId: '7d9c8540-061e-4e7e-b019-0d0f4a84e796', // VTI
symbolProfileId: '7d9c8540-061e-4e7e-b019-0d0f4a84e796',
type: Type.BUY, type: Type.BUY,
unitPrice: 203.15, unitPrice: 203.15,
userId: userDemo.id userId: userDemo.id

Loading…
Cancel
Save