diff --git a/src/documents/tests/test_workflows.py b/src/documents/tests/test_workflows.py index e8fd5c41e..5c5ff548f 100644 --- a/src/documents/tests/test_workflows.py +++ b/src/documents/tests/test_workflows.py @@ -5573,6 +5573,41 @@ class TestApplyAISuggestionsWorkflowAction( self.doc.refresh_from_db() return changed + def test_fields_persist_when_tags_are_applied_in_the_same_run(self) -> None: + """ + GIVEN: + - A document that already has a filename, as any consumed document does + - Suggestions carrying both a document type and tags + WHEN: + - The suggestions are applied + THEN: + - The document type is still set after the tags are added + + Adding tags fires m2m_changed, and update_filename_and_move_files + refreshes the document from the database. Assigning fields and then + adding tags before saving loses those assignments, and only for + documents with a filename, so it does not reproduce on a bare + Document.objects.create(). + """ + self.doc.filename = "originals/original.pdf" + self.doc.save(update_fields=["filename"]) + + action = self.make_action(ai_create_missing=True) + changed = self.apply(action) + + self.assertIn("document_type", changed) + self.assertIn("tags", changed) + self.assertIsNotNone( + self.doc.document_type, + "document_type was reported as applied but did not persist", + ) + self.assertEqual(self.doc.document_type.name, "Suggested Document Type") + self.assertEqual(self.doc.correspondent.name, "Existing Correspondent") + self.assertCountEqual( + [t.name for t in self.doc.tags.all()], + ["Existing Tag", "Suggested Tag"], + ) + def test_document_added_trigger_queues_task(self) -> None: """ GIVEN: diff --git a/src/documents/workflows/ai.py b/src/documents/workflows/ai.py index 386838782..23ffbd71c 100644 --- a/src/documents/workflows/ai.py +++ b/src/documents/workflows/ai.py @@ -226,20 +226,20 @@ def apply_ai_suggestions_to_document( document.created = created updated_fields.append("created") + tags_to_add: list[Tag] = [] if AISuggestionField.TAGS in selected: choice = suggestions["tags"] names = choice["new_names"] - tags = resolve_tags( + tags_to_add = resolve_tags( names, resolve_tag_ids(choice["existing_ids"], owner) + match_tags_by_name(names, owner), create_missing=create_missing, owner=owner, ) - if tags: + if tags_to_add: # Suggested tags are always added, so overwrite_existing # does not really apply here - document.add_nested_tags(tags) updated_fields.append("tags") if updated_fields: @@ -249,6 +249,10 @@ def apply_ai_suggestions_to_document( ] document.save(update_fields=[*direct_updated_fields, "modified"]) + # Tags at the end so m2m_changed doesn't trigger db and overwrite other changes + if tags_to_add: + document.add_nested_tags(tags_to_add) + logger.info( "Applied AI suggestions %s to document %s", updated_fields or "(none)",