mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-02-11 23:59:31 -06:00
Version label
This commit is contained in:
@@ -191,9 +191,12 @@ export class DocumentService extends AbstractPaperlessService<Document> {
|
|||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadVersion(documentId: number, file: File) {
|
uploadVersion(documentId: number, file: File, label?: string) {
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('document', file, file.name)
|
formData.append('document', file, file.name)
|
||||||
|
if (label) {
|
||||||
|
formData.append('label', label)
|
||||||
|
}
|
||||||
return this.http.post(
|
return this.http.post(
|
||||||
this.getResourceUrl(documentId, 'update_version'),
|
this.getResourceUrl(documentId, 'update_version'),
|
||||||
formData,
|
formData,
|
||||||
|
|||||||
@@ -511,6 +511,8 @@ class ConsumerPlugin(
|
|||||||
original_document.filename = None
|
original_document.filename = None
|
||||||
original_document.archive_filename = None
|
original_document.archive_filename = None
|
||||||
original_document.archive_checksum = None
|
original_document.archive_checksum = None
|
||||||
|
if self.metadata.version_label is not None:
|
||||||
|
original_document.version_label = self.metadata.version_label
|
||||||
original_document.modified = timezone.now()
|
original_document.modified = timezone.now()
|
||||||
original_document.save()
|
original_document.save()
|
||||||
document = original_document
|
document = original_document
|
||||||
@@ -738,6 +740,9 @@ class ConsumerPlugin(
|
|||||||
if self.metadata.asn is not None:
|
if self.metadata.asn is not None:
|
||||||
document.archive_serial_number = self.metadata.asn
|
document.archive_serial_number = self.metadata.asn
|
||||||
|
|
||||||
|
if self.metadata.version_label is not None:
|
||||||
|
document.version_label = self.metadata.version_label
|
||||||
|
|
||||||
if self.metadata.owner_id:
|
if self.metadata.owner_id:
|
||||||
document.owner = User.objects.get(
|
document.owner = User.objects.get(
|
||||||
pk=self.metadata.owner_id,
|
pk=self.metadata.owner_id,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class DocumentMetadataOverrides:
|
|||||||
change_groups: list[int] | None = None
|
change_groups: list[int] | None = None
|
||||||
custom_fields: dict | None = None
|
custom_fields: dict | None = None
|
||||||
skip_asn_if_exists: bool = False
|
skip_asn_if_exists: bool = False
|
||||||
|
version_label: str | None = None
|
||||||
|
|
||||||
def update(self, other: "DocumentMetadataOverrides") -> "DocumentMetadataOverrides":
|
def update(self, other: "DocumentMetadataOverrides") -> "DocumentMetadataOverrides":
|
||||||
"""
|
"""
|
||||||
@@ -52,6 +53,8 @@ class DocumentMetadataOverrides:
|
|||||||
self.owner_id = other.owner_id
|
self.owner_id = other.owner_id
|
||||||
if other.skip_asn_if_exists:
|
if other.skip_asn_if_exists:
|
||||||
self.skip_asn_if_exists = True
|
self.skip_asn_if_exists = True
|
||||||
|
if other.version_label is not None:
|
||||||
|
self.version_label = other.version_label
|
||||||
|
|
||||||
# merge
|
# merge
|
||||||
if self.tag_ids is None:
|
if self.tag_ids is None:
|
||||||
|
|||||||
24
src/documents/migrations/0012_document_version_label.py
Normal file
24
src/documents/migrations/0012_document_version_label.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Generated by Codex on 2026-02-10
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("documents", "0011_document_head_version"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="document",
|
||||||
|
name="version_label",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Optional short label for a document version.",
|
||||||
|
max_length=64,
|
||||||
|
null=True,
|
||||||
|
verbose_name="version label",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -317,6 +317,14 @@ class Document(SoftDeleteModel, ModelWithOwner):
|
|||||||
verbose_name=_("head version of document"),
|
verbose_name=_("head version of document"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
version_label = models.CharField(
|
||||||
|
_("version label"),
|
||||||
|
max_length=64,
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
help_text=_("Optional short label for a document version."),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ("-created",)
|
ordering = ("-created",)
|
||||||
verbose_name = _("document")
|
verbose_name = _("document")
|
||||||
|
|||||||
@@ -1062,6 +1062,12 @@ class DuplicateDocumentSummarySerializer(serializers.Serializer):
|
|||||||
deleted_at = serializers.DateTimeField(allow_null=True)
|
deleted_at = serializers.DateTimeField(allow_null=True)
|
||||||
|
|
||||||
|
|
||||||
|
class DocumentVersionInfoSerializer(serializers.Serializer):
|
||||||
|
id = serializers.IntegerField()
|
||||||
|
added = serializers.DateTimeField()
|
||||||
|
label = serializers.CharField(required=False, allow_null=True)
|
||||||
|
|
||||||
|
|
||||||
@extend_schema_serializer(
|
@extend_schema_serializer(
|
||||||
deprecate_fields=["created_date"],
|
deprecate_fields=["created_date"],
|
||||||
)
|
)
|
||||||
@@ -1083,7 +1089,7 @@ class DocumentSerializer(
|
|||||||
|
|
||||||
notes = NotesSerializer(many=True, required=False, read_only=True)
|
notes = NotesSerializer(many=True, required=False, read_only=True)
|
||||||
head_version = serializers.PrimaryKeyRelatedField(read_only=True)
|
head_version = serializers.PrimaryKeyRelatedField(read_only=True)
|
||||||
versions = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
versions = SerializerMethodField()
|
||||||
|
|
||||||
custom_fields = CustomFieldInstanceSerializer(
|
custom_fields = CustomFieldInstanceSerializer(
|
||||||
many=True,
|
many=True,
|
||||||
@@ -1117,6 +1123,28 @@ class DocumentSerializer(
|
|||||||
duplicates = _get_viewable_duplicates(obj, user)
|
duplicates = _get_viewable_duplicates(obj, user)
|
||||||
return list(duplicates.values("id", "title", "deleted_at"))
|
return list(duplicates.values("id", "title", "deleted_at"))
|
||||||
|
|
||||||
|
@extend_schema_field(DocumentVersionInfoSerializer(many=True))
|
||||||
|
def get_versions(self, obj):
|
||||||
|
head_doc = obj if obj.head_version_id is None else obj.head_version
|
||||||
|
versions_qs = Document.objects.filter(head_version=head_doc).only(
|
||||||
|
"id",
|
||||||
|
"added",
|
||||||
|
"checksum",
|
||||||
|
"version_label",
|
||||||
|
)
|
||||||
|
versions = [*versions_qs, head_doc]
|
||||||
|
|
||||||
|
def build_info(doc: Document) -> dict[str, object]:
|
||||||
|
return {
|
||||||
|
"id": doc.id,
|
||||||
|
"added": doc.added,
|
||||||
|
"label": doc.version_label,
|
||||||
|
}
|
||||||
|
|
||||||
|
info = [build_info(doc) for doc in versions]
|
||||||
|
info.sort(key=lambda item: item["id"], reverse=True)
|
||||||
|
return info
|
||||||
|
|
||||||
def get_original_file_name(self, obj) -> str | None:
|
def get_original_file_name(self, obj) -> str | None:
|
||||||
return obj.original_filename
|
return obj.original_filename
|
||||||
|
|
||||||
@@ -1136,10 +1164,6 @@ class DocumentSerializer(
|
|||||||
request.version if request else settings.REST_FRAMEWORK["DEFAULT_VERSION"],
|
request.version if request else settings.REST_FRAMEWORK["DEFAULT_VERSION"],
|
||||||
)
|
)
|
||||||
|
|
||||||
if doc.get("versions") is not None:
|
|
||||||
doc["versions"] = sorted(doc["versions"], reverse=True)
|
|
||||||
doc["versions"].append(doc["id"])
|
|
||||||
|
|
||||||
if api_version < 9 and "created" in self.fields:
|
if api_version < 9 and "created" in self.fields:
|
||||||
# provide created as a datetime for backwards compatibility
|
# provide created as a datetime for backwards compatibility
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
@@ -2010,6 +2034,13 @@ class DocumentVersionSerializer(serializers.Serializer):
|
|||||||
label="Document",
|
label="Document",
|
||||||
write_only=True,
|
write_only=True,
|
||||||
)
|
)
|
||||||
|
label = serializers.CharField(
|
||||||
|
label="Version label",
|
||||||
|
required=False,
|
||||||
|
allow_blank=True,
|
||||||
|
allow_null=True,
|
||||||
|
max_length=64,
|
||||||
|
)
|
||||||
|
|
||||||
validate_document = PostDocumentSerializer().validate_document
|
validate_document = PostDocumentSerializer().validate_document
|
||||||
|
|
||||||
|
|||||||
@@ -1471,7 +1471,7 @@ class DocumentViewSet(
|
|||||||
"Error emailing documents, check logs for more detail.",
|
"Error emailing documents, check logs for more detail.",
|
||||||
)
|
)
|
||||||
|
|
||||||
@action(methods=["post"], detail=True)
|
@action(methods=["post"], detail=True, parser_classes=[parsers.MultiPartParser])
|
||||||
def update_version(self, request, pk=None):
|
def update_version(self, request, pk=None):
|
||||||
serializer = DocumentVersionSerializer(data=request.data)
|
serializer = DocumentVersionSerializer(data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
@@ -1489,6 +1489,7 @@ class DocumentViewSet(
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
doc_name, doc_data = serializer.validated_data.get("document")
|
doc_name, doc_data = serializer.validated_data.get("document")
|
||||||
|
label = serializer.validated_data.get("label")
|
||||||
|
|
||||||
t = int(mktime(datetime.now().timetuple()))
|
t = int(mktime(datetime.now().timetuple()))
|
||||||
|
|
||||||
@@ -1508,9 +1509,13 @@ class DocumentViewSet(
|
|||||||
head_version_id=doc.pk,
|
head_version_id=doc.pk,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
overrides = DocumentMetadataOverrides()
|
||||||
|
if label:
|
||||||
|
overrides.version_label = label.strip()
|
||||||
|
|
||||||
async_task = consume_file.delay(
|
async_task = consume_file.delay(
|
||||||
input_doc,
|
input_doc,
|
||||||
None,
|
overrides,
|
||||||
)
|
)
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"Updated document {doc.id} with new version",
|
f"Updated document {doc.id} with new version",
|
||||||
|
|||||||
Reference in New Issue
Block a user