diff --git a/.github/workflows/translate-strings.yml b/.github/workflows/translate-strings.yml index bd9eafae5..1a82a5cee 100644 --- a/.github/workflows/translate-strings.yml +++ b/.github/workflows/translate-strings.yml @@ -12,9 +12,11 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v6 + env: + GH_REF: ${{ github.ref }} # sonar rule:githubactions:S7630 - avoid injection with: token: ${{ secrets.PNGX_BOT_PAT }} - ref: ${{ github.head_ref }} + ref: ${{ env.GH_REF }} - name: Set up Python id: setup-python uses: actions/setup-python@v6 diff --git a/src/documents/data_models.py b/src/documents/data_models.py index ffb816053..3acc3f51b 100644 --- a/src/documents/data_models.py +++ b/src/documents/data_models.py @@ -22,7 +22,7 @@ class DocumentMetadataOverrides: document_type_id: int | None = None tag_ids: list[int] | None = None storage_path_id: int | None = None - created: datetime.datetime | None = None + created: datetime.date | None = None asn: int | None = None owner_id: int | None = None view_users: list[int] | None = None @@ -103,6 +103,7 @@ class DocumentMetadataOverrides: overrides.storage_path_id = doc.storage_path.id if doc.storage_path else None overrides.owner_id = doc.owner.id if doc.owner else None overrides.tag_ids = list(doc.tags.values_list("id", flat=True)) + overrides.created = doc.created overrides.view_users = list( get_users_with_perms( diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 5c90c6f1c..5c71de9a9 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -18,6 +18,8 @@ from django.core.exceptions import ValidationError from django.core.validators import DecimalValidator from django.core.validators import EmailValidator from django.core.validators import MaxLengthValidator +from django.core.validators import MaxValueValidator +from django.core.validators import MinValueValidator from django.core.validators import RegexValidator from django.core.validators import integer_validator from django.db.models import Count @@ -875,6 +877,13 @@ class CustomFieldInstanceSerializer(serializers.ModelSerializer): uri_validator(data["value"]) elif field.data_type == CustomField.FieldDataType.INT: integer_validator(data["value"]) + try: + value_int = int(data["value"]) + except (TypeError, ValueError): + raise serializers.ValidationError("Enter a valid integer.") + # Keep values within the PostgreSQL integer range + MinValueValidator(-2147483648)(value_int) + MaxValueValidator(2147483647)(value_int) elif ( field.data_type == CustomField.FieldDataType.MONETARY and data["value"] != "" diff --git a/src/documents/tests/test_api_documents.py b/src/documents/tests/test_api_documents.py index 87190c23b..f40ef157f 100644 --- a/src/documents/tests/test_api_documents.py +++ b/src/documents/tests/test_api_documents.py @@ -1664,6 +1664,44 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): self.consume_file_mock.assert_not_called() + def test_patch_document_integer_custom_field_out_of_range(self): + """ + GIVEN: + - An integer custom field + - A document + WHEN: + - Patching the document with an integer value exceeding PostgreSQL's range + THEN: + - HTTP 400 is returned (validation catches the overflow) + - No custom field instance is created + """ + cf_int = CustomField.objects.create( + name="intfield", + data_type=CustomField.FieldDataType.INT, + ) + doc = Document.objects.create( + title="Doc", + checksum="123", + mime_type="application/pdf", + ) + + response = self.client.patch( + f"/api/documents/{doc.pk}/", + { + "custom_fields": [ + { + "field": cf_int.pk, + "value": 2**31, # overflow for PostgreSQL integer fields + }, + ], + }, + format="json", + ) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn("custom_fields", response.data) + self.assertEqual(CustomFieldInstance.objects.count(), 0) + def test_upload_with_webui_source(self): """ GIVEN: A document with a source file diff --git a/src/documents/tests/test_bulk_edit.py b/src/documents/tests/test_bulk_edit.py index 769ff9d66..c220c1e9b 100644 --- a/src/documents/tests/test_bulk_edit.py +++ b/src/documents/tests/test_bulk_edit.py @@ -581,7 +581,7 @@ class TestPDFActions(DirectoriesMixin, TestCase): - Consume file should be called """ doc_ids = [self.doc1.id, self.doc2.id, self.doc3.id] - metadata_document_id = self.doc1.id + metadata_document_id = self.doc2.id user = User.objects.create(username="test_user") result = bulk_edit.merge( @@ -607,7 +607,8 @@ class TestPDFActions(DirectoriesMixin, TestCase): # With metadata_document_id overrides result = bulk_edit.merge(doc_ids, metadata_document_id=metadata_document_id) consume_file_args, _ = mock_consume_file.call_args - self.assertEqual(consume_file_args[1].title, "A (merged)") + self.assertEqual(consume_file_args[1].title, "B (merged)") + self.assertEqual(consume_file_args[1].created, self.doc2.created) self.assertTrue(consume_file_args[1].skip_asn) self.assertEqual(result, "OK") diff --git a/src/documents/views.py b/src/documents/views.py index c423c6347..3786cf8cf 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -723,6 +723,7 @@ class DocumentViewSet( "title", "correspondent__name", "document_type__name", + "storage_path__name", "created", "modified", "added", diff --git a/src/locale/en_US/LC_MESSAGES/django.po b/src/locale/en_US/LC_MESSAGES/django.po index 65dce1cb4..53f2b32f5 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: 2025-12-24 05:27+0000\n" +"POT-Creation-Date: 2025-12-29 14:49+0000\n" "PO-Revision-Date: 2022-02-17 04:17\n" "Last-Translator: \n" "Language-Team: English\n" @@ -1219,35 +1219,35 @@ msgstr "" msgid "workflow runs" msgstr "" -#: documents/serialisers.py:640 +#: documents/serialisers.py:642 msgid "Invalid color." msgstr "" -#: documents/serialisers.py:1826 +#: documents/serialisers.py:1835 #, python-format msgid "File type %(type)s not supported" msgstr "" -#: documents/serialisers.py:1870 +#: documents/serialisers.py:1879 #, python-format msgid "Custom field id must be an integer: %(id)s" msgstr "" -#: documents/serialisers.py:1877 +#: documents/serialisers.py:1886 #, python-format msgid "Custom field with id %(id)s does not exist" msgstr "" -#: documents/serialisers.py:1894 documents/serialisers.py:1904 +#: documents/serialisers.py:1903 documents/serialisers.py:1913 msgid "" "Custom fields must be a list of integers or an object mapping ids to values." msgstr "" -#: documents/serialisers.py:1899 +#: documents/serialisers.py:1908 msgid "Some custom fields don't exist or were specified twice." msgstr "" -#: documents/serialisers.py:2014 +#: documents/serialisers.py:2023 msgid "Invalid variable detected." msgstr ""