diff --git a/src/documents/migrations/0001_initial_squashed_0003_workflowaction_order.py b/src/documents/migrations/0001_initial_squashed_0003_workflowaction_order.py new file mode 100644 index 000000000..a6e8391e9 --- /dev/null +++ b/src/documents/migrations/0001_initial_squashed_0003_workflowaction_order.py @@ -0,0 +1,2078 @@ +# Generated by Django 5.2.9 on 2026-01-20 20:34 + +import datetime + +import django.core.validators +import django.db.models.deletion +import django.db.models.functions.comparison +import django.db.models.functions.text +import django.utils.timezone +import multiselectfield.db.fields +from django.conf import settings +from django.db import migrations +from django.db import models + + +class Migration(migrations.Migration): + replaces = [ + ("documents", "0001_initial"), + ("documents", "0002_initial"), + ("documents", "0003_workflowaction_order"), + ] + + dependencies = [ + ("auth", "0012_alter_user_first_name_max_length"), + ("paperless_mail", "0001_initial"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="WorkflowActionEmail", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "subject", + models.CharField( + help_text="The subject of the email, can include some placeholders, see documentation.", + max_length=256, + verbose_name="email subject", + ), + ), + ( + "body", + models.TextField( + help_text="The body (message) of the email, can include some placeholders, see documentation.", + verbose_name="email body", + ), + ), + ( + "to", + models.TextField( + help_text="The destination email addresses, comma separated.", + verbose_name="emails to", + ), + ), + ( + "include_document", + models.BooleanField( + default=False, + verbose_name="include document in email", + ), + ), + ], + ), + migrations.CreateModel( + name="WorkflowActionWebhook", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "url", + models.CharField( + help_text="The destination URL for the notification.", + max_length=256, + verbose_name="webhook url", + ), + ), + ( + "use_params", + models.BooleanField(default=True, verbose_name="use parameters"), + ), + ( + "as_json", + models.BooleanField(default=False, verbose_name="send as JSON"), + ), + ( + "params", + models.JSONField( + blank=True, + help_text="The parameters to send with the webhook URL if body not used.", + null=True, + verbose_name="webhook parameters", + ), + ), + ( + "body", + models.TextField( + blank=True, + help_text="The body to send with the webhook URL if parameters not used.", + null=True, + verbose_name="webhook body", + ), + ), + ( + "headers", + models.JSONField( + blank=True, + help_text="The headers to send with the webhook URL.", + null=True, + verbose_name="webhook headers", + ), + ), + ( + "include_document", + models.BooleanField( + default=False, + verbose_name="include document in webhook", + ), + ), + ], + ), + migrations.CreateModel( + name="Correspondent", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=128, verbose_name="name")), + ( + "match", + models.CharField(blank=True, max_length=256, verbose_name="match"), + ), + ( + "matching_algorithm", + models.PositiveIntegerField( + choices=[ + (0, "None"), + (1, "Any word"), + (2, "All words"), + (3, "Exact match"), + (4, "Regular expression"), + (5, "Fuzzy word"), + (6, "Automatic"), + ], + default=1, + verbose_name="matching algorithm", + ), + ), + ( + "is_insensitive", + models.BooleanField(default=True, verbose_name="is insensitive"), + ), + ( + "owner", + models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + verbose_name="owner", + ), + ), + ], + options={ + "verbose_name": "correspondent", + "verbose_name_plural": "correspondents", + "ordering": ("name",), + "abstract": False, + }, + ), + migrations.CreateModel( + name="CustomField", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "created", + models.DateTimeField( + db_index=True, + default=django.utils.timezone.now, + editable=False, + verbose_name="created", + ), + ), + ("name", models.CharField(max_length=128)), + ( + "data_type", + models.CharField( + choices=[ + ("string", "String"), + ("url", "URL"), + ("date", "Date"), + ("boolean", "Boolean"), + ("integer", "Integer"), + ("float", "Float"), + ("monetary", "Monetary"), + ("documentlink", "Document Link"), + ("select", "Select"), + ("longtext", "Long Text"), + ], + editable=False, + max_length=50, + verbose_name="data type", + ), + ), + ( + "extra_data", + models.JSONField( + blank=True, + help_text="Extra data for the custom field, such as select options", + null=True, + verbose_name="extra data", + ), + ), + ], + options={ + "verbose_name": "custom field", + "verbose_name_plural": "custom fields", + "ordering": ("created",), + "constraints": [ + models.UniqueConstraint( + fields=("name",), + name="documents_customfield_unique_name", + ), + ], + }, + ), + migrations.CreateModel( + name="DocumentType", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=128, verbose_name="name")), + ( + "match", + models.CharField(blank=True, max_length=256, verbose_name="match"), + ), + ( + "matching_algorithm", + models.PositiveIntegerField( + choices=[ + (0, "None"), + (1, "Any word"), + (2, "All words"), + (3, "Exact match"), + (4, "Regular expression"), + (5, "Fuzzy word"), + (6, "Automatic"), + ], + default=1, + verbose_name="matching algorithm", + ), + ), + ( + "is_insensitive", + models.BooleanField(default=True, verbose_name="is insensitive"), + ), + ( + "owner", + models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + verbose_name="owner", + ), + ), + ], + options={ + "verbose_name": "document type", + "verbose_name_plural": "document types", + "ordering": ("name",), + "abstract": False, + }, + ), + migrations.CreateModel( + name="StoragePath", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=128, verbose_name="name")), + ( + "match", + models.CharField(blank=True, max_length=256, verbose_name="match"), + ), + ( + "matching_algorithm", + models.PositiveIntegerField( + choices=[ + (0, "None"), + (1, "Any word"), + (2, "All words"), + (3, "Exact match"), + (4, "Regular expression"), + (5, "Fuzzy word"), + (6, "Automatic"), + ], + default=1, + verbose_name="matching algorithm", + ), + ), + ( + "is_insensitive", + models.BooleanField(default=True, verbose_name="is insensitive"), + ), + ("path", models.TextField(verbose_name="path")), + ( + "owner", + models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + verbose_name="owner", + ), + ), + ], + options={ + "verbose_name": "storage path", + "verbose_name_plural": "storage paths", + "ordering": ("name",), + "abstract": False, + }, + ), + migrations.CreateModel( + name="Tag", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "tn_ancestors_pks", + models.TextField( + blank=True, + default="", + editable=False, + verbose_name="Ancestors pks", + ), + ), + ( + "tn_ancestors_count", + models.PositiveIntegerField( + default=0, + editable=False, + verbose_name="Ancestors count", + ), + ), + ( + "tn_children_pks", + models.TextField( + blank=True, + default="", + editable=False, + verbose_name="Children pks", + ), + ), + ( + "tn_children_count", + models.PositiveIntegerField( + default=0, + editable=False, + verbose_name="Children count", + ), + ), + ( + "tn_depth", + models.PositiveIntegerField( + default=0, + editable=False, + validators=[ + django.core.validators.MinValueValidator(0), + django.core.validators.MaxValueValidator(10), + ], + verbose_name="Depth", + ), + ), + ( + "tn_descendants_pks", + models.TextField( + blank=True, + default="", + editable=False, + verbose_name="Descendants pks", + ), + ), + ( + "tn_descendants_count", + models.PositiveIntegerField( + default=0, + editable=False, + verbose_name="Descendants count", + ), + ), + ( + "tn_index", + models.PositiveIntegerField( + default=0, + editable=False, + verbose_name="Index", + ), + ), + ( + "tn_level", + models.PositiveIntegerField( + default=1, + editable=False, + validators=[ + django.core.validators.MinValueValidator(1), + django.core.validators.MaxValueValidator(10), + ], + verbose_name="Level", + ), + ), + ( + "tn_priority", + models.PositiveIntegerField( + default=0, + validators=[ + django.core.validators.MinValueValidator(0), + django.core.validators.MaxValueValidator(9999999999), + ], + verbose_name="Priority", + ), + ), + ( + "tn_order", + models.PositiveIntegerField( + default=0, + editable=False, + verbose_name="Order", + ), + ), + ( + "tn_siblings_pks", + models.TextField( + blank=True, + default="", + editable=False, + verbose_name="Siblings pks", + ), + ), + ( + "tn_siblings_count", + models.PositiveIntegerField( + default=0, + editable=False, + verbose_name="Siblings count", + ), + ), + ("name", models.CharField(max_length=128, verbose_name="name")), + ( + "match", + models.CharField(blank=True, max_length=256, verbose_name="match"), + ), + ( + "matching_algorithm", + models.PositiveIntegerField( + choices=[ + (0, "None"), + (1, "Any word"), + (2, "All words"), + (3, "Exact match"), + (4, "Regular expression"), + (5, "Fuzzy word"), + (6, "Automatic"), + ], + default=1, + verbose_name="matching algorithm", + ), + ), + ( + "is_insensitive", + models.BooleanField(default=True, verbose_name="is insensitive"), + ), + ( + "color", + models.CharField( + default="#a6cee3", + max_length=7, + verbose_name="color", + ), + ), + ( + "is_inbox_tag", + models.BooleanField( + default=False, + help_text="Marks this tag as an inbox tag: All newly consumed documents will be tagged with inbox tags.", + verbose_name="is inbox tag", + ), + ), + ( + "owner", + models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + verbose_name="owner", + ), + ), + ( + "tn_parent", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="tn_children", + to="documents.tag", + verbose_name="Parent", + ), + ), + ], + options={ + "verbose_name": "tag", + "verbose_name_plural": "tags", + "ordering": ("name",), + "abstract": False, + }, + ), + migrations.CreateModel( + name="Document", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("deleted_at", models.DateTimeField(blank=True, null=True)), + ("restored_at", models.DateTimeField(blank=True, null=True)), + ("transaction_id", models.UUIDField(blank=True, null=True)), + ( + "title", + models.CharField( + blank=True, + db_index=True, + max_length=128, + verbose_name="title", + ), + ), + ( + "content", + models.TextField( + blank=True, + help_text="The raw, text-only data of the document. This field is primarily used for searching.", + verbose_name="content", + ), + ), + ( + "mime_type", + models.CharField( + editable=False, + max_length=256, + verbose_name="mime type", + ), + ), + ( + "checksum", + models.CharField( + editable=False, + help_text="The checksum of the original document.", + max_length=32, + unique=True, + verbose_name="checksum", + ), + ), + ( + "archive_checksum", + models.CharField( + blank=True, + editable=False, + help_text="The checksum of the archived document.", + max_length=32, + null=True, + verbose_name="archive checksum", + ), + ), + ( + "page_count", + models.PositiveIntegerField( + help_text="The number of pages of the document.", + null=True, + validators=[django.core.validators.MinValueValidator(1)], + verbose_name="page count", + ), + ), + ( + "created", + models.DateField( + db_index=True, + default=datetime.date.today, + verbose_name="created", + ), + ), + ( + "modified", + models.DateTimeField( + auto_now=True, + db_index=True, + verbose_name="modified", + ), + ), + ( + "storage_type", + models.CharField( + choices=[ + ("unencrypted", "Unencrypted"), + ("gpg", "Encrypted with GNU Privacy Guard"), + ], + default="unencrypted", + editable=False, + max_length=11, + verbose_name="storage type", + ), + ), + ( + "added", + models.DateTimeField( + db_index=True, + default=django.utils.timezone.now, + editable=False, + verbose_name="added", + ), + ), + ( + "filename", + models.FilePathField( + default=None, + editable=False, + help_text="Current filename in storage", + max_length=1024, + null=True, + unique=True, + verbose_name="filename", + ), + ), + ( + "archive_filename", + models.FilePathField( + default=None, + editable=False, + help_text="Current archive filename in storage", + max_length=1024, + null=True, + unique=True, + verbose_name="archive filename", + ), + ), + ( + "original_filename", + models.CharField( + default=None, + editable=False, + help_text="The original name of the file when it was uploaded", + max_length=1024, + null=True, + verbose_name="original filename", + ), + ), + ( + "archive_serial_number", + models.PositiveIntegerField( + blank=True, + db_index=True, + help_text="The position of this document in your physical document archive.", + null=True, + unique=True, + validators=[ + django.core.validators.MaxValueValidator(4294967295), + django.core.validators.MinValueValidator(0), + ], + verbose_name="archive serial number", + ), + ), + ( + "correspondent", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="documents", + to="documents.correspondent", + verbose_name="correspondent", + ), + ), + ( + "owner", + models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + verbose_name="owner", + ), + ), + ( + "document_type", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="documents", + to="documents.documenttype", + verbose_name="document type", + ), + ), + ( + "storage_path", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="documents", + to="documents.storagepath", + verbose_name="storage path", + ), + ), + ( + "tags", + models.ManyToManyField( + blank=True, + related_name="documents", + to="documents.tag", + verbose_name="tags", + ), + ), + ], + options={ + "verbose_name": "document", + "verbose_name_plural": "documents", + "ordering": ("-created",), + }, + ), + migrations.CreateModel( + name="CustomFieldInstance", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("deleted_at", models.DateTimeField(blank=True, null=True)), + ("restored_at", models.DateTimeField(blank=True, null=True)), + ("transaction_id", models.UUIDField(blank=True, null=True)), + ( + "created", + models.DateTimeField( + db_index=True, + default=django.utils.timezone.now, + editable=False, + verbose_name="created", + ), + ), + ("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(max_length=128, null=True)), + ( + "value_monetary_amount", + models.GeneratedField( + db_persist=True, + expression=models.Case( + models.When( + then=django.db.models.functions.comparison.Cast( + django.db.models.functions.text.Substr( + "value_monetary", + 1, + ), + output_field=models.DecimalField( + decimal_places=2, + max_digits=65, + ), + ), + value_monetary__regex="^\\d+", + ), + default=django.db.models.functions.comparison.Cast( + django.db.models.functions.text.Substr( + "value_monetary", + 4, + ), + output_field=models.DecimalField( + decimal_places=2, + max_digits=65, + ), + ), + output_field=models.DecimalField( + decimal_places=2, + max_digits=65, + ), + ), + output_field=models.DecimalField( + decimal_places=2, + max_digits=65, + ), + ), + ), + ("value_document_ids", models.JSONField(null=True)), + ("value_select", models.CharField(max_length=16, null=True)), + ("value_long_text", models.TextField(null=True)), + ( + "field", + models.ForeignKey( + editable=False, + on_delete=django.db.models.deletion.CASCADE, + related_name="fields", + to="documents.customfield", + ), + ), + ( + "document", + models.ForeignKey( + editable=False, + on_delete=django.db.models.deletion.CASCADE, + related_name="custom_fields", + to="documents.document", + ), + ), + ], + options={ + "verbose_name": "custom field instance", + "verbose_name_plural": "custom field instances", + "ordering": ("created",), + }, + ), + migrations.CreateModel( + name="Note", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("deleted_at", models.DateTimeField(blank=True, null=True)), + ("restored_at", models.DateTimeField(blank=True, null=True)), + ("transaction_id", models.UUIDField(blank=True, null=True)), + ( + "note", + models.TextField( + blank=True, + help_text="Note for the document", + verbose_name="content", + ), + ), + ( + "created", + models.DateTimeField( + db_index=True, + default=django.utils.timezone.now, + verbose_name="created", + ), + ), + ( + "document", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="notes", + to="documents.document", + verbose_name="document", + ), + ), + ( + "user", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="notes", + to=settings.AUTH_USER_MODEL, + verbose_name="user", + ), + ), + ], + options={ + "verbose_name": "note", + "verbose_name_plural": "notes", + "ordering": ("created",), + }, + ), + migrations.CreateModel( + name="PaperlessTask", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "task_id", + models.CharField( + help_text="Celery ID for the Task that was run", + max_length=255, + unique=True, + verbose_name="Task ID", + ), + ), + ( + "acknowledged", + models.BooleanField( + default=False, + help_text="If the task is acknowledged via the frontend or API", + verbose_name="Acknowledged", + ), + ), + ( + "task_file_name", + models.CharField( + help_text="Name of the file which the Task was run for", + max_length=255, + null=True, + verbose_name="Task Filename", + ), + ), + ( + "task_name", + models.CharField( + choices=[ + ("consume_file", "Consume File"), + ("train_classifier", "Train Classifier"), + ("check_sanity", "Check Sanity"), + ("index_optimize", "Index Optimize"), + ("llmindex_update", "LLM Index Update"), + ], + help_text="Name of the task that was run", + max_length=255, + null=True, + verbose_name="Task Name", + ), + ), + ( + "status", + models.CharField( + choices=[ + ("FAILURE", "FAILURE"), + ("PENDING", "PENDING"), + ("RECEIVED", "RECEIVED"), + ("RETRY", "RETRY"), + ("REVOKED", "REVOKED"), + ("STARTED", "STARTED"), + ("SUCCESS", "SUCCESS"), + ], + default="PENDING", + help_text="Current state of the task being run", + max_length=30, + verbose_name="Task State", + ), + ), + ( + "date_created", + models.DateTimeField( + default=django.utils.timezone.now, + help_text="Datetime field when the task result was created in UTC", + null=True, + verbose_name="Created DateTime", + ), + ), + ( + "date_started", + models.DateTimeField( + default=None, + help_text="Datetime field when the task was started in UTC", + null=True, + verbose_name="Started DateTime", + ), + ), + ( + "date_done", + models.DateTimeField( + default=None, + help_text="Datetime field when the task was completed in UTC", + null=True, + verbose_name="Completed DateTime", + ), + ), + ( + "result", + models.TextField( + default=None, + help_text="The data returned by the task", + null=True, + verbose_name="Result Data", + ), + ), + ( + "type", + models.CharField( + choices=[ + ("auto_task", "Auto Task"), + ("scheduled_task", "Scheduled Task"), + ("manual_task", "Manual Task"), + ], + default="auto_task", + help_text="The type of task that was run", + max_length=30, + verbose_name="Task Type", + ), + ), + ( + "owner", + models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + verbose_name="owner", + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="SavedView", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=128, verbose_name="name")), + ( + "show_on_dashboard", + models.BooleanField(verbose_name="show on dashboard"), + ), + ( + "show_in_sidebar", + models.BooleanField(verbose_name="show in sidebar"), + ), + ( + "sort_field", + models.CharField( + blank=True, + max_length=128, + null=True, + verbose_name="sort field", + ), + ), + ( + "sort_reverse", + models.BooleanField(default=False, verbose_name="sort reverse"), + ), + ( + "page_size", + models.PositiveIntegerField( + blank=True, + null=True, + validators=[django.core.validators.MinValueValidator(1)], + verbose_name="View page size", + ), + ), + ( + "display_mode", + models.CharField( + blank=True, + choices=[ + ("table", "Table"), + ("smallCards", "Small Cards"), + ("largeCards", "Large Cards"), + ], + max_length=128, + null=True, + verbose_name="View display mode", + ), + ), + ( + "display_fields", + models.JSONField( + blank=True, + null=True, + verbose_name="Document display fields", + ), + ), + ( + "owner", + models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + verbose_name="owner", + ), + ), + ], + options={ + "verbose_name": "saved view", + "verbose_name_plural": "saved views", + "ordering": ("name",), + }, + ), + migrations.CreateModel( + name="SavedViewFilterRule", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "rule_type", + models.PositiveIntegerField( + choices=[ + (0, "title contains"), + (1, "content contains"), + (2, "ASN is"), + (3, "correspondent is"), + (4, "document type is"), + (5, "is in inbox"), + (6, "has tag"), + (7, "has any tag"), + (8, "created before"), + (9, "created after"), + (10, "created year is"), + (11, "created month is"), + (12, "created day is"), + (13, "added before"), + (14, "added after"), + (15, "modified before"), + (16, "modified after"), + (17, "does not have tag"), + (18, "does not have ASN"), + (19, "title or content contains"), + (20, "fulltext query"), + (21, "more like this"), + (22, "has tags in"), + (23, "ASN greater than"), + (24, "ASN less than"), + (25, "storage path is"), + (26, "has correspondent in"), + (27, "does not have correspondent in"), + (28, "has document type in"), + (29, "does not have document type in"), + (30, "has storage path in"), + (31, "does not have storage path in"), + (32, "owner is"), + (33, "has owner in"), + (34, "does not have owner"), + (35, "does not have owner in"), + (36, "has custom field value"), + (37, "is shared by me"), + (38, "has custom fields"), + (39, "has custom field in"), + (40, "does not have custom field in"), + (41, "does not have custom field"), + (42, "custom fields query"), + (43, "created to"), + (44, "created from"), + (45, "added to"), + (46, "added from"), + (47, "mime type is"), + ], + verbose_name="rule type", + ), + ), + ( + "value", + models.CharField( + blank=True, + max_length=255, + null=True, + verbose_name="value", + ), + ), + ( + "saved_view", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="filter_rules", + to="documents.savedview", + verbose_name="saved view", + ), + ), + ], + options={ + "verbose_name": "filter rule", + "verbose_name_plural": "filter rules", + }, + ), + migrations.CreateModel( + name="ShareLink", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("deleted_at", models.DateTimeField(blank=True, null=True)), + ("restored_at", models.DateTimeField(blank=True, null=True)), + ("transaction_id", models.UUIDField(blank=True, null=True)), + ( + "created", + models.DateTimeField( + blank=True, + db_index=True, + default=django.utils.timezone.now, + editable=False, + verbose_name="created", + ), + ), + ( + "expiration", + models.DateTimeField( + blank=True, + db_index=True, + null=True, + verbose_name="expiration", + ), + ), + ( + "slug", + models.SlugField( + blank=True, + editable=False, + unique=True, + verbose_name="slug", + ), + ), + ( + "file_version", + models.CharField( + choices=[("archive", "Archive"), ("original", "Original")], + default="archive", + max_length=50, + ), + ), + ( + "document", + models.ForeignKey( + blank=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="share_links", + to="documents.document", + verbose_name="document", + ), + ), + ( + "owner", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="share_links", + to=settings.AUTH_USER_MODEL, + verbose_name="owner", + ), + ), + ], + options={ + "verbose_name": "share link", + "verbose_name_plural": "share links", + "ordering": ("created",), + }, + ), + migrations.CreateModel( + name="UiSettings", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("settings", models.JSONField(null=True)), + ( + "user", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="ui_settings", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + migrations.CreateModel( + name="WorkflowAction", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "type", + models.PositiveIntegerField( + choices=[ + (1, "Assignment"), + (2, "Removal"), + (3, "Email"), + (4, "Webhook"), + ], + default=1, + verbose_name="Workflow Action Type", + ), + ), + ( + "assign_title", + models.TextField( + blank=True, + help_text="Assign a document title, must be a Jinja2 template, see documentation.", + null=True, + verbose_name="assign title", + ), + ), + ( + "assign_custom_fields_values", + models.JSONField( + blank=True, + default=dict, + help_text="Optional values to assign to the custom fields.", + null=True, + verbose_name="custom field values", + ), + ), + ( + "remove_all_tags", + models.BooleanField(default=False, verbose_name="remove all tags"), + ), + ( + "remove_all_document_types", + models.BooleanField( + default=False, + verbose_name="remove all document types", + ), + ), + ( + "remove_all_correspondents", + models.BooleanField( + default=False, + verbose_name="remove all correspondents", + ), + ), + ( + "remove_all_storage_paths", + models.BooleanField( + default=False, + verbose_name="remove all storage paths", + ), + ), + ( + "remove_all_owners", + models.BooleanField( + default=False, + verbose_name="remove all owners", + ), + ), + ( + "remove_all_permissions", + models.BooleanField( + default=False, + verbose_name="remove all permissions", + ), + ), + ( + "remove_all_custom_fields", + models.BooleanField( + default=False, + verbose_name="remove all custom fields", + ), + ), + ( + "assign_change_groups", + models.ManyToManyField( + blank=True, + related_name="+", + to="auth.group", + verbose_name="grant change permissions to these groups", + ), + ), + ( + "assign_change_users", + models.ManyToManyField( + blank=True, + related_name="+", + to=settings.AUTH_USER_MODEL, + verbose_name="grant change permissions to these users", + ), + ), + ( + "assign_correspondent", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="documents.correspondent", + verbose_name="assign this correspondent", + ), + ), + ( + "assign_custom_fields", + models.ManyToManyField( + blank=True, + related_name="+", + to="documents.customfield", + verbose_name="assign these custom fields", + ), + ), + ( + "assign_document_type", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="documents.documenttype", + verbose_name="assign this document type", + ), + ), + ( + "assign_owner", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to=settings.AUTH_USER_MODEL, + verbose_name="assign this owner", + ), + ), + ( + "assign_storage_path", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="documents.storagepath", + verbose_name="assign this storage path", + ), + ), + ( + "assign_tags", + models.ManyToManyField( + blank=True, + related_name="+", + to="documents.tag", + verbose_name="assign this tag", + ), + ), + ( + "assign_view_groups", + models.ManyToManyField( + blank=True, + related_name="+", + to="auth.group", + verbose_name="grant view permissions to these groups", + ), + ), + ( + "assign_view_users", + models.ManyToManyField( + blank=True, + related_name="+", + to=settings.AUTH_USER_MODEL, + verbose_name="grant view permissions to these users", + ), + ), + ( + "remove_change_groups", + models.ManyToManyField( + blank=True, + related_name="+", + to="auth.group", + verbose_name="remove change permissions for these groups", + ), + ), + ( + "remove_change_users", + models.ManyToManyField( + blank=True, + related_name="+", + to=settings.AUTH_USER_MODEL, + verbose_name="remove change permissions for these users", + ), + ), + ( + "remove_correspondents", + models.ManyToManyField( + blank=True, + related_name="+", + to="documents.correspondent", + verbose_name="remove these correspondent(s)", + ), + ), + ( + "remove_custom_fields", + models.ManyToManyField( + blank=True, + related_name="+", + to="documents.customfield", + verbose_name="remove these custom fields", + ), + ), + ( + "remove_document_types", + models.ManyToManyField( + blank=True, + related_name="+", + to="documents.documenttype", + verbose_name="remove these document type(s)", + ), + ), + ( + "remove_owners", + models.ManyToManyField( + blank=True, + related_name="+", + to=settings.AUTH_USER_MODEL, + verbose_name="remove these owner(s)", + ), + ), + ( + "remove_storage_paths", + models.ManyToManyField( + blank=True, + related_name="+", + to="documents.storagepath", + verbose_name="remove these storage path(s)", + ), + ), + ( + "remove_tags", + models.ManyToManyField( + blank=True, + related_name="+", + to="documents.tag", + verbose_name="remove these tag(s)", + ), + ), + ( + "remove_view_groups", + models.ManyToManyField( + blank=True, + related_name="+", + to="auth.group", + verbose_name="remove view permissions for these groups", + ), + ), + ( + "remove_view_users", + models.ManyToManyField( + blank=True, + related_name="+", + to=settings.AUTH_USER_MODEL, + verbose_name="remove view permissions for these users", + ), + ), + ( + "email", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="action", + to="documents.workflowactionemail", + verbose_name="email", + ), + ), + ( + "webhook", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="action", + to="documents.workflowactionwebhook", + verbose_name="webhook", + ), + ), + ], + options={ + "verbose_name": "workflow action", + "verbose_name_plural": "workflow actions", + }, + ), + migrations.CreateModel( + name="Workflow", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField(max_length=256, unique=True, verbose_name="name"), + ), + ("order", models.IntegerField(default=0, verbose_name="order")), + ("enabled", models.BooleanField(default=True, verbose_name="enabled")), + ( + "actions", + models.ManyToManyField( + related_name="workflows", + to="documents.workflowaction", + verbose_name="actions", + ), + ), + ], + ), + migrations.CreateModel( + name="WorkflowRun", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("deleted_at", models.DateTimeField(blank=True, null=True)), + ("restored_at", models.DateTimeField(blank=True, null=True)), + ("transaction_id", models.UUIDField(blank=True, null=True)), + ( + "type", + models.PositiveIntegerField( + choices=[ + (1, "Consumption Started"), + (2, "Document Added"), + (3, "Document Updated"), + (4, "Scheduled"), + ], + null=True, + verbose_name="workflow trigger type", + ), + ), + ( + "run_at", + models.DateTimeField( + db_index=True, + default=django.utils.timezone.now, + verbose_name="date run", + ), + ), + ( + "document", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="workflow_runs", + to="documents.document", + verbose_name="document", + ), + ), + ( + "workflow", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="runs", + to="documents.workflow", + verbose_name="workflow", + ), + ), + ], + options={ + "verbose_name": "workflow run", + "verbose_name_plural": "workflow runs", + }, + ), + migrations.CreateModel( + name="WorkflowTrigger", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "type", + models.PositiveIntegerField( + choices=[ + (1, "Consumption Started"), + (2, "Document Added"), + (3, "Document Updated"), + (4, "Scheduled"), + ], + default=1, + verbose_name="Workflow Trigger Type", + ), + ), + ( + "sources", + multiselectfield.db.fields.MultiSelectField( + choices=[ + (1, "Consume Folder"), + (2, "Api Upload"), + (3, "Mail Fetch"), + (4, "Web UI"), + ], + default="1,2,3,4", + max_length=7, + ), + ), + ( + "filter_path", + models.CharField( + blank=True, + help_text="Only consume documents with a path that matches this if specified. Wildcards specified as * are allowed. Case insensitive.", + max_length=256, + null=True, + verbose_name="filter path", + ), + ), + ( + "filter_filename", + models.CharField( + blank=True, + help_text="Only consume documents which entirely match this filename if specified. Wildcards such as *.pdf or *invoice* are allowed. Case insensitive.", + max_length=256, + null=True, + verbose_name="filter filename", + ), + ), + ( + "match", + models.CharField(blank=True, max_length=256, verbose_name="match"), + ), + ( + "matching_algorithm", + models.PositiveIntegerField( + choices=[ + (0, "None"), + (1, "Any word"), + (2, "All words"), + (3, "Exact match"), + (4, "Regular expression"), + (5, "Fuzzy word"), + ], + default=0, + verbose_name="matching algorithm", + ), + ), + ( + "is_insensitive", + models.BooleanField(default=True, verbose_name="is insensitive"), + ), + ( + "filter_custom_field_query", + models.TextField( + blank=True, + help_text="JSON-encoded custom field query expression.", + null=True, + verbose_name="filter custom field query", + ), + ), + ( + "schedule_offset_days", + models.IntegerField( + default=0, + help_text="The number of days to offset the schedule trigger by.", + verbose_name="schedule offset days", + ), + ), + ( + "schedule_is_recurring", + models.BooleanField( + default=False, + help_text="If the schedule should be recurring.", + verbose_name="schedule is recurring", + ), + ), + ( + "schedule_recurring_interval_days", + models.PositiveIntegerField( + default=1, + help_text="The number of days between recurring schedule triggers.", + validators=[django.core.validators.MinValueValidator(1)], + verbose_name="schedule recurring delay in days", + ), + ), + ( + "schedule_date_field", + models.CharField( + choices=[ + ("added", "Added"), + ("created", "Created"), + ("modified", "Modified"), + ("custom_field", "Custom Field"), + ], + default="added", + help_text="The field to check for a schedule trigger.", + max_length=20, + verbose_name="schedule date field", + ), + ), + ( + "filter_has_all_tags", + models.ManyToManyField( + blank=True, + related_name="workflowtriggers_has_all", + to="documents.tag", + verbose_name="has all of these tag(s)", + ), + ), + ( + "filter_has_correspondent", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="documents.correspondent", + verbose_name="has this correspondent", + ), + ), + ( + "filter_has_document_type", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="documents.documenttype", + verbose_name="has this document type", + ), + ), + ( + "filter_has_not_correspondents", + models.ManyToManyField( + blank=True, + related_name="workflowtriggers_has_not_correspondent", + to="documents.correspondent", + verbose_name="does not have these correspondent(s)", + ), + ), + ( + "filter_has_not_document_types", + models.ManyToManyField( + blank=True, + related_name="workflowtriggers_has_not_document_type", + to="documents.documenttype", + verbose_name="does not have these document type(s)", + ), + ), + ( + "filter_has_not_storage_paths", + models.ManyToManyField( + blank=True, + related_name="workflowtriggers_has_not_storage_path", + to="documents.storagepath", + verbose_name="does not have these storage path(s)", + ), + ), + ( + "filter_has_not_tags", + models.ManyToManyField( + blank=True, + related_name="workflowtriggers_has_not", + to="documents.tag", + verbose_name="does not have these tag(s)", + ), + ), + ( + "filter_has_storage_path", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="documents.storagepath", + verbose_name="has this storage path", + ), + ), + ( + "filter_has_tags", + models.ManyToManyField( + blank=True, + to="documents.tag", + verbose_name="has these tag(s)", + ), + ), + ( + "filter_mailrule", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="paperless_mail.mailrule", + verbose_name="filter documents from this mail rule", + ), + ), + ( + "schedule_date_custom_field", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="documents.customfield", + verbose_name="schedule date custom field", + ), + ), + ], + options={ + "verbose_name": "workflow trigger", + "verbose_name_plural": "workflow triggers", + }, + ), + migrations.AddField( + model_name="workflow", + name="triggers", + field=models.ManyToManyField( + related_name="workflows", + to="documents.workflowtrigger", + verbose_name="triggers", + ), + ), + migrations.AddConstraint( + model_name="correspondent", + constraint=models.UniqueConstraint( + fields=("name", "owner"), + name="documents_correspondent_unique_name_owner", + ), + ), + migrations.AddConstraint( + model_name="correspondent", + constraint=models.UniqueConstraint( + condition=models.Q(("owner__isnull", True)), + fields=("name",), + name="documents_correspondent_name_uniq", + ), + ), + migrations.AddConstraint( + model_name="customfieldinstance", + constraint=models.UniqueConstraint( + fields=("document", "field"), + name="documents_customfieldinstance_unique_document_field", + ), + ), + migrations.AddConstraint( + model_name="documenttype", + constraint=models.UniqueConstraint( + fields=("name", "owner"), + name="documents_documenttype_unique_name_owner", + ), + ), + migrations.AddConstraint( + model_name="documenttype", + constraint=models.UniqueConstraint( + condition=models.Q(("owner__isnull", True)), + fields=("name",), + name="documents_documenttype_name_uniq", + ), + ), + migrations.AddConstraint( + model_name="storagepath", + constraint=models.UniqueConstraint( + fields=("name", "owner"), + name="documents_storagepath_unique_name_owner", + ), + ), + migrations.AddConstraint( + model_name="storagepath", + constraint=models.UniqueConstraint( + condition=models.Q(("owner__isnull", True)), + fields=("name",), + name="documents_storagepath_name_uniq", + ), + ), + migrations.AddConstraint( + model_name="tag", + constraint=models.UniqueConstraint( + fields=("name", "owner"), + name="documents_tag_unique_name_owner", + ), + ), + migrations.AddConstraint( + model_name="tag", + constraint=models.UniqueConstraint( + condition=models.Q(("owner__isnull", True)), + fields=("name",), + name="documents_tag_name_uniq", + ), + ), + migrations.AddField( + model_name="workflowaction", + name="order", + field=models.PositiveIntegerField(default=0, verbose_name="order"), + ), + ]