Compare commits

..

1 Commits

Author SHA1 Message Date
Antoine Mérino
65aed2405c Documentation: update notes for DB pool size (#11600) 2025-12-30 13:06:21 -08:00
23 changed files with 83 additions and 168 deletions

View File

@@ -12,11 +12,9 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6 uses: actions/checkout@v6
env:
GH_REF: ${{ github.ref }} # sonar rule:githubactions:S7630 - avoid injection
with: with:
token: ${{ secrets.PNGX_BOT_PAT }} token: ${{ secrets.PNGX_BOT_PAT }}
ref: ${{ env.GH_REF }} ref: ${{ github.head_ref }}
- name: Set up Python - name: Set up Python
id: setup-python id: setup-python
uses: actions/setup-python@v6 uses: actions/setup-python@v6

View File

@@ -32,7 +32,7 @@ RUN set -eux \
# Purpose: Installs s6-overlay and rootfs # Purpose: Installs s6-overlay and rootfs
# Comments: # Comments:
# - Don't leave anything extra in here either # - Don't leave anything extra in here either
FROM ghcr.io/astral-sh/uv:0.9.19-python3.12-trixie-slim AS s6-overlay-base FROM ghcr.io/astral-sh/uv:0.9.15-python3.12-trixie-slim AS s6-overlay-base
WORKDIR /usr/src/s6 WORKDIR /usr/src/s6

View File

@@ -170,11 +170,18 @@ Available options are `postgresql` and `mariadb`.
!!! note !!! note
A small pool is typically sufficient — for example, a size of 4. A pool of 8-10 connections per worker is typically sufficient.
Make sure your PostgreSQL server's max_connections setting is large enough to handle: If you encounter error messages such as `couldn't get a connection`
```(Paperless workers + Celery workers) × pool size + safety margin``` or database connection timeouts, you probably need to increase the pool size.
For example, with 4 Paperless workers and 2 Celery workers, and a pool size of 4:
(4 + 2) × 4 + 10 = 34 connections required. !!! warning
Make sure your PostgreSQL `max_connections` setting is large enough to handle the connection pools:
`(NB_PAPERLESS_WORKERS + NB_CELERY_WORKERS) × POOL_SIZE + SAFETY_MARGIN`. For example, with
4 Paperless workers and 2 Celery workers, and a pool size of 8:``(4 + 2) × 8 + 10 = 58`,
so `max_connections = 60` (or even more) is appropriate.
This assumes only Paperless-ngx connects to your PostgreSQL instance. If you have other applications,
you should increase `max_connections` accordingly.
#### [`PAPERLESS_DB_READ_CACHE_ENABLED=<bool>`](#PAPERLESS_DB_READ_CACHE_ENABLED) {#PAPERLESS_DB_READ_CACHE_ENABLED} #### [`PAPERLESS_DB_READ_CACHE_ENABLED=<bool>`](#PAPERLESS_DB_READ_CACHE_ENABLED) {#PAPERLESS_DB_READ_CACHE_ENABLED}

View File

@@ -31,7 +31,6 @@
"fi-FI": "src/locale/messages.fi_FI.xlf", "fi-FI": "src/locale/messages.fi_FI.xlf",
"fr-FR": "src/locale/messages.fr_FR.xlf", "fr-FR": "src/locale/messages.fr_FR.xlf",
"hu-HU": "src/locale/messages.hu_HU.xlf", "hu-HU": "src/locale/messages.hu_HU.xlf",
"id-ID": "src/locale/messages.id_ID.xlf",
"it-IT": "src/locale/messages.it_IT.xlf", "it-IT": "src/locale/messages.it_IT.xlf",
"ja-JP": "src/locale/messages.ja_JP.xlf", "ja-JP": "src/locale/messages.ja_JP.xlf",
"lb-LU": "src/locale/messages.lb_LU.xlf", "lb-LU": "src/locale/messages.lb_LU.xlf",

View File

@@ -10028,186 +10028,179 @@
<context context-type="linenumber">135</context> <context context-type="linenumber">135</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8312065814232621608" datatype="html">
<source>Indonesian</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">141</context>
</context-group>
</trans-unit>
<trans-unit id="2935232983274991580" datatype="html"> <trans-unit id="2935232983274991580" datatype="html">
<source>Italian</source> <source>Italian</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">147</context> <context context-type="linenumber">141</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="6924606686202701860" datatype="html"> <trans-unit id="6924606686202701860" datatype="html">
<source>Japanese</source> <source>Japanese</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">153</context> <context context-type="linenumber">147</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="6145439649200570157" datatype="html"> <trans-unit id="6145439649200570157" datatype="html">
<source>Korean</source> <source>Korean</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">159</context> <context context-type="linenumber">153</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1334425850005897370" datatype="html"> <trans-unit id="1334425850005897370" datatype="html">
<source>Luxembourgish</source> <source>Luxembourgish</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">165</context> <context context-type="linenumber">159</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="3071065188816255493" datatype="html"> <trans-unit id="3071065188816255493" datatype="html">
<source>Dutch</source> <source>Dutch</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">171</context> <context context-type="linenumber">165</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8069284467804715623" datatype="html"> <trans-unit id="8069284467804715623" datatype="html">
<source>Norwegian</source> <source>Norwegian</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">177</context> <context context-type="linenumber">171</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="4977087909184008115" datatype="html"> <trans-unit id="4977087909184008115" datatype="html">
<source>Persian</source> <source>Persian</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">183</context> <context context-type="linenumber">177</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="792060551707690640" datatype="html"> <trans-unit id="792060551707690640" datatype="html">
<source>Polish</source> <source>Polish</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">189</context> <context context-type="linenumber">183</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="9184513005098760425" datatype="html"> <trans-unit id="9184513005098760425" datatype="html">
<source>Portuguese (Brazil)</source> <source>Portuguese (Brazil)</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">195</context> <context context-type="linenumber">189</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="153799456510623899" datatype="html"> <trans-unit id="153799456510623899" datatype="html">
<source>Portuguese</source> <source>Portuguese</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">201</context> <context context-type="linenumber">195</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8118856427047826368" datatype="html"> <trans-unit id="8118856427047826368" datatype="html">
<source>Romanian</source> <source>Romanian</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">207</context> <context context-type="linenumber">201</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="7137419789978325708" datatype="html"> <trans-unit id="7137419789978325708" datatype="html">
<source>Russian</source> <source>Russian</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">213</context> <context context-type="linenumber">207</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="9102963095355753902" datatype="html"> <trans-unit id="9102963095355753902" datatype="html">
<source>Slovak</source> <source>Slovak</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">219</context> <context context-type="linenumber">213</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="4287008301409320881" datatype="html"> <trans-unit id="4287008301409320881" datatype="html">
<source>Slovenian</source> <source>Slovenian</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">225</context> <context context-type="linenumber">219</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8608389829607915090" datatype="html"> <trans-unit id="8608389829607915090" datatype="html">
<source>Serbian</source> <source>Serbian</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">231</context> <context context-type="linenumber">225</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="499386805970351976" datatype="html"> <trans-unit id="499386805970351976" datatype="html">
<source>Swedish</source> <source>Swedish</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">237</context> <context context-type="linenumber">231</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5682359291233237791" datatype="html"> <trans-unit id="5682359291233237791" datatype="html">
<source>Turkish</source> <source>Turkish</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">243</context> <context context-type="linenumber">237</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="3578644052206125685" datatype="html"> <trans-unit id="3578644052206125685" datatype="html">
<source>Ukrainian</source> <source>Ukrainian</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">249</context> <context context-type="linenumber">243</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="3611216939636790848" datatype="html"> <trans-unit id="3611216939636790848" datatype="html">
<source>Vietnamese</source> <source>Vietnamese</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">255</context> <context context-type="linenumber">249</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="4689443708886954687" datatype="html"> <trans-unit id="4689443708886954687" datatype="html">
<source>Chinese Simplified</source> <source>Chinese Simplified</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">261</context> <context context-type="linenumber">255</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8082606363137705994" datatype="html"> <trans-unit id="8082606363137705994" datatype="html">
<source>Chinese Traditional</source> <source>Chinese Traditional</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">267</context> <context context-type="linenumber">261</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="4912706592792948707" datatype="html"> <trans-unit id="4912706592792948707" datatype="html">
<source>ISO 8601</source> <source>ISO 8601</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">275</context> <context context-type="linenumber">269</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="313643372755303297" datatype="html"> <trans-unit id="313643372755303297" datatype="html">
<source>Successfully completed one-time migratration of settings to the database!</source> <source>Successfully completed one-time migratration of settings to the database!</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">609</context> <context context-type="linenumber">603</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5558341108007064934" datatype="html"> <trans-unit id="5558341108007064934" datatype="html">
<source>Unable to migrate settings to the database, please try saving manually.</source> <source>Unable to migrate settings to the database, please try saving manually.</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">610</context> <context context-type="linenumber">604</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1168781785897678748" datatype="html"> <trans-unit id="1168781785897678748" datatype="html">
<source>You can restart the tour from the settings page.</source> <source>You can restart the tour from the settings page.</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">683</context> <context context-type="linenumber">677</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="3852289441366561594" datatype="html"> <trans-unit id="3852289441366561594" datatype="html">

View File

@@ -28,7 +28,6 @@ import localeFa from '@angular/common/locales/fa'
import localeFi from '@angular/common/locales/fi' import localeFi from '@angular/common/locales/fi'
import localeFr from '@angular/common/locales/fr' import localeFr from '@angular/common/locales/fr'
import localeHu from '@angular/common/locales/hu' import localeHu from '@angular/common/locales/hu'
import localeId from '@angular/common/locales/id'
import localeIt from '@angular/common/locales/it' import localeIt from '@angular/common/locales/it'
import localeJa from '@angular/common/locales/ja' import localeJa from '@angular/common/locales/ja'
import localeKo from '@angular/common/locales/ko' import localeKo from '@angular/common/locales/ko'
@@ -64,7 +63,6 @@ registerLocaleData(localeFa)
registerLocaleData(localeFi) registerLocaleData(localeFi)
registerLocaleData(localeFr) registerLocaleData(localeFr)
registerLocaleData(localeHu) registerLocaleData(localeHu)
registerLocaleData(localeId)
registerLocaleData(localeIt) registerLocaleData(localeIt)
registerLocaleData(localeJa) registerLocaleData(localeJa)
registerLocaleData(localeKo) registerLocaleData(localeKo)

View File

@@ -14,7 +14,7 @@
@if (previewText) { @if (previewText) {
<div class="bg-light p-3 overflow-auto whitespace-preserve" width="100%">{{previewText}}</div> <div class="bg-light p-3 overflow-auto whitespace-preserve" width="100%">{{previewText}}</div>
} @else { } @else {
<object [data]="previewUrl | safeUrl" width="100%" class="bg-light" [class.p-2]="!isPdf"></object> <object [data]="previewURL | safeUrl" width="100%" class="bg-light" [class.p-2]="!isPdf"></object>
} }
} @else { } @else {
@if (requiresPassword) { @if (requiresPassword) {
@@ -24,7 +24,7 @@
} }
@if (!requiresPassword) { @if (!requiresPassword) {
<pdf-viewer <pdf-viewer
[src]="previewUrl" [src]="previewURL"
[original-size]="false" [original-size]="false"
[show-borders]="false" [show-borders]="false"
[show-all]="true" [show-all]="true"

View File

@@ -71,7 +71,7 @@ export class PreviewPopupComponent implements OnDestroy {
return (this.isPdf && this.useNativePdfViewer) || !this.isPdf return (this.isPdf && this.useNativePdfViewer) || !this.isPdf
} }
get previewUrl() { get previewURL() {
return this.documentService.getPreviewUrl(this.document.id) return this.documentService.getPreviewUrl(this.document.id)
} }
@@ -93,7 +93,7 @@ export class PreviewPopupComponent implements OnDestroy {
init() { init() {
if (this.document.mime_type?.includes('text')) { if (this.document.mime_type?.includes('text')) {
this.http this.http
.get(this.previewUrl, { responseType: 'text' }) .get(this.previewURL, { responseType: 'text' })
.pipe(first(), takeUntil(this.unsubscribeNotifier)) .pipe(first(), takeUntil(this.unsubscribeNotifier))
.subscribe({ .subscribe({
next: (res) => { next: (res) => {
@@ -126,6 +126,10 @@ export class PreviewPopupComponent implements OnDestroy {
} }
} }
get previewUrl() {
return this.documentService.getPreviewUrl(this.document.id)
}
mouseEnterPreview() { mouseEnterPreview() {
this.mouseOnPreview = true this.mouseOnPreview = true
if (!this.popover.isOpen()) { if (!this.popover.isOpen()) {

View File

@@ -379,7 +379,7 @@
<ng-template #previewContent> <ng-template #previewContent>
<div class="thumb-preview position-absolute pe-none text-center" [class.fade]="previewLoaded"> <div class="thumb-preview position-absolute pe-none text-center" [class.fade]="previewLoaded">
@if (showThumbnailOverlay) { @if (showThumbnailOverlay) {
<img [src]="thumbUrl" class="mx-auto" [attr.width]="previewZoomScale === 'page-fit' ? 'auto' : '100%'" [attr.height]="previewZoomScale === 'page-fit' ? '100%' : 'auto'" alt="Document loading..." i18n-alt /> <img [src]="thumbUrl | safeUrl" class="mx-auto" [attr.width]="previewZoomScale === 'page-fit' ? 'auto' : '100%'" [attr.height]="previewZoomScale === 'page-fit' ? '100%' : 'auto'" alt="Document loading..." i18n-alt />
} }
<div class="position-absolute top-0 start-0 m-2 p-2 d-flex align-items-center justify-content-center"> <div class="position-absolute top-0 start-0 m-2 p-2 d-flex align-items-center justify-content-center">
<div> <div>
@@ -414,7 +414,7 @@
} }
@case (ContentRenderType.Image) { @case (ContentRenderType.Image) {
<div class="preview-sticky"> <div class="preview-sticky">
<img [src]="previewUrl" width="100%" height="100%" alt="{{title}}" /> <img [src]="previewUrl | safeUrl" width="100%" height="100%" alt="{{title}}" />
</div> </div>
} }
@case (ContentRenderType.TIFF) { @case (ContentRenderType.TIFF) {

View File

@@ -136,12 +136,6 @@ const LANGUAGE_OPTIONS = [
englishName: 'Hungarian', englishName: 'Hungarian',
dateInputFormat: 'yyyy.mm.dd', dateInputFormat: 'yyyy.mm.dd',
}, },
{
code: 'id-id',
name: $localize`Indonesian`,
englishName: 'Indonesian',
dateInputFormat: 'dd-mm-yyyy',
},
{ {
code: 'it-it', code: 'it-it',
name: $localize`Italian`, name: $localize`Italian`,

View File

@@ -171,7 +171,6 @@ import localeFa from '@angular/common/locales/fa'
import localeFi from '@angular/common/locales/fi' import localeFi from '@angular/common/locales/fi'
import localeFr from '@angular/common/locales/fr' import localeFr from '@angular/common/locales/fr'
import localeHu from '@angular/common/locales/hu' import localeHu from '@angular/common/locales/hu'
import localeId from '@angular/common/locales/id'
import localeIt from '@angular/common/locales/it' import localeIt from '@angular/common/locales/it'
import localeJa from '@angular/common/locales/ja' import localeJa from '@angular/common/locales/ja'
import localeKo from '@angular/common/locales/ko' import localeKo from '@angular/common/locales/ko'
@@ -210,7 +209,6 @@ registerLocaleData(localeFa)
registerLocaleData(localeFi) registerLocaleData(localeFi)
registerLocaleData(localeFr) registerLocaleData(localeFr)
registerLocaleData(localeHu) registerLocaleData(localeHu)
registerLocaleData(localeId)
registerLocaleData(localeIt) registerLocaleData(localeIt)
registerLocaleData(localeJa) registerLocaleData(localeJa)
registerLocaleData(localeKo) registerLocaleData(localeKo)

View File

@@ -186,11 +186,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:
self.settings.barcode_enable_asn
and not self.metadata.skip_asn
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

View File

@@ -433,8 +433,6 @@ def merge(
if user is not None: if user is not None:
overrides.owner_id = user.id overrides.owner_id = user.id
# Avoid copying or detecting ASN from merged PDFs to prevent collision
overrides.skip_asn = True
logger.info("Adding merged document to the task queue.") logger.info("Adding merged document to the task queue.")

View File

@@ -696,7 +696,7 @@ class ConsumerPlugin(
pk=self.metadata.storage_path_id, pk=self.metadata.storage_path_id,
) )
if self.metadata.asn is not None and not self.metadata.skip_asn: if self.metadata.asn is not None:
document.archive_serial_number = self.metadata.asn document.archive_serial_number = self.metadata.asn
if self.metadata.owner_id: if self.metadata.owner_id:
@@ -812,8 +812,8 @@ class ConsumerPreflightPlugin(
""" """
Check that if override_asn is given, it is unique and within a valid range Check that if override_asn is given, it is unique and within a valid range
""" """
if self.metadata.skip_asn or self.metadata.asn is None: if self.metadata.asn is None:
# if skip is set or ASN is None # check not necessary in case no ASN gets set
return return
# Validate the range is above zero and less than uint32_t max # Validate the range is above zero and less than uint32_t max
# otherwise, Whoosh can't handle it in the index # otherwise, Whoosh can't handle it in the index

View File

@@ -22,7 +22,7 @@ class DocumentMetadataOverrides:
document_type_id: int | None = None document_type_id: int | None = None
tag_ids: list[int] | None = None tag_ids: list[int] | None = None
storage_path_id: int | None = None storage_path_id: int | None = None
created: datetime.date | None = None created: datetime.datetime | None = None
asn: int | None = None asn: int | None = None
owner_id: int | None = None owner_id: int | None = None
view_users: list[int] | None = None view_users: list[int] | None = None
@@ -30,7 +30,6 @@ class DocumentMetadataOverrides:
change_users: list[int] | None = None change_users: list[int] | None = None
change_groups: list[int] | None = None change_groups: list[int] | None = None
custom_fields: dict | None = None custom_fields: dict | None = None
skip_asn: bool = False
def update(self, other: "DocumentMetadataOverrides") -> "DocumentMetadataOverrides": def update(self, other: "DocumentMetadataOverrides") -> "DocumentMetadataOverrides":
""" """
@@ -50,8 +49,6 @@ class DocumentMetadataOverrides:
self.storage_path_id = other.storage_path_id self.storage_path_id = other.storage_path_id
if other.owner_id is not None: if other.owner_id is not None:
self.owner_id = other.owner_id self.owner_id = other.owner_id
if other.skip_asn:
self.skip_asn = True
# merge # merge
if self.tag_ids is None: if self.tag_ids is None:
@@ -103,7 +100,6 @@ class DocumentMetadataOverrides:
overrides.storage_path_id = doc.storage_path.id if doc.storage_path else None 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.owner_id = doc.owner.id if doc.owner else None
overrides.tag_ids = list(doc.tags.values_list("id", flat=True)) overrides.tag_ids = list(doc.tags.values_list("id", flat=True))
overrides.created = doc.created
overrides.view_users = list( overrides.view_users = list(
get_users_with_perms( get_users_with_perms(

View File

@@ -18,8 +18,6 @@ from django.core.exceptions import ValidationError
from django.core.validators import DecimalValidator from django.core.validators import DecimalValidator
from django.core.validators import EmailValidator from django.core.validators import EmailValidator
from django.core.validators import MaxLengthValidator 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 RegexValidator
from django.core.validators import integer_validator from django.core.validators import integer_validator
from django.db.models import Count from django.db.models import Count
@@ -877,13 +875,6 @@ class CustomFieldInstanceSerializer(serializers.ModelSerializer):
uri_validator(data["value"]) uri_validator(data["value"])
elif field.data_type == CustomField.FieldDataType.INT: elif field.data_type == CustomField.FieldDataType.INT:
integer_validator(data["value"]) 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 ( elif (
field.data_type == CustomField.FieldDataType.MONETARY field.data_type == CustomField.FieldDataType.MONETARY
and data["value"] != "" and data["value"] != ""

View File

@@ -1664,44 +1664,6 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
self.consume_file_mock.assert_not_called() 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): def test_upload_with_webui_source(self):
""" """
GIVEN: A document with a source file GIVEN: A document with a source file

View File

@@ -581,7 +581,7 @@ class TestPDFActions(DirectoriesMixin, TestCase):
- Consume file should be called - Consume file should be called
""" """
doc_ids = [self.doc1.id, self.doc2.id, self.doc3.id] doc_ids = [self.doc1.id, self.doc2.id, self.doc3.id]
metadata_document_id = self.doc2.id metadata_document_id = self.doc1.id
user = User.objects.create(username="test_user") user = User.objects.create(username="test_user")
result = bulk_edit.merge( result = bulk_edit.merge(
@@ -602,14 +602,11 @@ class TestPDFActions(DirectoriesMixin, TestCase):
expected_filename, expected_filename,
) )
self.assertEqual(consume_file_args[1].title, None) self.assertEqual(consume_file_args[1].title, None)
self.assertTrue(consume_file_args[1].skip_asn)
# With metadata_document_id overrides # With metadata_document_id overrides
result = bulk_edit.merge(doc_ids, metadata_document_id=metadata_document_id) result = bulk_edit.merge(doc_ids, metadata_document_id=metadata_document_id)
consume_file_args, _ = mock_consume_file.call_args consume_file_args, _ = mock_consume_file.call_args
self.assertEqual(consume_file_args[1].title, "B (merged)") self.assertEqual(consume_file_args[1].title, "A (merged)")
self.assertEqual(consume_file_args[1].created, self.doc2.created)
self.assertTrue(consume_file_args[1].skip_asn)
self.assertEqual(result, "OK") self.assertEqual(result, "OK")
@@ -650,7 +647,6 @@ class TestPDFActions(DirectoriesMixin, TestCase):
expected_filename, expected_filename,
) )
self.assertEqual(consume_file_args[1].title, None) self.assertEqual(consume_file_args[1].title, None)
self.assertTrue(consume_file_args[1].skip_asn)
delete_documents_args, _ = mock_delete_documents.call_args delete_documents_args, _ = mock_delete_documents.call_args
self.assertEqual( self.assertEqual(

View File

@@ -412,14 +412,6 @@ class TestConsumer(
self.assertEqual(document.archive_serial_number, 123) self.assertEqual(document.archive_serial_number, 123)
self._assert_first_last_send_progress() self._assert_first_last_send_progress()
def testMetadataOverridesSkipAsnPropagation(self):
overrides = DocumentMetadataOverrides()
incoming = DocumentMetadataOverrides(skip_asn=True)
overrides.update(incoming)
self.assertTrue(overrides.skip_asn)
def testOverrideTitlePlaceholders(self): def testOverrideTitlePlaceholders(self):
c = Correspondent.objects.create(name="Correspondent Name") c = Correspondent.objects.create(name="Correspondent Name")
dt = DocumentType.objects.create(name="DocType Name") dt = DocumentType.objects.create(name="DocType Name")

View File

@@ -708,7 +708,6 @@ class DocumentViewSet(
"title", "title",
"correspondent__name", "correspondent__name",
"document_type__name", "document_type__name",
"storage_path__name",
"created", "created",
"modified", "modified",
"added", "added",

View File

@@ -2,7 +2,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-12-29 14:49+0000\n" "POT-Creation-Date: 2025-12-12 17:41+0000\n"
"PO-Revision-Date: 2022-02-17 04:17\n" "PO-Revision-Date: 2022-02-17 04:17\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: English\n" "Language-Team: English\n"
@@ -1219,35 +1219,35 @@ msgstr ""
msgid "workflow runs" msgid "workflow runs"
msgstr "" msgstr ""
#: documents/serialisers.py:642 #: documents/serialisers.py:640
msgid "Invalid color." msgid "Invalid color."
msgstr "" msgstr ""
#: documents/serialisers.py:1835 #: documents/serialisers.py:1826
#, python-format #, python-format
msgid "File type %(type)s not supported" msgid "File type %(type)s not supported"
msgstr "" msgstr ""
#: documents/serialisers.py:1879 #: documents/serialisers.py:1870
#, python-format #, python-format
msgid "Custom field id must be an integer: %(id)s" msgid "Custom field id must be an integer: %(id)s"
msgstr "" msgstr ""
#: documents/serialisers.py:1886 #: documents/serialisers.py:1877
#, python-format #, python-format
msgid "Custom field with id %(id)s does not exist" msgid "Custom field with id %(id)s does not exist"
msgstr "" msgstr ""
#: documents/serialisers.py:1903 documents/serialisers.py:1913 #: documents/serialisers.py:1894 documents/serialisers.py:1904
msgid "" msgid ""
"Custom fields must be a list of integers or an object mapping ids to values." "Custom fields must be a list of integers or an object mapping ids to values."
msgstr "" msgstr ""
#: documents/serialisers.py:1908 #: documents/serialisers.py:1899
msgid "Some custom fields don't exist or were specified twice." msgid "Some custom fields don't exist or were specified twice."
msgstr "" msgstr ""
#: documents/serialisers.py:2023 #: documents/serialisers.py:2014
msgid "Invalid variable detected." msgid "Invalid variable detected."
msgstr "" msgstr ""
@@ -1767,86 +1767,82 @@ msgid "Hungarian"
msgstr "" msgstr ""
#: paperless/settings.py:789 #: paperless/settings.py:789
msgid "Indonesian"
msgstr ""
#: paperless/settings.py:790
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: paperless/settings.py:791 #: paperless/settings.py:790
msgid "Japanese" msgid "Japanese"
msgstr "" msgstr ""
#: paperless/settings.py:792 #: paperless/settings.py:791
msgid "Korean" msgid "Korean"
msgstr "" msgstr ""
#: paperless/settings.py:793 #: paperless/settings.py:792
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "" msgstr ""
#: paperless/settings.py:794 #: paperless/settings.py:793
msgid "Norwegian" msgid "Norwegian"
msgstr "" msgstr ""
#: paperless/settings.py:795 #: paperless/settings.py:794
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: paperless/settings.py:796 #: paperless/settings.py:795
msgid "Polish" msgid "Polish"
msgstr "" msgstr ""
#: paperless/settings.py:797 #: paperless/settings.py:796
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "" msgstr ""
#: paperless/settings.py:798 #: paperless/settings.py:797
msgid "Portuguese" msgid "Portuguese"
msgstr "" msgstr ""
#: paperless/settings.py:799 #: paperless/settings.py:798
msgid "Romanian" msgid "Romanian"
msgstr "" msgstr ""
#: paperless/settings.py:800 #: paperless/settings.py:799
msgid "Russian" msgid "Russian"
msgstr "" msgstr ""
#: paperless/settings.py:801 #: paperless/settings.py:800
msgid "Slovak" msgid "Slovak"
msgstr "" msgstr ""
#: paperless/settings.py:802 #: paperless/settings.py:801
msgid "Slovenian" msgid "Slovenian"
msgstr "" msgstr ""
#: paperless/settings.py:803 #: paperless/settings.py:802
msgid "Serbian" msgid "Serbian"
msgstr "" msgstr ""
#: paperless/settings.py:804 #: paperless/settings.py:803
msgid "Swedish" msgid "Swedish"
msgstr "" msgstr ""
#: paperless/settings.py:805 #: paperless/settings.py:804
msgid "Turkish" msgid "Turkish"
msgstr "" msgstr ""
#: paperless/settings.py:806 #: paperless/settings.py:805
msgid "Ukrainian" msgid "Ukrainian"
msgstr "" msgstr ""
#: paperless/settings.py:807 #: paperless/settings.py:806
msgid "Vietnamese" msgid "Vietnamese"
msgstr "" msgstr ""
#: paperless/settings.py:808 #: paperless/settings.py:807
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "" msgstr ""
#: paperless/settings.py:809 #: paperless/settings.py:808
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "" msgstr ""

View File

@@ -786,7 +786,6 @@ LANGUAGES = [
("fi-fi", _("Finnish")), ("fi-fi", _("Finnish")),
("fr-fr", _("French")), ("fr-fr", _("French")),
("hu-hu", _("Hungarian")), ("hu-hu", _("Hungarian")),
("id-id", _("Indonesian")),
("it-it", _("Italian")), ("it-it", _("Italian")),
("ja-jp", _("Japanese")), ("ja-jp", _("Japanese")),
("ko-kr", _("Korean")), ("ko-kr", _("Korean")),

View File

@@ -1108,7 +1108,6 @@ class TestMail(
self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 2) self.assertEqual(len(self.mailMocker.bogus_mailbox.messages), 2)
self.assertEqual(len(self.mailMocker.bogus_mailbox.messages_spam), 1) self.assertEqual(len(self.mailMocker.bogus_mailbox.messages_spam), 1)
@pytest.mark.flaky(reruns=4)
def test_error_skip_rule(self): def test_error_skip_rule(self):
account = MailAccount.objects.create( account = MailAccount.objects.create(
name="test2", name="test2",