From d236ecfe8501201a344d8e345a186f294ca1b032 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 14 Sep 2024 17:29:01 +0200 Subject: [PATCH 1/7] Feature/extend personal finance tools 20240914 (#3767) * Add Buxfer * Add Moneydance * Add Banktivity * Add Microsoft Money * Add Masttro * Add WealthPosition --- libs/common/src/lib/personal-finance-tools.ts | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/libs/common/src/lib/personal-finance-tools.ts b/libs/common/src/lib/personal-finance-tools.ts index ec1d78564..29fcc391e 100644 --- a/libs/common/src/lib/personal-finance-tools.ts +++ b/libs/common/src/lib/personal-finance-tools.ts @@ -44,6 +44,15 @@ export const personalFinanceTools: Product[] = [ pricingPerYear: '$120', slogan: 'Analyze and track your portfolio.' }, + { + hasFreePlan: false, + hasSelfHostingAbility: true, + key: 'banktivity', + name: 'Banktivity', + origin: 'United States', + pricingPerYear: '$59.99', + slogan: 'Proactive money management app for macOS & iOS' + }, { founded: 2022, hasFreePlan: true, @@ -62,6 +71,17 @@ export const personalFinanceTools: Product[] = [ pricingPerYear: '$100', slogan: 'Stock Portfolio Tracker for Smart Investors' }, + { + founded: 2007, + hasFreePlan: false, + hasSelfHostingAbility: false, + key: 'buxfer', + name: 'Buxfer', + origin: 'United States', + pricingPerYear: '$48', + regions: ['Global'], + slogan: 'Take control of your financial future' + }, { founded: 2013, hasFreePlan: true, @@ -329,6 +349,13 @@ export const personalFinanceTools: Product[] = [ regions: ['Global'], slogan: 'Track your investments' }, + { + founded: 2010, + key: 'masttro', + name: 'Masttro', + origin: 'United States', + slogan: 'Your platform for wealth in full view' + }, { founded: 2021, hasSelfHostingAbility: false, @@ -352,6 +379,14 @@ export const personalFinanceTools: Product[] = [ regions: ['Canada', 'United States'], slogan: 'The smartest way to track your crypto' }, + { + founded: 1991, + hasSelfHostingAbility: true, + key: 'microsoft-money', + name: 'Microsoft Money', + note: 'Microsoft Money was discontinued in 2010', + origin: 'United States' + }, { founded: 2019, hasFreePlan: false, @@ -362,6 +397,16 @@ export const personalFinanceTools: Product[] = [ pricingPerYear: '$99.99', slogan: 'The modern way to manage your money' }, + { + founded: 1999, + hasFreePlan: false, + hasSelfHostingAbility: true, + key: 'moneydance', + name: 'Moneydance', + origin: 'Scotland', + pricingPerYear: '$100', + slogan: 'Personal Finance Manager for Mac, Windows, and Linux' + }, { hasFreePlan: false, hasSelfHostingAbility: false, @@ -640,6 +685,14 @@ export const personalFinanceTools: Product[] = [ pricingPerYear: '$50', slogan: 'See all your investments in one place' }, + { + founded: 2018, + hasFreePlan: true, + key: 'wealthposition', + name: 'WealthPosition', + pricingPerYear: '$60', + slogan: 'Personal Finance & Budgeting App' + }, { founded: 2018, hasSelfHostingAbility: false, From 9fb80e5067e991b101813d7b0782dc99df78cb46 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 14 Sep 2024 17:29:52 +0200 Subject: [PATCH 2/7] Feature/remove accounts from holding endpoint (#3765) * Clean up accounts --- .../portfolio-holding-detail.interface.ts | 3 +-- apps/api/src/app/portfolio/portfolio.service.ts | 14 +------------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/apps/api/src/app/portfolio/interfaces/portfolio-holding-detail.interface.ts b/apps/api/src/app/portfolio/interfaces/portfolio-holding-detail.interface.ts index 3ce23a3bc..79e4d40dc 100644 --- a/apps/api/src/app/portfolio/interfaces/portfolio-holding-detail.interface.ts +++ b/apps/api/src/app/portfolio/interfaces/portfolio-holding-detail.interface.ts @@ -5,10 +5,9 @@ import { HistoricalDataItem } from '@ghostfolio/common/interfaces'; -import { Account, Tag } from '@prisma/client'; +import { Tag } from '@prisma/client'; export interface PortfolioHoldingDetail { - accounts: Account[]; averagePrice: number; dataProviderInfo: DataProviderInfo; dividendInBaseCurrency: number; diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index e1dfc888d..39ac9cc6f 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -73,7 +73,7 @@ import { parseISO, set } from 'date-fns'; -import { isEmpty, last, uniq, uniqBy } from 'lodash'; +import { isEmpty, last, uniq } from 'lodash'; import { PortfolioCalculator } from './calculator/portfolio-calculator'; import { @@ -625,7 +625,6 @@ export class PortfolioService { if (activities.length === 0) { return { - accounts: [], averagePrice: undefined, dataProviderInfo: undefined, dividendInBaseCurrency: undefined, @@ -699,15 +698,6 @@ export class PortfolioService { ); }); - const accounts: PortfolioHoldingDetail['accounts'] = uniqBy( - activitiesOfPosition.filter(({ Account }) => { - return Account; - }), - 'Account.id' - ).map(({ Account }) => { - return Account; - }); - const dividendYieldPercent = getAnnualizedPerformancePercent({ daysInMarket: differenceInDays(new Date(), parseDate(firstBuyDate)), netPerformancePercentage: timeWeightedInvestment.eq(0) @@ -788,7 +778,6 @@ export class PortfolioService { } return { - accounts, firstBuyDate, marketPrice, maxPrice, @@ -883,7 +872,6 @@ export class PortfolioService { maxPrice, minPrice, SymbolProfile, - accounts: [], averagePrice: 0, dataProviderInfo: undefined, dividendInBaseCurrency: 0, From 3de192c65ef65b8e0e4b9c8f26a3921e1524d073 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 14 Sep 2024 19:42:37 +0200 Subject: [PATCH 3/7] Feature/expose thresholds in rule settings (#3770) --- apps/api/src/app/portfolio/rules.service.ts | 21 ++++++++++++------- .../portfolio-report-rule.interface.ts | 4 ++++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/apps/api/src/app/portfolio/rules.service.ts b/apps/api/src/app/portfolio/rules.service.ts index 6b6526144..fd9d794b2 100644 --- a/apps/api/src/app/portfolio/rules.service.ts +++ b/apps/api/src/app/portfolio/rules.service.ts @@ -1,6 +1,9 @@ import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; import { Rule } from '@ghostfolio/api/models/rule'; -import { UserSettings } from '@ghostfolio/common/interfaces'; +import { + PortfolioReportRule, + UserSettings +} from '@ghostfolio/common/interfaces'; import { Injectable } from '@nestjs/common'; @@ -11,19 +14,23 @@ export class RulesService { public async evaluate( aRules: Rule[], aUserSettings: UserSettings - ) { + ): Promise { return aRules.map((rule) => { - if (rule.getSettings(aUserSettings)?.isActive) { - const { evaluation, value } = rule.evaluate( - rule.getSettings(aUserSettings) - ); + const settings = rule.getSettings(aUserSettings); + + if (settings?.isActive) { + const { evaluation, value } = rule.evaluate(settings); return { evaluation, value, isActive: true, key: rule.getKey(), - name: rule.getName() + name: rule.getName(), + settings: { + thresholdMax: settings['thresholdMax'], + thresholdMin: settings['thresholdMin'] + } }; } else { return { diff --git a/libs/common/src/lib/interfaces/portfolio-report-rule.interface.ts b/libs/common/src/lib/interfaces/portfolio-report-rule.interface.ts index 61fe389af..29cbb4a8f 100644 --- a/libs/common/src/lib/interfaces/portfolio-report-rule.interface.ts +++ b/libs/common/src/lib/interfaces/portfolio-report-rule.interface.ts @@ -3,5 +3,9 @@ export interface PortfolioReportRule { isActive: boolean; key: string; name: string; + settings?: { + thresholdMax?: number; + thresholdMin?: number; + }; value?: boolean; } From fbf377f67f938395e961dbf599349e0f9156d4a8 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 14 Sep 2024 20:44:36 +0200 Subject: [PATCH 4/7] Feature/set up rule settings dialog (#3771) --- .../interfaces/interfaces.ts | 5 ++ .../rule-settings-dialog.component.ts | 40 +++++++++++++++ .../rule-settings-dialog.html | 23 +++++++++ .../rule-settings-dialog.scss | 2 + .../app/components/rule/rule.component.html | 5 ++ .../src/app/components/rule/rule.component.ts | 49 ++++++++++++++++++- 6 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 apps/client/src/app/components/rule/rule-settings-dialog/interfaces/interfaces.ts create mode 100644 apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.component.ts create mode 100644 apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html create mode 100644 apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.scss diff --git a/apps/client/src/app/components/rule/rule-settings-dialog/interfaces/interfaces.ts b/apps/client/src/app/components/rule/rule-settings-dialog/interfaces/interfaces.ts new file mode 100644 index 000000000..a409ab503 --- /dev/null +++ b/apps/client/src/app/components/rule/rule-settings-dialog/interfaces/interfaces.ts @@ -0,0 +1,5 @@ +import { PortfolioReportRule } from '@ghostfolio/common/interfaces'; + +export interface IRuleSettingsDialogParams { + rule: PortfolioReportRule; +} diff --git a/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.component.ts b/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.component.ts new file mode 100644 index 000000000..41ebf49db --- /dev/null +++ b/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.component.ts @@ -0,0 +1,40 @@ +import { PortfolioReportRule } from '@ghostfolio/common/interfaces'; + +import { CommonModule } from '@angular/common'; +import { Component, Inject } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { + MAT_DIALOG_DATA, + MatDialogModule, + MatDialogRef +} from '@angular/material/dialog'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; + +import { IRuleSettingsDialogParams } from './interfaces/interfaces'; + +@Component({ + imports: [ + CommonModule, + MatButtonModule, + MatDialogModule, + MatFormFieldModule, + MatInputModule + ], + selector: 'gf-rule-settings-dialog', + standalone: true, + styleUrls: ['./rule-settings-dialog.scss'], + templateUrl: './rule-settings-dialog.html' +}) +export class GfRuleSettingsDialogComponent { + public settings: PortfolioReportRule['settings']; + + public constructor( + @Inject(MAT_DIALOG_DATA) public data: IRuleSettingsDialogParams, + public dialogRef: MatDialogRef + ) { + console.log(this.data.rule); + + this.settings = this.data.rule.settings; + } +} diff --git a/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html b/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html new file mode 100644 index 000000000..e24db29f7 --- /dev/null +++ b/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html @@ -0,0 +1,23 @@ +
{{ data.rule.name }}
+ +
+ + Threshold Min + + + + Threshold Max + + +
+ +
+ + +
diff --git a/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.scss b/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.scss new file mode 100644 index 000000000..dc9093b45 --- /dev/null +++ b/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.scss @@ -0,0 +1,2 @@ +:host { +} diff --git a/apps/client/src/app/components/rule/rule.component.html b/apps/client/src/app/components/rule/rule.component.html index 80b442b7b..f19436aba 100644 --- a/apps/client/src/app/components/rule/rule.component.html +++ b/apps/client/src/app/components/rule/rule.component.html @@ -62,6 +62,11 @@ + @if (rule?.isActive && !isEmpty(rule.settings) && false) { + + }