]> git.ipfire.org Git - thirdparty/paperless-ngx.git/commitdiff
Basic start of update endpoint
authorshamoon <4887959+shamoon@users.noreply.github.com>
Thu, 17 Apr 2025 04:20:54 +0000 (21:20 -0700)
committershamoon <4887959+shamoon@users.noreply.github.com>
Sat, 20 Sep 2025 17:09:34 +0000 (10:09 -0700)
src/documents/consumer.py
src/documents/data_models.py
src/documents/serialisers.py
src/documents/tasks.py
src/documents/views.py

index 86641a2433feec8444e2a448522b46bd36de0c58..da0d14063e0e2668b0473937f3427e8c44e7371d 100644 (file)
@@ -113,6 +113,12 @@ class ConsumerPluginMixin:
 
         self.filename = self.metadata.filename or self.input_doc.original_file.name
 
+        if input_doc.head_version_id:
+            self.log.debug(f"Document head version id: {input_doc.head_version_id}")
+            head_version = Document.objects.get(pk=input_doc.head_version_id)
+            version_index = head_version.versions.count()
+            self.filename += f"_v{version_index}"
+
     def _send_progress(
         self,
         current_progress: int,
@@ -470,12 +476,28 @@ class ConsumerPlugin(
         try:
             with transaction.atomic():
                 # store the document.
-                document = self._store(
-                    text=text,
-                    date=date,
-                    page_count=page_count,
-                    mime_type=mime_type,
-                )
+                if self.input_doc.head_version_id:
+                    # If this is a new version of an existing document, we need
+                    # to make sure we're not creating a new document, but updating
+                    # the existing one.
+                    original_document = Document.objects.get(
+                        pk=self.input_doc.head_version_id,
+                    )
+                    self.log.debug("Saving record for updated version to database")
+                    original_document.pk = None
+                    original_document.head_version = Document.objects.get(
+                        pk=self.input_doc.head_version_id,
+                    )
+                    original_document.modified = timezone.now()
+                    original_document.save()
+                    document = original_document
+                else:
+                    document = self._store(
+                        text=text,
+                        date=date,
+                        page_count=page_count,
+                        mime_type=mime_type,
+                    )
 
                 # If we get here, it was successful. Proceed with post-consume
                 # hooks. If they fail, nothing will get changed.
index fbba36dccdad039f416cfcadd032f2c43eb1e5cb..8ffca601006e5d3253e3101d9dc2174d582e006b 100644 (file)
@@ -156,6 +156,7 @@ class ConsumableDocument:
 
     source: DocumentSource
     original_file: Path
+    head_version_id: int | None = None
     mailrule_id: int | None = None
     mime_type: str = dataclasses.field(init=False, default=None)
 
index db58b29bb50480716fba8fcc39acd307d109d880..192c4e36ae643e51b1871dbb68643abbe2c433b0 100644 (file)
@@ -1869,6 +1869,15 @@ class PostDocumentSerializer(serializers.Serializer):
             return created.date()
 
 
+class DocumentVersionSerializer(serializers.Serializer):
+    document = serializers.FileField(
+        label="Document",
+        write_only=True,
+    )
+
+    validate_document = PostDocumentSerializer().validate_document
+
+
 class BulkDownloadSerializer(DocumentListSerializer):
     content = serializers.ChoiceField(
         choices=["archive", "originals", "both"],
index 17bfce3b066c845b3df81037380ea460bdc0d145..f6690687f2ae88005b0d2cbbd9132c8a610119ee 100644 (file)
@@ -145,13 +145,17 @@ def consume_file(
     if overrides is None:
         overrides = DocumentMetadataOverrides()
 
-    plugins: list[type[ConsumeTaskPlugin]] = [
-        ConsumerPreflightPlugin,
-        CollatePlugin,
-        BarcodePlugin,
-        WorkflowTriggerPlugin,
-        ConsumerPlugin,
-    ]
+    plugins: list[type[ConsumeTaskPlugin]] = (
+        [ConsumerPreflightPlugin,ConsumerPlugin]
+        if input_doc.head_version_id is not None
+        else [
+            ConsumerPreflightPlugin,
+            CollatePlugin,
+            BarcodePlugin,
+            WorkflowTriggerPlugin,
+            ConsumerPlugin,
+        ]
+    )
 
     with (
         ProgressManager(
index bb25d608b82e75c82da6aa8f5678d9b3953b6217..5b5df794e65f979adab359424355367f95ff6f7f 100644 (file)
@@ -147,6 +147,7 @@ from documents.serialisers import CustomFieldSerializer
 from documents.serialisers import DocumentListSerializer
 from documents.serialisers import DocumentSerializer
 from documents.serialisers import DocumentTypeSerializer
+from documents.serialisers import DocumentVersionSerializer
 from documents.serialisers import NotesSerializer
 from documents.serialisers import PostDocumentSerializer
 from documents.serialisers import RunTaskViewSerializer
@@ -1104,6 +1105,56 @@ class DocumentViewSet(
                 "Error emailing document, check logs for more detail.",
             )
 
+    @action(methods=["post"], detail=True)
+    def update_version(self, request, pk=None):
+        serializer = DocumentVersionSerializer(data=request.data)
+        serializer.is_valid(raise_exception=True)
+
+        try:
+            doc = Document.objects.select_related("owner").get(pk=pk)
+            if request.user is not None and not has_perms_owner_aware(
+                request.user,
+                "change_document",
+                doc,
+            ):
+                return HttpResponseForbidden("Insufficient permissions")
+        except Document.DoesNotExist:
+            raise Http404
+
+        try:
+            doc_name, doc_data = serializer.validated_data.get("document")
+
+            t = int(mktime(datetime.now().timetuple()))
+
+            settings.SCRATCH_DIR.mkdir(parents=True, exist_ok=True)
+
+            temp_file_path = Path(tempfile.mkdtemp(dir=settings.SCRATCH_DIR)) / Path(
+                pathvalidate.sanitize_filename(doc_name),
+            )
+
+            temp_file_path.write_bytes(doc_data)
+
+            os.utime(temp_file_path, times=(t, t))
+
+            input_doc = ConsumableDocument(
+                source=DocumentSource.ApiUpload,
+                original_file=temp_file_path,
+                head_version_id=doc.pk,
+            )
+
+            async_task = consume_file.delay(
+                input_doc,
+            )
+            logger.debug(
+                f"Updated document {doc.id} with new version",
+            )
+            return Response(async_task.id)
+        except Exception as e:
+            logger.warning(f"An error occurred updating document: {e!s}")
+            return HttpResponseServerError(
+                "Error updating document, check logs for more detail.",
+            )
+
 
 @extend_schema_view(
     list=extend_schema(