diff --git a/apps/api/src/app/admin/admin.controller.ts b/apps/api/src/app/admin/admin.controller.ts index 5cc6caf61..f9b158f5b 100644 --- a/apps/api/src/app/admin/admin.controller.ts +++ b/apps/api/src/app/admin/admin.controller.ts @@ -334,14 +334,14 @@ export class AdminController { @Patch('profile-data/:dataSource/:symbol') @UseGuards(AuthGuard('jwt'), HasPermissionGuard) public async patchAssetProfileData( - @Body() assetProfileData: UpdateAssetProfileDto, + @Body() assetProfile: UpdateAssetProfileDto, @Param('dataSource') dataSource: DataSource, @Param('symbol') symbol: string ): Promise { return this.adminService.patchAssetProfileData( { dataSource, symbol }, { - ...assetProfileData + ...assetProfile } ); } diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index 9c1c8f109..20a2d2f50 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -492,14 +492,14 @@ export class AdminService { newDataSource && (newSymbol !== symbol || newDataSource !== dataSource) ) { - const [profile] = await this.symbolProfileService.getSymbolProfiles([ + const [assetProfile] = await this.symbolProfileService.getSymbolProfiles([ { dataSource: newDataSource as DataSource, symbol: newSymbol as string } ]); - if (profile) { + if (assetProfile) { throw new HttpException( getReasonPhrase(StatusCodes.CONFLICT), StatusCodes.CONFLICT @@ -507,37 +507,35 @@ export class AdminService { } try { - await this.symbolProfileService.updateAssetProfileIdentifier( - { - dataSource, - symbol - }, - { - dataSource: newDataSource as DataSource, - symbol: newSymbol as string - } - ); - - await this.marketDataService.updateAssetProfileIdentifier( - { - dataSource, - symbol - }, - { - dataSource: newDataSource as DataSource, - symbol: newSymbol as string - } - ); - - const [symbolProfile] = - await this.symbolProfileService.getSymbolProfiles([ + Promise.all([ + await this.symbolProfileService.updateAssetProfileIdentifier( + { + dataSource, + symbol + }, + { + dataSource: newDataSource as DataSource, + symbol: newSymbol as string + } + ), + await this.marketDataService.updateAssetProfileIdentifier( + { + dataSource, + symbol + }, { dataSource: newDataSource as DataSource, symbol: newSymbol as string } - ]); + ) + ]); - return symbolProfile; + return this.symbolProfileService.getSymbolProfiles([ + { + dataSource: newDataSource as DataSource, + symbol: newSymbol as string + } + ])?.[0]; } catch { throw new HttpException( getReasonPhrase(StatusCodes.BAD_REQUEST), @@ -582,16 +580,12 @@ export class AdminService { updatedSymbolProfile ); - const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles( - [ - { - dataSource: dataSource as DataSource, - symbol: symbol as string - } - ] - ); - - return symbolProfile; + return this.symbolProfileService.getSymbolProfiles([ + { + dataSource: dataSource as DataSource, + symbol: symbol as string + } + ])?.[0]; } } diff --git a/apps/api/src/services/market-data/market-data.service.ts b/apps/api/src/services/market-data/market-data.service.ts index 0cab0bcc0..390586b37 100644 --- a/apps/api/src/services/market-data/market-data.service.ts +++ b/apps/api/src/services/market-data/market-data.service.ts @@ -120,14 +120,8 @@ export class MarketDataService { symbol: newAssetProfileIdentifier.symbol }, where: { - AND: [ - { - dataSource: oldAssetProfileIdentifier.dataSource - }, - { - symbol: oldAssetProfileIdentifier.symbol - } - ] + dataSource: oldAssetProfileIdentifier.dataSource, + symbol: oldAssetProfileIdentifier.symbol } }); } diff --git a/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts b/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts index bf067bd7f..5fe268142 100644 --- a/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts +++ b/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts @@ -390,9 +390,9 @@ export class AdminMarketDataComponent .afterClosed() .pipe(takeUntil(this.unsubscribeSubject)) .subscribe( - (newAssetProfileIdentifer: AssetProfileIdentifier | undefined) => { - if (newAssetProfileIdentifer) { - this.onOpenAssetProfileDialog(newAssetProfileIdentifer); + (newAssetProfileIdentifier: AssetProfileIdentifier | undefined) => { + if (newAssetProfileIdentifier) { + this.onOpenAssetProfileDialog(newAssetProfileIdentifier); } else { this.router.navigate(['.'], { relativeTo: this.route }); } diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts index 33eaa346f..66790ce45 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts @@ -43,6 +43,7 @@ import { SymbolProfile } from '@prisma/client'; import { format } from 'date-fns'; +import { StatusCodes } from 'http-status-codes'; import ms from 'ms'; import { EMPTY, Subject } from 'rxjs'; import { catchError, takeUntil } from 'rxjs/operators'; @@ -93,7 +94,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { }); public assetProfileIdentifierForm = this.formBuilder.group( { - symbol: new FormControl( + assetProfileIdentifier: new FormControl( { symbol: null, dataSource: null }, [Validators.required] ) @@ -111,7 +112,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { public ghostfolioScraperApiSymbolPrefix = ghostfolioScraperApiSymbolPrefix; public historicalDataItems: LineChartItem[]; public isBenchmark = false; - public isEditSymbolMode = false; + public isEditAssetProfileIdentifierMode = false; public marketDataItems: MarketData[] = []; public modeValues = [ { @@ -250,30 +251,16 @@ export class AssetProfileDialog implements OnDestroy, OnInit { }); } - private isNewSymbolValid(control: AbstractControl): ValidationErrors { - const currentAssetProfileIdentifier: AssetProfileIdentifier | undefined = - control.get('symbol').value; - - if ( - currentAssetProfileIdentifier?.dataSource === this.data?.dataSource && - currentAssetProfileIdentifier?.symbol === this.data?.symbol - ) { - return { - equalsPreviousSymbol: true - }; - } - } - - public get isSymbolEditable() { + public get isAssetProfileIdentifierEditable() { return !this.assetProfileForm.dirty; } - public get isSymbolEditButtonInvisible() { - return this.assetProfile?.dataSource === 'MANUAL'; + public get isAssetProfileIdentifierEditButtonVisible() { + return this.assetProfile?.dataSource !== 'MANUAL'; } - public onCancelEditSymboleMode() { - this.isEditSymbolMode = false; + public onCancelEditAssetProfileIdentifierMode() { + this.isEditAssetProfileIdentifierMode = false; this.assetProfileForm.enable(); @@ -328,8 +315,8 @@ export class AssetProfileDialog implements OnDestroy, OnInit { }); } - public onSetEditSymboleMode() { - this.isEditSymbolMode = true; + public onSetEditAssetProfileIdentifierMode() { + this.isEditAssetProfileIdentifierMode = true; this.assetProfileForm.disable(); @@ -337,17 +324,18 @@ export class AssetProfileDialog implements OnDestroy, OnInit { } public async onSubmitAssetProfileIdentifierForm() { - const assetProfileIdentifierData: UpdateAssetProfileDto = { - dataSource: - this.assetProfileIdentifierForm.get('symbol').value.dataSource, - symbol: this.assetProfileIdentifierForm.get('symbol').value.symbol + const assetProfileIdentifier: UpdateAssetProfileDto = { + dataSource: this.assetProfileIdentifierForm.get('assetProfileIdentifier') + .value.dataSource, + symbol: this.assetProfileIdentifierForm.get('assetProfileIdentifier') + .value.symbol }; try { await validateObjectForForm({ classDto: UpdateAssetProfileDto, form: this.assetProfileIdentifierForm, - object: assetProfileIdentifierData + object: assetProfileIdentifier }); } catch (error) { console.error(error); @@ -362,14 +350,14 @@ export class AssetProfileDialog implements OnDestroy, OnInit { symbol: this.data.symbol }, { - ...assetProfileIdentifierData + ...assetProfileIdentifier } ) .pipe( catchError((error: HttpErrorResponse) => { - if (error.status === 409) { + if (error.status === StatusCodes.CONFLICT) { this.snackBar.open( - $localize`This symbol is already in use.`, + $localize`${assetProfileIdentifier.symbol} (${assetProfileIdentifier.dataSource}) is already in use.`, undefined, { duration: ms('3 seconds') @@ -377,7 +365,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { ); } else { this.snackBar.open( - $localize`An error occurred while updating the symbol.`, + $localize`An error occurred while updating to ${assetProfileIdentifier.symbol} (${assetProfileIdentifier.dataSource}).`, undefined, { duration: ms('3 seconds') @@ -390,12 +378,12 @@ export class AssetProfileDialog implements OnDestroy, OnInit { takeUntil(this.unsubscribeSubject) ) .subscribe(() => { - const newAssetProfileIdentifer = { - dataSource: assetProfileIdentifierData.dataSource, - symbol: assetProfileIdentifierData.symbol + const newAssetProfileIdentifier = { + dataSource: assetProfileIdentifier.dataSource, + symbol: assetProfileIdentifier.symbol }; - this.dialogRef.close(newAssetProfileIdentifer); + this.dialogRef.close(newAssetProfileIdentifier); }); } @@ -554,7 +542,6 @@ export class AssetProfileDialog implements OnDestroy, OnInit { public ngOnDestroy() { this.unsubscribeSubject.next(); - this.unsubscribeSubject.complete(); } @@ -563,4 +550,18 @@ export class AssetProfileDialog implements OnDestroy, OnInit { this.assetProfileFormElement.nativeElement.requestSubmit(); } } + + private isNewSymbolValid(control: AbstractControl): ValidationErrors { + const currentAssetProfileIdentifier: AssetProfileIdentifier | undefined = + control.get('assetProfileIdentifier').value; + + if ( + currentAssetProfileIdentifier?.dataSource === this.data?.dataSource && + currentAssetProfileIdentifier?.symbol === this.data?.symbol + ) { + return { + equalsPreviousProfileIdentifier: true + }; + } + } } diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html index 86210c890..4628c42e1 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -86,7 +86,7 @@ />
- @if (isEditSymbolMode) { + @if (isEditAssetProfileIdentifierMode) {
Name, symbol or ISIN @@ -110,19 +110,24 @@ mat-flat-button type="submit" [disabled]=" - assetProfileIdentifierForm.hasError('invalidData', 'symbol') || - assetProfileIdentifierForm.hasError('equalsPreviousSymbol') + assetProfileIdentifierForm.hasError( + 'invalidData', + 'assetProfileIdentifier' + ) || + assetProfileIdentifierForm.hasError( + 'equalsPreviousProfileIdentifier' + ) " > - Apply + Apply
@@ -132,7 +137,7 @@ >Symbol
-
+
@@ -307,7 +312,7 @@ color="primary" i18n [checked]="isBenchmark" - [disabled]="isEditSymbolMode" + [disabled]="isEditAssetProfileIdentifierMode" (change)=" isBenchmark ? onUnsetBenchmark({