|
|
@ -6,7 +6,7 @@ import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; |
|
|
import { DATE_FORMAT } from '@ghostfolio/common/helper'; |
|
|
import { DATE_FORMAT } from '@ghostfolio/common/helper'; |
|
|
import { Filter } from '@ghostfolio/common/interfaces'; |
|
|
import { Filter } from '@ghostfolio/common/interfaces'; |
|
|
|
|
|
|
|
|
import { Injectable } from '@nestjs/common'; |
|
|
import { HttpException, Injectable } from '@nestjs/common'; |
|
|
import { EventEmitter2 } from '@nestjs/event-emitter'; |
|
|
import { EventEmitter2 } from '@nestjs/event-emitter'; |
|
|
import { |
|
|
import { |
|
|
Account, |
|
|
Account, |
|
|
@ -19,6 +19,7 @@ import { |
|
|
} from '@prisma/client'; |
|
|
} from '@prisma/client'; |
|
|
import { Big } from 'big.js'; |
|
|
import { Big } from 'big.js'; |
|
|
import { format } from 'date-fns'; |
|
|
import { format } from 'date-fns'; |
|
|
|
|
|
import { StatusCodes, getReasonPhrase } from 'http-status-codes'; |
|
|
import { groupBy } from 'lodash'; |
|
|
import { groupBy } from 'lodash'; |
|
|
|
|
|
|
|
|
import { CashDetails } from './interfaces/cash-details.interface'; |
|
|
import { CashDetails } from './interfaces/cash-details.interface'; |
|
|
@ -74,17 +75,20 @@ export class AccountService { |
|
|
const { include = {}, skip, take, cursor, where, orderBy } = params; |
|
|
const { include = {}, skip, take, cursor, where, orderBy } = params; |
|
|
|
|
|
|
|
|
const isBalancesIncluded = !!include.balances; |
|
|
const isBalancesIncluded = !!include.balances; |
|
|
|
|
|
const isTagsIncluded = !!include.tags; |
|
|
|
|
|
|
|
|
include.balances = { |
|
|
include.balances = { |
|
|
orderBy: { date: 'desc' }, |
|
|
orderBy: { date: 'desc' }, |
|
|
...(isBalancesIncluded ? {} : { take: 1 }) |
|
|
...(isBalancesIncluded ? {} : { take: 1 }) |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
include.tags = { |
|
|
if (isTagsIncluded) { |
|
|
include: { |
|
|
include.tags = { |
|
|
tag: true |
|
|
include: { |
|
|
} |
|
|
tag: true |
|
|
}; |
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
const accounts = await this.prismaService.account.findMany({ |
|
|
const accounts = await this.prismaService.account.findMany({ |
|
|
cursor, |
|
|
cursor, |
|
|
@ -99,32 +103,38 @@ export class AccountService { |
|
|
const result = { |
|
|
const result = { |
|
|
...account, |
|
|
...account, |
|
|
balance: account.balances[0]?.value ?? 0, |
|
|
balance: account.balances[0]?.value ?? 0, |
|
|
tags: (account.tags as unknown as { tag: Tag }[]).map(({ tag }) => { |
|
|
tags: isTagsIncluded |
|
|
return tag; |
|
|
? (account.tags as unknown as { tag: Tag }[]).map(({ tag }) => { |
|
|
}) |
|
|
return tag; |
|
|
|
|
|
}) |
|
|
|
|
|
: undefined |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
if (!isBalancesIncluded) { |
|
|
if (!isBalancesIncluded) { |
|
|
delete result.balances; |
|
|
delete result.balances; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!isTagsIncluded) { |
|
|
|
|
|
delete result.tags; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
return result; |
|
|
return result; |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public async createAccount( |
|
|
public async createAccount( |
|
|
data: Prisma.AccountCreateInput & { tags?: { id: string }[] }, |
|
|
data: Prisma.AccountCreateInput, |
|
|
aUserId: string |
|
|
aUserId: string, |
|
|
|
|
|
tagIds?: string[] |
|
|
): Promise<Account> { |
|
|
): Promise<Account> { |
|
|
const tags = data.tags; |
|
|
await this.validateTagIds(tagIds, aUserId); |
|
|
delete data.tags; |
|
|
|
|
|
|
|
|
|
|
|
const account = await this.prismaService.account.create({ |
|
|
const account = await this.prismaService.account.create({ |
|
|
data: { |
|
|
data: { |
|
|
...data, |
|
|
...data, |
|
|
tags: tags |
|
|
tags: tagIds |
|
|
? { |
|
|
? { |
|
|
create: tags.map(({ id: tagId }) => { |
|
|
create: tagIds.map((tagId) => { |
|
|
return { |
|
|
return { |
|
|
tag: { connect: { id: tagId } } |
|
|
tag: { connect: { id: tagId } } |
|
|
}; |
|
|
}; |
|
|
@ -172,7 +182,8 @@ export class AccountService { |
|
|
const accounts = await this.accounts({ |
|
|
const accounts = await this.accounts({ |
|
|
include: { |
|
|
include: { |
|
|
activities: true, |
|
|
activities: true, |
|
|
platform: true |
|
|
platform: true, |
|
|
|
|
|
tags: true |
|
|
}, |
|
|
}, |
|
|
orderBy: { name: 'asc' }, |
|
|
orderBy: { name: 'asc' }, |
|
|
where: { userId: aUserId } |
|
|
where: { userId: aUserId } |
|
|
@ -249,28 +260,21 @@ export class AccountService { |
|
|
public async updateAccount( |
|
|
public async updateAccount( |
|
|
params: { |
|
|
params: { |
|
|
where: Prisma.AccountWhereUniqueInput; |
|
|
where: Prisma.AccountWhereUniqueInput; |
|
|
data: Prisma.AccountUpdateInput & { tags?: { id: string }[] }; |
|
|
data: Prisma.AccountUpdateInput; |
|
|
}, |
|
|
}, |
|
|
aUserId: string |
|
|
aUserId: string, |
|
|
|
|
|
tagIds?: string[] |
|
|
): Promise<Account> { |
|
|
): Promise<Account> { |
|
|
const { data, where } = params; |
|
|
const { data, where } = params; |
|
|
|
|
|
|
|
|
const tags = data.tags; |
|
|
await this.validateTagIds(tagIds, aUserId); |
|
|
delete data.tags; |
|
|
|
|
|
|
|
|
|
|
|
await this.accountBalanceService.createOrUpdateAccountBalance({ |
|
|
|
|
|
accountId: data.id as string, |
|
|
|
|
|
balance: data.balance as number, |
|
|
|
|
|
date: format(new Date(), DATE_FORMAT), |
|
|
|
|
|
userId: aUserId |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
const account = await this.prismaService.account.update({ |
|
|
const account = await this.prismaService.account.update({ |
|
|
data: { |
|
|
data: { |
|
|
...data, |
|
|
...data, |
|
|
tags: tags |
|
|
tags: tagIds |
|
|
? { |
|
|
? { |
|
|
create: tags.map(({ id: tagId }) => { |
|
|
create: tagIds.map((tagId) => { |
|
|
return { |
|
|
return { |
|
|
tag: { connect: { id: tagId } } |
|
|
tag: { connect: { id: tagId } } |
|
|
}; |
|
|
}; |
|
|
@ -282,6 +286,13 @@ export class AccountService { |
|
|
where |
|
|
where |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
await this.accountBalanceService.createOrUpdateAccountBalance({ |
|
|
|
|
|
accountId: account.id, |
|
|
|
|
|
balance: data.balance as number, |
|
|
|
|
|
date: format(new Date(), DATE_FORMAT), |
|
|
|
|
|
userId: aUserId |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
this.eventEmitter.emit( |
|
|
this.eventEmitter.emit( |
|
|
PortfolioChangedEvent.getName(), |
|
|
PortfolioChangedEvent.getName(), |
|
|
new PortfolioChangedEvent({ |
|
|
new PortfolioChangedEvent({ |
|
|
@ -329,4 +340,26 @@ export class AccountService { |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private async validateTagIds(tagIds: string[], userId: string) { |
|
|
|
|
|
if (!tagIds?.length) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const uniqueTagIds = Array.from(new Set(tagIds)); |
|
|
|
|
|
|
|
|
|
|
|
const tagsCount = await this.prismaService.tag.count({ |
|
|
|
|
|
where: { |
|
|
|
|
|
id: { in: uniqueTagIds }, |
|
|
|
|
|
OR: [{ userId }, { userId: null }] |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
if (tagsCount !== uniqueTagIds.length) { |
|
|
|
|
|
throw new HttpException( |
|
|
|
|
|
getReasonPhrase(StatusCodes.BAD_REQUEST), |
|
|
|
|
|
StatusCodes.BAD_REQUEST |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|