diff --git a/.github/workflows/ci-backend.yml b/.github/workflows/ci-backend.yml
index 85d1fe3a9..0f05d1c94 100644
--- a/.github/workflows/ci-backend.yml
+++ b/.github/workflows/ci-backend.yml
@@ -129,6 +129,7 @@ jobs:
run: |
uv pip list
- name: Check typing (pyrefly)
+ continue-on-error: true
run: |
uv run pyrefly \
check \
@@ -143,6 +144,7 @@ jobs:
${{ runner.os }}-mypy-py${{ env.DEFAULT_PYTHON }}-
${{ runner.os }}-mypy-
- name: Check typing (mypy)
+ continue-on-error: true
run: |
uv run mypy \
--show-error-codes \
diff --git a/.mypy-baseline.txt b/.mypy-baseline.txt
index a63eed9ac..0c5bf368f 100644
--- a/.mypy-baseline.txt
+++ b/.mypy-baseline.txt
@@ -691,15 +691,11 @@ src/documents/signals/handlers.py:0: error: Function is missing a type annotatio
src/documents/signals/handlers.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/signals/handlers.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/documents/signals/handlers.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
-src/documents/signals/handlers.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
-src/documents/signals/handlers.py:0: error: Incompatible return value type (got "tuple[DocumentMetadataOverrides | None, str]", expected "tuple[DocumentMetadataOverrides, str] | None") [return-value]
src/documents/signals/handlers.py:0: error: Incompatible types in assignment (expression has type "list[Tag]", variable has type "set[Tag]") [assignment]
src/documents/signals/handlers.py:0: error: Incompatible types in assignment (expression has type "tuple[Any, Any, Any]", variable has type "tuple[Any, Any]") [assignment]
-src/documents/signals/handlers.py:0: error: Item "ConsumableDocument" of "Document | ConsumableDocument" has no attribute "refresh_from_db" [union-attr]
src/documents/signals/handlers.py:0: error: Item "ConsumableDocument" of "Document | ConsumableDocument" has no attribute "save" [union-attr]
src/documents/signals/handlers.py:0: error: Item "ConsumableDocument" of "Document | ConsumableDocument" has no attribute "source_path" [union-attr]
src/documents/signals/handlers.py:0: error: Item "ConsumableDocument" of "Document | ConsumableDocument" has no attribute "tags" [union-attr]
-src/documents/signals/handlers.py:0: error: Item "ConsumableDocument" of "Document | ConsumableDocument" has no attribute "tags" [union-attr]
src/documents/signals/handlers.py:0: error: Item "ConsumableDocument" of "Document | ConsumableDocument" has no attribute "title" [union-attr]
src/documents/signals/handlers.py:0: error: Item "None" of "Any | None" has no attribute "get" [union-attr]
src/documents/signals/handlers.py:0: error: Item "None" of "Any | None" has no attribute "get" [union-attr]
@@ -2179,34 +2175,34 @@ src/paperless_mail/tests/test_mail.py:0: error: "MailMessage" has no attribute "
src/paperless_mail/tests/test_mail.py:0: error: "MailMessage" has no attribute "flagged" [attr-defined]
src/paperless_mail/tests/test_mail.py:0: error: "MailMessage" has no attribute "seen" [attr-defined]
src/paperless_mail/tests/test_mail.py:0: error: "MailMessage" has no attribute "seen" [attr-defined]
-src/paperless_mail/tests/test_mail.py:0: error: "type[att@480]" has no attribute "filename" [attr-defined]
-src/paperless_mail/tests/test_mail.py:0: error: "type[message2@426]" has no attribute "from_" [attr-defined]
-src/paperless_mail/tests/test_mail.py:0: error: "type[message2@426]" has no attribute "from_" [attr-defined]
-src/paperless_mail/tests/test_mail.py:0: error: "type[message2@426]" has no attribute "from_values" [attr-defined]
-src/paperless_mail/tests/test_mail.py:0: error: "type[message@419]" has no attribute "from_" [attr-defined]
-src/paperless_mail/tests/test_mail.py:0: error: "type[message@419]" has no attribute "from_values" [attr-defined]
-src/paperless_mail/tests/test_mail.py:0: error: "type[message@478]" has no attribute "subject" [attr-defined]
-src/paperless_mail/tests/test_mail.py:0: error: "type[message@531]" has no attribute "attachments" [attr-defined]
+src/paperless_mail/tests/test_mail.py:0: error: "type[att@481]" has no attribute "filename" [attr-defined]
+src/paperless_mail/tests/test_mail.py:0: error: "type[message2@427]" has no attribute "from_" [attr-defined]
+src/paperless_mail/tests/test_mail.py:0: error: "type[message2@427]" has no attribute "from_" [attr-defined]
+src/paperless_mail/tests/test_mail.py:0: error: "type[message2@427]" has no attribute "from_values" [attr-defined]
+src/paperless_mail/tests/test_mail.py:0: error: "type[message@420]" has no attribute "from_" [attr-defined]
+src/paperless_mail/tests/test_mail.py:0: error: "type[message@420]" has no attribute "from_values" [attr-defined]
+src/paperless_mail/tests/test_mail.py:0: error: "type[message@479]" has no attribute "subject" [attr-defined]
+src/paperless_mail/tests/test_mail.py:0: error: "type[message@532]" has no attribute "attachments" [attr-defined]
src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "MailboxFolderSelectError" has incompatible type "None"; expected "tuple[Any, ...]" [arg-type]
src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "MailboxFolderSelectError" has incompatible type "None"; expected "tuple[Any, ...]" [arg-type]
src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "MailboxLoginError" has incompatible type "str"; expected "tuple[Any, ...]" [arg-type]
src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "MailboxLoginError" has incompatible type "str"; expected "tuple[Any, ...]" [arg-type]
src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "MailboxLoginError" has incompatible type "str"; expected "tuple[Any, ...]" [arg-type]
src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "MailboxLoginError" has incompatible type "str"; expected "tuple[Any, ...]" [arg-type]
-src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "_get_correspondent" of "MailAccountHandler" has incompatible type "type[message2@426]"; expected "MailMessage" [arg-type]
-src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "_get_correspondent" of "MailAccountHandler" has incompatible type "type[message2@426]"; expected "MailMessage" [arg-type]
-src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "_get_correspondent" of "MailAccountHandler" has incompatible type "type[message@419]"; expected "MailMessage" [arg-type]
-src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "_get_correspondent" of "MailAccountHandler" has incompatible type "type[message@419]"; expected "MailMessage" [arg-type]
-src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "_get_correspondent" of "MailAccountHandler" has incompatible type "type[message@419]"; expected "MailMessage" [arg-type]
-src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "_get_correspondent" of "MailAccountHandler" has incompatible type "type[message@419]"; expected "MailMessage" [arg-type]
-src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "_get_title" of "MailAccountHandler" has incompatible type "type[message@478]"; expected "MailMessage" [arg-type]
-src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "_get_title" of "MailAccountHandler" has incompatible type "type[message@478]"; expected "MailMessage" [arg-type]
-src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "_get_title" of "MailAccountHandler" has incompatible type "type[message@478]"; expected "MailMessage" [arg-type]
+src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "_get_correspondent" of "MailAccountHandler" has incompatible type "type[message2@427]"; expected "MailMessage" [arg-type]
+src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "_get_correspondent" of "MailAccountHandler" has incompatible type "type[message2@427]"; expected "MailMessage" [arg-type]
+src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "_get_correspondent" of "MailAccountHandler" has incompatible type "type[message@420]"; expected "MailMessage" [arg-type]
+src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "_get_correspondent" of "MailAccountHandler" has incompatible type "type[message@420]"; expected "MailMessage" [arg-type]
+src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "_get_correspondent" of "MailAccountHandler" has incompatible type "type[message@420]"; expected "MailMessage" [arg-type]
+src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "_get_correspondent" of "MailAccountHandler" has incompatible type "type[message@420]"; expected "MailMessage" [arg-type]
+src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "_get_title" of "MailAccountHandler" has incompatible type "type[message@479]"; expected "MailMessage" [arg-type]
+src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "_get_title" of "MailAccountHandler" has incompatible type "type[message@479]"; expected "MailMessage" [arg-type]
+src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "_get_title" of "MailAccountHandler" has incompatible type "type[message@479]"; expected "MailMessage" [arg-type]
src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "filter" has incompatible type "Callable[[Any], bool]"; expected "Callable[[MailMessage], TypeGuard[Never]]" [arg-type]
src/paperless_mail/tests/test_mail.py:0: error: Argument 1 to "filter" has incompatible type "Callable[[Any], bool]"; expected "Callable[[MailMessage], TypeGuard[Never]]" [arg-type]
-src/paperless_mail/tests/test_mail.py:0: error: Argument 2 to "_get_title" of "MailAccountHandler" has incompatible type "type[att@480]"; expected "MailAttachment" [arg-type]
-src/paperless_mail/tests/test_mail.py:0: error: Argument 2 to "_get_title" of "MailAccountHandler" has incompatible type "type[att@480]"; expected "MailAttachment" [arg-type]
-src/paperless_mail/tests/test_mail.py:0: error: Argument 2 to "_get_title" of "MailAccountHandler" has incompatible type "type[att@480]"; expected "MailAttachment" [arg-type]
+src/paperless_mail/tests/test_mail.py:0: error: Argument 2 to "_get_title" of "MailAccountHandler" has incompatible type "type[att@481]"; expected "MailAttachment" [arg-type]
+src/paperless_mail/tests/test_mail.py:0: error: Argument 2 to "_get_title" of "MailAccountHandler" has incompatible type "type[att@481]"; expected "MailAttachment" [arg-type]
+src/paperless_mail/tests/test_mail.py:0: error: Argument 2 to "_get_title" of "MailAccountHandler" has incompatible type "type[att@481]"; expected "MailAttachment" [arg-type]
src/paperless_mail/tests/test_mail.py:0: error: Argument 2 to "assertIn" of "TestCase" has incompatible type "str | None"; expected "Iterable[Any] | Container[Any]" [arg-type]
src/paperless_mail/tests/test_mail.py:0: error: Dict entry 0 has incompatible type "str": "None"; expected "str": "str" [dict-item]
src/paperless_mail/tests/test_mail.py:0: error: Dict entry 0 has incompatible type "str": "int"; expected "str": "str" [dict-item]
diff --git a/docs/advanced_usage.md b/docs/advanced_usage.md
index d932ac586..a293eb91d 100644
--- a/docs/advanced_usage.md
+++ b/docs/advanced_usage.md
@@ -784,9 +784,17 @@ below.
### Document Splitting {#document-splitting}
-When enabled, Paperless will look for a barcode with the configured value and create a new document
-starting from the next page. The page with the barcode on it will _not_ be retained. It
-is expected to be a page existing only for triggering the split.
+If document splitting is enabled, Paperless splits _after_ a separator barcode by default.
+This means:
+
+- any page containing the configured separator barcode starts a new document, starting with the **next** page
+- pages containing the separator barcode are discarded
+
+This is intended for dedicated separator sheets such as PATCH-T pages.
+
+If [`PAPERLESS_CONSUMER_BARCODE_RETAIN_SPLIT_PAGES`](configuration.md#PAPERLESS_CONSUMER_BARCODE_RETAIN_SPLIT_PAGES)
+is enabled, the page containing the separator barcode is retained instead. In this mode,
+each page containing the separator barcode becomes the **first** page of a new document.
### Archive Serial Number Assignment
@@ -795,8 +803,9 @@ archive serial number, allowing quick reference back to the original, paper docu
If document splitting via barcode is also enabled, documents will be split when an ASN
barcode is located. However, differing from the splitting, the page with the
-barcode _will_ be retained. This allows application of a barcode to any page, including
-one which holds data to keep in the document.
+barcode _will_ be retained. Each detected ASN barcode starts a new document _starting with
+that page_. This allows placing ASN barcodes on content pages that should remain part of
+the document.
### Tag Assignment
diff --git a/docs/changelog.md b/docs/changelog.md
index fc66aead3..fb4229e5a 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -1,5 +1,7 @@
# Changelog
+## paperless-ngx 2.20.8
+
## paperless-ngx 2.20.7
### Bug Fixes
diff --git a/docs/usage.md b/docs/usage.md
index 5269f7556..82c2a5ccd 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -574,6 +574,18 @@ For security reasons, webhooks can be limited to specific ports and disallowed f
[configuration settings](configuration.md#workflow-webhooks) to change this behavior. If you are allowing non-admins to create workflows,
you may want to adjust these settings to prevent abuse.
+##### Move to Trash {#workflow-action-move-to-trash}
+
+"Move to Trash" actions move the document to the trash. The document can be restored
+from the trash until the trash is emptied (after the configured delay or manually).
+
+The "Move to Trash" action will always be executed at the end of the workflow run,
+regardless of its position in the action list. After a "Move to Trash" action is executed
+no other workflow will be executed on the document.
+
+If a "Move to Trash" action is executed in a consume pipeline, the consumption
+will be aborted and the file will be deleted.
+
#### Workflow placeholders
Titles and webhook payloads can be generated by workflows using [Jinja templates](https://jinja.palletsprojects.com/en/3.1.x/templates/).
diff --git a/pyproject.toml b/pyproject.toml
index 673fd5c0b..7edde8dcf 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "paperless-ngx"
-version = "2.20.7"
+version = "2.20.8"
description = "A community-supported supercharged document management system: scan, index and archive all your physical documents"
readme = "README.md"
requires-python = ">=3.10"
diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf
index 0d99d1535..22244221a 100644
--- a/src-ui/messages.xlf
+++ b/src-ui/messages.xlf
@@ -1781,11 +1781,15 @@
src/app/components/app-frame/app-frame.component.ts
- 216
+ 156src/app/components/app-frame/app-frame.component.ts
- 241
+ 230
+
+
+ src/app/components/app-frame/app-frame.component.ts
+ 255
@@ -3113,21 +3117,21 @@
Sidebar views updatedsrc/app/components/app-frame/app-frame.component.ts
- 329
+ 343Error updating sidebar viewssrc/app/components/app-frame/app-frame.component.ts
- 332
+ 346An error occurred while saving update checking settings.src/app/components/app-frame/app-frame.component.ts
- 353
+ 367
@@ -5351,6 +5355,13 @@
445
+
+ The document will be moved to the trash at the end of the workflow run.
+
+ src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html
+ 454
+
+ Consume Folder
@@ -5453,109 +5464,124 @@
144
+
+ Move to trash
+
+ src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts
+ 148
+
+
+ src/app/components/document-detail/document-detail.component.ts
+ 1087
+
+
+ src/app/components/document-list/bulk-editor/bulk-editor.component.ts
+ 760
+
+ Has any of these tagssrc/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts
- 213
+ 217Has all of these tagssrc/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts
- 220
+ 224Does not have these tagssrc/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts
- 227
+ 231Has any of these correspondentssrc/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts
- 234
+ 238Has correspondentsrc/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts
- 242
+ 246Does not have correspondentssrc/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts
- 250
+ 254Has document typesrc/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts
- 258
+ 262Has any of these document typessrc/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts
- 266
+ 270Does not have document typessrc/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts
- 274
+ 278Has storage pathsrc/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts
- 282
+ 286Has any of these storage pathssrc/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts
- 290
+ 294Does not have storage pathssrc/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts
- 298
+ 302Matches custom field querysrc/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts
- 306
+ 310Create new workflowsrc/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts
- 535
+ 539Edit workflowsrc/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts
- 539
+ 543
@@ -7769,17 +7795,6 @@
758
-
- Move to trash
-
- src/app/components/document-detail/document-detail.component.ts
- 1087
-
-
- src/app/components/document-list/bulk-editor/bulk-editor.component.ts
- 760
-
- Error deleting document
@@ -8486,7 +8501,7 @@
src/app/components/document-list/document-list.component.ts
- 315
+ 323src/app/components/manage/document-attributes/document-attributes.component.html
@@ -8501,7 +8516,7 @@
src/app/components/document-list/document-list.component.ts
- 308
+ 316src/app/components/manage/document-attributes/document-attributes.component.html
@@ -8767,49 +8782,49 @@
Reset filters / selectionsrc/app/components/document-list/document-list.component.ts
- 296
+ 304Open first [selected] documentsrc/app/components/document-list/document-list.component.ts
- 324
+ 332Previous pagesrc/app/components/document-list/document-list.component.ts
- 340
+ 348Next pagesrc/app/components/document-list/document-list.component.ts
- 352
+ 360View "" saved successfully.src/app/components/document-list/document-list.component.ts
- 385
+ 393Failed to save view "".src/app/components/document-list/document-list.component.ts
- 391
+ 399View "" created successfully.src/app/components/document-list/document-list.component.ts
- 437
+ 445
diff --git a/src-ui/package.json b/src-ui/package.json
index e3528a45a..59d29ca74 100644
--- a/src-ui/package.json
+++ b/src-ui/package.json
@@ -1,6 +1,6 @@
{
"name": "paperless-ngx-ui",
- "version": "2.20.7",
+ "version": "2.20.8",
"scripts": {
"preinstall": "npx only-allow pnpm",
"ng": "ng",
diff --git a/src-ui/src/app/components/app-frame/app-frame.component.spec.ts b/src-ui/src/app/components/app-frame/app-frame.component.spec.ts
index 15151782d..931f46254 100644
--- a/src-ui/src/app/components/app-frame/app-frame.component.spec.ts
+++ b/src-ui/src/app/components/app-frame/app-frame.component.spec.ts
@@ -243,9 +243,19 @@ describe('AppFrameComponent', () => {
it('should support toggling slim sidebar and saving', fakeAsync(() => {
const saveSettingSpy = jest.spyOn(settingsService, 'set')
+ settingsService.set(SETTINGS_KEYS.ATTRIBUTES_SECTIONS_COLLAPSED, [])
expect(component.slimSidebarEnabled).toBeFalsy()
expect(component.slimSidebarAnimating).toBeFalsy()
component.toggleSlimSidebar()
+ const requests = httpTestingController.match(
+ `${environment.apiBaseUrl}ui_settings/`
+ )
+ expect(requests).toHaveLength(1)
+ expect(requests[0].request.body.settings.slim_sidebar).toBe(true)
+ expect(
+ requests[0].request.body.settings.attributes_sections_collapsed
+ ).toEqual(['attributes'])
+ requests[0].flush({ success: true })
expect(component.slimSidebarAnimating).toBeTruthy()
tick(200)
expect(component.slimSidebarAnimating).toBeFalsy()
@@ -254,6 +264,10 @@ describe('AppFrameComponent', () => {
SETTINGS_KEYS.SLIM_SIDEBAR,
true
)
+ expect(saveSettingSpy).toHaveBeenCalledWith(
+ SETTINGS_KEYS.ATTRIBUTES_SECTIONS_COLLAPSED,
+ ['attributes']
+ )
}))
it('should show error on toggle slim sidebar if store settings fails', () => {
diff --git a/src-ui/src/app/components/app-frame/app-frame.component.ts b/src-ui/src/app/components/app-frame/app-frame.component.ts
index a063c5095..5218d829c 100644
--- a/src-ui/src/app/components/app-frame/app-frame.component.ts
+++ b/src-ui/src/app/components/app-frame/app-frame.component.ts
@@ -140,10 +140,24 @@ export class AppFrameComponent
toggleSlimSidebar(): void {
this.slimSidebarAnimating = true
- this.slimSidebarEnabled = !this.slimSidebarEnabled
- if (this.slimSidebarEnabled) {
- this.attributesSectionsCollapsed = true
+ const slimSidebarEnabled = !this.slimSidebarEnabled
+ this.settingsService.set(SETTINGS_KEYS.SLIM_SIDEBAR, slimSidebarEnabled)
+ if (slimSidebarEnabled) {
+ this.settingsService.set(SETTINGS_KEYS.ATTRIBUTES_SECTIONS_COLLAPSED, [
+ CollapsibleSection.ATTRIBUTES,
+ ])
}
+ this.settingsService
+ .storeSettings()
+ .pipe(first())
+ .subscribe({
+ error: (error) => {
+ this.toastService.showError(
+ $localize`An error occurred while saving settings.`
+ )
+ console.warn(error)
+ },
+ })
setTimeout(() => {
this.slimSidebarAnimating = false
}, 200) // slightly longer than css animation for slim sidebar
diff --git a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html
index b83a5b344..51b8a2a5d 100644
--- a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html
+++ b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html
@@ -448,6 +448,13 @@
}
+ @case (WorkflowActionType.MoveToTrash) {
+
+
+
The document will be moved to the trash at the end of the workflow run.