From e6a7b92a57fbaebf0ad2cb86659f025783380dd1 Mon Sep 17 00:00:00 2001 From: stumpylog <797416+stumpylog@users.noreply.github.com> Date: Tue, 3 Nov 2026 13:19:22 -0800 Subject: [PATCH] Fix: return the post-move modified time from document updates Editing a document in a way that renames its file returned a response carrying the modified time from before the move. The deferred file move runs after the response is built and bumps modified, so the stored value was newer than the one the client received. The frontend then saw a newer server modified time and showed a spurious "document was updated" prompt whose reload discarded local edits. DocumentViewSet.update now flushes pending file moves right after saving and before reading the document back, so the response reflects the final filename and modified time. --- src/documents/tests/test_file_moves.py | 27 ++++++++++++++++++++++++++ src/documents/views.py | 3 +++ 2 files changed, 30 insertions(+) diff --git a/src/documents/tests/test_file_moves.py b/src/documents/tests/test_file_moves.py index 3cfd59be8e..985fa1739b 100644 --- a/src/documents/tests/test_file_moves.py +++ b/src/documents/tests/test_file_moves.py @@ -15,6 +15,7 @@ from django.http import HttpRequest from django.http import HttpResponse from django.test import RequestFactory from django.utils import timezone +from django.utils.dateparse import parse_datetime from filelock import Timeout from rest_framework import status @@ -672,6 +673,32 @@ class TestFileMovesThroughApi: assert (paperless_dirs.originals_dir / "renamed.pdf").is_file() assert Document.objects.get(pk=document.pk).filename == "renamed.pdf" + def test_patch_response_reports_post_move_modified( + self, + admin_client: APIClient, + document: Document, + paperless_dirs: PaperlessDirs, + ) -> None: + """ + GIVEN: + - A document named after its title + WHEN: + - Its title is changed through the API, causing a rename + THEN: + - The modified time in the response equals the stored value, + which the file move bumped after the edit was saved + """ + response = admin_client.patch( + f"/api/documents/{document.pk}/", + {"title": "renamed"}, + format="json", + ) + + assert response.status_code == status.HTTP_200_OK + stored = Document.objects.get(pk=document.pk) + assert stored.filename == "renamed.pdf" + assert parse_datetime(response.json()["modified"]) == stored.modified + def test_trash_restore_keeps_db_and_disk_consistent( self, admin_client: APIClient, diff --git a/src/documents/views.py b/src/documents/views.py index aeed498cbb..391eb397c1 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -126,6 +126,7 @@ from documents.data_models import ConsumableDocument from documents.data_models import DocumentMetadataOverrides from documents.data_models import DocumentSource from documents.file_handling import format_filename +from documents.file_moves import flush_pending_file_moves from documents.filters import CorrespondentFilterSet from documents.filters import CustomFieldFilterSet from documents.filters import DocumentFilterSet @@ -1264,6 +1265,8 @@ class DocumentViewSet( ) serializer.is_valid(raise_exception=True) self.perform_update(serializer) + # The response must reflect the post-move filename and modified time + flush_pending_file_moves() if content_updated and content_doc.id != root_doc.id: content_doc.content = (