Browse Source

Extend form

pull/666/head
Thomas 3 years ago
parent
commit
beb4ee23fb
  1. 3
      apps/api/src/app/export/export.service.ts
  2. 17
      apps/api/src/app/order/order.service.ts
  3. 131
      apps/client/src/app/pages/portfolio/transactions/create-or-update-transaction-dialog/create-or-update-transaction-dialog.component.ts
  4. 79
      apps/client/src/app/pages/portfolio/transactions/create-or-update-transaction-dialog/create-or-update-transaction-dialog.html
  5. 5
      apps/client/src/app/pages/portfolio/transactions/create-or-update-transaction-dialog/interfaces/interfaces.ts
  6. 32
      apps/client/src/app/pages/portfolio/transactions/transactions-page.component.ts
  7. 2
      apps/client/src/app/services/admin.service.ts
  8. 6
      libs/common/src/lib/helper.ts
  9. 7
      libs/ui/src/lib/activities-table/activities-table.component.html
  10. 8
      libs/ui/src/lib/activities-table/activities-table.component.ts

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

@ -1,5 +1,6 @@
import { environment } from '@ghostfolio/api/environments/environment'; import { environment } from '@ghostfolio/api/environments/environment';
import { PrismaService } from '@ghostfolio/api/services/prisma.service'; import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { isUUID } from '@ghostfolio/common/helper';
import { Export } from '@ghostfolio/common/interfaces'; import { Export } from '@ghostfolio/common/interfaces';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
@ -59,7 +60,7 @@ export class ExportService {
type, type,
unitPrice, unitPrice,
dataSource: SymbolProfile.dataSource, dataSource: SymbolProfile.dataSource,
symbol: SymbolProfile.symbol symbol: type === 'ITEM' ? SymbolProfile.name : SymbolProfile.symbol
}; };
} }
) )

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

