|
@ -6,11 +6,15 @@ import { |
|
|
OnDestroy, |
|
|
OnDestroy, |
|
|
ViewChild |
|
|
ViewChild |
|
|
} from '@angular/core'; |
|
|
} from '@angular/core'; |
|
|
import { FormControl, Validators } from '@angular/forms'; |
|
|
import { FormBuilder, FormGroup, 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 { isUUID } from 'class-validator'; |
|
|
import { isString } from 'lodash'; |
|
|
import { isString } from 'lodash'; |
|
|
import { EMPTY, Observable, Subject } from 'rxjs'; |
|
|
import { EMPTY, Observable, Subject } from 'rxjs'; |
|
|
import { |
|
|
import { |
|
@ -34,19 +38,15 @@ import { CreateOrUpdateTransactionDialogParams } from './interfaces/interfaces'; |
|
|
export class CreateOrUpdateTransactionDialog implements OnDestroy { |
|
|
export class CreateOrUpdateTransactionDialog implements OnDestroy { |
|
|
@ViewChild('autocomplete') autocomplete; |
|
|
@ViewChild('autocomplete') autocomplete; |
|
|
|
|
|
|
|
|
|
|
|
public activityForm: FormGroup; |
|
|
|
|
|
|
|
|
public currencies: string[] = []; |
|
|
public currencies: string[] = []; |
|
|
public currentMarketPrice = null; |
|
|
public currentMarketPrice = null; |
|
|
public filteredLookupItems: LookupItem[]; |
|
|
public filteredLookupItems: LookupItem[]; |
|
|
public filteredLookupItemsObservable: Observable<LookupItem[]>; |
|
|
public filteredLookupItemsObservable: Observable<LookupItem[]>; |
|
|
public isLoading = false; |
|
|
public isLoading = false; |
|
|
public platforms: { id: string; name: string }[]; |
|
|
public platforms: { id: string; name: string }[]; |
|
|
public searchSymbolCtrl = new FormControl( |
|
|
public Validators = Validators; |
|
|
{ |
|
|
|
|
|
dataSource: this.data.transaction.dataSource, |
|
|
|
|
|
symbol: this.data.transaction.symbol |
|
|
|
|
|
}, |
|
|
|
|
|
Validators.required |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
private unsubscribeSubject = new Subject<void>(); |
|
|
private unsubscribeSubject = new Subject<void>(); |
|
|
|
|
|
|
|
@ -54,6 +54,7 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy { |
|
|
private changeDetectorRef: ChangeDetectorRef, |
|
|
private changeDetectorRef: ChangeDetectorRef, |
|
|
private dataService: DataService, |
|
|
private dataService: DataService, |
|
|
public dialogRef: MatDialogRef<CreateOrUpdateTransactionDialog>, |
|
|
public dialogRef: MatDialogRef<CreateOrUpdateTransactionDialog>, |
|
|
|
|
|
private formBuilder: FormBuilder, |
|
|
@Inject(MAT_DIALOG_DATA) public data: CreateOrUpdateTransactionDialogParams |
|
|
@Inject(MAT_DIALOG_DATA) public data: CreateOrUpdateTransactionDialogParams |
|
|
) {} |
|
|
) {} |
|
|
|
|
|
|
|
@ -63,36 +64,105 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy { |
|
|
this.currencies = currencies; |
|
|
this.currencies = currencies; |
|
|
this.platforms = platforms; |
|
|
this.platforms = platforms; |
|
|
|
|
|
|
|
|
this.filteredLookupItemsObservable = |
|
|
this.activityForm = this.formBuilder.group({ |
|
|
this.searchSymbolCtrl.valueChanges.pipe( |
|
|
accountId: [this.data.activity?.accountId, Validators.required], |
|
|
startWith(''), |
|
|
currency: [ |
|
|
debounceTime(400), |
|
|
this.data.activity?.SymbolProfile?.currency, |
|
|
distinctUntilChanged(), |
|
|
Validators.required |
|
|
switchMap((query: string) => { |
|
|
], |
|
|
if (isString(query)) { |
|
|
dataSource: [ |
|
|
const filteredLookupItemsObservable = |
|
|
this.data.activity?.SymbolProfile?.dataSource, |
|
|
this.dataService.fetchSymbols(query); |
|
|
Validators.required |
|
|
|
|
|
], |
|
|
filteredLookupItemsObservable.subscribe((filteredLookupItems) => { |
|
|
date: [this.data.activity?.date, Validators.required], |
|
|
this.filteredLookupItems = filteredLookupItems; |
|
|
fee: [this.data.activity?.fee, Validators.required], |
|
|
}); |
|
|
name: [this.data.activity?.SymbolProfile?.name, Validators.required], |
|
|
|
|
|
quantity: [this.data.activity?.quantity, Validators.required], |
|
|
|
|
|
searchSymbol: [ |
|
|
|
|
|
{ |
|
|
|
|
|
dataSource: this.data.activity?.SymbolProfile?.dataSource, |
|
|
|
|
|
symbol: this.data.activity?.SymbolProfile?.symbol |
|
|
|
|
|
}, |
|
|
|
|
|
Validators.required |
|
|
|
|
|
], |
|
|
|
|
|
type: [undefined, Validators.required], // Set after value changes subscription
|
|
|
|
|
|
unitPrice: [this.data.activity?.unitPrice, Validators.required] |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
return filteredLookupItemsObservable; |
|
|
this.filteredLookupItemsObservable = this.activityForm.controls[ |
|
|
} |
|
|
'searchSymbol' |
|
|
|
|
|
].valueChanges.pipe( |
|
|
|
|
|
startWith(''), |
|
|
|
|
|
debounceTime(400), |
|
|
|
|
|
distinctUntilChanged(), |
|
|
|
|
|
switchMap((query: string) => { |
|
|
|
|
|
if (isString(query)) { |
|
|
|
|
|
const filteredLookupItemsObservable = |
|
|
|
|
|
this.dataService.fetchSymbols(query); |
|
|
|
|
|
|
|
|
|
|
|
filteredLookupItemsObservable.subscribe((filteredLookupItems) => { |
|
|
|
|
|
this.filteredLookupItems = filteredLookupItems; |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
return filteredLookupItemsObservable; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return []; |
|
|
|
|
|
}) |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
this.activityForm.controls['type'].valueChanges.subscribe((type: Type) => { |
|
|
|
|
|
if (type === 'ITEM') { |
|
|
|
|
|
this.activityForm.controls['accountId'].removeValidators( |
|
|
|
|
|
Validators.required |
|
|
|
|
|
); |
|
|
|
|
|
this.activityForm.controls['accountId'].updateValueAndValidity(); |
|
|
|
|
|
this.activityForm.controls['currency'].setValue( |
|
|
|
|
|
this.data.user.settings.baseCurrency |
|
|
|
|
|
); |
|
|
|
|
|
this.activityForm.controls['dataSource'].removeValidators( |
|
|
|
|
|
Validators.required |
|
|
|
|
|
); |
|
|
|
|
|
this.activityForm.controls['dataSource'].updateValueAndValidity(); |
|
|
|
|
|
this.activityForm.controls['name'].setValidators(Validators.required); |
|
|
|
|
|
this.activityForm.controls['name'].updateValueAndValidity(); |
|
|
|
|
|
this.activityForm.controls['quantity'].setValue(1); |
|
|
|
|
|
this.activityForm.controls['searchSymbol'].removeValidators( |
|
|
|
|
|
Validators.required |
|
|
|
|
|
); |
|
|
|
|
|
this.activityForm.controls['searchSymbol'].updateValueAndValidity(); |
|
|
|
|
|
} else { |
|
|
|
|
|
this.activityForm.controls['accountId'].setValidators( |
|
|
|
|
|
Validators.required |
|
|
|
|
|
); |
|
|
|
|
|
this.activityForm.controls['accountId'].updateValueAndValidity(); |
|
|
|
|
|
this.activityForm.controls['dataSource'].setValidators( |
|
|
|
|
|
Validators.required |
|
|
|
|
|
); |
|
|
|
|
|
this.activityForm.controls['dataSource'].updateValueAndValidity(); |
|
|
|
|
|
this.activityForm.controls['name'].removeValidators( |
|
|
|
|
|
Validators.required |
|
|
|
|
|
); |
|
|
|
|
|
this.activityForm.controls['name'].updateValueAndValidity(); |
|
|
|
|
|
this.activityForm.controls['searchSymbol'].setValidators( |
|
|
|
|
|
Validators.required |
|
|
|
|
|
); |
|
|
|
|
|
this.activityForm.controls['searchSymbol'].updateValueAndValidity(); |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
return []; |
|
|
this.activityForm.controls['type'].setValue(this.data.activity?.type); |
|
|
}) |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
if (this.data.transaction.id) { |
|
|
if (this.data.activity?.id) { |
|
|
this.searchSymbolCtrl.disable(); |
|
|
this.activityForm.controls['searchSymbol'].disable(); |
|
|
|
|
|
this.activityForm.controls['type'].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 +174,9 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public applyCurrentMarketPrice() { |
|
|
public applyCurrentMarketPrice() { |
|
|
this.data.transaction.unitPrice = this.currentMarketPrice; |
|
|
this.activityForm.patchValue({ |
|
|
|
|
|
unitPrice: this.currentMarketPrice |
|
|
|
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public displayFn(aLookupItem: LookupItem) { |
|
|
public displayFn(aLookupItem: LookupItem) { |
|
@ -113,17 +185,20 @@ 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.activityForm.controls['searchSymbol'].value.symbol |
|
|
|
|
|
); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
if (currentLookupItem) { |
|
|
if (currentLookupItem) { |
|
|
this.updateSymbol(currentLookupItem.symbol); |
|
|
this.updateSymbol(currentLookupItem.symbol); |
|
|
} else { |
|
|
} else { |
|
|
this.searchSymbolCtrl.setErrors({ incorrect: true }); |
|
|
this.activityForm.controls['searchSymbol'].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 +208,32 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy { |
|
|
this.dialogRef.close(); |
|
|
this.dialogRef.close(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public onSubmit() { |
|
|
|
|
|
const activity: CreateOrderDto | UpdateOrderDto = { |
|
|
|
|
|
accountId: this.activityForm.controls['accountId'].value, |
|
|
|
|
|
currency: this.activityForm.controls['currency'].value, |
|
|
|
|
|
date: this.activityForm.controls['date'].value, |
|
|
|
|
|
dataSource: this.activityForm.controls['dataSource'].value, |
|
|
|
|
|
fee: this.activityForm.controls['fee'].value, |
|
|
|
|
|
quantity: this.activityForm.controls['quantity'].value, |
|
|
|
|
|
symbol: isUUID(this.activityForm.controls['searchSymbol'].value.symbol) |
|
|
|
|
|
? this.activityForm.controls['name'].value |
|
|
|
|
|
: this.activityForm.controls['searchSymbol'].value.symbol, |
|
|
|
|
|
type: this.activityForm.controls['type'].value, |
|
|
|
|
|
unitPrice: this.activityForm.controls['unitPrice'].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.activityForm.controls['dataSource'].setValue( |
|
|
|
|
|
event.option.value.dataSource |
|
|
|
|
|
); |
|
|
this.updateSymbol(event.option.value.symbol); |
|
|
this.updateSymbol(event.option.value.symbol); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -146,20 +245,21 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy { |
|
|
private updateSymbol(symbol: string) { |
|
|
private updateSymbol(symbol: string) { |
|
|
this.isLoading = true; |
|
|
this.isLoading = true; |
|
|
|
|
|
|
|
|
this.searchSymbolCtrl.setErrors(null); |
|
|
this.activityForm.controls['searchSymbol'].setErrors(null); |
|
|
|
|
|
this.activityForm.controls['searchSymbol'].setValue({ symbol }); |
|
|
|
|
|
|
|
|
this.data.transaction.symbol = symbol; |
|
|
this.changeDetectorRef.markForCheck(); |
|
|
|
|
|
|
|
|
this.dataService |
|
|
this.dataService |
|
|
.fetchSymbolItem({ |
|
|
.fetchSymbolItem({ |
|
|
dataSource: this.data.transaction.dataSource, |
|
|
dataSource: this.activityForm.controls['dataSource'].value, |
|
|
symbol: this.data.transaction.symbol |
|
|
symbol: this.activityForm.controls['searchSymbol'].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 +270,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.activityForm.controls['currency'].setValue(currency); |
|
|
this.data.transaction.dataSource = dataSource; |
|
|
this.activityForm.controls['dataSource'].setValue(dataSource); |
|
|
|
|
|
|
|
|
this.currentMarketPrice = marketPrice; |
|
|
this.currentMarketPrice = marketPrice; |
|
|
|
|
|
|
|
|
this.isLoading = false; |
|
|
this.isLoading = false; |
|
|