fix: validate duplicate operation UUIDs

This commit is contained in:
2026-08-19 17:48:39 +03:00
parent 5c059cbc62
commit b99555ca23
3 changed files with 15 additions and 1 deletions

View File

@@ -1,5 +1,11 @@
# Changelog # Changelog
## [Backend 0.6.5] - 2026-08-19
### Fixed
- Duplicate operation UUIDs in one import file now return a validation error instead of a database failure.
## [Frontend 0.9.2 / Backend 0.6.4 / Shared 0.2.2] - 2026-08-19 ## [Frontend 0.9.2 / Backend 0.6.4 / Shared 0.2.2] - 2026-08-19
### Added ### Added

View File

@@ -1,6 +1,6 @@
{ {
"name": "@family-budget/backend", "name": "@family-budget/backend",
"version": "0.6.4", "version": "0.6.5",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "tsx watch src/app.ts", "dev": "tsx watch src/app.ts",

View File

@@ -133,12 +133,20 @@ function validateSemantics(data: StatementFile): ValidationError | null {
} }
const fps = new Set<string>(); const fps = new Set<string>();
const operationIds = new Set<string>();
for (let i = 0; i < data.transactions.length; i++) { for (let i = 0; i < data.transactions.length; i++) {
const fp = computeFingerprint(data.statement.accountNumber, data.transactions[i]); const fp = computeFingerprint(data.statement.accountNumber, data.transactions[i]);
if (fps.has(fp)) { if (fps.has(fp)) {
return { status: 422, error: 'VALIDATION_ERROR', message: `Duplicate fingerprint found within file at transaction index ${i}` }; return { status: 422, error: 'VALIDATION_ERROR', message: `Duplicate fingerprint found within file at transaction index ${i}` };
} }
fps.add(fp); fps.add(fp);
const operationId = data.transactions[i].operationId;
if (operationId) {
if (operationIds.has(operationId.toLowerCase())) {
return { status: 422, error: 'VALIDATION_ERROR', message: `Duplicate operationId found within file at transaction index ${i}` };
}
operationIds.add(operationId.toLowerCase());
}
} }
return null; return null;