Enhancement: add barcode frontend config (#9742)

This commit is contained in:
shamoon 2025-05-11 12:44:06 -07:00 committed by GitHub
parent bcb0ae1ee5
commit 6a5be992c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 437 additions and 49 deletions

View File

@ -105,9 +105,9 @@ describe('ConfigComponent', () => {
it('should support JSON validation for e.g. user_args', () => { it('should support JSON validation for e.g. user_args', () => {
component.configForm.patchValue({ user_args: '{ foo bar }' }) component.configForm.patchValue({ user_args: '{ foo bar }' })
expect(component.errors).toEqual({ user_args: 'Invalid JSON' }) expect(component.errors['user_args']).toEqual('Invalid JSON')
component.configForm.patchValue({ user_args: '{ "foo": "bar" }' }) component.configForm.patchValue({ user_args: '{ "foo": "bar" }' })
expect(component.errors).toEqual({ user_args: null }) expect(component.errors['user_args']).toBeNull()
}) })
it('should upload file, show error if necessary', () => { it('should upload file, show error if necessary', () => {

View File

@ -49,6 +49,7 @@ export enum ConfigOptionType {
export const ConfigCategory = { export const ConfigCategory = {
General: $localize`General Settings`, General: $localize`General Settings`,
OCR: $localize`OCR Settings`, OCR: $localize`OCR Settings`,
Barcode: $localize`Barcode Settings`,
} }
export interface ConfigOption { export interface ConfigOption {
@ -180,6 +181,83 @@ export const PaperlessConfigOptions: ConfigOption[] = [
config_key: 'PAPERLESS_APP_TITLE', config_key: 'PAPERLESS_APP_TITLE',
category: ConfigCategory.General, category: ConfigCategory.General,
}, },
{
key: 'barcodes_enabled',
title: $localize`Enable Barcodes`,
type: ConfigOptionType.Boolean,
config_key: 'PAPERLESS_CONSUMER_ENABLE_BARCODES',
category: ConfigCategory.Barcode,
},
{
key: 'barcode_enable_tiff_support',
title: $localize`Enable TIFF Support`,
type: ConfigOptionType.Boolean,
config_key: 'PAPERLESS_CONSUMER_BARCODE_TIFF_SUPPORT',
category: ConfigCategory.Barcode,
},
{
key: 'barcode_string',
title: $localize`Barcode String`,
type: ConfigOptionType.String,
config_key: 'PAPERLESS_CONSUMER_BARCODE_STRING',
category: ConfigCategory.Barcode,
},
{
key: 'barcode_retain_split_pages',
title: $localize`Retain Split Pages`,
type: ConfigOptionType.Boolean,
config_key: 'PAPERLESS_CONSUMER_BARCODE_RETAIN_SPLIT_PAGES',
category: ConfigCategory.Barcode,
},
{
key: 'barcode_enable_asn',
title: $localize`Enable ASN`,
type: ConfigOptionType.Boolean,
config_key: 'PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE',
category: ConfigCategory.Barcode,
},
{
key: 'barcode_asn_prefix',
title: $localize`ASN Prefix`,
type: ConfigOptionType.String,
config_key: 'PAPERLESS_CONSUMER_ASN_BARCODE_PREFIX',
category: ConfigCategory.Barcode,
},
{
key: 'barcode_upscale',
title: $localize`Upscale`,
type: ConfigOptionType.Number,
config_key: 'PAPERLESS_CONSUMER_BARCODE_UPSCALE',
category: ConfigCategory.Barcode,
},
{
key: 'barcode_dpi',
title: $localize`DPI`,
type: ConfigOptionType.Number,
config_key: 'PAPERLESS_CONSUMER_BARCODE_DPI',
category: ConfigCategory.Barcode,
},
{
key: 'barcode_max_pages',
title: $localize`Max Pages`,
type: ConfigOptionType.Number,
config_key: 'PAPERLESS_CONSUMER_BARCODE_MAX_PAGES',
category: ConfigCategory.Barcode,
},
{
key: 'barcode_enable_tag',
title: $localize`Enable Tag Detection`,
type: ConfigOptionType.Boolean,
config_key: 'PAPERLESS_CONSUMER_ENABLE_TAG_BARCODE',
category: ConfigCategory.Barcode,
},
{
key: 'barcode_tag_mapping',
title: $localize`Tag Mapping`,
type: ConfigOptionType.JSON,
config_key: 'PAPERLESS_CONSUMER_TAG_BARCODE_MAPPING',
category: ConfigCategory.Barcode,
},
] ]
export interface PaperlessConfig extends ObjectWithId { export interface PaperlessConfig extends ObjectWithId {
@ -198,4 +276,15 @@ export interface PaperlessConfig extends ObjectWithId {
user_args: object user_args: object
app_logo: string app_logo: string
app_title: string app_title: string
barcodes_enabled: boolean
barcode_enable_tiff_support: boolean
barcode_string: string
barcode_retain_split_pages: boolean
barcode_enable_asn: boolean
barcode_asn_prefix: string
barcode_upscale: number
barcode_dpi: number
barcode_max_pages: number
barcode_enable_tag: boolean
barcode_tag_mapping: object
} }

View File

@ -15,13 +15,16 @@ from pikepdf import Pdf
from documents.converters import convert_from_tiff_to_pdf from documents.converters import convert_from_tiff_to_pdf
from documents.data_models import ConsumableDocument from documents.data_models import ConsumableDocument
from documents.data_models import DocumentMetadataOverrides
from documents.models import Tag from documents.models import Tag
from documents.plugins.base import ConsumeTaskPlugin from documents.plugins.base import ConsumeTaskPlugin
from documents.plugins.base import StopConsumeTaskError from documents.plugins.base import StopConsumeTaskError
from documents.plugins.helpers import ProgressManager
from documents.plugins.helpers import ProgressStatusOptions from documents.plugins.helpers import ProgressStatusOptions
from documents.utils import copy_basic_file_stats from documents.utils import copy_basic_file_stats
from documents.utils import copy_file_with_basic_stats from documents.utils import copy_file_with_basic_stats
from documents.utils import maybe_override_pixel_limit from documents.utils import maybe_override_pixel_limit
from paperless.config import BarcodeConfig
if TYPE_CHECKING: if TYPE_CHECKING:
from collections.abc import Callable from collections.abc import Callable
@ -39,6 +42,7 @@ class Barcode:
page: int page: int
value: str value: str
settings: BarcodeConfig
@property @property
def is_separator(self) -> bool: def is_separator(self) -> bool:
@ -46,7 +50,7 @@ class Barcode:
Returns True if the barcode value equals the configured separation value, Returns True if the barcode value equals the configured separation value,
False otherwise False otherwise
""" """
return self.value == settings.CONSUMER_BARCODE_STRING return self.value == self.settings.barcode_string
@property @property
def is_asn(self) -> bool: def is_asn(self) -> bool:
@ -54,7 +58,7 @@ class Barcode:
Returns True if the barcode value matches the configured ASN prefix, Returns True if the barcode value matches the configured ASN prefix,
False otherwise False otherwise
""" """
return self.value.startswith(settings.CONSUMER_ASN_BARCODE_PREFIX) return self.value.startswith(self.settings.barcode_asn_prefix)
class BarcodePlugin(ConsumeTaskPlugin): class BarcodePlugin(ConsumeTaskPlugin):
@ -67,17 +71,41 @@ class BarcodePlugin(ConsumeTaskPlugin):
- ASN from barcode detection is enabled or - ASN from barcode detection is enabled or
- Barcode support is enabled and the mime type is supported - Barcode support is enabled and the mime type is supported
""" """
if settings.CONSUMER_BARCODE_TIFF_SUPPORT: if self.settings.barcode_enable_tiff_support:
supported_mimes: set[str] = {"application/pdf", "image/tiff"} supported_mimes: set[str] = {"application/pdf", "image/tiff"}
else: else:
supported_mimes = {"application/pdf"} supported_mimes = {"application/pdf"}
return ( return (
settings.CONSUMER_ENABLE_ASN_BARCODE self.settings.barcode_enable_asn
or settings.CONSUMER_ENABLE_BARCODES or self.settings.barcodes_enabled
or settings.CONSUMER_ENABLE_TAG_BARCODE or self.settings.barcode_enable_tag
) and self.input_doc.mime_type in supported_mimes ) and self.input_doc.mime_type in supported_mimes
def get_settings(self) -> BarcodeConfig:
"""
Returns the settings for this plugin (Django settings or app config)
"""
return BarcodeConfig()
def __init__(
self,
input_doc: ConsumableDocument,
metadata: DocumentMetadataOverrides,
status_mgr: ProgressManager,
base_tmp_dir: Path,
task_id: str,
) -> None:
super().__init__(
input_doc,
metadata,
status_mgr,
base_tmp_dir,
task_id,
)
# need these for able_to_run
self.settings = self.get_settings()
def setup(self) -> None: def setup(self) -> None:
self.temp_dir = tempfile.TemporaryDirectory( self.temp_dir = tempfile.TemporaryDirectory(
dir=self.base_tmp_dir, dir=self.base_tmp_dir,
@ -99,7 +127,7 @@ class BarcodePlugin(ConsumeTaskPlugin):
# try reading tags from barcodes # try reading tags from barcodes
if ( if (
settings.CONSUMER_ENABLE_TAG_BARCODE self.settings.barcode_enable_tag
and (tags := self.tags) is not None and (tags := self.tags) is not None
and len(tags) > 0 and len(tags) > 0
): ):
@ -110,7 +138,7 @@ class BarcodePlugin(ConsumeTaskPlugin):
logger.info(f"Found tags in barcode: {tags}") logger.info(f"Found tags in barcode: {tags}")
# Lastly attempt to split documents # Lastly attempt to split documents
if settings.CONSUMER_ENABLE_BARCODES and ( if self.settings.barcodes_enabled and (
separator_pages := self.get_separation_pages() separator_pages := self.get_separation_pages()
): ):
# We have pages to split against # We have pages to split against
@ -155,10 +183,7 @@ class BarcodePlugin(ConsumeTaskPlugin):
# Update/overwrite an ASN if possible # Update/overwrite an ASN if possible
# After splitting, as otherwise each split document gets the same ASN # After splitting, as otherwise each split document gets the same ASN
if ( if self.settings.barcode_enable_asn and (located_asn := self.asn) is not None:
settings.CONSUMER_ENABLE_ASN_BARCODE
and (located_asn := self.asn) is not None
):
logger.info(f"Found ASN in barcode: {located_asn}") logger.info(f"Found ASN in barcode: {located_asn}")
self.metadata.asn = located_asn self.metadata.asn = located_asn
@ -245,8 +270,8 @@ class BarcodePlugin(ConsumeTaskPlugin):
# Get limit from configuration # Get limit from configuration
barcode_max_pages: int = ( barcode_max_pages: int = (
num_of_pages num_of_pages
if settings.CONSUMER_BARCODE_MAX_PAGES == 0 if self.settings.barcode_max_pages == 0
else settings.CONSUMER_BARCODE_MAX_PAGES else self.settings.barcode_max_pages
) )
if barcode_max_pages < num_of_pages: # pragma: no cover if barcode_max_pages < num_of_pages: # pragma: no cover
@ -261,7 +286,7 @@ class BarcodePlugin(ConsumeTaskPlugin):
# Convert page to image # Convert page to image
page = convert_from_path( page = convert_from_path(
self.pdf_file, self.pdf_file,
dpi=settings.CONSUMER_BARCODE_DPI, dpi=self.settings.barcode_dpi,
output_folder=self.temp_dir.name, output_folder=self.temp_dir.name,
first_page=current_page_number + 1, first_page=current_page_number + 1,
last_page=current_page_number + 1, last_page=current_page_number + 1,
@ -272,7 +297,7 @@ class BarcodePlugin(ConsumeTaskPlugin):
logger.debug(f"Image is at {page_filepath}") logger.debug(f"Image is at {page_filepath}")
# Upscale image if configured # Upscale image if configured
factor = settings.CONSUMER_BARCODE_UPSCALE factor = self.settings.barcode_upscale
if factor > 1.0: if factor > 1.0:
logger.debug( logger.debug(
f"Upscaling image by {factor} for better barcode detection", f"Upscaling image by {factor} for better barcode detection",
@ -285,7 +310,7 @@ class BarcodePlugin(ConsumeTaskPlugin):
# Detect barcodes # Detect barcodes
for barcode_value in reader(page): for barcode_value in reader(page):
self.barcodes.append( self.barcodes.append(
Barcode(current_page_number, barcode_value), Barcode(current_page_number, barcode_value, self.settings),
) )
# Delete temporary image file # Delete temporary image file
@ -308,7 +333,7 @@ class BarcodePlugin(ConsumeTaskPlugin):
def asn(self) -> int | None: def asn(self) -> int | None:
""" """
Search the parsed barcodes for any ASNs. Search the parsed barcodes for any ASNs.
The first barcode that starts with CONSUMER_ASN_BARCODE_PREFIX The first barcode that starts with barcode_asn_prefix
is considered the ASN to be used. is considered the ASN to be used.
Returns the detected ASN (or None) Returns the detected ASN (or None)
""" """
@ -317,7 +342,7 @@ class BarcodePlugin(ConsumeTaskPlugin):
# Ensure the barcodes have been read # Ensure the barcodes have been read
self.detect() self.detect()
# get the first barcode that starts with CONSUMER_ASN_BARCODE_PREFIX # get the first barcode that starts with barcode_asn_prefix
asn_text: str | None = next( asn_text: str | None = next(
(x.value for x in self.barcodes if x.is_asn), (x.value for x in self.barcodes if x.is_asn),
None, None,
@ -326,7 +351,7 @@ class BarcodePlugin(ConsumeTaskPlugin):
if asn_text: if asn_text:
logger.debug(f"Found ASN Barcode: {asn_text}") logger.debug(f"Found ASN Barcode: {asn_text}")
# remove the prefix and remove whitespace # remove the prefix and remove whitespace
asn_text = asn_text[len(settings.CONSUMER_ASN_BARCODE_PREFIX) :].strip() asn_text = asn_text[len(self.settings.barcode_asn_prefix) :].strip()
# remove non-numeric parts of the remaining string # remove non-numeric parts of the remaining string
asn_text = re.sub(r"\D", "", asn_text) asn_text = re.sub(r"\D", "", asn_text)
@ -356,9 +381,9 @@ class BarcodePlugin(ConsumeTaskPlugin):
for raw in tag_texts.split(","): for raw in tag_texts.split(","):
try: try:
tag_str: str | None = None tag_str: str | None = None
for regex in settings.CONSUMER_TAG_BARCODE_MAPPING: for regex in self.settings.barcode_tag_mapping:
if re.match(regex, raw, flags=re.IGNORECASE): if re.match(regex, raw, flags=re.IGNORECASE):
sub = settings.CONSUMER_TAG_BARCODE_MAPPING[regex] sub = self.settings.barcode_tag_mapping[regex]
tag_str = ( tag_str = (
re.sub(regex, sub, raw, flags=re.IGNORECASE) re.sub(regex, sub, raw, flags=re.IGNORECASE)
if sub if sub
@ -394,13 +419,13 @@ class BarcodePlugin(ConsumeTaskPlugin):
""" """
# filter all barcodes for the separator string # filter all barcodes for the separator string
# get the page numbers of the separating barcodes # get the page numbers of the separating barcodes
retain = settings.CONSUMER_BARCODE_RETAIN_SPLIT_PAGES retain = self.settings.barcode_retain_split_pages
separator_pages = { separator_pages = {
bc.page: retain bc.page: retain
for bc in self.barcodes for bc in self.barcodes
if bc.is_separator and (not retain or (retain and bc.page > 0)) if bc.is_separator and (not retain or (retain and bc.page > 0))
} # as below, dont include the first page if retain is enabled } # as below, dont include the first page if retain is enabled
if not settings.CONSUMER_ENABLE_ASN_BARCODE: if not self.settings.barcode_enable_asn:
return separator_pages return separator_pages
# add the page numbers of the ASN barcodes # add the page numbers of the ASN barcodes

View File

@ -32,28 +32,39 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual( self.maxDiff = None
json.dumps(response.data[0]),
json.dumps( self.assertDictEqual(
{ response.data[0],
"id": 1, {
"user_args": None, "id": 1,
"output_type": None, "output_type": None,
"pages": None, "pages": None,
"language": None, "language": None,
"mode": None, "mode": None,
"skip_archive_file": None, "skip_archive_file": None,
"image_dpi": None, "image_dpi": None,
"unpaper_clean": None, "unpaper_clean": None,
"deskew": None, "deskew": None,
"rotate_pages": None, "rotate_pages": None,
"rotate_pages_threshold": None, "rotate_pages_threshold": None,
"max_image_pixels": None, "max_image_pixels": None,
"color_conversion_strategy": None, "color_conversion_strategy": None,
"app_title": None, "user_args": None,
"app_logo": None, "app_title": None,
}, "app_logo": None,
), "barcodes_enabled": None,
"barcode_enable_tiff_support": None,
"barcode_string": None,
"barcode_retain_split_pages": None,
"barcode_enable_asn": None,
"barcode_asn_prefix": None,
"barcode_upscale": None,
"barcode_dpi": None,
"barcode_max_pages": None,
"barcode_enable_tag": None,
"barcode_tag_mapping": None,
},
) )
def test_api_get_ui_settings_with_config(self): def test_api_get_ui_settings_with_config(self):
@ -118,6 +129,7 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
{ {
"user_args": "", "user_args": "",
"language": "", "language": "",
"barcode_tag_mapping": "",
}, },
), ),
content_type="application/json", content_type="application/json",
@ -126,6 +138,7 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
config = ApplicationConfiguration.objects.first() config = ApplicationConfiguration.objects.first()
self.assertEqual(config.user_args, None) self.assertEqual(config.user_args, None)
self.assertEqual(config.language, None) self.assertEqual(config.language, None)
self.assertEqual(config.barcode_tag_mapping, None)
def test_api_replace_app_logo(self): def test_api_replace_app_logo(self):
""" """

View File

@ -22,6 +22,7 @@ from documents.tests.utils import DocumentConsumeDelayMixin
from documents.tests.utils import DummyProgressManager from documents.tests.utils import DummyProgressManager
from documents.tests.utils import FileSystemAssertsMixin from documents.tests.utils import FileSystemAssertsMixin
from documents.tests.utils import SampleDirMixin from documents.tests.utils import SampleDirMixin
from paperless.models import ApplicationConfiguration
try: try:
import zxingcpp # noqa: F401 import zxingcpp # noqa: F401
@ -547,6 +548,27 @@ class TestBarcode(
}, },
) )
def test_barcode_config(self):
"""
GIVEN:
- Barcode app config is set (settings are not)
WHEN:
- Document with barcode is processed
THEN:
- The barcode config is used
"""
app_config = ApplicationConfiguration.objects.first()
app_config.barcodes_enabled = True
app_config.barcode_string = "CUSTOM BARCODE"
app_config.save()
test_file = self.BARCODE_SAMPLE_DIR / "barcode-39-custom.pdf"
with self.get_reader(test_file) as reader:
reader.detect()
separator_page_numbers = reader.get_separation_pages()
self.assertEqual(reader.pdf_file, test_file)
self.assertDictEqual(separator_page_numbers, {0: False})
@override_settings(CONSUMER_BARCODE_SCANNER="PYZBAR") @override_settings(CONSUMER_BARCODE_SCANNER="PYZBAR")
class TestBarcodeNewConsume( class TestBarcodeNewConsume(

View File

@ -96,10 +96,65 @@ class OcrConfig(OutputTypeConfig):
user_args = json.loads(settings.OCR_USER_ARGS) user_args = json.loads(settings.OCR_USER_ARGS)
except json.JSONDecodeError: except json.JSONDecodeError:
user_args = {} user_args = {}
self.user_args = user_args self.user_args = user_args
@dataclasses.dataclass
class BarcodeConfig(BaseConfig):
"""
Barcodes settings
"""
barcodes_enabled: bool = dataclasses.field(init=False)
barcode_enable_tiff_support: bool = dataclasses.field(init=False)
barcode_string: str = dataclasses.field(init=False)
barcode_retain_split_pages: bool = dataclasses.field(init=False)
barcode_enable_asn: bool = dataclasses.field(init=False)
barcode_asn_prefix: str = dataclasses.field(init=False)
barcode_upscale: float = dataclasses.field(init=False)
barcode_dpi: int = dataclasses.field(init=False)
barcode_max_pages: int = dataclasses.field(init=False)
barcode_enable_tag: bool = dataclasses.field(init=False)
barcode_tag_mapping: dict[str, str] = dataclasses.field(init=False)
def __post_init__(self) -> None:
app_config = self._get_config_instance()
self.barcodes_enabled = (
app_config.barcodes_enabled or settings.CONSUMER_ENABLE_BARCODES
)
self.barcode_enable_tiff_support = (
app_config.barcode_enable_tiff_support
or settings.CONSUMER_BARCODE_TIFF_SUPPORT
)
self.barcode_string = (
app_config.barcode_string or settings.CONSUMER_BARCODE_STRING
)
self.barcode_retain_split_pages = (
app_config.barcode_retain_split_pages
or settings.CONSUMER_BARCODE_RETAIN_SPLIT_PAGES
)
self.barcode_enable_asn = (
app_config.barcode_enable_asn or settings.CONSUMER_ENABLE_ASN_BARCODE
)
self.barcode_asn_prefix = (
app_config.barcode_asn_prefix or settings.CONSUMER_ASN_BARCODE_PREFIX
)
self.barcode_upscale = (
app_config.barcode_upscale or settings.CONSUMER_BARCODE_UPSCALE
)
self.barcode_dpi = app_config.barcode_dpi or settings.CONSUMER_BARCODE_DPI
self.barcode_max_pages = (
app_config.barcode_max_pages or settings.CONSUMER_BARCODE_MAX_PAGES
)
self.barcode_enable_tag = (
app_config.barcode_enable_tag or settings.CONSUMER_ENABLE_TAG_BARCODE
)
self.barcode_tag_mapping = (
app_config.barcode_tag_mapping or settings.CONSUMER_TAG_BARCODE_MAPPING
)
@dataclasses.dataclass @dataclasses.dataclass
class GeneralConfig(BaseConfig): class GeneralConfig(BaseConfig):
""" """

View File

@ -0,0 +1,100 @@
# Generated by Django 5.1.7 on 2025-04-02 19:21
import django.core.validators
from django.db import migrations
from django.db import models
class Migration(migrations.Migration):
dependencies = [
("paperless", "0003_alter_applicationconfiguration_max_image_pixels"),
]
operations = [
migrations.AddField(
model_name="applicationconfiguration",
name="barcode_asn_prefix",
field=models.CharField(
blank=True,
max_length=32,
null=True,
verbose_name="Sets the ASN barcode prefix",
),
),
migrations.AddField(
model_name="applicationconfiguration",
name="barcode_dpi",
field=models.PositiveIntegerField(
null=True,
validators=[django.core.validators.MinValueValidator(1)],
verbose_name="Sets the barcode DPI",
),
),
migrations.AddField(
model_name="applicationconfiguration",
name="barcode_enable_asn",
field=models.BooleanField(null=True, verbose_name="Enables ASN barcode"),
),
migrations.AddField(
model_name="applicationconfiguration",
name="barcode_enable_tag",
field=models.BooleanField(null=True, verbose_name="Enables tag barcode"),
),
migrations.AddField(
model_name="applicationconfiguration",
name="barcode_enable_tiff_support",
field=models.BooleanField(
null=True,
verbose_name="Enables barcode TIFF support",
),
),
migrations.AddField(
model_name="applicationconfiguration",
name="barcode_max_pages",
field=models.PositiveIntegerField(
null=True,
validators=[django.core.validators.MinValueValidator(1)],
verbose_name="Sets the maximum pages for barcode",
),
),
migrations.AddField(
model_name="applicationconfiguration",
name="barcode_retain_split_pages",
field=models.BooleanField(null=True, verbose_name="Retains split pages"),
),
migrations.AddField(
model_name="applicationconfiguration",
name="barcode_string",
field=models.CharField(
blank=True,
max_length=32,
null=True,
verbose_name="Sets the barcode string",
),
),
migrations.AddField(
model_name="applicationconfiguration",
name="barcode_tag_mapping",
field=models.JSONField(
null=True,
verbose_name="Sets the tag barcode mapping",
),
),
migrations.AddField(
model_name="applicationconfiguration",
name="barcode_upscale",
field=models.FloatField(
null=True,
validators=[django.core.validators.MinValueValidator(1.0)],
verbose_name="Sets the barcode upscale factor",
),
),
migrations.AddField(
model_name="applicationconfiguration",
name="barcodes_enabled",
field=models.BooleanField(
null=True,
verbose_name="Enables barcode scanning",
),
),
]

View File

@ -167,6 +167,10 @@ class ApplicationConfiguration(AbstractSingletonModel):
null=True, null=True,
) )
"""
Settings for the Paperless application
"""
app_title = models.CharField( app_title = models.CharField(
verbose_name=_("Application title"), verbose_name=_("Application title"),
null=True, null=True,
@ -184,6 +188,83 @@ class ApplicationConfiguration(AbstractSingletonModel):
upload_to="logo/", upload_to="logo/",
) )
"""
Settings for the barcode scanner
"""
# PAPERLESS_CONSUMER_ENABLE_BARCODES
barcodes_enabled = models.BooleanField(
verbose_name=_("Enables barcode scanning"),
null=True,
)
# PAPERLESS_CONSUMER_BARCODE_TIFF_SUPPORT
barcode_enable_tiff_support = models.BooleanField(
verbose_name=_("Enables barcode TIFF support"),
null=True,
)
# PAPERLESS_CONSUMER_BARCODE_STRING
barcode_string = models.CharField(
verbose_name=_("Sets the barcode string"),
null=True,
blank=True,
max_length=32,
)
# PAPERLESS_CONSUMER_BARCODE_RETAIN_SPLIT_PAGES
barcode_retain_split_pages = models.BooleanField(
verbose_name=_("Retains split pages"),
null=True,
)
# PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE
barcode_enable_asn = models.BooleanField(
verbose_name=_("Enables ASN barcode"),
null=True,
)
# PAPERLESS_CONSUMER_ASN_BARCODE_PREFIX
barcode_asn_prefix = models.CharField(
verbose_name=_("Sets the ASN barcode prefix"),
null=True,
blank=True,
max_length=32,
)
# PAPERLESS_CONSUMER_BARCODE_UPSCALE
barcode_upscale = models.FloatField(
verbose_name=_("Sets the barcode upscale factor"),
null=True,
validators=[MinValueValidator(1.0)],
)
# PAPERLESS_CONSUMER_BARCODE_DPI
barcode_dpi = models.PositiveIntegerField(
verbose_name=_("Sets the barcode DPI"),
null=True,
validators=[MinValueValidator(1)],
)
# PAPERLESS_CONSUMER_BARCODE_MAX_PAGES
barcode_max_pages = models.PositiveIntegerField(
verbose_name=_("Sets the maximum pages for barcode"),
null=True,
validators=[MinValueValidator(1)],
)
# PAPERLESS_CONSUMER_ENABLE_TAG_BARCODE
barcode_enable_tag = models.BooleanField(
verbose_name=_("Enables tag barcode"),
null=True,
)
# PAPERLESS_CONSUMER_TAG_BARCODE_MAPPING
barcode_tag_mapping = models.JSONField(
verbose_name=_("Sets the tag barcode mapping"),
null=True,
)
class Meta: class Meta:
verbose_name = _("paperless application settings") verbose_name = _("paperless application settings")

View File

@ -185,11 +185,14 @@ class ProfileSerializer(serializers.ModelSerializer):
class ApplicationConfigurationSerializer(serializers.ModelSerializer): class ApplicationConfigurationSerializer(serializers.ModelSerializer):
user_args = serializers.JSONField(binary=True, allow_null=True) user_args = serializers.JSONField(binary=True, allow_null=True)
barcode_tag_mapping = serializers.JSONField(binary=True, allow_null=True)
def run_validation(self, data): def run_validation(self, data):
# Empty strings treated as None to avoid unexpected behavior # Empty strings treated as None to avoid unexpected behavior
if "user_args" in data and data["user_args"] == "": if "user_args" in data and data["user_args"] == "":
data["user_args"] = None data["user_args"] = None
if "barcode_tag_mapping" in data and data["barcode_tag_mapping"] == "":
data["barcode_tag_mapping"] = None
if "language" in data and data["language"] == "": if "language" in data and data["language"] == "":
data["language"] = None data["language"] = None
return super().run_validation(data) return super().run_validation(data)