fix(importing): make document receivers/tags canonical-authoritative on re-import
The DocumentImporter accumulated receivers/tags via addAll without pruning, so a shrunk canonical row left stale links on a re-imported PLACEHOLDER document. Clear the collections before re-populating so the canonical row is authoritative: a removed receiver/tag is now pruned. Raw sender_text/receiver_text retention is unchanged. Refs #669 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -165,6 +165,10 @@ public class DocumentImporter {
|
||||
doc.setContentType(contentType);
|
||||
doc.setSender(sender);
|
||||
doc.setSenderText(blankToNull(senderName));
|
||||
// The canonical row is authoritative for receivers/tags (ADR-025): clear then
|
||||
// re-populate so a shrunk set on re-import prunes stale links rather than
|
||||
// accumulating them. The raw sender_text/receiver_text retention is separate.
|
||||
doc.getReceivers().clear();
|
||||
doc.getReceivers().addAll(receivers);
|
||||
doc.setReceiverText(blankToNull(receiverNames));
|
||||
doc.setDocumentDate(parseIsoDate(row.get("date_iso")));
|
||||
@@ -203,7 +207,10 @@ public class DocumentImporter {
|
||||
.build()));
|
||||
}
|
||||
|
||||
// Authoritative: the canonical row defines the document's tags exactly. Clearing first
|
||||
// means a tag removed from the row is pruned on re-import (ADR-025).
|
||||
private void attachTag(Document doc, String tagPath) {
|
||||
doc.getTags().clear();
|
||||
if (tagPath.isBlank()) return;
|
||||
tagService.findBySourceRef(tagPath).ifPresent(tag -> doc.getTags().add(tag));
|
||||
}
|
||||
|
||||
@@ -382,6 +382,28 @@ class DocumentImporterTest {
|
||||
verify(documentService).save(org.mockito.ArgumentMatchers.argThat(d -> d.getId().equals(existing.getId())));
|
||||
}
|
||||
|
||||
// ─── canonical collections are authoritative — re-import prunes removed links ──────
|
||||
|
||||
@Test
|
||||
void load_prunesReceiversAndTags_whenCanonicalRowShrinks(@TempDir Path tempDir) throws Exception {
|
||||
ReflectionTestUtils.setField(importer, "importDir", tempDir.toString());
|
||||
Person staleReceiver = Person.builder().id(UUID.randomUUID()).sourceRef("stale-receiver").lastName("Stale").build();
|
||||
Tag staleTag = Tag.builder().id(UUID.randomUUID()).name("Stale").sourceRef("Themen/Stale").build();
|
||||
Document existing = Document.builder().id(UUID.randomUUID())
|
||||
.originalFilename("W-0008").status(DocumentStatus.PLACEHOLDER).build();
|
||||
existing.getReceivers().add(staleReceiver);
|
||||
existing.getTags().add(staleTag);
|
||||
when(documentService.findByOriginalFilename("W-0008")).thenReturn(Optional.of(existing));
|
||||
when(documentService.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
// The canonical row now carries no receiver and no tag: both stale links must go.
|
||||
Path xlsx = writeDocs(tempDir, docRow("W-0008", "", "", "", "", "", "", "", "", ""));
|
||||
|
||||
importer.load(xlsx.toFile());
|
||||
|
||||
verify(documentService).save(org.mockito.ArgumentMatchers.argThat(d ->
|
||||
d.getReceivers().isEmpty() && d.getTags().isEmpty()));
|
||||
}
|
||||
|
||||
// ─── helpers ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
private Map<String, String> docRow(String index, String file, String senderId, String senderName,
|
||||
|
||||
Reference in New Issue
Block a user