@ -59,7 +59,7 @@ export class OrderService {
return account.isDefault === true; return account.isDefault === true;
}); });
const Account = { let Account = {
connect: { connect: {
id_userId: { id_userId: {
userId: data.userId, userId: data.userId,
@ -70,14 +70,22 @@ export class OrderService {
if (data.type === 'ITEM') { if (data.type === 'ITEM') {
const currency = data.currency; const currency = data.currency;
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;
data.dataSource = dataSource;
data.id = id; data.id = id;
data.symbol = null; 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.name = name; data.SymbolProfile.connectOrCreate.create.name = name;
data.SymbolProfile.connectOrCreate.create.symbol = id; data.SymbolProfile.connectOrCreate.create.symbol = id;
data.SymbolProfile.connectOrCreate.where.dataSource_symbol = {
dataSource,
symbol: id
};
} else { } else {
data.SymbolProfile.connectOrCreate.create.symbol = data.SymbolProfile.connectOrCreate.create.symbol =
data.SymbolProfile.connectOrCreate.create.symbol.toUpperCase(); data.SymbolProfile.connectOrCreate.create.symbol.toUpperCase();
@ -195,6 +203,13 @@ export class OrderService {
}): Promise<Order> { }): Promise<Order> {
const { data, where } = params; const { data, where } = params;
if (data.type === 'ITEM') {
const name = data.symbol;
data.symbol = null;
data.SymbolProfile = { update: { name } };
}
const isDraft = isAfter(data.date as Date, endOfToday()); const isDraft = isAfter(data.date as Date, endOfToday());
if (!isDraft) { if (!isDraft) {

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

@ -9,8 +9,11 @@ import {
import { FormControl, Validators } from '@angular/forms'; import { FormControl, Validators } from '@angular/forms';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto';
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { Type } from '@prisma/client';
import { isString } from 'lodash'; import { isString } from 'lodash';
import { EMPTY, Observable, Subject } from 'rxjs'; import { EMPTY, Observable, Subject } from 'rxjs';
import { import {
@ -34,19 +37,25 @@ import { CreateOrUpdateTransactionDialogParams } from './interfaces/interfaces';
export class CreateOrUpdateTransactionDialog implements OnDestroy { export class CreateOrUpdateTransactionDialog implements OnDestroy {
@ViewChild('autocomplete') autocomplete; @ViewChild('autocomplete') autocomplete;
public accountIdCtrl = new FormControl({}, Validators.required);
public currencyCtrl = new FormControl({}, Validators.required);
public currencies: string[] = []; public currencies: string[] = [];
public currentMarketPrice = null; public currentMarketPrice = null;
public dataSourceCtrl = new FormControl({}, Validators.required);
public dateCtrl = new FormControl({}, Validators.required);
public feeCtrl = new FormControl({}, Validators.required);
public filteredLookupItems: LookupItem[]; public filteredLookupItems: LookupItem[];
public filteredLookupItemsObservable: Observable<LookupItem[]>; public filteredLookupItemsObservable: Observable<LookupItem[]>;
public isLoading = false; public isLoading = false;
public nameCtrl = new FormControl({}, Validators.required);
public platforms: { id: string; name: string }[]; public platforms: { id: string; name: string }[];
public searchSymbolCtrl = new FormControl( public quantityCtrl = new FormControl({}, Validators.required);
{ public searchSymbolCtrl = new FormControl({}, Validators.required);
dataSource: this.data.transaction.dataSource, public showAccountIdCtrl = true;
symbol: this.data.transaction.symbol public showNameCtrl = true;
}, public showSearchSymbolCtrl = true;
Validators.required public typeCtrl = new FormControl({}, Validators.required);
); public unitPriceCtrl = new FormControl({}, Validators.required);
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();
@ -84,15 +93,64 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy {
}) })
); );
if (this.data.transaction.id) { this.typeCtrl.valueChanges.subscribe((type: Type) => {
if (type === 'ITEM') {
this.accountIdCtrl.removeValidators(Validators.required);
this.accountIdCtrl.updateValueAndValidity();
this.currencyCtrl.setValue(this.data.user.settings.baseCurrency);
this.dataSourceCtrl.removeValidators(Validators.required);
this.dataSourceCtrl.updateValueAndValidity();
this.nameCtrl.setValidators(Validators.required);
this.nameCtrl.updateValueAndValidity();
this.quantityCtrl.setValue(1);
this.searchSymbolCtrl.removeValidators(Validators.required);
this.searchSymbolCtrl.updateValueAndValidity();
this.showAccountIdCtrl = false;
this.showNameCtrl = true;
this.showSearchSymbolCtrl = false;
} else {
this.accountIdCtrl.setValidators(Validators.required);
this.accountIdCtrl.updateValueAndValidity();
this.currencyCtrl.setValue(undefined);
this.dataSourceCtrl.setValidators(Validators.required);
this.dataSourceCtrl.updateValueAndValidity();
this.nameCtrl.removeValidators(Validators.required);
this.nameCtrl.updateValueAndValidity();
this.quantityCtrl.setValue(undefined);
this.searchSymbolCtrl.setValidators(Validators.required);
this.searchSymbolCtrl.updateValueAndValidity();
this.showAccountIdCtrl = true;
this.showNameCtrl = false;
this.showSearchSymbolCtrl = true;
}
this.changeDetectorRef.markForCheck();
});
this.accountIdCtrl.setValue(this.data.activity?.accountId);
this.currencyCtrl.setValue(this.data.activity?.currency);
this.dataSourceCtrl.setValue(this.data.activity?.dataSource);
this.dateCtrl.setValue(this.data.activity?.date);
this.feeCtrl.setValue(this.data.activity?.fee);
this.nameCtrl.setValue(this.data.activity?.SymbolProfile?.name);
this.quantityCtrl.setValue(this.data.activity?.quantity);
this.searchSymbolCtrl.setValue({
dataSource: this.data.activity?.dataSource,
symbol: this.data.activity?.symbol
});
this.typeCtrl.setValue(this.data.activity?.type);
this.unitPriceCtrl.setValue(this.data.activity?.unitPrice);
if (this.data.activity?.id) {
this.searchSymbolCtrl.disable(); this.searchSymbolCtrl.disable();
this.typeCtrl.disable();
} }
if (this.data.transaction.symbol) { if (this.data.activity?.symbol) {
this.dataService this.dataService
.fetchSymbolItem({ .fetchSymbolItem({
dataSource: this.data.transaction.dataSource, dataSource: this.data.activity?.dataSource,
symbol: this.data.transaction.symbol symbol: this.data.activity?.symbol
}) })
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ marketPrice }) => { .subscribe(({ marketPrice }) => {
@ -104,7 +162,7 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy {
} }
public applyCurrentMarketPrice() { public applyCurrentMarketPrice() {
this.data.transaction.unitPrice = this.currentMarketPrice; this.unitPriceCtrl.setValue(this.currentMarketPrice);
} }
public displayFn(aLookupItem: LookupItem) { public displayFn(aLookupItem: LookupItem) {
@ -113,7 +171,7 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy {
public onBlurSymbol() { public onBlurSymbol() {
const currentLookupItem = this.filteredLookupItems.find((lookupItem) => { const currentLookupItem = this.filteredLookupItems.find((lookupItem) => {
return lookupItem.symbol === this.data.transaction.symbol; return lookupItem.symbol === this.data.activity.symbol;
}); });
if (currentLookupItem) { if (currentLookupItem) {
@ -121,9 +179,9 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy {
} else { } else {
this.searchSymbolCtrl.setErrors({ incorrect: true }); this.searchSymbolCtrl.setErrors({ incorrect: true });
this.data.transaction.currency = null; this.data.activity.currency = null;
this.data.transaction.dataSource = null; this.data.activity.dataSource = null;
this.data.transaction.symbol = null; this.data.activity.symbol = null;
} }
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
@ -133,8 +191,28 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy {
this.dialogRef.close(); this.dialogRef.close();
} }
public onSubmit() {
const activity: CreateOrderDto | UpdateOrderDto = {
accountId: this.accountIdCtrl.value,
currency: this.currencyCtrl.value,
date: this.dateCtrl.value,
dataSource: this.dataSourceCtrl.value,
fee: this.feeCtrl.value,
quantity: this.quantityCtrl.value,
symbol: this.searchSymbolCtrl.value.symbol ?? this.nameCtrl.value,
type: this.typeCtrl.value,
unitPrice: this.unitPriceCtrl.value
};
if (this.data.activity.id) {
(activity as UpdateOrderDto).id = this.data.activity.id;
}
this.dialogRef.close({ activity });
}
public onUpdateSymbol(event: MatAutocompleteSelectedEvent) { public onUpdateSymbol(event: MatAutocompleteSelectedEvent) {
this.data.transaction.dataSource = event.option.value.dataSource; this.dataSourceCtrl.setValue(event.option.value.dataSource);
this.updateSymbol(event.option.value.symbol); this.updateSymbol(event.option.value.symbol);
} }
@ -148,18 +226,20 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy {
this.searchSymbolCtrl.setErrors(null); this.searchSymbolCtrl.setErrors(null);
this.data.transaction.symbol = symbol; this.searchSymbolCtrl.setValue({ symbol });
this.changeDetectorRef.markForCheck();
this.dataService this.dataService
.fetchSymbolItem({ .fetchSymbolItem({
dataSource: this.data.transaction.dataSource, dataSource: this.dataSourceCtrl.value,
symbol: this.data.transaction.symbol symbol: this.searchSymbolCtrl.value.symbol
}) })
.pipe( .pipe(
catchError(() => { catchError(() => {
this.data.transaction.currency = null; this.data.activity.currency = null;
this.data.transaction.dataSource = null; this.data.activity.dataSource = null;
this.data.transaction.unitPrice = null; this.data.activity.unitPrice = null;
this.isLoading = false; this.isLoading = false;
@ -170,8 +250,9 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy {
takeUntil(this.unsubscribeSubject) takeUntil(this.unsubscribeSubject)
) )
.subscribe(({ currency, dataSource, marketPrice }) => { .subscribe(({ currency, dataSource, marketPrice }) => {
this.data.transaction.currency = currency; this.currencyCtrl.setValue(currency);
this.data.transaction.dataSource = dataSource; this.dataSourceCtrl.setValue(dataSource);
this.currentMarketPrice = marketPrice; this.currentMarketPrice = marketPrice;
this.isLoading = false; this.isLoading = false;

79
apps/client/src/app/pages/portfolio/transactions/create-or-update-transaction-dialog/create-or-update-transaction-dialog.html

@ -1,22 +1,29 @@
<form #addTransactionForm="ngForm" class="d-flex flex-column h-100"> <form #addTransactionForm="ngForm" class="d-flex flex-column h-100">
<h1 *ngIf="data.transaction.id" mat-dialog-title i18n>Update activity</h1> <h1 *ngIf="data.activity.id" mat-dialog-title i18n>Update activity</h1>
<h1 *ngIf="!data.transaction.id" mat-dialog-title i18n>Add activity</h1> <h1 *ngIf="!data.activity.id" mat-dialog-title i18n>Add activity</h1>
<div class="flex-grow-1" mat-dialog-content> <div class="flex-grow-1" mat-dialog-content>
<div> <div>
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Type</mat-label>
<mat-select name="type" [formControl]="typeCtrl">
<mat-option value="BUY" i18n>BUY</mat-option>
<mat-option value="DIVIDEND" i18n>DIVIDEND</mat-option>
<mat-option value="ITEM" i18n>ITEM</mat-option>
<mat-option value="SELL" i18n>SELL</mat-option>
</mat-select>
</mat-form-field>
</div>
<div [ngClass]="{ 'd-none': !showAccountIdCtrl }">
<mat-form-field appearance="outline" class="w-100"> <mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Account</mat-label> <mat-label i18n>Account</mat-label>
<mat-select <mat-select name="accountId" [formControl]="accountIdCtrl">
name="accountId"
required
[(value)]="data.transaction.accountId"
>
<mat-option *ngFor="let account of data.accounts" [value]="account.id" <mat-option *ngFor="let account of data.accounts" [value]="account.id"
>{{ account.name }}</mat-option >{{ account.name }}</mat-option
> >
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
<div> <div [ngClass]="{ 'd-none': !showSearchSymbolCtrl }">
<mat-form-field appearance="outline" class="w-100"> <mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Symbol or ISIN</mat-label> <mat-label i18n>Symbol or ISIN</mat-label>
<input <input
@ -24,7 +31,6 @@
autocomplete="off" autocomplete="off"
autocorrect="off" autocorrect="off"
matInput matInput
required
[formControl]="searchSymbolCtrl" [formControl]="searchSymbolCtrl"
[matAutocomplete]="autocomplete" [matAutocomplete]="autocomplete"
(blur)="onBlurSymbol()" (blur)="onBlurSymbol()"
@ -48,15 +54,10 @@
<mat-spinner *ngIf="isLoading" matSuffix [diameter]="20"></mat-spinner> <mat-spinner *ngIf="isLoading" matSuffix [diameter]="20"></mat-spinner>
</mat-form-field> </mat-form-field>
</div> </div>
<div> <div [ngClass]="{ 'd-none': !showNameCtrl }">
<mat-form-field appearance="outline" class="w-100"> <mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Type</mat-label> <mat-label i18n>Name</mat-label>
<mat-select name="type" required [(value)]="data.transaction.type"> <input matInput name="name" [formControl]="nameCtrl" />
<mat-option value="BUY" i18n>BUY</mat-option>
<mat-option value="DIVIDEND" i18n>DIVIDEND</mat-option>
<mat-option value="ITEM" i18n>ITEM</mat-option>
<mat-option value="SELL" i18n>SELL</mat-option>
</mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="d-none"> <div class="d-none">
@ -64,10 +65,8 @@
<mat-label i18n>Currency</mat-label> <mat-label i18n>Currency</mat-label>
<mat-select <mat-select
class="no-arrow" class="no-arrow"
disabled
name="currency" name="currency"
required [formControl]="currencyCtrl"
[(value)]="data.transaction.currency"
> >
<mat-option *ngFor="let currency of currencies" [value]="currency" <mat-option *ngFor="let currency of currencies" [value]="currency"
>{{ currency }}</mat-option >{{ currency }}</mat-option
@ -78,25 +77,17 @@
<div class="d-none"> <div class="d-none">
<mat-form-field appearance="outline" class="w-100"> <mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Data Source</mat-label> <mat-label i18n>Data Source</mat-label>
<input <input matInput name="dataSource" [formControl]="dataSourceCtrl" />
disabled
matInput
name="dataSource"
required
[(ngModel)]="data.transaction.dataSource"
/>
</mat-form-field> </mat-form-field>
</div> </div>
<div> <div>
<mat-form-field appearance="outline" class="w-100"> <mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Date</mat-label> <mat-label i18n>Date</mat-label>
<input <input
disabled
matInput matInput
name="date" name="date"
required [formControl]="dateCtrl"
[matDatepicker]="date" [matDatepicker]="date"
[(ngModel)]="data.transaction.date"
/> />
<mat-datepicker-toggle matSuffix [for]="date"> <mat-datepicker-toggle matSuffix [for]="date">
<ion-icon <ion-icon
@ -114,9 +105,8 @@
<input <input
matInput matInput
name="quantity" name="quantity"
required
type="number" type="number"
[(ngModel)]="data.transaction.quantity" [formControl]="quantityCtrl"
/> />
</mat-form-field> </mat-form-field>
</div> </div>
@ -126,13 +116,12 @@
<input <input
matInput matInput
name="unitPrice" name="unitPrice"
required
type="number" type="number"
[(ngModel)]="data.transaction.unitPrice" [formControl]="unitPriceCtrl"
/> />
<span class="ml-2" matSuffix>{{ data.transaction.currency }}</span> <span class="ml-2" matSuffix>{{ currencyCtrl.value }}</span>
<button <button
*ngIf="currentMarketPrice && (data.transaction.type === 'BUY' || data.transaction.type === 'SELL')" *ngIf="currentMarketPrice && (data.activity.type === 'BUY' || data.activity.type === 'SELL')"
mat-icon-button mat-icon-button
matSuffix matSuffix
title="Apply current market price" title="Apply current market price"
@ -145,23 +134,17 @@
<div> <div>
<mat-form-field appearance="outline" class="w-100"> <mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Fee</mat-label> <mat-label i18n>Fee</mat-label>
<input <input matInput name="fee" type="number" [formControl]="feeCtrl" />
matInput <span class="ml-2" matSuffix>{{ currencyCtrl.value }}</span>
name="fee"
required
type="number"
[(ngModel)]="data.transaction.fee"
/>
<span class="ml-2" matSuffix>{{ data.transaction.currency }}</span>
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
<div class="d-flex" mat-dialog-actions> <div class="d-flex" mat-dialog-actions>
<gf-value <gf-value
class="flex-grow-1" class="flex-grow-1"
[currency]="data.transaction.currency" [currency]="currencyCtrl.value"
[locale]="data.user?.settings?.locale" [locale]="data.user?.settings?.locale"
[value]="data.transaction.fee + (data.transaction.quantity * data.transaction.unitPrice)" [value]="feeCtrl.value + (quantityCtrl.value * unitPriceCtrl.value) ?? 0"
></gf-value> ></gf-value>
<div> <div>
<button i18n mat-button (click)="onCancel()">Cancel</button> <button i18n mat-button (click)="onCancel()">Cancel</button>
@ -169,8 +152,8 @@
color="primary" color="primary"
i18n i18n
mat-flat-button mat-flat-button
[disabled]="!(addTransactionForm.form.valid && data.transaction.currency && data.transaction.symbol)" [disabled]="!(addTransactionForm.form.valid)"
[mat-dialog-close]="data" (click)="onSubmit()"
> >
Save Save
</button> </button>

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

@ -1,9 +1,10 @@
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { User } from '@ghostfolio/common/interfaces'; import { User } from '@ghostfolio/common/interfaces';
import { Account, Order } from '@prisma/client'; import { Account } from '@prisma/client';
export interface CreateOrUpdateTransactionDialogParams { export interface CreateOrUpdateTransactionDialogParams {
accountId: string; accountId: string;
accounts: Account[]; accounts: Account[];
transaction: Order; activity: Activity;
user: User; user: User;
} }

32
apps/client/src/app/pages/portfolio/transactions/transactions-page.component.ts

@ -242,35 +242,13 @@ export class TransactionsPageComponent implements OnDestroy, OnInit {
}); });
} }
public openUpdateTransactionDialog({ public openUpdateTransactionDialog(activity: Activity): void {
accountId,
currency,
dataSource,
date,
fee,
id,
quantity,
symbol,
type,
unitPrice
}: OrderModel): void {
const dialogRef = this.dialog.open(CreateOrUpdateTransactionDialog, { const dialogRef = this.dialog.open(CreateOrUpdateTransactionDialog, {
data: { data: {
activity,
accounts: this.user?.accounts?.filter((account) => { accounts: this.user?.accounts?.filter((account) => {
return account.accountType === 'SECURITIES'; return account.accountType === 'SECURITIES';
}), }),
transaction: {
accountId,
currency,
dataSource,
date,
fee,
id,
quantity,
symbol,
type,
unitPrice
},
user: this.user user: this.user
}, },
height: this.deviceType === 'mobile' ? '97.5vh' : '80vh', height: this.deviceType === 'mobile' ? '97.5vh' : '80vh',
@ -281,7 +259,7 @@ export class TransactionsPageComponent implements OnDestroy, OnInit {
.afterClosed() .afterClosed()
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe((data: any) => { .subscribe((data: any) => {
const transaction: UpdateOrderDto = data?.transaction; const transaction: UpdateOrderDto = data?.activity;
if (transaction) { if (transaction) {
this.dataService this.dataService
@ -336,7 +314,7 @@ export class TransactionsPageComponent implements OnDestroy, OnInit {
accounts: this.user?.accounts?.filter((account) => { accounts: this.user?.accounts?.filter((account) => {
return account.accountType === 'SECURITIES'; return account.accountType === 'SECURITIES';
}), }),
transaction: { activity: {
accountId: aTransaction?.accountId ?? this.defaultAccountId, accountId: aTransaction?.accountId ?? this.defaultAccountId,
currency: aTransaction?.currency ?? null, currency: aTransaction?.currency ?? null,
dataSource: aTransaction?.dataSource ?? null, dataSource: aTransaction?.dataSource ?? null,
@ -357,7 +335,7 @@ export class TransactionsPageComponent implements OnDestroy, OnInit {
.afterClosed() .afterClosed()
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe((data: any) => { .subscribe((data: any) => {
const transaction: CreateOrderDto = data?.transaction; const transaction: CreateOrderDto = data?.activity;
if (transaction) { if (transaction) {
this.dataService.postOrder(transaction).subscribe({ this.dataService.postOrder(transaction).subscribe({

2
apps/client/src/app/services/admin.service.ts

@ -6,7 +6,7 @@ import { DATE_FORMAT } from '@ghostfolio/common/helper';
import { AdminMarketDataDetails } from '@ghostfolio/common/interfaces'; import { AdminMarketDataDetails } from '@ghostfolio/common/interfaces';
import { DataSource, MarketData } from '@prisma/client'; import { DataSource, MarketData } from '@prisma/client';
import { format, parseISO } from 'date-fns'; import { format, parseISO } from 'date-fns';
import { map, Observable } from 'rxjs'; import { Observable, map } from 'rxjs';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'

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

@ -106,6 +106,12 @@ export function isGhostfolioScraperApiSymbol(aSymbol = '') {
return aSymbol.startsWith(ghostfolioScraperApiSymbolPrefix); return aSymbol.startsWith(ghostfolioScraperApiSymbolPrefix);
} }
export function isUUID(aString: string) {
const regexExp =
/^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/gi;
return regexExp.test(aString);
}
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);

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

@ -115,7 +115,12 @@
</th> </th>
<td *matCellDef="let element" class="px-1" mat-cell> <td *matCellDef="let element" class="px-1" mat-cell>
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
{{ element.SymbolProfile.symbol | gfSymbol }} <span *ngIf="isUUID(element.SymbolProfile.symbol); else symbol">
{{ element.SymbolProfile.name }}
</span>
<ng-template #symbol>
{{ element.SymbolProfile.symbol | gfSymbol }}
</ng-template>
<span *ngIf="element.isDraft" class="badge badge-secondary ml-1" i18n <span *ngIf="element.isDraft" class="badge badge-secondary ml-1" i18n
>Draft</span >Draft</span
> >

8
libs/ui/src/lib/activities-table/activities-table.component.ts

@ -21,6 +21,7 @@ import { MatTableDataSource } from '@angular/material/table';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { DEFAULT_DATE_FORMAT } from '@ghostfolio/common/config'; import { DEFAULT_DATE_FORMAT } from '@ghostfolio/common/config';
import { isUUID } from '@ghostfolio/common/helper';
import { OrderWithAccount } from '@ghostfolio/common/types'; import { OrderWithAccount } from '@ghostfolio/common/types';
import { DataSource } from '@prisma/client'; import { DataSource } from '@prisma/client';
import Big from 'big.js'; import Big from 'big.js';
@ -69,6 +70,7 @@ export class ActivitiesTableComponent implements OnChanges, OnDestroy {
public filters: Observable<string[]> = this.filters$.asObservable(); public filters: Observable<string[]> = this.filters$.asObservable();
public isAfter = isAfter; public isAfter = isAfter;
public isLoading = true; public isLoading = true;
public isUUID = isUUID;
public placeholder = ''; public placeholder = '';
public routeQueryParams: Subscription; public routeQueryParams: Subscription;
public searchControl = new FormControl(); public searchControl = new FormControl();
@ -274,7 +276,11 @@ export class ActivitiesTableComponent implements OnChanges, OnDestroy {
fieldValues.add(activity.Account?.name); fieldValues.add(activity.Account?.name);
fieldValues.add(activity.Account?.Platform?.name); fieldValues.add(activity.Account?.Platform?.name);
fieldValues.add(activity.SymbolProfile.currency); fieldValues.add(activity.SymbolProfile.currency);
fieldValues.add(activity.SymbolProfile.symbol); fieldValues.add(
isUUID(activity.SymbolProfile.symbol)
? activity.SymbolProfile.name
: activity.SymbolProfile.symbol
);
fieldValues.add(activity.type); fieldValues.add(activity.type);
fieldValues.add(format(activity.date, 'yyyy')); fieldValues.add(format(activity.date, 'yyyy'));

Loading…
Cancel
Save