diff --git a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/service/TargetFilenameBuildingService.java b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/service/TargetFilenameBuildingService.java
index de41c2a..09d35ca 100644
--- a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/service/TargetFilenameBuildingService.java
+++ b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/service/TargetFilenameBuildingService.java
@@ -93,7 +93,7 @@ public final class TargetFilenameBuildingService {
*
Validated title must be non-null and non-blank.
* Validated title must not exceed the configured maximum length
* (before Windows cleaning).
- * After Windows-character cleaning, title must contain only letters, digits, and spaces.
+ * After Windows-character cleaning, title must contain only letters, digits, spaces, and hyphens.
*
* If any rule is violated, the state is treated as an
* {@link InconsistentProposalState}.
@@ -148,11 +148,11 @@ public final class TargetFilenameBuildingService {
+ title + "'");
}
- // After cleaning, verify that only letters, digits, and spaces remain
+ // After cleaning, verify that only letters, digits, spaces, and hyphens remain
if (!isAllowedTitleCharacters(cleanedTitle)) {
return new InconsistentProposalState(
"After Windows-compatibility cleaning, title contains disallowed characters "
- + "(only letters, digits, and spaces are permitted): '"
+ + "(only letters, digits, spaces, and hyphens are permitted): '"
+ cleanedTitle + "'");
}
@@ -166,13 +166,13 @@ public final class TargetFilenameBuildingService {
// -------------------------------------------------------------------------
/**
- * Returns {@code true} if every character in the title is a letter, a digit, or a space.
+ * Returns {@code true} if every character in the title is a letter, a digit, space, or hyphen.
* Unicode letters (including German Umlauts and ß) are permitted.
*/
private static boolean isAllowedTitleCharacters(String title) {
for (int i = 0; i < title.length(); i++) {
char c = title.charAt(i);
- if (!Character.isLetter(c) && !Character.isDigit(c) && c != ' ') {
+ if (!Character.isLetter(c) && !Character.isDigit(c) && c != ' ' && c != '-') {
return false;
}
}
diff --git a/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/service/DocumentProcessingCoordinatorTest.java b/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/service/DocumentProcessingCoordinatorTest.java
index 3d1beaa..0f7897b 100644
--- a/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/service/DocumentProcessingCoordinatorTest.java
+++ b/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/service/DocumentProcessingCoordinatorTest.java
@@ -978,14 +978,14 @@ class DocumentProcessingCoordinatorTest {
DocumentRecord existingRecord = buildRecord(ProcessingStatus.PROPOSAL_READY, FailureCounters.zero());
recordRepo.setLookupResult(new DocumentKnownProcessable(existingRecord));
- // Hyphen is a disallowed character in the fachliche Titelregel
+ // Exclamation mark is a disallowed character in the fachliche Titelregel
ProcessingAttempt badProposal = new ProcessingAttempt(
fingerprint, context.runId(), 1, Instant.now(), Instant.now(),
ProcessingStatus.PROPOSAL_READY, null, null, false,
null,
"model", "prompt", 1, 100, "{}", "reason",
LocalDate.of(2026, 1, 15), DateSource.AI_PROVIDED,
- "Rechnung-2026", null);
+ "Rechnung!", null);
attemptRepo.savedAttempts.add(badProposal);
boolean result = processor.processDeferredOutcome(candidate, fingerprint, context, attemptStart, c -> null);
diff --git a/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/service/TargetFilenameBuildingServiceTest.java b/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/service/TargetFilenameBuildingServiceTest.java
index 405f7aa..8fa2d85 100644
--- a/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/service/TargetFilenameBuildingServiceTest.java
+++ b/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/service/TargetFilenameBuildingServiceTest.java
@@ -212,15 +212,15 @@ class TargetFilenameBuildingServiceTest {
// -------------------------------------------------------------------------
@Test
- void buildBaseFilename_titleWithHyphen_returnsInconsistentProposalState() {
- // Hyphens are not letters, digits, or spaces — disallowed by fachliche Titelregel
+ void buildBaseFilename_titleWithHyphen_succeeds() {
+ // Hyphens are allowed per fachliche Titelregel
ProcessingAttempt attempt = proposalAttempt(LocalDate.of(2026, 1, 1), "Rechnung-2026");
BaseFilenameResult result = TargetFilenameBuildingService.buildBaseFilename(attempt, TEST_MAX_TITLE_LENGTH);
- assertThat(result).isInstanceOf(InconsistentProposalState.class);
- assertThat(((InconsistentProposalState) result).reason())
- .contains("disallowed characters");
+ assertThat(result).isInstanceOf(BaseFilenameReady.class);
+ assertThat(((BaseFilenameReady) result).baseFilename())
+ .isEqualTo("2026-01-01 - Rechnung-2026.pdf");
}
@Test