diff --git a/src/documents/migrations/0003_remove_document_storage_type_squashed_0005_alter_document_checksum_unique.py b/src/documents/migrations/0003_remove_document_storage_type_squashed_0005_alter_document_checksum_unique.py new file mode 100644 index 000000000..21eca291c --- /dev/null +++ b/src/documents/migrations/0003_remove_document_storage_type_squashed_0005_alter_document_checksum_unique.py @@ -0,0 +1,63 @@ +# Generated by Django 5.2.14 on 2026-06-04 15:31 + +from django.db import migrations +from django.db import models + + +class Migration(migrations.Migration): + replaces = [ + ("documents", "0003_remove_document_storage_type"), + ("documents", "0004_workflowtrigger_filter_has_any_correspondents_and_more"), + ("documents", "0005_alter_document_checksum_unique"), + ] + + dependencies = [ + ("documents", "0002_squashed"), + ] + + operations = [ + migrations.RemoveField( + model_name="document", + name="storage_type", + ), + migrations.AddField( + model_name="workflowtrigger", + name="filter_has_any_correspondents", + field=models.ManyToManyField( + blank=True, + related_name="workflowtriggers_has_any_correspondent", + to="documents.correspondent", + verbose_name="has one of these correspondents", + ), + ), + migrations.AddField( + model_name="workflowtrigger", + name="filter_has_any_document_types", + field=models.ManyToManyField( + blank=True, + related_name="workflowtriggers_has_any_document_type", + to="documents.documenttype", + verbose_name="has one of these document types", + ), + ), + migrations.AddField( + model_name="workflowtrigger", + name="filter_has_any_storage_paths", + field=models.ManyToManyField( + blank=True, + related_name="workflowtriggers_has_any_storage_path", + to="documents.storagepath", + verbose_name="has one of these storage paths", + ), + ), + migrations.AlterField( + model_name="document", + name="checksum", + field=models.CharField( + editable=False, + help_text="The checksum of the original document.", + max_length=32, + verbose_name="checksum", + ), + ), + ] diff --git a/src/documents/migrations/0008_workflowaction_passwords_alter_workflowaction_type_squashed_0012_document_root_document.py b/src/documents/migrations/0008_workflowaction_passwords_alter_workflowaction_type_squashed_0012_document_root_document.py new file mode 100644 index 000000000..998aaeb92 --- /dev/null +++ b/src/documents/migrations/0008_workflowaction_passwords_alter_workflowaction_type_squashed_0012_document_root_document.py @@ -0,0 +1,252 @@ +# Generated by Django 5.2.14 on 2026-06-04 15:31 + +import django.db.models.deletion +import django.db.models.functions.text +from django.db import migrations +from django.db import models + + +class Migration(migrations.Migration): + replaces = [ + ("documents", "0008_workflowaction_passwords_alter_workflowaction_type"), + ("documents", "0009_alter_document_content_length"), + ("documents", "0010_optimize_integer_field_sizes"), + ("documents", "0011_alter_workflowaction_type"), + ("documents", "0012_document_root_document"), + ] + + dependencies = [ + ("documents", "0007_sharelinkbundle"), + ] + + operations = [ + migrations.AddField( + model_name="workflowaction", + name="passwords", + field=models.JSONField( + blank=True, + help_text="Passwords to try when removing PDF protection. Separate with commas or new lines.", + null=True, + verbose_name="passwords", + ), + ), + migrations.AlterField( + model_name="document", + name="content_length", + field=models.GeneratedField( + db_persist=True, + expression=django.db.models.functions.text.Length("content"), + help_text="Length of the content field in characters. Automatically maintained by the database for faster statistics computation.", + output_field=models.PositiveIntegerField(default=0), + serialize=False, + ), + ), + migrations.AlterField( + model_name="correspondent", + name="matching_algorithm", + field=models.PositiveSmallIntegerField( + 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", + ), + ), + migrations.AlterField( + model_name="documenttype", + name="matching_algorithm", + field=models.PositiveSmallIntegerField( + 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", + ), + ), + migrations.AlterField( + model_name="savedviewfilterrule", + name="rule_type", + field=models.PositiveSmallIntegerField( + 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", + ), + ), + migrations.AlterField( + model_name="storagepath", + name="matching_algorithm", + field=models.PositiveSmallIntegerField( + 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", + ), + ), + migrations.AlterField( + model_name="tag", + name="matching_algorithm", + field=models.PositiveSmallIntegerField( + 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", + ), + ), + migrations.AlterField( + model_name="workflowrun", + name="type", + field=models.PositiveSmallIntegerField( + choices=[ + (1, "Consumption Started"), + (2, "Document Added"), + (3, "Document Updated"), + (4, "Scheduled"), + ], + null=True, + verbose_name="workflow trigger type", + ), + ), + migrations.AlterField( + model_name="workflowtrigger", + name="matching_algorithm", + field=models.PositiveSmallIntegerField( + choices=[ + (0, "None"), + (1, "Any word"), + (2, "All words"), + (3, "Exact match"), + (4, "Regular expression"), + (5, "Fuzzy word"), + ], + default=0, + verbose_name="matching algorithm", + ), + ), + migrations.AlterField( + model_name="workflowtrigger", + name="type", + field=models.PositiveSmallIntegerField( + choices=[ + (1, "Consumption Started"), + (2, "Document Added"), + (3, "Document Updated"), + (4, "Scheduled"), + ], + default=1, + verbose_name="Workflow Trigger Type", + ), + ), + migrations.AlterField( + model_name="workflowaction", + name="type", + field=models.PositiveSmallIntegerField( + choices=[ + (1, "Assignment"), + (2, "Removal"), + (3, "Email"), + (4, "Webhook"), + (5, "Password removal"), + (6, "Move to trash"), + ], + default=1, + verbose_name="Workflow Action Type", + ), + ), + migrations.AddField( + model_name="document", + name="root_document", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="versions", + to="documents.document", + verbose_name="root document for this version", + ), + ), + 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", + ), + ), + ] diff --git a/src/paperless/migrations/0001_initial_squashed_0006_applicationconfiguration_barcode_tag_split.py b/src/paperless/migrations/0001_initial_squashed_0006_applicationconfiguration_barcode_tag_split.py new file mode 100644 index 000000000..f1ab27428 --- /dev/null +++ b/src/paperless/migrations/0001_initial_squashed_0006_applicationconfiguration_barcode_tag_split.py @@ -0,0 +1,365 @@ +# Generated by Django 5.2.14 on 2026-06-04 15:30 + +import django.core.validators +from django.db import migrations +from django.db import models + + +def _create_singleton(apps, schema_editor): + settings_model = apps.get_model("paperless", "ApplicationConfiguration") + settings_model.objects.create() + + +class Migration(migrations.Migration): + replaces = [ + ("paperless", "0001_initial"), + ("paperless", "0002_applicationconfiguration_app_logo_and_more"), + ("paperless", "0003_alter_applicationconfiguration_max_image_pixels"), + ("paperless", "0004_applicationconfiguration_barcode_asn_prefix_and_more"), + ("paperless", "0005_applicationconfiguration_ai_enabled_and_more"), + ("paperless", "0006_applicationconfiguration_barcode_tag_split"), + ] + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="ApplicationConfiguration", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "output_type", + models.CharField( + blank=True, + choices=[ + ("pdf", "pdf"), + ("pdfa", "pdfa"), + ("pdfa-1", "pdfa-1"), + ("pdfa-2", "pdfa-2"), + ("pdfa-3", "pdfa-3"), + ], + max_length=8, + null=True, + verbose_name="Sets the output PDF type", + ), + ), + ( + "pages", + models.PositiveIntegerField( + null=True, + validators=[django.core.validators.MinValueValidator(1)], + verbose_name="Do OCR from page 1 to this value", + ), + ), + ( + "language", + models.CharField( + blank=True, + max_length=32, + null=True, + verbose_name="Do OCR using these languages", + ), + ), + ( + "mode", + models.CharField( + blank=True, + choices=[ + ("skip", "skip"), + ("redo", "redo"), + ("force", "force"), + ("skip_noarchive", "skip_noarchive"), + ], + max_length=16, + null=True, + verbose_name="Sets the OCR mode", + ), + ), + ( + "skip_archive_file", + models.CharField( + blank=True, + choices=[ + ("never", "never"), + ("with_text", "with_text"), + ("always", "always"), + ], + max_length=16, + null=True, + verbose_name="Controls the generation of an archive file", + ), + ), + ( + "image_dpi", + models.PositiveIntegerField( + null=True, + validators=[django.core.validators.MinValueValidator(1)], + verbose_name="Sets image DPI fallback value", + ), + ), + ( + "unpaper_clean", + models.CharField( + blank=True, + choices=[ + ("clean", "clean"), + ("clean-final", "clean-final"), + ("none", "none"), + ], + max_length=16, + null=True, + verbose_name="Controls the unpaper cleaning", + ), + ), + ( + "deskew", + models.BooleanField(null=True, verbose_name="Enables deskew"), + ), + ( + "rotate_pages", + models.BooleanField( + null=True, + verbose_name="Enables page rotation", + ), + ), + ( + "rotate_pages_threshold", + models.FloatField( + null=True, + validators=[django.core.validators.MinValueValidator(0.0)], + verbose_name="Sets the threshold for rotation of pages", + ), + ), + ( + "max_image_pixels", + models.FloatField( + null=True, + validators=[django.core.validators.MinValueValidator(0.0)], + verbose_name="Sets the maximum image size for decompression", + ), + ), + ( + "color_conversion_strategy", + models.CharField( + blank=True, + choices=[ + ("LeaveColorUnchanged", "LeaveColorUnchanged"), + ("RGB", "RGB"), + ("UseDeviceIndependentColor", "UseDeviceIndependentColor"), + ("Gray", "Gray"), + ("CMYK", "CMYK"), + ], + max_length=32, + null=True, + verbose_name="Sets the Ghostscript color conversion strategy", + ), + ), + ( + "user_args", + models.JSONField( + null=True, + verbose_name="Adds additional user arguments for OCRMyPDF", + ), + ), + ( + "app_logo", + models.FileField( + blank=True, + null=True, + upload_to="logo/", + validators=[ + django.core.validators.FileExtensionValidator( + allowed_extensions=["jpg", "png", "gif", "svg"], + ), + ], + verbose_name="Application logo", + ), + ), + ( + "app_title", + models.CharField( + blank=True, + max_length=48, + null=True, + verbose_name="Application title", + ), + ), + ( + "barcode_asn_prefix", + models.CharField( + blank=True, + max_length=32, + null=True, + verbose_name="Sets the ASN barcode prefix", + ), + ), + ( + "barcode_dpi", + models.PositiveIntegerField( + null=True, + validators=[django.core.validators.MinValueValidator(1)], + verbose_name="Sets the barcode DPI", + ), + ), + ( + "barcode_enable_asn", + models.BooleanField( + null=True, + verbose_name="Enables ASN barcode", + ), + ), + ( + "barcode_enable_tag", + models.BooleanField( + null=True, + verbose_name="Enables tag barcode", + ), + ), + ( + "barcode_enable_tiff_support", + models.BooleanField( + null=True, + verbose_name="Enables barcode TIFF support", + ), + ), + ( + "barcode_max_pages", + models.PositiveIntegerField( + null=True, + validators=[django.core.validators.MinValueValidator(1)], + verbose_name="Sets the maximum pages for barcode", + ), + ), + ( + "barcode_retain_split_pages", + models.BooleanField( + null=True, + verbose_name="Retains split pages", + ), + ), + ( + "barcode_string", + models.CharField( + blank=True, + max_length=32, + null=True, + verbose_name="Sets the barcode string", + ), + ), + ( + "barcode_tag_mapping", + models.JSONField( + null=True, + verbose_name="Sets the tag barcode mapping", + ), + ), + ( + "barcode_upscale", + models.FloatField( + null=True, + validators=[django.core.validators.MinValueValidator(1.0)], + verbose_name="Sets the barcode upscale factor", + ), + ), + ( + "barcodes_enabled", + models.BooleanField( + null=True, + verbose_name="Enables barcode scanning", + ), + ), + ( + "ai_enabled", + models.BooleanField( + default=False, + null=True, + verbose_name="Enables AI features", + ), + ), + ( + "llm_api_key", + models.CharField( + blank=True, + max_length=1024, + null=True, + verbose_name="Sets the LLM API key", + ), + ), + ( + "llm_backend", + models.CharField( + blank=True, + choices=[ + ("openai-like", "OpenAI-compatible"), + ("ollama", "Ollama"), + ], + max_length=128, + null=True, + verbose_name="Sets the LLM backend", + ), + ), + ( + "llm_embedding_backend", + models.CharField( + blank=True, + choices=[ + ("openai-like", "OpenAI-compatible"), + ("huggingface", "Huggingface"), + ], + max_length=128, + null=True, + verbose_name="Sets the LLM embedding backend", + ), + ), + ( + "llm_embedding_model", + models.CharField( + blank=True, + max_length=128, + null=True, + verbose_name="Sets the LLM embedding model", + ), + ), + ( + "llm_endpoint", + models.CharField( + blank=True, + max_length=256, + null=True, + verbose_name="Sets the LLM endpoint, optional", + ), + ), + ( + "llm_model", + models.CharField( + blank=True, + max_length=128, + null=True, + verbose_name="Sets the LLM model", + ), + ), + ( + "barcode_tag_split", + models.BooleanField( + null=True, + verbose_name="Enables splitting on tag barcodes", + ), + ), + ], + options={ + "verbose_name": "paperless application settings", + }, + ), + migrations.RunPython( + code=_create_singleton, + reverse_code=migrations.RunPython.noop, + ), + ] diff --git a/src/paperless/migrations/0009_alter_applicationconfiguration_options_squashed_0012_applicationconfiguration_llm_output_language.py b/src/paperless/migrations/0009_alter_applicationconfiguration_options_squashed_0012_applicationconfiguration_llm_output_language.py new file mode 100644 index 000000000..b70af6256 --- /dev/null +++ b/src/paperless/migrations/0009_alter_applicationconfiguration_options_squashed_0012_applicationconfiguration_llm_output_language.py @@ -0,0 +1,84 @@ +# Generated by Django 5.2.14 on 2026-06-04 15:19 + +import django.core.validators +from django.db import migrations +from django.db import models + + +class Migration(migrations.Migration): + replaces = [ + ("paperless", "0009_alter_applicationconfiguration_options"), + ("paperless", "0010_alter_applicationconfiguration_llm_embedding_backend"), + ("paperless", "0011_applicationconfiguration_llm_embedding_chunk_size"), + ("paperless", "0012_applicationconfiguration_llm_output_language"), + ] + + dependencies = [ + ("paperless", "0008_replace_skip_archive_file"), + ] + + operations = [ + migrations.AlterModelOptions( + name="applicationconfiguration", + options={ + "permissions": [ + ("view_global_statistics", "Can view global object counts"), + ("view_system_monitoring", "Can view system status information"), + ], + "verbose_name": "paperless application settings", + }, + ), + migrations.AlterField( + model_name="applicationconfiguration", + name="llm_embedding_backend", + field=models.CharField( + blank=True, + choices=[ + ("openai-like", "OpenAI-compatible"), + ("huggingface", "Huggingface"), + ("ollama", "Ollama"), + ], + max_length=128, + null=True, + verbose_name="Sets the LLM embedding backend", + ), + ), + migrations.AddField( + model_name="applicationconfiguration", + name="llm_embedding_endpoint", + field=models.CharField( + blank=True, + max_length=256, + null=True, + verbose_name="Sets the LLM embedding endpoint, optional", + ), + ), + migrations.AddField( + model_name="applicationconfiguration", + name="llm_embedding_chunk_size", + field=models.PositiveSmallIntegerField( + null=True, + validators=[django.core.validators.MinValueValidator(1)], + verbose_name="Sets the LLM embedding chunk size", + ), + ), + migrations.AddField( + model_name="applicationconfiguration", + name="llm_context_size", + field=models.PositiveIntegerField( + null=True, + validators=[django.core.validators.MinValueValidator(1)], + verbose_name="Sets the LLM context size", + ), + ), + migrations.AddField( + model_name="applicationconfiguration", + name="llm_output_language", + field=models.CharField( + blank=True, + max_length=32, + null=True, + verbose_name="Sets the LLM output language", + ), + ), + ] diff --git a/src/paperless_mail/migrations/0002_optimize_integer_field_sizes_squashed_0003_mailrule_stop_processing.py b/src/paperless_mail/migrations/0002_optimize_integer_field_sizes_squashed_0003_mailrule_stop_processing.py new file mode 100644 index 000000000..975012acc --- /dev/null +++ b/src/paperless_mail/migrations/0002_optimize_integer_field_sizes_squashed_0003_mailrule_stop_processing.py @@ -0,0 +1,158 @@ +# Generated by Django 5.2.14 on 2026-06-04 15:10 + +from django.db import migrations +from django.db import models + + +class Migration(migrations.Migration): + replaces = [ + ("paperless_mail", "0002_optimize_integer_field_sizes"), + ("paperless_mail", "0003_mailrule_stop_processing"), + ] + + dependencies = [ + ("paperless_mail", "0001_squashed"), + ] + + operations = [ + migrations.AlterField( + model_name="mailaccount", + name="account_type", + field=models.PositiveSmallIntegerField( + choices=[(1, "IMAP"), (2, "Gmail OAuth"), (3, "Outlook OAuth")], + default=1, + verbose_name="account type", + ), + ), + migrations.AlterField( + model_name="mailaccount", + name="imap_port", + field=models.PositiveIntegerField( + blank=True, + help_text="This is usually 143 for unencrypted and STARTTLS connections, and 993 for SSL connections.", + null=True, + verbose_name="IMAP port", + ), + ), + migrations.AlterField( + model_name="mailaccount", + name="imap_security", + field=models.PositiveSmallIntegerField( + choices=[(1, "No encryption"), (2, "Use SSL"), (3, "Use STARTTLS")], + default=2, + verbose_name="IMAP security", + ), + ), + migrations.AlterField( + model_name="mailrule", + name="action", + field=models.PositiveSmallIntegerField( + choices=[ + (1, "Delete"), + (2, "Move to specified folder"), + (3, "Mark as read, don't process read mails"), + (4, "Flag the mail, don't process flagged mails"), + (5, "Tag the mail with specified tag, don't process tagged mails"), + ], + default=3, + verbose_name="action", + ), + ), + migrations.AlterField( + model_name="mailrule", + name="assign_correspondent_from", + field=models.PositiveSmallIntegerField( + choices=[ + (1, "Do not assign a correspondent"), + (2, "Use mail address"), + (3, "Use name (or mail address if not available)"), + (4, "Use correspondent selected below"), + ], + default=1, + verbose_name="assign correspondent from", + ), + ), + migrations.AlterField( + model_name="mailrule", + name="assign_title_from", + field=models.PositiveSmallIntegerField( + choices=[ + (1, "Use subject as title"), + (2, "Use attachment filename as title"), + (3, "Do not assign title from rule"), + ], + default=1, + verbose_name="assign title from", + ), + ), + migrations.AlterField( + model_name="mailrule", + name="attachment_type", + field=models.PositiveSmallIntegerField( + choices=[ + (1, "Only process attachments."), + (2, "Process all files, including 'inline' attachments."), + ], + default=1, + help_text="Inline attachments include embedded images, so it's best to combine this option with a filename filter.", + verbose_name="attachment type", + ), + ), + migrations.AlterField( + model_name="mailrule", + name="consumption_scope", + field=models.PositiveSmallIntegerField( + choices=[ + (1, "Only process attachments."), + ( + 2, + "Process full Mail (with embedded attachments in file) as .eml", + ), + ( + 3, + "Process full Mail (with embedded attachments in file) as .eml + process attachments as separate documents", + ), + ], + default=1, + verbose_name="consumption scope", + ), + ), + migrations.AlterField( + model_name="mailrule", + name="maximum_age", + field=models.PositiveSmallIntegerField( + default=30, + help_text="Specified in days.", + verbose_name="maximum age", + ), + ), + migrations.AlterField( + model_name="mailrule", + name="order", + field=models.SmallIntegerField(default=0, verbose_name="order"), + ), + migrations.AlterField( + model_name="mailrule", + name="pdf_layout", + field=models.PositiveSmallIntegerField( + choices=[ + (0, "System default"), + (1, "Text, then HTML"), + (2, "HTML, then text"), + (3, "HTML only"), + (4, "Text only"), + ], + default=0, + verbose_name="pdf layout", + ), + ), + migrations.AddField( + model_name="mailrule", + name="stop_processing", + field=models.BooleanField( + default=False, + help_text="If True, no further rules will be processed after this one if any document is queued.", + verbose_name="Stop processing further rules", + ), + ), + ]