Compare commits

..

7 Commits

Author SHA1 Message Date
shamoon
bd7752e4d6 Happy Sonar 2026-01-29 11:47:21 -08:00
shamoon
fbcaaf2e9b Also in the tests 2026-01-29 11:45:14 -08:00
shamoon
2229479499 Refactor to separate AsnCheckPlugin 2026-01-29 11:32:13 -08:00
shamoon
7a044763dd Merge branch 'dev' into fix-11679 2026-01-29 11:25:07 -08:00
shamoon
f23433bd57 Merge branch 'dev' into fix-11679 2026-01-09 20:45:38 -08:00
shamoon
c968505f64 Fix: run pre-flight ASN check after barcode detection 2025-12-30 10:02:32 -08:00
shamoon
fbb5864757 Add test to reproduce asn in trash error 2025-12-30 10:02:31 -08:00
20 changed files with 110 additions and 128 deletions

View File

@@ -7,11 +7,6 @@ cd "${PAPERLESS_SRC_DIR}"
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
python3 manage.py management_command "$@"
elif [[ $(id -u) == 0 ]]; then
s6-setuidgid paperless python3 manage.py management_command "$@"
elif [[ $(id -un) == "paperless" ]]; then
python3 manage.py management_command "$@"
else
echo "Unknown user."
exit 1
s6-setuidgid paperless python3 manage.py management_command "$@"
fi

View File

@@ -6,12 +6,7 @@ set -e
cd "${PAPERLESS_SRC_DIR}"
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
python3 manage.py convert_mariadb_uuid "$@"
elif [[ $(id -u) == 0 ]]; then
s6-setuidgid paperless python3 manage.py convert_mariadb_uuid "$@"
python3 manage.py convert_mariadb_uuid "$@"
elif [[ $(id -un) == "paperless" ]]; then
python3 manage.py convert_mariadb_uuid "$@"
else
echo "Unknown user."
exit 1
s6-setuidgid paperless python3 manage.py convert_mariadb_uuid "$@"
fi

View File

@@ -6,12 +6,7 @@ set -e
cd "${PAPERLESS_SRC_DIR}"
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
python3 manage.py createsuperuser "$@"
elif [[ $(id -u) == 0 ]]; then
s6-setuidgid paperless python3 manage.py createsuperuser "$@"
python3 manage.py createsuperuser "$@"
elif [[ $(id -un) == "paperless" ]]; then
python3 manage.py createsuperuser "$@"
else
echo "Unknown user."
exit 1
s6-setuidgid paperless python3 manage.py createsuperuser "$@"
fi

View File

@@ -6,12 +6,7 @@ set -e
cd "${PAPERLESS_SRC_DIR}"
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
python3 manage.py document_archiver "$@"
elif [[ $(id -u) == 0 ]]; then
s6-setuidgid paperless python3 manage.py document_archiver "$@"
python3 manage.py document_archiver "$@"
elif [[ $(id -un) == "paperless" ]]; then
python3 manage.py document_archiver "$@"
else
echo "Unknown user."
exit 1
s6-setuidgid paperless python3 manage.py document_archiver "$@"
fi

View File

@@ -6,16 +6,7 @@ set -e
cd "${PAPERLESS_SRC_DIR}"
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
python3 manage.py document_create_classifier "$@"
elif [[ $(id -u) == 0 ]]; then
s6-setuidgid paperless python3 manage.py document_create_classifier "$@"
elif [[ $(id -un) == "paperless" ]]; then
python3 manage.py document_create_classifier "$@"
else
echo "Unknown user."
exit 1
fi
er "$@"
python3 manage.py document_create_classifier "$@"
elif [[ $(id -un) == "paperless" ]]; then
s6-setuidgid paperless python3 manage.py document_create_classifier "$@"
fi

View File

