Compare commits

..

1 Commits

Author SHA1 Message Date
Crowdin Bot
4bfb605137 New Crowdin translations by GitHub Action 2025-09-17 00:33:23 +00:00
9 changed files with 36 additions and 167 deletions

View File

@@ -192,8 +192,8 @@ The endpoint supports the following optional form fields:
- `tags`: Similar to correspondent. Specify this multiple times to
have multiple tags added to the document.
- `archive_serial_number`: An optional archive serial number to set.
- `custom_fields`: Either an array of custom field ids to assign (with an empty
value) to the document or an object mapping field id -> value.
- `custom_fields`: An array of custom field ids to assign (with an empty
value) to the document.
The endpoint will immediately return HTTP 200 if the document consumption
process was started successfully, with the UUID of the consumption task

View File

@@ -5192,7 +5192,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">180</context>
</context-group>
<target state="needs-translation">Has storage path</target>
<target state="translated">Té ruta emmagatzematge</target>
</trans-unit>
<trans-unit id="6417103744331194518" datatype="html">
<source>Action type</source>
@@ -7264,7 +7264,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
<context context-type="linenumber">58</context>
</context-group>
<target state="needs-translation">Print</target>
<target state="translated">Imprimir</target>
</trans-unit>
<trans-unit id="1418444397960583910" datatype="html">
<source>More like this</source>
@@ -7852,7 +7852,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">1452</context>
</context-group>
<target state="needs-translation">Print failed.</target>
<target state="translated">Impressió fallida.</target>
</trans-unit>
<trans-unit id="6457245677384603573" datatype="html">
<source>Error loading document for printing.</source>
@@ -7860,7 +7860,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">1460</context>
</context-group>
<target state="needs-translation">Error loading document for printing.</target>
<target state="translated">Error carregant document per imprimir.</target>
</trans-unit>
<trans-unit id="6085793215710522488" datatype="html">
<source>An error occurred loading tiff: <x id="PH" equiv-text="err.toString()"/></source>
@@ -10180,7 +10180,7 @@
<context context-type="sourcefile">src/app/data/custom-field.ts</context>
<context context-type="linenumber">55</context>
</context-group>
<target state="needs-translation">Long Text</target>
<target state="translated">Text Llarg</target>
</trans-unit>
<trans-unit id="4460262093225954455" datatype="html">
<source>Search score</source>

View File

@@ -5192,7 +5192,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">180</context>
</context-group>
<target state="needs-translation">Has storage path</target>
<target state="translated">Ima putanju za skladištenje</target>
</trans-unit>
<trans-unit id="6417103744331194518" datatype="html">
<source>Action type</source>
@@ -6874,7 +6874,7 @@
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
<context context-type="linenumber">257</context>
</context-group>
<target state="needs-translation">WebSocket Connection</target>
<target state="translated">WebSocket konekcija</target>
</trans-unit>
<trans-unit id="8998179362936748717" datatype="html">
<source>OK</source>
@@ -6882,7 +6882,7 @@
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
<context context-type="linenumber">261</context>
</context-group>
<target state="needs-translation">OK</target>
<target state="translated">OK</target>
</trans-unit>
<trans-unit id="6732151329960766506" datatype="html">
<source>Copy Raw Error</source>
@@ -7264,7 +7264,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
<context context-type="linenumber">58</context>
</context-group>
<target state="needs-translation">Print</target>
<target state="translated">Štampaj</target>
</trans-unit>
<trans-unit id="1418444397960583910" datatype="html">
<source>More like this</source>
@@ -7852,7 +7852,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">1452</context>
</context-group>
<target state="needs-translation">Print failed.</target>
<target state="translated">Štampanje nije uspelo.</target>
</trans-unit>
<trans-unit id="6457245677384603573" datatype="html">
<source>Error loading document for printing.</source>
@@ -7860,7 +7860,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">1460</context>
</context-group>
<target state="needs-translation">Error loading document for printing.</target>
<target state="translated">Greška pri učitavanju dokumenta za štampanje.</target>
</trans-unit>
<trans-unit id="6085793215710522488" datatype="html">
<source>An error occurred loading tiff: <x id="PH" equiv-text="err.toString()"/></source>
@@ -10181,7 +10181,7 @@
<context context-type="sourcefile">src/app/data/custom-field.ts</context>
<context context-type="linenumber">55</context>
</context-group>
<target state="needs-translation">Long Text</target>
<target state="translated">Dugi tekst</target>
</trans-unit>
<trans-unit id="4460262093225954455" datatype="html">
<source>Search score</source>

