diff --git a/src/documents/migrations/1047_savedview_display_mode_and_more.py b/src/documents/migrations/1047_savedview_display_mode_and_more.py index 28e13a797..3bfd638f9 100644 --- a/src/documents/migrations/1047_savedview_display_mode_and_more.py +++ b/src/documents/migrations/1047_savedview_display_mode_and_more.py @@ -1,7 +1,6 @@ # Generated by Django 4.2.11 on 2024-04-16 18:35 import django.core.validators -import multiselectfield.db.fields from django.db import migrations from django.db import models @@ -41,18 +40,8 @@ class Migration(migrations.Migration): migrations.AddField( model_name="savedview", name="display_fields", - field=multiselectfield.db.fields.MultiSelectField( + field=models.JSONField( blank=True, - choices=[ - ("title", "Title"), - ("created", "Created"), - ("added", "Added"), - ("tag", "Tags"), - ("documenttype", "Document Type"), - ("correspondent", "Correspondent"), - ("storagepath", "Storage Path"), - ], - max_length=128, null=True, verbose_name="Document display fields", ), diff --git a/src/documents/models.py b/src/documents/models.py index 53c4e7f62..2460825a7 100644 --- a/src/documents/models.py +++ b/src/documents/models.py @@ -393,164 +393,6 @@ class Log(models.Model): return self.message -class CustomField(models.Model): - """ - Defines the name and type of a custom field - """ - - class FieldDataType(models.TextChoices): - STRING = ("string", _("String")) - URL = ("url", _("URL")) - DATE = ("date", _("Date")) - BOOL = ("boolean"), _("Boolean") - INT = ("integer", _("Integer")) - FLOAT = ("float", _("Float")) - MONETARY = ("monetary", _("Monetary")) - DOCUMENTLINK = ("documentlink", _("Document Link")) - - created = models.DateTimeField( - _("created"), - default=timezone.now, - db_index=True, - editable=False, - ) - - name = models.CharField(max_length=128) - - data_type = models.CharField( - _("data type"), - max_length=50, - choices=FieldDataType.choices, - editable=False, - ) - - class Meta: - ordering = ("created",) - verbose_name = _("custom field") - verbose_name_plural = _("custom fields") - constraints = [ - models.UniqueConstraint( - fields=["name"], - name="%(app_label)s_%(class)s_unique_name", - ), - ] - - def __str__(self) -> str: - return f"{self.name} : {self.data_type}" - - -class CustomFieldInstance(models.Model): - """ - A single instance of a field, attached to a CustomField for the name and type - and attached to a single Document to be metadata for it - """ - - created = models.DateTimeField( - _("created"), - default=timezone.now, - db_index=True, - editable=False, - ) - - document = models.ForeignKey( - Document, - blank=False, - null=False, - on_delete=models.CASCADE, - related_name="custom_fields", - editable=False, - ) - - field = models.ForeignKey( - CustomField, - blank=False, - null=False, - on_delete=models.CASCADE, - related_name="fields", - editable=False, - ) - - # Actual data storage - value_text = models.CharField(max_length=128, null=True) - - value_bool = models.BooleanField(null=True) - - value_url = models.URLField(null=True) - - value_date = models.DateField(null=True) - - value_int = models.IntegerField(null=True) - - value_float = models.FloatField(null=True) - - value_monetary = models.CharField(null=True, max_length=128) - - value_document_ids = models.JSONField(null=True) - - class Meta: - ordering = ("created",) - verbose_name = _("custom field instance") - verbose_name_plural = _("custom field instances") - constraints = [ - models.UniqueConstraint( - fields=["document", "field"], - name="%(app_label)s_%(class)s_unique_document_field", - ), - ] - - def __str__(self) -> str: - return str(self.field.name) + f" : {self.value}" - - @property - def value(self): - """ - Based on the data type, access the actual value the instance stores - A little shorthand/quick way to get what is actually here - """ - if self.field.data_type == CustomField.FieldDataType.STRING: - return self.value_text - elif self.field.data_type == CustomField.FieldDataType.URL: - return self.value_url - elif self.field.data_type == CustomField.FieldDataType.DATE: - return self.value_date - elif self.field.data_type == CustomField.FieldDataType.BOOL: - return self.value_bool - elif self.field.data_type == CustomField.FieldDataType.INT: - return self.value_int - elif self.field.data_type == CustomField.FieldDataType.FLOAT: - return self.value_float - elif self.field.data_type == CustomField.FieldDataType.MONETARY: - return self.value_monetary - elif self.field.data_type == CustomField.FieldDataType.DOCUMENTLINK: - return self.value_document_ids - raise NotImplementedError(self.field.data_type) # pragma: no cover - - -class DynamicMultiSelectField(MultiSelectField): - """ - A MultiSelectField that can have dynamic choices from a model - """ - - def __init__(self, *args, **kwargs): - self.dynamic_choices = kwargs.pop("dyanmic_choices", None) - super().__init__(*args, **kwargs) - - def _get_choices(self): - return self._choices - - def _set_choices(self, value): - if self.dynamic_choices: - for key, model in self.dynamic_choices: - try: - for obj in model.objects.all(): # pragma: no cover - value.append((key % obj.pk, obj.name)) - except Exception: - pass - self._choices = value - - choices = property(_get_choices, _set_choices) - - class SavedView(ModelWithOwner): class DisplayMode(models.TextChoices): TABLE = ("table", _("Table")) @@ -565,9 +407,7 @@ class SavedView(ModelWithOwner): DOCUMENT_TYPE = ("documenttype", _("Document Type")) CORRESPONDENT = ("correspondent", _("Correspondent")) STORAGE_PATH = ("storagepath", _("Storage Path")) - - class DynamicDisplayFields: - CUSTOM_FIELD = ("custom_field_%d", CustomField) + CUSTOM_FIELD = ("custom_field_%d", ("Custom Field")) name = models.CharField(_("name"), max_length=128) @@ -601,11 +441,8 @@ class SavedView(ModelWithOwner): blank=True, ) - display_fields = DynamicMultiSelectField( - max_length=128, + display_fields = models.JSONField( verbose_name=_("Document display fields"), - choices=DisplayFields.choices, - dyanmic_choices=[DynamicDisplayFields.CUSTOM_FIELD], null=True, blank=True, ) @@ -947,6 +784,139 @@ class ShareLink(models.Model): return f"Share Link for {self.document.title}" +class CustomField(models.Model): + """ + Defines the name and type of a custom field + """ + + class FieldDataType(models.TextChoices): + STRING = ("string", _("String")) + URL = ("url", _("URL")) + DATE = ("date", _("Date")) + BOOL = ("boolean"), _("Boolean") + INT = ("integer", _("Integer")) + FLOAT = ("float", _("Float")) + MONETARY = ("monetary", _("Monetary")) + DOCUMENTLINK = ("documentlink", _("Document Link")) + + created = models.DateTimeField( + _("created"), + default=timezone.now, + db_index=True, + editable=False, + ) + + name = models.CharField(max_length=128) + + data_type = models.CharField( + _("data type"), + max_length=50, + choices=FieldDataType.choices, + editable=False, + ) + + class Meta: + ordering = ("created",) + verbose_name = _("custom field") + verbose_name_plural = _("custom fields") + constraints = [ + models.UniqueConstraint( + fields=["name"], + name="%(app_label)s_%(class)s_unique_name", + ), + ] + + def __str__(self) -> str: + return f"{self.name} : {self.data_type}" + + +class CustomFieldInstance(models.Model): + """ + A single instance of a field, attached to a CustomField for the name and type + and attached to a single Document to be metadata for it + """ + + created = models.DateTimeField( + _("created"), + default=timezone.now, + db_index=True, + editable=False, + ) + + document = models.ForeignKey( + Document, + blank=False, + null=False, + on_delete=models.CASCADE, + related_name="custom_fields", + editable=False, + ) + + field = models.ForeignKey( + CustomField, + blank=False, + null=False, + on_delete=models.CASCADE, + related_name="fields", + editable=False, + ) + + # Actual data storage + value_text = models.CharField(max_length=128, null=True) + + value_bool = models.BooleanField(null=True) + + value_url = models.URLField(null=True) + + value_date = models.DateField(null=True) + + value_int = models.IntegerField(null=True) + + value_float = models.FloatField(null=True) + + value_monetary = models.CharField(null=True, max_length=128) + + value_document_ids = models.JSONField(null=True) + + class Meta: + ordering = ("created",) + verbose_name = _("custom field instance") + verbose_name_plural = _("custom field instances") + constraints = [ + models.UniqueConstraint( + fields=["document", "field"], + name="%(app_label)s_%(class)s_unique_document_field", + ), + ] + + def __str__(self) -> str: + return str(self.field.name) + f" : {self.value}" + + @property + def value(self): + """ + Based on the data type, access the actual value the instance stores + A little shorthand/quick way to get what is actually here + """ + if self.field.data_type == CustomField.FieldDataType.STRING: + return self.value_text + elif self.field.data_type == CustomField.FieldDataType.URL: + return self.value_url + elif self.field.data_type == CustomField.FieldDataType.DATE: + return self.value_date + elif self.field.data_type == CustomField.FieldDataType.BOOL: + return self.value_bool + elif self.field.data_type == CustomField.FieldDataType.INT: + return self.value_int + elif self.field.data_type == CustomField.FieldDataType.FLOAT: + return self.value_float + elif self.field.data_type == CustomField.FieldDataType.MONETARY: + return self.value_monetary + elif self.field.data_type == CustomField.FieldDataType.DOCUMENTLINK: + return self.value_document_ids + raise NotImplementedError(self.field.data_type) + + if settings.AUDIT_LOG_ENABLED: auditlog.register( Document, diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index a3cec7916..d0cfab7bf 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -802,48 +802,8 @@ class SavedViewFilterRuleSerializer(serializers.ModelSerializer): fields = ["rule_type", "value"] -class DynamicOrderedMultipleChoiceField(fields.MultipleChoiceField): - """ - A MultipleChoiceField that allows for dynamic choices from a model - and preserves the order of the choices. - """ - - def __init__(self, **kwargs): - self.dyanmic_choices = kwargs.pop("dyanmic_choices", None) - super().__init__(**kwargs) - - def _get_choices(self): - return super()._get_choices() - - def _set_choices(self, choices): - if self.dyanmic_choices is not None: - for key, Model in self.dyanmic_choices: - try: - for obj in Model.objects.all(): - choices.append((key % obj.pk, obj.name)) - except Exception: - pass - return super()._set_choices(choices) - - choices = property(_get_choices, _set_choices) - - def to_internal_value(self, data): - super().to_internal_value(data) - # MultipleChoiceField doesn't preserve order, so we use an array - return [fields.ChoiceField.to_internal_value(self, item) for item in data] - - def to_representation(self, value): - # MultipleChoiceField doesn't preserve order, so we return as array to match the original order - return [self.choice_strings_to_values.get(str(item), item) for item in value] - - class SavedViewSerializer(OwnedObjectSerializer): filter_rules = SavedViewFilterRuleSerializer(many=True) - display_fields = DynamicOrderedMultipleChoiceField( - choices=SavedView.DisplayFields.choices, - dyanmic_choices=[("custom_field_%d", CustomField)], - required=False, - ) class Meta: model = SavedView @@ -864,6 +824,22 @@ class SavedViewSerializer(OwnedObjectSerializer): "set_permissions", ] + def validate(self, attrs): + attrs = super().validate(attrs) + if "display_fields" in attrs: + for field in attrs["display_fields"]: + if re.sub(r"\d+", "%d", field) == SavedView.DisplayFields.CUSTOM_FIELD: + field_id = int(re.search(r"\d+", field)[0]) + if not CustomField.objects.filter(id=field_id).exists(): + raise serializers.ValidationError( + f"Invalid field: {field}", + ) + elif field not in SavedView.DisplayFields.values: + raise serializers.ValidationError( + f"Invalid field: {field}", + ) + return attrs + def update(self, instance, validated_data): if "filter_rules" in validated_data: rules_data = validated_data.pop("filter_rules") diff --git a/src/documents/tests/test_api_documents.py b/src/documents/tests/test_api_documents.py index aa0ee2dcf..62c0002b2 100644 --- a/src/documents/tests/test_api_documents.py +++ b/src/documents/tests/test_api_documents.py @@ -1749,7 +1749,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): "display_fields": [ SavedView.DisplayFields.TITLE, SavedView.DisplayFields.CREATED, - SavedView.DynamicDisplayFields.CUSTOM_FIELD[0] % custom_field.id, + SavedView.DisplayFields.CUSTOM_FIELD % custom_field.id, ], }, format="json", @@ -1762,7 +1762,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): [ str(SavedView.DisplayFields.TITLE), str(SavedView.DisplayFields.CREATED), - SavedView.DynamicDisplayFields.CUSTOM_FIELD[0] % custom_field.id, + SavedView.DisplayFields.CUSTOM_FIELD % custom_field.id, ], ) @@ -1773,7 +1773,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): "display_fields": [ SavedView.DisplayFields.TITLE, SavedView.DisplayFields.CREATED, - SavedView.DynamicDisplayFields.CUSTOM_FIELD[0] % 99, + SavedView.DisplayFields.CUSTOM_FIELD % 99, ], }, format="json", diff --git a/src/locale/en_US/LC_MESSAGES/django.po b/src/locale/en_US/LC_MESSAGES/django.po index 7564b6077..8ac79bab9 100644 --- a/src/locale/en_US/LC_MESSAGES/django.po +++ b/src/locale/en_US/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: paperless-ngx\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-04-19 01:14-0700\n" +"POT-Creation-Date: 2024-04-20 22:11-0700\n" "PO-Revision-Date: 2022-02-17 04:17\n" "Last-Translator: \n" "Language-Team: English\n" @@ -21,31 +21,31 @@ msgstr "" msgid "Documents" msgstr "" -#: documents/models.py:36 documents/models.py:938 +#: documents/models.py:36 documents/models.py:775 msgid "owner" msgstr "" -#: documents/models.py:53 documents/models.py:963 +#: documents/models.py:53 documents/models.py:933 msgid "None" msgstr "" -#: documents/models.py:54 documents/models.py:964 +#: documents/models.py:54 documents/models.py:934 msgid "Any word" msgstr "" -#: documents/models.py:55 documents/models.py:965 +#: documents/models.py:55 documents/models.py:935 msgid "All words" msgstr "" -#: documents/models.py:56 documents/models.py:966 +#: documents/models.py:56 documents/models.py:936 msgid "Exact match" msgstr "" -#: documents/models.py:57 documents/models.py:967 +#: documents/models.py:57 documents/models.py:937 msgid "Regular expression" msgstr "" -#: documents/models.py:58 documents/models.py:968 +#: documents/models.py:58 documents/models.py:938 msgid "Fuzzy word" msgstr "" @@ -53,20 +53,20 @@ msgstr "" msgid "Automatic" msgstr "" -#: documents/models.py:62 documents/models.py:572 documents/models.py:1284 +#: documents/models.py:62 documents/models.py:412 documents/models.py:1254 #: paperless_mail/models.py:18 paperless_mail/models.py:93 msgid "name" msgstr "" -#: documents/models.py:64 documents/models.py:1024 +#: documents/models.py:64 documents/models.py:994 msgid "match" msgstr "" -#: documents/models.py:67 documents/models.py:1027 +#: documents/models.py:67 documents/models.py:997 msgid "matching algorithm" msgstr "" -#: documents/models.py:72 documents/models.py:1032 +#: documents/models.py:72 documents/models.py:1002 msgid "is insensitive" msgstr "" @@ -132,7 +132,7 @@ msgstr "" msgid "title" msgstr "" -#: documents/models.py:171 documents/models.py:852 +#: documents/models.py:171 documents/models.py:689 msgid "content" msgstr "" @@ -162,8 +162,8 @@ msgstr "" msgid "The checksum of the archived document." msgstr "" -#: documents/models.py:205 documents/models.py:385 documents/models.py:412 -#: documents/models.py:449 documents/models.py:858 documents/models.py:896 +#: documents/models.py:205 documents/models.py:385 documents/models.py:695 +#: documents/models.py:733 documents/models.py:803 documents/models.py:840 msgid "created" msgstr "" @@ -211,7 +211,7 @@ msgstr "" msgid "The position of this document in your physical document archive." msgstr "" -#: documents/models.py:279 documents/models.py:869 documents/models.py:923 +#: documents/models.py:279 documents/models.py:706 documents/models.py:760 msgid "document" msgstr "" @@ -259,636 +259,636 @@ msgstr "" msgid "logs" msgstr "" -#: documents/models.py:402 -msgid "String" -msgstr "" - -#: documents/models.py:403 -msgid "URL" -msgstr "" - -#: documents/models.py:404 -msgid "Date" -msgstr "" - -#: documents/models.py:405 -msgid "Boolean" -msgstr "" - -#: documents/models.py:406 -msgid "Integer" -msgstr "" - -#: documents/models.py:407 -msgid "Float" -msgstr "" - -#: documents/models.py:408 -msgid "Monetary" -msgstr "" - -#: documents/models.py:409 -msgid "Document Link" -msgstr "" - -#: documents/models.py:421 -msgid "data type" -msgstr "" - -#: documents/models.py:429 -msgid "custom field" -msgstr "" - -#: documents/models.py:430 -msgid "custom fields" -msgstr "" - -#: documents/models.py:492 -msgid "custom field instance" -msgstr "" - -#: documents/models.py:493 -msgid "custom field instances" -msgstr "" - -#: documents/models.py:556 +#: documents/models.py:398 msgid "Table" msgstr "" -#: documents/models.py:557 +#: documents/models.py:399 msgid "Small Cards" msgstr "" -#: documents/models.py:558 +#: documents/models.py:400 msgid "Large Cards" msgstr "" -#: documents/models.py:561 +#: documents/models.py:403 msgid "Title" msgstr "" -#: documents/models.py:562 +#: documents/models.py:404 msgid "Created" msgstr "" -#: documents/models.py:563 +#: documents/models.py:405 msgid "Added" msgstr "" -#: documents/models.py:564 +#: documents/models.py:406 msgid "Tags" msgstr "" -#: documents/models.py:565 +#: documents/models.py:407 msgid "Document Type" msgstr "" -#: documents/models.py:566 +#: documents/models.py:408 msgid "Correspondent" msgstr "" -#: documents/models.py:567 +#: documents/models.py:409 msgid "Storage Path" msgstr "" -#: documents/models.py:575 +#: documents/models.py:415 msgid "show on dashboard" msgstr "" -#: documents/models.py:578 +#: documents/models.py:418 msgid "show in sidebar" msgstr "" -#: documents/models.py:582 +#: documents/models.py:422 msgid "sort field" msgstr "" -#: documents/models.py:587 +#: documents/models.py:427 msgid "sort reverse" msgstr "" -#: documents/models.py:590 +#: documents/models.py:430 msgid "View page size" msgstr "" -#: documents/models.py:598 +#: documents/models.py:438 msgid "View display mode" msgstr "" -#: documents/models.py:606 +#: documents/models.py:445 msgid "Document display fields" msgstr "" -#: documents/models.py:615 documents/models.py:668 +#: documents/models.py:452 documents/models.py:505 msgid "saved view" msgstr "" -#: documents/models.py:616 +#: documents/models.py:453 msgid "saved views" msgstr "" -#: documents/models.py:624 +#: documents/models.py:461 msgid "title contains" msgstr "" -#: documents/models.py:625 +#: documents/models.py:462 msgid "content contains" msgstr "" -#: documents/models.py:626 +#: documents/models.py:463 msgid "ASN is" msgstr "" -#: documents/models.py:627 +#: documents/models.py:464 msgid "correspondent is" msgstr "" -#: documents/models.py:628 +#: documents/models.py:465 msgid "document type is" msgstr "" -#: documents/models.py:629 +#: documents/models.py:466 msgid "is in inbox" msgstr "" -#: documents/models.py:630 +#: documents/models.py:467 msgid "has tag" msgstr "" -#: documents/models.py:631 +#: documents/models.py:468 msgid "has any tag" msgstr "" -#: documents/models.py:632 +#: documents/models.py:469 msgid "created before" msgstr "" -#: documents/models.py:633 +#: documents/models.py:470 msgid "created after" msgstr "" -#: documents/models.py:634 +#: documents/models.py:471 msgid "created year is" msgstr "" -#: documents/models.py:635 +#: documents/models.py:472 msgid "created month is" msgstr "" -#: documents/models.py:636 +#: documents/models.py:473 msgid "created day is" msgstr "" -#: documents/models.py:637 +#: documents/models.py:474 msgid "added before" msgstr "" -#: documents/models.py:638 +#: documents/models.py:475 msgid "added after" msgstr "" -#: documents/models.py:639 +#: documents/models.py:476 msgid "modified before" msgstr "" -#: documents/models.py:640 +#: documents/models.py:477 msgid "modified after" msgstr "" -#: documents/models.py:641 +#: documents/models.py:478 msgid "does not have tag" msgstr "" -#: documents/models.py:642 +#: documents/models.py:479 msgid "does not have ASN" msgstr "" -#: documents/models.py:643 +#: documents/models.py:480 msgid "title or content contains" msgstr "" -#: documents/models.py:644 +#: documents/models.py:481 msgid "fulltext query" msgstr "" -#: documents/models.py:645 +#: documents/models.py:482 msgid "more like this" msgstr "" -#: documents/models.py:646 +#: documents/models.py:483 msgid "has tags in" msgstr "" -#: documents/models.py:647 +#: documents/models.py:484 msgid "ASN greater than" msgstr "" -#: documents/models.py:648 +#: documents/models.py:485 msgid "ASN less than" msgstr "" -#: documents/models.py:649 +#: documents/models.py:486 msgid "storage path is" msgstr "" -#: documents/models.py:650 +#: documents/models.py:487 msgid "has correspondent in" msgstr "" -#: documents/models.py:651 +#: documents/models.py:488 msgid "does not have correspondent in" msgstr "" -#: documents/models.py:652 +#: documents/models.py:489 msgid "has document type in" msgstr "" -#: documents/models.py:653 +#: documents/models.py:490 msgid "does not have document type in" msgstr "" -#: documents/models.py:654 +#: documents/models.py:491 msgid "has storage path in" msgstr "" -#: documents/models.py:655 +#: documents/models.py:492 msgid "does not have storage path in" msgstr "" -#: documents/models.py:656 +#: documents/models.py:493 msgid "owner is" msgstr "" -#: documents/models.py:657 +#: documents/models.py:494 msgid "has owner in" msgstr "" -#: documents/models.py:658 +#: documents/models.py:495 msgid "does not have owner" msgstr "" -#: documents/models.py:659 +#: documents/models.py:496 msgid "does not have owner in" msgstr "" -#: documents/models.py:660 +#: documents/models.py:497 msgid "has custom field value" msgstr "" -#: documents/models.py:661 +#: documents/models.py:498 msgid "is shared by me" msgstr "" -#: documents/models.py:671 +#: documents/models.py:508 msgid "rule type" msgstr "" -#: documents/models.py:673 +#: documents/models.py:510 msgid "value" msgstr "" -#: documents/models.py:676 +#: documents/models.py:513 msgid "filter rule" msgstr "" -#: documents/models.py:677 +#: documents/models.py:514 msgid "filter rules" msgstr "" -#: documents/models.py:788 +#: documents/models.py:625 msgid "Task ID" msgstr "" -#: documents/models.py:789 +#: documents/models.py:626 msgid "Celery ID for the Task that was run" msgstr "" -#: documents/models.py:794 +#: documents/models.py:631 msgid "Acknowledged" msgstr "" -#: documents/models.py:795 +#: documents/models.py:632 msgid "If the task is acknowledged via the frontend or API" msgstr "" -#: documents/models.py:801 +#: documents/models.py:638 msgid "Task Filename" msgstr "" -#: documents/models.py:802 +#: documents/models.py:639 msgid "Name of the file which the Task was run for" msgstr "" -#: documents/models.py:808 +#: documents/models.py:645 msgid "Task Name" msgstr "" -#: documents/models.py:809 +#: documents/models.py:646 msgid "Name of the Task which was run" msgstr "" -#: documents/models.py:816 +#: documents/models.py:653 msgid "Task State" msgstr "" -#: documents/models.py:817 +#: documents/models.py:654 msgid "Current state of the task being run" msgstr "" -#: documents/models.py:822 +#: documents/models.py:659 msgid "Created DateTime" msgstr "" -#: documents/models.py:823 +#: documents/models.py:660 msgid "Datetime field when the task result was created in UTC" msgstr "" -#: documents/models.py:828 +#: documents/models.py:665 msgid "Started DateTime" msgstr "" -#: documents/models.py:829 +#: documents/models.py:666 msgid "Datetime field when the task was started in UTC" msgstr "" -#: documents/models.py:834 +#: documents/models.py:671 msgid "Completed DateTime" msgstr "" -#: documents/models.py:835 +#: documents/models.py:672 msgid "Datetime field when the task was completed in UTC" msgstr "" -#: documents/models.py:840 +#: documents/models.py:677 msgid "Result Data" msgstr "" -#: documents/models.py:842 +#: documents/models.py:679 msgid "The data returned by the task" msgstr "" -#: documents/models.py:854 +#: documents/models.py:691 msgid "Note for the document" msgstr "" -#: documents/models.py:878 +#: documents/models.py:715 msgid "user" msgstr "" -#: documents/models.py:883 +#: documents/models.py:720 msgid "note" msgstr "" -#: documents/models.py:884 +#: documents/models.py:721 msgid "notes" msgstr "" -#: documents/models.py:892 +#: documents/models.py:729 msgid "Archive" msgstr "" -#: documents/models.py:893 +#: documents/models.py:730 msgid "Original" msgstr "" -#: documents/models.py:904 +#: documents/models.py:741 msgid "expiration" msgstr "" -#: documents/models.py:911 +#: documents/models.py:748 msgid "slug" msgstr "" -#: documents/models.py:943 +#: documents/models.py:780 msgid "share link" msgstr "" -#: documents/models.py:944 +#: documents/models.py:781 msgid "share links" msgstr "" -#: documents/models.py:971 +#: documents/models.py:793 +msgid "String" +msgstr "" + +#: documents/models.py:794 +msgid "URL" +msgstr "" + +#: documents/models.py:795 +msgid "Date" +msgstr "" + +#: documents/models.py:796 +msgid "Boolean" +msgstr "" + +#: documents/models.py:797 +msgid "Integer" +msgstr "" + +#: documents/models.py:798 +msgid "Float" +msgstr "" + +#: documents/models.py:799 +msgid "Monetary" +msgstr "" + +#: documents/models.py:800 +msgid "Document Link" +msgstr "" + +#: documents/models.py:812 +msgid "data type" +msgstr "" + +#: documents/models.py:820 +msgid "custom field" +msgstr "" + +#: documents/models.py:821 +msgid "custom fields" +msgstr "" + +#: documents/models.py:883 +msgid "custom field instance" +msgstr "" + +#: documents/models.py:884 +msgid "custom field instances" +msgstr "" + +#: documents/models.py:941 msgid "Consumption Started" msgstr "" -#: documents/models.py:972 +#: documents/models.py:942 msgid "Document Added" msgstr "" -#: documents/models.py:973 +#: documents/models.py:943 msgid "Document Updated" msgstr "" -#: documents/models.py:976 +#: documents/models.py:946 msgid "Consume Folder" msgstr "" -#: documents/models.py:977 +#: documents/models.py:947 msgid "Api Upload" msgstr "" -#: documents/models.py:978 +#: documents/models.py:948 msgid "Mail Fetch" msgstr "" -#: documents/models.py:981 +#: documents/models.py:951 msgid "Workflow Trigger Type" msgstr "" -#: documents/models.py:993 +#: documents/models.py:963 msgid "filter path" msgstr "" -#: documents/models.py:998 +#: documents/models.py:968 msgid "" "Only consume documents with a path that matches this if specified. Wildcards " "specified as * are allowed. Case insensitive." msgstr "" -#: documents/models.py:1005 +#: documents/models.py:975 msgid "filter filename" msgstr "" -#: documents/models.py:1010 paperless_mail/models.py:148 +#: documents/models.py:980 paperless_mail/models.py:148 msgid "" "Only consume documents which entirely match this filename if specified. " "Wildcards such as *.pdf or *invoice* are allowed. Case insensitive." msgstr "" -#: documents/models.py:1021 +#: documents/models.py:991 msgid "filter documents from this mail rule" msgstr "" -#: documents/models.py:1037 +#: documents/models.py:1007 msgid "has these tag(s)" msgstr "" -#: documents/models.py:1045 +#: documents/models.py:1015 msgid "has this document type" msgstr "" -#: documents/models.py:1053 +#: documents/models.py:1023 msgid "has this correspondent" msgstr "" -#: documents/models.py:1057 +#: documents/models.py:1027 msgid "workflow trigger" msgstr "" -#: documents/models.py:1058 +#: documents/models.py:1028 msgid "workflow triggers" msgstr "" -#: documents/models.py:1068 +#: documents/models.py:1038 msgid "Assignment" msgstr "" -#: documents/models.py:1072 +#: documents/models.py:1042 msgid "Removal" msgstr "" -#: documents/models.py:1076 +#: documents/models.py:1046 msgid "Workflow Action Type" msgstr "" -#: documents/models.py:1082 +#: documents/models.py:1052 msgid "assign title" msgstr "" -#: documents/models.py:1087 +#: documents/models.py:1057 msgid "" "Assign a document title, can include some placeholders, see documentation." msgstr "" -#: documents/models.py:1096 paperless_mail/models.py:216 +#: documents/models.py:1066 paperless_mail/models.py:216 msgid "assign this tag" msgstr "" -#: documents/models.py:1105 paperless_mail/models.py:224 +#: documents/models.py:1075 paperless_mail/models.py:224 msgid "assign this document type" msgstr "" -#: documents/models.py:1114 paperless_mail/models.py:238 +#: documents/models.py:1084 paperless_mail/models.py:238 msgid "assign this correspondent" msgstr "" -#: documents/models.py:1123 +#: documents/models.py:1093 msgid "assign this storage path" msgstr "" -#: documents/models.py:1132 +#: documents/models.py:1102 msgid "assign this owner" msgstr "" -#: documents/models.py:1139 +#: documents/models.py:1109 msgid "grant view permissions to these users" msgstr "" -#: documents/models.py:1146 +#: documents/models.py:1116 msgid "grant view permissions to these groups" msgstr "" -#: documents/models.py:1153 +#: documents/models.py:1123 msgid "grant change permissions to these users" msgstr "" -#: documents/models.py:1160 +#: documents/models.py:1130 msgid "grant change permissions to these groups" msgstr "" -#: documents/models.py:1167 +#: documents/models.py:1137 msgid "assign these custom fields" msgstr "" -#: documents/models.py:1174 +#: documents/models.py:1144 msgid "remove these tag(s)" msgstr "" -#: documents/models.py:1179 +#: documents/models.py:1149 msgid "remove all tags" msgstr "" -#: documents/models.py:1186 +#: documents/models.py:1156 msgid "remove these document type(s)" msgstr "" -#: documents/models.py:1191 +#: documents/models.py:1161 msgid "remove all document types" msgstr "" -#: documents/models.py:1198 +#: documents/models.py:1168 msgid "remove these correspondent(s)" msgstr "" -#: documents/models.py:1203 +#: documents/models.py:1173 msgid "remove all correspondents" msgstr "" -#: documents/models.py:1210 +#: documents/models.py:1180 msgid "remove these storage path(s)" msgstr "" -#: documents/models.py:1215 +#: documents/models.py:1185 msgid "remove all storage paths" msgstr "" -#: documents/models.py:1222 +#: documents/models.py:1192 msgid "remove these owner(s)" msgstr "" -#: documents/models.py:1227 +#: documents/models.py:1197 msgid "remove all owners" msgstr "" -#: documents/models.py:1234 +#: documents/models.py:1204 msgid "remove view permissions for these users" msgstr "" -#: documents/models.py:1241 +#: documents/models.py:1211 msgid "remove view permissions for these groups" msgstr "" -#: documents/models.py:1248 +#: documents/models.py:1218 msgid "remove change permissions for these users" msgstr "" -#: documents/models.py:1255 +#: documents/models.py:1225 msgid "remove change permissions for these groups" msgstr "" -#: documents/models.py:1260 +#: documents/models.py:1230 msgid "remove all permissions" msgstr "" -#: documents/models.py:1267 +#: documents/models.py:1237 msgid "remove these custom fields" msgstr "" -#: documents/models.py:1272 +#: documents/models.py:1242 msgid "remove all custom fields" msgstr "" -#: documents/models.py:1276 +#: documents/models.py:1246 msgid "workflow action" msgstr "" -#: documents/models.py:1277 +#: documents/models.py:1247 msgid "workflow actions" msgstr "" -#: documents/models.py:1286 paperless_mail/models.py:95 +#: documents/models.py:1256 paperless_mail/models.py:95 msgid "order" msgstr "" -#: documents/models.py:1292 +#: documents/models.py:1262 msgid "triggers" msgstr "" -#: documents/models.py:1299 +#: documents/models.py:1269 msgid "actions" msgstr "" -#: documents/models.py:1302 +#: documents/models.py:1272 msgid "enabled" msgstr "" @@ -901,12 +901,12 @@ msgstr "" msgid "Invalid color." msgstr "" -#: documents/serialisers.py:1186 +#: documents/serialisers.py:1162 #, python-format msgid "File type %(type)s not supported" msgstr "" -#: documents/serialisers.py:1295 +#: documents/serialisers.py:1271 msgid "Invalid variable detected." msgstr ""