Browse Source

fix(import): scope duplicate detection to account and detect in-batch duplicates (#7728, #7406)

pull/8119/head
hrushikeshramilla 5 days ago
parent
commit
38b084fd05
  1. 52
      apps/api/src/app/import/import.service.spec.ts
  2. 31
      apps/api/src/app/import/import.service.ts

52
apps/api/src/app/import/import.service.spec.ts

@ -456,6 +456,58 @@ describe('ImportService', () => {
{ dataSource: DataSource.YAHOO, symbol: 'AAPL' } { dataSource: DataSource.YAHOO, symbol: 'AAPL' }
]); ]);
}); });
it('detects duplicate activities within the same import payload', async () => {
const activities = await importActivities({
activitiesDto: [
createActivityDto({
dataSource: DataSource.YAHOO,
date: '2024-01-01T00:00:00.000Z',
symbol: 'AAPL',
type: 'BUY'
}),
createActivityDto({
dataSource: DataSource.YAHOO,
date: '2024-01-01T00:00:00.000Z',
symbol: 'AAPL',
type: 'BUY'
})
],
isDryRun: true
});
expect(activities[0].error).toBeUndefined();
expect(activities[1].error).toEqual({ code: 'IS_DUPLICATE' });
});
it('does not flag identical activities across different accounts as duplicates', async () => {
const activities = await importActivities({
activitiesDto: [
{
...createActivityDto({
dataSource: DataSource.YAHOO,
date: '2024-01-01T00:00:00.000Z',
symbol: 'AAPL',
type: 'BUY'
}),
accountId: 'account-1'
},
{
...createActivityDto({
dataSource: DataSource.YAHOO,
date: '2024-01-01T00:00:00.000Z',
symbol: 'AAPL',
type: 'BUY'
}),
accountId: 'account-2'
}
],
isDryRun: true
});
expect(activities[0].error).toBeUndefined();
expect(activities[1].error).toBeUndefined();
});
}); });
function importActivities({ function importActivities({

31
apps/api/src/app/import/import.service.ts

@ -1045,6 +1045,9 @@ export class ImportService {
withExcludedAccountsAndActivities: true withExcludedAccountsAndActivities: true
}); });
const processedActivities: (Partial<Activity> &
Pick<Activity, 'assetProfile'>)[] = [];
return activitiesDto.map( return activitiesDto.map(
({ ({
accountId, accountId,
@ -1061,10 +1064,12 @@ export class ImportService {
}) => { }) => {
const date = parseISO(dateString); const date = parseISO(dateString);
const isDuplicate = existingActivities.some((activity) => { const isDuplicate =
existingActivities.some((activity) => {
return ( return (
(!accountId || activity.accountId === accountId) &&
(activity.comment || null) === (comment || null) && (activity.comment || null) === (comment || null) &&
(activity.currency === currency || ((activity.currency || null) === (currency || null) ||
activity.assetProfile.currency === currency) && activity.assetProfile.currency === currency) &&
activity.assetProfile.dataSource === dataSource && activity.assetProfile.dataSource === dataSource &&
isSameSecond(activity.date, date) && isSameSecond(activity.date, date) &&
@ -1074,13 +1079,27 @@ export class ImportService {
activity.type === type && activity.type === type &&
activity.unitPrice === unitPrice activity.unitPrice === unitPrice
); );
}) ||
processedActivities.some((activity) => {
return (
(!accountId || activity.accountId === accountId) &&
(activity.comment || null) === (comment || null) &&
(activity.currency || null) === (currency || null) &&
activity.assetProfile.dataSource === dataSource &&
isSameSecond(activity.date, date) &&
activity.fee === fee &&
activity.quantity === quantity &&
activity.assetProfile.symbol === symbol &&
activity.type === type &&
activity.unitPrice === unitPrice
);
}); });
const error: ActivityError = isDuplicate const error: ActivityError = isDuplicate
? { code: 'IS_DUPLICATE' } ? { code: 'IS_DUPLICATE' }
: undefined; : undefined;
return { const activityExtended = {
accountId, accountId,
comment, comment,
currency, currency,
@ -1107,6 +1126,12 @@ export class ImportService {
}, },
tagIds: tags tagIds: tags
}; };
if (!error) {
processedActivities.push(activityExtended);
}
return activityExtended;
} }
); );
} }

Loading…
Cancel
Save