@@ -6,12 +6,7 @@ set -e
cd "${PAPERLESS_SRC_DIR}"
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
python3 manage.py document_exporter "$@"
elif [[ $(id -u) == 0 ]]; then
s6-setuidgid paperless python3 manage.py document_exporter "$@"
python3 manage.py document_exporter "$@"
elif [[ $(id -un) == "paperless" ]]; then
python3 manage.py document_exporter "$@"
else
echo "Unknown user."
exit 1
s6-setuidgid paperless python3 manage.py document_exporter "$@"
fi

View File

@@ -6,12 +6,7 @@ set -e
cd "${PAPERLESS_SRC_DIR}"
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
python3 manage.py document_fuzzy_match "$@"
elif [[ $(id -u) == 0 ]]; then
s6-setuidgid paperless python3 manage.py document_fuzzy_match "$@"
python3 manage.py document_fuzzy_match "$@"
elif [[ $(id -un) == "paperless" ]]; then
python3 manage.py document_fuzzy_match "$@"
else
echo "Unknown user."
exit 1
s6-setuidgid paperless python3 manage.py document_fuzzy_match "$@"
fi

View File

@@ -6,12 +6,7 @@ set -e
cd "${PAPERLESS_SRC_DIR}"
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
python3 manage.py document_importer "$@"
elif [[ $(id -u) == 0 ]]; then
s6-setuidgid paperless python3 manage.py document_importer "$@"
python3 manage.py document_importer "$@"
elif [[ $(id -un) == "paperless" ]]; then
python3 manage.py document_importer "$@"
else
echo "Unknown user."
exit 1
s6-setuidgid paperless python3 manage.py document_importer "$@"
fi

View File

@@ -6,12 +6,7 @@ set -e
cd "${PAPERLESS_SRC_DIR}"
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
python3 manage.py document_index "$@"
elif [[ $(id -u) == 0 ]]; then
s6-setuidgid paperless python3 manage.py document_index "$@"
python3 manage.py document_index "$@"
elif [[ $(id -un) == "paperless" ]]; then
python3 manage.py document_index "$@"
else
echo "Unknown user."
exit 1
s6-setuidgid paperless python3 manage.py document_index "$@"
fi

View File

@@ -6,12 +6,7 @@ set -e
cd "${PAPERLESS_SRC_DIR}"
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
python3 manage.py document_renamer "$@"
elif [[ $(id -u) == 0 ]]; then
s6-setuidgid paperless python3 manage.py document_renamer "$@"
python3 manage.py document_renamer "$@"
elif [[ $(id -un) == "paperless" ]]; then
python3 manage.py document_renamer "$@"
else
echo "Unknown user."
exit 1
s6-setuidgid paperless python3 manage.py document_renamer "$@"
fi

View File

@@ -6,12 +6,7 @@ set -e
cd "${PAPERLESS_SRC_DIR}"
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
python3 manage.py document_retagger "$@"
elif [[ $(id -u) == 0 ]]; then
s6-setuidgid paperless python3 manage.py document_retagger "$@"
python3 manage.py document_retagger "$@"
elif [[ $(id -un) == "paperless" ]]; then
python3 manage.py document_retagger "$@"
else
echo "Unknown user."
exit 1
s6-setuidgid paperless python3 manage.py document_retagger "$@"
fi

View File

@@ -6,12 +6,7 @@ set -e
cd "${PAPERLESS_SRC_DIR}"
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
python3 manage.py document_sanity_checker "$@"
elif [[ $(id -u) == 0 ]]; then
s6-setuidgid paperless python3 manage.py document_sanity_checker "$@"
python3 manage.py document_sanity_checker "$@"
elif [[ $(id -un) == "paperless" ]]; then
python3 manage.py document_sanity_checker "$@"
else
echo "Unknown user."
exit 1
s6-setuidgid paperless python3 manage.py document_sanity_checker "$@"
fi

View File

