parent
c61a415fb2
commit
b7bbc029ac
@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- Added the tags to the create or edit transaction dialog
|
||||
- Added the tags to the position detail dialog
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed the date to UTC in the data gathering service
|
||||
|
@ -202,7 +202,8 @@ export class OrderService {
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
SymbolProfile: true
|
||||
SymbolProfile: true,
|
||||
tags: true
|
||||
},
|
||||
orderBy: { date: 'asc' }
|
||||
})
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { EnhancedSymbolProfile } from '@ghostfolio/api/services/interfaces/symbol-profile.interface';
|
||||
import { OrderWithAccount } from '@ghostfolio/common/types';
|
||||
import { Tag } from '@prisma/client';
|
||||
|
||||
export interface PortfolioPositionDetail {
|
||||
averagePrice: number;
|
||||
@ -16,6 +17,7 @@ export interface PortfolioPositionDetail {
|
||||
orders: OrderWithAccount[];
|
||||
quantity: number;
|
||||
SymbolProfile: EnhancedSymbolProfile;
|
||||
tags: Tag[];
|
||||
transactionCount: number;
|
||||
value: number;
|
||||
}
|
||||
|
@ -46,7 +46,12 @@ import type {
|
||||
} from '@ghostfolio/common/types';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { REQUEST } from '@nestjs/core';
|
||||
import { AssetClass, DataSource, Type as TypeOfOrder } from '@prisma/client';
|
||||
import {
|
||||
AssetClass,
|
||||
DataSource,
|
||||
Tag,
|
||||
Type as TypeOfOrder
|
||||
} from '@prisma/client';
|
||||
import Big from 'big.js';
|
||||
import {
|
||||
differenceInDays,
|
||||
@ -62,7 +67,7 @@ import {
|
||||
subDays,
|
||||
subYears
|
||||
} from 'date-fns';
|
||||
import { isEmpty, sortBy } from 'lodash';
|
||||
import { isEmpty, sortBy, uniqBy } from 'lodash';
|
||||
|
||||
import {
|
||||
HistoricalDataContainer,
|
||||
@ -476,8 +481,11 @@ export class PortfolioService {
|
||||
);
|
||||
});
|
||||
|
||||
let tags: Tag[] = [];
|
||||
|
||||
if (orders.length <= 0) {
|
||||
return {
|
||||
tags,
|
||||
averagePrice: undefined,
|
||||
firstBuyDate: undefined,
|
||||
grossPerformance: undefined,
|
||||
@ -504,6 +512,8 @@ export class PortfolioService {
|
||||
|
||||
const portfolioOrders: PortfolioOrder[] = orders
|
||||
.filter((order) => {
|
||||
tags = tags.concat(order.tags);
|
||||
|
||||
return order.type === 'BUY' || order.type === 'SELL';
|
||||
})
|
||||
.map((order) => ({
|
||||
@ -518,6 +528,8 @@ export class PortfolioService {
|
||||
unitPrice: new Big(order.unitPrice)
|
||||
}));
|
||||
|
||||
tags = uniqBy(tags, 'id');
|
||||
|
||||
const portfolioCalculator = new PortfolioCalculator({
|
||||
currency: positionCurrency,
|
||||
currentRateService: this.currentRateService,
|
||||
@ -626,6 +638,7 @@ export class PortfolioService {
|
||||
netPerformance,
|
||||
orders,
|
||||
SymbolProfile,
|
||||
tags,
|
||||
transactionCount,
|
||||
averagePrice: averagePrice.toNumber(),
|
||||
grossPerformancePercent:
|
||||
@ -682,6 +695,7 @@ export class PortfolioService {
|
||||
minPrice,
|
||||
orders,
|
||||
SymbolProfile,
|
||||
tags,
|
||||
averagePrice: 0,
|
||||
firstBuyDate: undefined,
|
||||
grossPerformance: undefined,
|
||||
|
@ -11,7 +11,7 @@ import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { DATE_FORMAT, downloadAsFile } from '@ghostfolio/common/helper';
|
||||
import { OrderWithAccount } from '@ghostfolio/common/types';
|
||||
import { LineChartItem } from '@ghostfolio/ui/line-chart/interfaces/line-chart.interface';
|
||||
import { SymbolProfile } from '@prisma/client';
|
||||
import { SymbolProfile, Tag } from '@prisma/client';
|
||||
import { format, isSameMonth, isToday, parseISO } from 'date-fns';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
@ -48,6 +48,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
||||
[name: string]: { name: string; value: number };
|
||||
};
|
||||
public SymbolProfile: SymbolProfile;
|
||||
public tags: Tag[];
|
||||
public transactionCount: number;
|
||||
public value: number;
|
||||
|
||||
@ -83,6 +84,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
||||
orders,
|
||||
quantity,
|
||||
SymbolProfile,
|
||||
tags,
|
||||
transactionCount,
|
||||
value
|
||||
}) => {
|
||||
@ -115,6 +117,7 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
|
||||
this.quantity = quantity;
|
||||
this.sectors = {};
|
||||
this.SymbolProfile = SymbolProfile;
|
||||
this.tags = tags;
|
||||
this.transactionCount = transactionCount;
|
||||
this.value = value;
|
||||
|
||||
|
@ -194,6 +194,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="h4 mb-0" i18n>Activities</div>
|
||||
<gf-activities-table
|
||||
*ngIf="orders?.length > 0"
|
||||
[activities]="orders"
|
||||
@ -211,6 +213,14 @@
|
||||
></gf-activities-table>
|
||||
</div>
|
||||
|
||||
<div *ngIf="tags?.length > 0">
|
||||
<div class="h4" i18n>Tags</div>
|
||||
<mat-chip-list>
|
||||
<mat-chip *ngFor="let tag of tags">{{ tag.name }}</mat-chip>
|
||||
</mat-chip-list>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<gf-dialog-footer
|
||||
mat-dialog-actions
|
||||
[deviceType]="data.deviceType"
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatChipsModule } from '@angular/material/chips';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-footer/dialog-footer.module';
|
||||
import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module';
|
||||
@ -24,6 +25,7 @@ import { PositionDetailDialog } from './position-detail-dialog.component';
|
||||
GfPortfolioProportionChartModule,
|
||||
GfValueModule,
|
||||
MatButtonModule,
|
||||
MatChipsModule,
|
||||
MatDialogModule,
|
||||
NgxSkeletonLoaderModule
|
||||
],
|
||||
|
@ -86,6 +86,7 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy {
|
||||
},
|
||||
Validators.required
|
||||
],
|
||||
tags: [this.data.activity?.tags],
|
||||
type: [undefined, Validators.required], // Set after value changes subscription
|
||||
unitPrice: [this.data.activity?.unitPrice, Validators.required]
|
||||
});
|
||||
|
@ -134,6 +134,18 @@
|
||||
>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div
|
||||
[ngClass]="{ 'd-none': activityForm.controls['tags']?.value?.length <= 0 }"
|
||||
>
|
||||
<mat-form-field appearance="outline" class="w-100">
|
||||
<mat-label i18n>Tags</mat-label>
|
||||
<mat-chip-list>
|
||||
<mat-chip *ngFor="let tag of activityForm.controls['tags']?.value">
|
||||
{{ tag.name }}
|
||||
</mat-chip>
|
||||
</mat-chip-list>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex" mat-dialog-actions>
|
||||
<gf-value
|
||||
|
@ -3,6 +3,7 @@ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatChipsModule } from '@angular/material/chips';
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
@ -24,6 +25,7 @@ import { CreateOrUpdateTransactionDialog } from './create-or-update-transaction-
|
||||
FormsModule,
|
||||
MatAutocompleteModule,
|
||||
MatButtonModule,
|
||||
MatChipsModule,
|
||||
MatDatepickerModule,
|
||||
MatDialogModule,
|
||||
MatFormFieldModule,
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { Account, Order, Platform, SymbolProfile } from '@prisma/client';
|
||||
import { Account, Order, Platform, SymbolProfile, Tag } from '@prisma/client';
|
||||
|
||||
type AccountWithPlatform = Account & { Platform?: Platform };
|
||||
|
||||
export type OrderWithAccount = Order & {
|
||||
Account?: AccountWithPlatform;
|
||||
SymbolProfile?: SymbolProfile;
|
||||
tags?: Tag[];
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user