Browse Source

Bugfix/incorrect error log output when deleting activities (#7629)

* Fix incorrect error log output when deleting activities

* Update changelog
pull/7636/head
Thomas Kaul 3 days ago
committed by GitHub
parent
commit
e5273cc91a
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      CHANGELOG.md
  2. 366
      apps/api/src/app/activities/activities.service.ts

2
CHANGELOG.md

@ -19,12 +19,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Deprecated the `permissions` attribute of the access in favor of the scopes
- Extended the `GET api/v1/access` endpoint by the scopes
- Extended the `GET api/v1/user` endpoint by the scopes
- Improved the performance of deleting activities by loading only the required data
### Fixed
- Fixed the missing currency conversion of the dividends on the analysis page
- Fixed the missing error state in the watchlist
- Fixed the missing loading indicator in the benchmarks of the markets overview
- Fixed the incorrect error log output when deleting activities
## 3.51.0 - 2026-08-14

366
apps/api/src/app/activities/activities.service.ts

@ -393,36 +393,32 @@ export class ActivitiesService {
types?: ActivityType[];
userId: string;
}): Promise<number> {
const { activities } = await this.getActivities({
const where = this.getWhereClause({
endDate,
filters,
startDate,
types,
userId,
includeDrafts: true,
userCurrency: undefined,
withExcludedAccountsAndActivities: true
});
const { count } = await this.prismaService.order.deleteMany({
where: {
id: {
in: activities.map(({ id }) => {
return id;
})
}
}
const activities = await this.prismaService.order.findMany({
where,
distinct: ['symbolProfileId'],
select: { symbolProfileId: true }
});
const symbolProfiles =
await this.symbolProfileService.getSymbolProfilesByIds(
const { count } = await this.prismaService.order.deleteMany({ where });
const [benchmarkAssetProfiles, symbolProfiles] = await Promise.all([
this.benchmarkService.getBenchmarkAssetProfiles(),
this.symbolProfileService.getSymbolProfilesByIds(
activities.map(({ symbolProfileId }) => {
return symbolProfileId;
})
);
const benchmarkAssetProfiles =
await this.benchmarkService.getBenchmarkAssetProfiles();
)
]);
for (const {
activitiesCount,
@ -627,164 +623,19 @@ export class ActivitiesService {
{ date: 'asc' }
];
const andConditions: Prisma.OrderWhereInput[] = [];
const where: Prisma.OrderWhereInput = { userId, AND: andConditions };
if (endDate) {
andConditions.push({ date: { lte: endDate } });
}
if (startDate) {
andConditions.push({ date: { gt: startDate } });
}
const {
ACCOUNT: filtersByAccount = [],
ASSET_CLASS: filtersByAssetClass = [],
DATA_SOURCE: [filterByDataSource] = [],
SEARCH_QUERY: [filterBySearchQuery] = [],
SYMBOL: [filterBySymbol] = [],
TAG: filtersByTag = []
} = groupBy(filters, ({ type }) => {
return type;
});
if (filtersByAccount.length > 0) {
where.accountId = {
in: filtersByAccount.map(({ id }) => {
return id;
})
};
}
const isFilteredByDraftTag = filtersByTag.some(({ id }) => {
return id === TAG_ID_DRAFT;
});
if (includeDrafts === false && !isFilteredByDraftTag) {
andConditions.push(WHERE_ACTIVITY_NOT_DRAFT);
}
if (filtersByAssetClass.length > 0) {
where.SymbolProfile = {
OR: [
{
AND: [
{
OR: filtersByAssetClass.map(({ id }) => {
return { assetClass: AssetClass[id] };
})
},
{
OR: [
{ assetProfileOverrides: { is: null } },
{ assetProfileOverrides: { assetClass: null } }
]
}
]
},
{
assetProfileOverrides: {
OR: filtersByAssetClass.map(({ id }) => {
return { assetClass: AssetClass[id] };
})
}
}
]
};
}
if (filterByDataSource && filterBySymbol) {
if (where.SymbolProfile) {
where.SymbolProfile = {
AND: [
where.SymbolProfile,
{
AND: [
{ dataSource: filterByDataSource.id as DataSource },
{ symbol: filterBySymbol.id }
]
}
]
};
} else {
where.SymbolProfile = {
AND: [
{ dataSource: filterByDataSource.id as DataSource },
{ symbol: filterBySymbol.id }
]
};
}
}
if (filterBySearchQuery) {
const searchQueryWhereInput: Prisma.SymbolProfileWhereInput[] = [
{ id: { mode: 'insensitive', startsWith: filterBySearchQuery.id } },
{ isin: { mode: 'insensitive', startsWith: filterBySearchQuery.id } },
{ name: { mode: 'insensitive', startsWith: filterBySearchQuery.id } },
{ symbol: { mode: 'insensitive', startsWith: filterBySearchQuery.id } }
];
if (where.SymbolProfile) {
where.SymbolProfile = {
AND: [
where.SymbolProfile,
{
OR: searchQueryWhereInput
}
]
};
} else {
where.SymbolProfile = {
OR: searchQueryWhereInput
};
}
}
if (filtersByTag.length > 0) {
andConditions.push({
OR: [
{
tags: {
some: {
OR: filtersByTag.map(({ id }) => {
return { id };
})
}
}
},
{
account: {
tags: {
some: {
OR: filtersByTag.map(({ id }) => {
return { tagId: id };
})
}
}
}
}
]
});
}
if (sortColumn) {
orderBy = [{ [sortColumn]: sortDirection }];
}
if (types?.length > 0) {
where.type = { in: types };
}
if (withExcludedAccountsAndActivities === false) {
where.OR = [{ account: null }, { account: WHERE_ACCOUNT_NOT_EXCLUDED }];
where.tags = {
none: {
id: TAG_ID_EXCLUDE_FROM_ANALYSIS
}
};
}
const where = this.getWhereClause({
endDate,
filters,
includeDrafts,
startDate,
types,
userId,
withExcludedAccountsAndActivities
});
const [orders, count] = await Promise.all([
this.orders({
@ -1113,6 +964,181 @@ export class ActivitiesService {
return activity;
}
private getWhereClause({
endDate,
filters,
includeDrafts,
startDate,
types,
userId,
withExcludedAccountsAndActivities
}: {
endDate?: Date;
filters?: Filter[];
includeDrafts: boolean;
startDate?: Date;
types?: ActivityType[];
userId: string;
withExcludedAccountsAndActivities: boolean;
}): Prisma.OrderWhereInput {
const andConditions: Prisma.OrderWhereInput[] = [];
const where: Prisma.OrderWhereInput = { userId, AND: andConditions };
if (endDate) {
andConditions.push({ date: { lte: endDate } });
}
if (startDate) {
andConditions.push({ date: { gt: startDate } });
}
const {
ACCOUNT: filtersByAccount = [],
ASSET_CLASS: filtersByAssetClass = [],
DATA_SOURCE: [filterByDataSource] = [],
SEARCH_QUERY: [filterBySearchQuery] = [],
SYMBOL: [filterBySymbol] = [],
TAG: filtersByTag = []
} = groupBy(filters, ({ type }) => {
return type;
});
if (filtersByAccount.length > 0) {
where.accountId = {
in: filtersByAccount.map(({ id }) => {
return id;
})
};
}
const isFilteredByDraftTag = filtersByTag.some(({ id }) => {
return id === TAG_ID_DRAFT;
});
if (includeDrafts === false && !isFilteredByDraftTag) {
andConditions.push(WHERE_ACTIVITY_NOT_DRAFT);
}
if (filtersByAssetClass.length > 0) {
where.SymbolProfile = {
OR: [
{
AND: [
{
OR: filtersByAssetClass.map(({ id }) => {
return { assetClass: AssetClass[id] };
})
},
{
OR: [
{ assetProfileOverrides: { is: null } },
{ assetProfileOverrides: { assetClass: null } }
]
}
]
},
{
assetProfileOverrides: {
OR: filtersByAssetClass.map(({ id }) => {
return { assetClass: AssetClass[id] };
})
}
}
]
};
}
if (filterByDataSource && filterBySymbol) {
if (where.SymbolProfile) {
where.SymbolProfile = {
AND: [
where.SymbolProfile,
{
AND: [
{ dataSource: filterByDataSource.id as DataSource },
{ symbol: filterBySymbol.id }
]
}
]
};
} else {
where.SymbolProfile = {
AND: [
{ dataSource: filterByDataSource.id as DataSource },
{ symbol: filterBySymbol.id }
]
};
}
}
if (filterBySearchQuery) {
const searchQueryWhereInput: Prisma.SymbolProfileWhereInput[] = [
{ id: { mode: 'insensitive', startsWith: filterBySearchQuery.id } },
{ isin: { mode: 'insensitive', startsWith: filterBySearchQuery.id } },
{ name: { mode: 'insensitive', startsWith: filterBySearchQuery.id } },
{ symbol: { mode: 'insensitive', startsWith: filterBySearchQuery.id } }
];
if (where.SymbolProfile) {
where.SymbolProfile = {
AND: [
where.SymbolProfile,
{
OR: searchQueryWhereInput
}
]
};
} else {
where.SymbolProfile = {
OR: searchQueryWhereInput
};
}
}
if (filtersByTag.length > 0) {
andConditions.push({
OR: [
{
tags: {
some: {
OR: filtersByTag.map(({ id }) => {
return { id };
})
}
}
},
{
account: {
tags: {
some: {
OR: filtersByTag.map(({ id }) => {
return { tagId: id };
})
}
}
}
}
]
});
}
if (types?.length > 0) {
where.type = { in: types };
}
if (withExcludedAccountsAndActivities === false) {
where.OR = [{ account: null }, { account: WHERE_ACCOUNT_NOT_EXCLUDED }];
where.tags = {
none: {
id: TAG_ID_EXCLUDE_FROM_ANALYSIS
}
};
}
return where;
}
private async orders(params: {
include?: Prisma.OrderInclude;
skip?: number;

Loading…
Cancel
Save