@@ -6,12 +6,7 @@ set -e
cd "${PAPERLESS_SRC_DIR}"
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
python3 manage.py document_thumbnails "$@"
elif [[ $(id -u) == 0 ]]; then
s6-setuidgid paperless python3 manage.py document_thumbnails "$@"
python3 manage.py document_thumbnails "$@"
elif [[ $(id -un) == "paperless" ]]; then
python3 manage.py document_thumbnails "$@"
else
echo "Unknown user."
exit 1
s6-setuidgid paperless python3 manage.py document_thumbnails "$@"
fi

View File

@@ -6,12 +6,7 @@ set -e
cd "${PAPERLESS_SRC_DIR}"
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
python3 manage.py mail_fetcher "$@"
elif [[ $(id -u) == 0 ]]; then
s6-setuidgid paperless python3 manage.py mail_fetcher "$@"
python3 manage.py mail_fetcher "$@"
elif [[ $(id -un) == "paperless" ]]; then
python3 manage.py mail_fetcher "$@"
else
echo "Unknown user."
exit 1
s6-setuidgid paperless python3 manage.py mail_fetcher "$@"
fi

View File

@@ -6,12 +6,7 @@ set -e
cd "${PAPERLESS_SRC_DIR}"
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
python3 manage.py manage_superuser "$@"
elif [[ $(id -u) == 0 ]]; then
s6-setuidgid paperless python3 manage.py manage_superuser "$@"
python3 manage.py manage_superuser "$@"
elif [[ $(id -un) == "paperless" ]]; then
python3 manage.py manage_superuser "$@"
else
echo "Unknown user."
exit 1
s6-setuidgid paperless python3 manage.py manage_superuser "$@"
fi

View File

@@ -6,12 +6,7 @@ set -e
cd "${PAPERLESS_SRC_DIR}"
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
python3 manage.py prune_audit_logs "$@"
elif [[ $(id -u) == 0 ]]; then
s6-setuidgid paperless python3 manage.py prune_audit_logs "$@"
python3 manage.py prune_audit_logs "$@"
elif [[ $(id -un) == "paperless" ]]; then
python3 manage.py prune_audit_logs "$@"
else
echo "Unknown user."
exit 1
s6-setuidgid paperless python3 manage.py prune_audit_logs "$@"
fi

View File

