feat(sprint10): Phase 3 — BankImportService + REST API
Implements the orchestrator and REST endpoints for the bank statement
import wizard (Sprint 10 Phase 3).
Service layer (cannamanage-service):
- BankImportService: upload → SHA-256 dedup → parse → match → persist
in two transactional steps (file I/O outside @Transactional, persist
in @Transactional helper). Methods: uploadAndParse, confirmMatch,
confirmAllMatched (≥90% confidence), manualAssign, skipTransaction,
completeSession, query helpers.
- GoBD §147 AO immutability guard: assertSessionMutable() rejects any
mutation on COMPLETED/FAILED sessions with German error messages.
- Hard 5MB upload cap enforced before parsing.
- Audit events: BANK_IMPORT_STARTED / BANK_PAYMENT_CONFIRMED /
BANK_IMPORT_COMPLETED. Uploader notified via NotificationService.
REST layer (cannamanage-api):
- BankImportController under /api/v1/finance/import/*:
POST sessions (multipart), GET sessions/single/transactions(?status=),
POST {id}/transactions/{txnId}/confirm|assign|skip,
POST {id}/confirm-all, POST {id}/complete,
GET/POST/DELETE csv-mappings.
- Permission: FINANCE_IMPORT with MANAGE_FINANCES fallback.
- Defence-in-depth tenant check on every path-parameter ID.
DTOs (cannamanage-api/dto/bankimport):
- ImportSessionResponse, TransactionResponse, ConfirmRequest,
AssignRequest, SkipRequest, BulkConfirmResponse, CreateMappingRequest.
Persistence:
- V33__bank_import_file_hash.sql: adds file_hash VARCHAR(64) + unique
partial index (club_id, file_hash) for duplicate-upload detection.
- BankImportSession.fileHash field, repository.existsByClubIdAndFileHash.
Configuration:
- application.properties: multipart enabled, max-file-size=5MB,
max-request-size=6MB.
Build: mvn package -DskipTests ✅ (cannamanage-api fat JAR 92MB).
This commit is contained in:
@@ -54,6 +54,14 @@ public class BankImportSession extends AbstractTenantEntity {
|
||||
@Column(name = "completed_at")
|
||||
private Instant completedAt;
|
||||
|
||||
/**
|
||||
* SHA-256 hex digest of the uploaded file content (Sprint 10 Phase 3).
|
||||
* Used together with {@code clubId} to reject byte-identical re-uploads (HTTP 409),
|
||||
* even when the file has been renamed. Nullable for legacy rows created before V33.
|
||||
*/
|
||||
@Column(name = "file_hash", length = 64)
|
||||
private String fileHash;
|
||||
|
||||
// Getters and setters
|
||||
|
||||
public UUID getClubId() { return clubId; }
|
||||
@@ -88,4 +96,7 @@ public class BankImportSession extends AbstractTenantEntity {
|
||||
|
||||
public Instant getCompletedAt() { return completedAt; }
|
||||
public void setCompletedAt(Instant completedAt) { this.completedAt = completedAt; }
|
||||
|
||||
public String getFileHash() { return fileHash; }
|
||||
public void setFileHash(String fileHash) { this.fileHash = fileHash; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user