View File

@@ -1668,8 +1668,9 @@ class PostDocumentSerializer(serializers.Serializer):
max_value=Document.ARCHIVE_SERIAL_NUMBER_MAX,
)
# Accept either a list of custom field ids or a dict mapping id -> value
custom_fields = serializers.JSONField(
custom_fields = serializers.PrimaryKeyRelatedField(
many=True,
queryset=CustomField.objects.all(),
label="Custom fields",
write_only=True,
required=False,
@@ -1726,60 +1727,11 @@ class PostDocumentSerializer(serializers.Serializer):
return None
def validate_custom_fields(self, custom_fields):
if not custom_fields:
if custom_fields:
return [custom_field.id for custom_field in custom_fields]
else:
return None
# Normalize single values to a list
if isinstance(custom_fields, int):
custom_fields = [custom_fields]
if isinstance(custom_fields, dict):
custom_field_serializer = CustomFieldInstanceSerializer()
normalized = {}
for field_id, value in custom_fields.items():
try:
field_id_int = int(field_id)
except (TypeError, ValueError):
raise serializers.ValidationError(
_("Custom field id must be an integer: %(id)s")
% {"id": field_id},
)
try:
field = CustomField.objects.get(id=field_id_int)
except CustomField.DoesNotExist:
raise serializers.ValidationError(
_("Custom field with id %(id)s does not exist")
% {"id": field_id_int},
)
custom_field_serializer.validate(
{
"field": field,
"value": value,
},
)
normalized[field_id_int] = value
return normalized
elif isinstance(custom_fields, list):
try:
ids = [int(i) for i in custom_fields]
except (TypeError, ValueError):
raise serializers.ValidationError(
_(
"Custom fields must be a list of integers or an object mapping ids to values.",
),
)
if CustomField.objects.filter(id__in=ids).count() != len(set(ids)):
raise serializers.ValidationError(
_("Some custom fields don't exist or were specified twice."),
)
return ids
raise serializers.ValidationError(
_(
"Custom fields must be a list of integers or an object mapping ids to values.",
),
)
# custom_fields_w_values handled via validate_custom_fields
def validate_created(self, created):
# support datetime format for created for backwards compatibility
if isinstance(created, datetime):

View File

@@ -1,5 +1,4 @@
import datetime
import json
import shutil
import tempfile
import uuid
@@ -1538,86 +1537,6 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
overrides.update(new_overrides)
self.assertEqual(overrides.custom_fields, {cf.id: None, cf2.id: 123})
def test_upload_with_custom_field_values(self):
"""
GIVEN: A document with a source file
WHEN: Upload the document with custom fields and values
THEN: Metadata is set correctly
"""
self.consume_file_mock.return_value = celery.result.AsyncResult(
id=str(uuid.uuid4()),
)
cf_string = CustomField.objects.create(
name="stringfield",
data_type=CustomField.FieldDataType.STRING,
)
cf_int = CustomField.objects.create(
name="intfield",
data_type=CustomField.FieldDataType.INT,
)
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{
"document": f,
"custom_fields": json.dumps(
{
str(cf_string.id): "a string",
str(cf_int.id): 123,
},
),
},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.consume_file_mock.assert_called_once()
input_doc, overrides = self.get_last_consume_delay_call_args()
self.assertEqual(input_doc.original_file.name, "simple.pdf")
self.assertEqual(overrides.filename, "simple.pdf")
self.assertEqual(
overrides.custom_fields,
{cf_string.id: "a string", cf_int.id: 123},
)
def test_upload_with_custom_fields_errors(self):
"""
GIVEN: A document with a source file
WHEN: Upload the document with invalid custom fields payloads
THEN: The upload is rejected
"""
self.consume_file_mock.return_value = celery.result.AsyncResult(
id=str(uuid.uuid4()),
)
error_payloads = [
# Non-integer key in mapping
{"custom_fields": json.dumps({"abc": "a string"})},
# List with non-integer entry
{"custom_fields": json.dumps(["abc"])},
# Nonexistent id in mapping
{"custom_fields": json.dumps({99999999: "a string"})},
# Nonexistent id in list
{"custom_fields": json.dumps([99999999])},
# Invalid type (JSON string, not list/dict/int)
{"custom_fields": json.dumps("not-a-supported-structure")},
]
for payload in error_payloads:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
data = {"document": f, **payload}
response = self.client.post(
"/api/documents/post_document/",
data,
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.consume_file_mock.assert_not_called()
def test_upload_with_webui_source(self):
"""
GIVEN: A document with a source file

View File

@@ -1497,7 +1497,7 @@ class PostDocumentView(GenericAPIView):
title = serializer.validated_data.get("title")
created = serializer.validated_data.get("created")
archive_serial_number = serializer.validated_data.get("archive_serial_number")
cf = serializer.validated_data.get("custom_fields")
custom_field_ids = serializer.validated_data.get("custom_fields")
from_webui = serializer.validated_data.get("from_webui")
t = int(mktime(datetime.now().timetuple()))
@@ -1516,11 +1516,6 @@ class PostDocumentView(GenericAPIView):
source=DocumentSource.WebUI if from_webui else DocumentSource.ApiUpload,
original_file=temp_file_path,
)
custom_fields = None
if isinstance(cf, dict) and cf:
custom_fields = cf
elif isinstance(cf, list) and cf:
custom_fields = dict.fromkeys(cf, None)
input_doc_overrides = DocumentMetadataOverrides(
filename=doc_name,
title=title,
@@ -1531,7 +1526,10 @@ class PostDocumentView(GenericAPIView):
created=created,
asn=archive_serial_number,
owner_id=request.user.id,
custom_fields=custom_fields,
# TODO: set values
custom_fields={cf_id: None for cf_id in custom_field_ids}
if custom_field_ids
else None,
)
async_task = consume_file.delay(

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-09-14 03:21+0000\n"
"PO-Revision-Date: 2025-09-14 03:22\n"
"PO-Revision-Date: 2025-09-17 00:33\n"
"Last-Translator: \n"
"Language-Team: Catalan\n"
"Language: ca_ES\n"
@@ -19,7 +19,7 @@ msgstr ""
#: documents/apps.py:8
msgid "Documents"
msgstr "documents"
msgstr "Documents "
#: documents/filters.py:386
msgid "Value must be valid JSON."
@@ -750,7 +750,7 @@ msgstr "Selecciona"
#: documents/models.py:762
msgid "Long Text"
msgstr ""
msgstr "Text Llarg"
#: documents/models.py:774
msgid "data type"
@@ -858,7 +858,7 @@ msgstr "té aquest corresponsal"
#: documents/models.py:1056
msgid "has this storage path"
msgstr ""
msgstr "té aquesta ruta emmagatzematge"
#: documents/models.py:1060
msgid "schedule offset days"
@@ -1002,7 +1002,7 @@ msgstr "assigna títol"
#: documents/models.py:1227
msgid "Assign a document title, must be a Jinja2 template, see documentation."
msgstr ""
msgstr "Asigna títol de doculent, ha de ser plantilla Jinja2, veure documentació."
#: documents/models.py:1235 paperless_mail/models.py:274
msgid "assign this tag"

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-09-14 03:21+0000\n"
"PO-Revision-Date: 2025-09-14 03:22\n"
"PO-Revision-Date: 2025-09-17 00:33\n"
"Last-Translator: \n"
"Language-Team: Serbian (Latin)\n"
"Language: sr_CS\n"
@@ -750,7 +750,7 @@ msgstr "Odaberi"
#: documents/models.py:762
msgid "Long Text"
msgstr ""
msgstr "Dugi tekst"
#: documents/models.py:774
msgid "data type"
@@ -858,7 +858,7 @@ msgstr "poseduje ovog korespondenta"
#: documents/models.py:1056
msgid "has this storage path"
msgstr ""
msgstr "ima ovu putanju za skladištenje"
#: documents/models.py:1060
msgid "schedule offset days"
@@ -1002,7 +1002,7 @@ msgstr "dodeli naslov"
#: documents/models.py:1227
msgid "Assign a document title, must be a Jinja2 template, see documentation."
msgstr ""
msgstr "Dodelite naslov dokumenta, mora biti Jinja2 šablon, pogledajte dokumentaciju."
#: documents/models.py:1235 paperless_mail/models.py:274
msgid "assign this tag"

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-09-14 03:21+0000\n"
"PO-Revision-Date: 2025-09-14 03:22\n"
"PO-Revision-Date: 2025-09-17 00:33\n"
"Last-Translator: \n"
"Language-Team: Chinese Simplified\n"
"Language: zh_CN\n"
@@ -750,7 +750,7 @@ msgstr "选择"
#: documents/models.py:762
msgid "Long Text"
msgstr ""
msgstr "Good"
#: documents/models.py:774
msgid "data type"