@@ -5,6 +5,7 @@ import tempfile
from enum import Enum
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Final
import magic
from django.conf import settings
@@ -49,6 +50,8 @@ from documents.utils import copy_file_with_basic_stats
from documents.utils import run_subprocess
from paperless_mail.parsers import MailDocumentParser
LOGGING_NAME: Final[str] = "paperless.consumer"
class WorkflowTriggerPlugin(
NoCleanupPluginMixin,
@@ -156,7 +159,7 @@ class ConsumerPlugin(
ConsumerPluginMixin,
ConsumeTaskPlugin,
):
logging_name = "paperless.consumer"
logging_name = LOGGING_NAME
def run_pre_consume_script(self):
"""
@@ -753,7 +756,7 @@ class ConsumerPreflightPlugin(
ConsumeTaskPlugin,
):
NAME: str = "ConsumerPreflightPlugin"
logging_name = "paperless.consumer"
logging_name = LOGGING_NAME
def pre_check_file_exists(self):
"""
@@ -828,6 +831,32 @@ class ConsumerPreflightPlugin(
settings.ORIGINALS_DIR.mkdir(parents=True, exist_ok=True)
settings.ARCHIVE_DIR.mkdir(parents=True, exist_ok=True)
def run(self) -> None:
self._send_progress(
0,
100,
ProgressStatusOptions.STARTED,
ConsumerStatusShortMessage.NEW_FILE,
)
# Make sure that preconditions for consuming the file are met.
self.pre_check_file_exists()
self.pre_check_duplicate()
self.pre_check_directories()
class AsnCheckPlugin(
NoCleanupPluginMixin,
NoSetupPluginMixin,
AlwaysRunPluginMixin,
LoggingMixin,
ConsumerPluginMixin,
ConsumeTaskPlugin,
):
NAME: str = "AsnCheckPlugin"
logging_name = LOGGING_NAME
def pre_check_asn_value(self):
"""
Check that if override_asn is given, it is unique and within a valid range
@@ -865,16 +894,4 @@ class ConsumerPreflightPlugin(
)
def run(self) -> None:
self._send_progress(
0,
100,
ProgressStatusOptions.STARTED,
ConsumerStatusShortMessage.NEW_FILE,
)
# Make sure that preconditions for consuming the file are met.
self.pre_check_file_exists()
self.pre_check_duplicate()
self.pre_check_directories()
self.pre_check_asn_value()

View File

@@ -29,6 +29,7 @@ from documents.bulk_download import OriginalsOnlyStrategy
from documents.caching import clear_document_caches
from documents.classifier import DocumentClassifier
from documents.classifier import load_classifier
from documents.consumer import AsnCheckPlugin
from documents.consumer import ConsumerPlugin
from documents.consumer import ConsumerPreflightPlugin
from documents.consumer import WorkflowTriggerPlugin
@@ -157,8 +158,10 @@ def consume_file(
plugins: list[type[ConsumeTaskPlugin]] = [
ConsumerPreflightPlugin,
AsnCheckPlugin,
CollatePlugin,
BarcodePlugin,
AsnCheckPlugin, # Re-run ASN check after barcode reading
WorkflowTriggerPlugin,
ConsumerPlugin,
]

View File

@@ -11,6 +11,7 @@ from django.test import override_settings
from documents import tasks
from documents.barcodes import BarcodePlugin
from documents.consumer import ConsumerError
from documents.data_models import ConsumableDocument
from documents.data_models import DocumentMetadataOverrides
from documents.data_models import DocumentSource
@@ -93,6 +94,41 @@ class TestBarcode(
self.assertDictEqual(separator_page_numbers, {1: False})
@override_settings(CONSUMER_ENABLE_ASN_BARCODE=True)
def test_asn_barcode_duplicate_in_trash_fails(self):
"""
GIVEN:
- A document with ASN barcode 123 is in the trash
WHEN:
- A file with the same barcode ASN is consumed
THEN:
- The ASN check is re-run and consumption fails
"""
test_file = self.BARCODE_SAMPLE_DIR / "barcode-39-asn-123.pdf"
first_doc = Document.objects.create(
title="First ASN 123",
content="",
checksum="asn123first",
mime_type="application/pdf",
archive_serial_number=123,
)
first_doc.delete()
dupe_asn = settings.SCRATCH_DIR / "barcode-39-asn-123-second.pdf"
shutil.copy(test_file, dupe_asn)
with mock.patch("documents.tasks.ProgressManager", DummyProgressManager):
with self.assertRaisesRegex(ConsumerError, r"ASN 123.*trash"):
tasks.consume_file(
ConsumableDocument(
source=DocumentSource.ConsumeFolder,
original_file=dupe_asn,
),
None,
)
@override_settings(
CONSUMER_BARCODE_TIFF_SUPPORT=True,
)

View File

@@ -20,6 +20,7 @@ from django.db.migrations.executor import MigrationExecutor
from django.test import TransactionTestCase
from django.test import override_settings
from documents.consumer import AsnCheckPlugin
from documents.consumer import ConsumerPlugin
from documents.consumer import ConsumerPreflightPlugin
from documents.data_models import ConsumableDocument
@@ -371,6 +372,14 @@ class GetConsumerMixin:
"task-id",
)
preflight_plugin.setup()
asncheck_plugin = AsnCheckPlugin(
doc,
overrides or DocumentMetadataOverrides(),
self.status, # type: ignore
self.dirs.scratch_dir,
"task-id",
)
asncheck_plugin.setup()
reader = ConsumerPlugin(
doc,
overrides or DocumentMetadataOverrides(),
@@ -381,6 +390,7 @@ class GetConsumerMixin:
reader.setup()
try:
preflight_plugin.run()
asncheck_plugin.run()
yield reader
finally:
reader.cleanup()