Repository: paperless-ngx/paperless-ngx

Issue:
[BUG] ASN barcodes not detected
### Description

Currently paperless uses pikepdf to convert pdf pages to PIL images, which internally [may produce tif or jpg images](https://github.com/pikepdf/pikepdf/blob/3f37e806b68724aac5af96930e21986536f19bd3/src/pikepdf/models/image.py#L503-L519).
Barcode detection however works best on rasterised images.
Currently, the pages are [only rasterised when pikepdf raises an exception](https://github.com/paperless-ngx/paperless-ngx/blob/main/src/documents/barcodes.py#L230-L241). I don't understanding the details of when pikepdf would do that.
For my documents, pikepdf doesn't seem to raise an exception.
My ASN barcodes are not detected.

For my barcode stickers, it would be beneficial (that is, make it a usable feature for me) for the ASN barcode detection feature to always use rasterisation or at least always rasterise them when that feature is enabled.
I also would love to see the dpi of the rasterisation set to 300 ([default is 150](https://linux.die.net/man/1/pdftoppm)) to improve the chances of detecting the barcode and also [match the dpi value used in the convert call](https://github.com/paperless-ngx/paperless-ngx/blob/454264a87f59a354546f487eba258cb01a364b55/src/documents/parsers.py#L190) - or make that a configuration option. This would mean calling [convert_from_path](https://github.com/paperless-ngx/paperless-ngx/blob/92fb390f7baf5e88290062f31cba0804e4dd5d8e/src/documents/barcodes.py#L202) with an additional parameter `dpi=300`.

### Steps to reproduce

Set PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE to True.
Consume a document with an ASN barcode.
Look at the log to see that there are no lines mentioning detected barcodes.

### Webserver logs

```bash
[2023-01-25 20:28:38,689] [INFO] [paperless.management.consumer] Adding /opt/paperless-ng/consumption/20230125_Scan_002072.pdf to the task queue.

[2023-01-25 20:28:38,978] [DEBUG] [paperless.barcodes] Detected mime type: application/pdf

[2023-01-25 20:28:39,756] [INFO] [paperless.consumer] Consuming 20230125_Scan_002072.pdf

[2023-01-25 20:28:39,758] [DEBUG] [paperless.consumer] Detected mime type: application/pdf

[2023-01-25 20:28:39,768] [DEBUG] [paperless.consumer] Parser: RasterisedDocumentParser

[2023-01-25 20:28:39,780] [DEBUG] [paperless.consumer] Parsing 20230125_Scan_002072.pdf...

[2023-01-25 20:28:40,111] [DEBUG] [paperless.parsing.tesseract] Calling OCRmyPDF with args: {'input_file': PosixPath('/opt/paperless-ng/consumption/20230125_Scan_002072.pdf'), 'output_file': PosixPath('/tmp/paperless/paperless-zu4zp_z0/archive.pdf'), 'use_threads': True, 'jobs': 6, 'language': 'deu', 'output_type': 'pdfa', 'progress_bar': False, 'skip_text': True, 'clean': True, 'deskew': True, 'rotate_pages': True, 'rotate_pages_threshold': 12.0, 'sidecar': PosixPath('/tmp/paperless/paperless-zu4zp_z0/sidecar.txt'), 'optimize': 1}

[2023-01-25 20:29:28,550] [DEBUG] [paperless.parsing.tesseract] Using text from sidecar file

[2023-01-25 20:29:28,552] [DEBUG] [paperless.consumer] Generating thumbnail for 20230125_Scan_002072.pdf...

[2023-01-25 20:29:28,579] [DEBUG] [paperless.parsing] Execute: convert -density 300 -scale 500x5000> -alpha remove -strip -auto-orient /tmp/paperless/paperless-zu4zp_z0/archive.pdf[0] /tmp/paperless/paperless-zu4zp_z0/convert.webp

[2023-01-25 20:29:34,005] [DEBUG] [paperless.consumer] Saving record to database

[2023-01-25 20:29:34,006] [DEBUG] [paperless.consumer] Creation date from parse_date: 2022-07-22 00:00:00+01:00
```


### Browser logs

_No response_

### Paperless-ngx version

1.12.0

### Host OS

Armbian 22.11.4 aarch64

### Installation method

Bare metal

### Browser

_No response_

### Configuration changes

_No response_

### Other

PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE=true


Discussion:
Do you have some documentation or manual that rasterised images work better?  Or that a DPI of 300 is also better?  Having a sample page would be useful too, maybe the ASN isn't getting placed in a image.

The `pikepdf` method is used since it's faster and less memory intensive than converting every page to an image, but if per page actually is better, it could be used only instead.

(Also the default for pdf2image is a [DPI of 200](https://github.com/Belval/pdf2image/blob/d415156659f76f898ff2d5c9e2884212ee7fac76/pdf2image/pdf2image.py#L39), so getting closer.
> Do you have some documentation or manual that rasterised images work better?

When I built my pre-/post-consumption scripts, that was what I read, but I can't find any hard evidence for that claim right now.
Here are some pointers however:
- [Accelerated Shape Detection in Images (W3C)](https://wicg.github.io/shape-detection-api/#image-sources-for-detection) expects a bitmap as input
- [StackOverflow post about jpg vs raster image formats](https://stackoverflow.com/q/1013729) answered in favor of raster image
- [Scanprep](https://github.com/baltpeter/scanprep/blob/4144a729734b45e74a009b20287b179f8b0daf3c/scanprep/scanprep.py#L45) prepares the images as bitmaps before detecting barcodes
- [ZXing](https://github.com/zxing/zxing/blob/dfb06fa33b17a9e68321be151c22846c7b78048f/javase/src/main/java/com/google/zxing/client/j2se/DecodeWorker.java#L136) converts the input to a bitmap before trying to detect barcodes

> Or that a DPI of 300 is also better?

For me, 300 DPI is a good compromise between file size and pdf quality (and scanner speed). So all my scanned inputs use 300 DPI. When playing around with the options when setting up my pre-/post-consumption scripts, setting conversion to 300 DPI helped zbar in recognising my barcodes a lot. I also manually inspected the conversion results (of pdftoppm) and could see a notable improvement for a human eye.

> Having a sample page would be useful too, maybe the ASN isn't getting placed in a image.

I attached a sample page. Note that my prefix is 'O' instead of 'ASN'.
[Test_missed_barcode.pdf](https://github.com/paperless-ngx/paperless-ngx/files/10512793/Test_missed_barcode.pdf)

> The `pikepdf` method is used since it's faster and less memory intensive than converting every page to an image, but if per page actually is better, it could be used only instead.

I see the performance benefit and it may be sufficient for stop pages and potentially larger barcodes. For smaller barcodes (or at least for my case) it doesn't to the trick.

> (Also the default for pdf2image is a [DPI of 200](https://github.com/Belval/pdf2image/blob/d415156659f76f898ff2d5c9e2884212ee7fac76/pdf2image/pdf2image.py#L39), so getting closer.

You are right.

Thank you, I'll have to do some reading.  The SO about JPEG smoothing is interesting indeed.
I've been having issues with barcode detection as well. Sometimes barcodes are detected, sometimes not. So I did some testing.

I've found out that the size of the barcode matters. If it is too small, there's a chance that paperless won't detect it.

At the same time, I have found out that even small flaws in the barcode can lead to paperless not picking it up.

For example, this one was not detected (it's a bit bent and the resolution isn't that good):
![image](https://user-images.githubusercontent.com/17665959/215026572-21b254e8-5734-47f9-a291-494819e66307.png)

But this one (it's exactly the same, just straightened up) was detected just fine:
![image](https://user-images.githubusercontent.com/17665959/215026677-b2ca3c8f-99bc-4405-88cc-8886c15049cc.png)

All in all, assigning ASN using barcodes is a bit unreliable, at least for me. Whether or not this is a problem within paperless, I'm not sure.
Same situation here as well. Left is not detected and right is detected. 
![Greenshot 2023-01-27 13 37 47](https://user-images.githubusercontent.com/110458099/215089350-e37cf515-ba9c-46b5-957f-464829b7ea82.png)

Do keep in mind we're not writing the actual detection code.  It's dependent on libzbar for that, which hasn't seen many updates recently.  

If to you would like to test more directly, so zbarimg uses the same library against images from the command line.  It it doesn't find the barcode, paperless won't either.
I did some testing as @stumpylog suggested. And the results got me a bit confused.

Barcode wasn't detected on any my images attached above:
```
➜  barcodes zbarimg detected.png 
scanned 0 barcode symbols from 1 images in 0 seconds

➜  barcodes zbarimg not-detected.png
scanned 0 barcode symbols from 1 images in 0,01 seconds
```

Then I tested barcodes provided by @muued and @ritschido.
```
➜  barcodes zbarimg muued.png       
CODE-128:O00200
scanned 1 barcode symbols from 1 images in 0,17 seconds

➜  barcodes zbarimg ritschido.png
scanned 0 barcode symbols from 1 images in 0,08 seconds
````

Barcode is detected on the document that @muued provided (I converted it to PNG using `pdftoppm`). But neither barcode was detected on the PNG from @ritschido.

This does not match with what we've seen in Paperless.

I'm not sure how to test this further.

Btw, I get the same results in Python. But that's to be expected if both `zbarimg` and `pyzbar` are using the same library.
```
Python 3.10.9 (main, Dec 19 2022, 17:35:49) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from PIL import Image
>>> from pyzbar import pyzbar
>>> pyzbar.decode(Image.open('not-detected.png'))
[]
>>> pyzbar.decode(Image.open('detected.png'))
[]
>>> pyzbar.decode(Image.open('ritschido.png'))
[]
>>> pyzbar.decode(Image.open('muued.png'))
[Decoded(data=b'O00200', type='CODE128', rect=Rect(left=982, top=768, width=148, height=58), polygon=[Point(x=982, y=777), Point(x=982, y=823), Point(x=1129, y=826), Point(x=1130, y=796), Point(x=1130, y=768), Point(x=983, y=769)], quality=54, orientation='UP')]
```

Resolving patch:
diff --git a/src/documents/barcodes.py b/src/documents/barcodes.py
--- a/src/documents/barcodes.py
+++ b/src/documents/barcodes.py
@@ -4,7 +4,6 @@
 import tempfile
 from dataclasses import dataclass
 from functools import lru_cache
-from math import ceil
 from pathlib import Path
 from typing import List
 from typing import Optional
@@ -12,10 +11,9 @@
 import magic
 from django.conf import settings
 from pdf2image import convert_from_path
+from pdf2image.exceptions import PDFPageCountError
 from pikepdf import Page
-from pikepdf import PasswordError
 from pikepdf import Pdf
-from pikepdf import PdfImage
 from PIL import Image
 from PIL import ImageSequence
 from pyzbar import pyzbar
@@ -154,52 +152,15 @@ def scan_file_for_barcodes(
     (page_number, barcode_text) tuples
     """
 
-    def _pikepdf_barcode_scan(pdf_filepath: str) -> List[Barcode]:
-        detected_barcodes = []
-        with Pdf.open(pdf_filepath) as pdf:
-            for page_num, page in enumerate(pdf.pages):
-                for image_key in page.images:
-                    pdfimage = PdfImage(page.images[image_key])
-
-                    # This type is known to have issues:
-                    # https://github.com/pikepdf/pikepdf/issues/401
-                    if "/CCITTFaxDecode" in pdfimage.filters:
-                        raise BarcodeImageFormatError(
-                            "Unable to decode CCITTFaxDecode images",
-                        )
-
-                    # Not all images can be transcoded to a PIL image, which
-                    # is what pyzbar expects to receive, so this may
-                    # raise an exception, triggering fallback
-                    pillow_img = pdfimage.as_pil_image()
-
-                    # Scale the image down
-                    # See: https://github.com/paperless-ngx/paperless-ngx/issues/2385
-                    # TLDR: zbar has issues with larger images
-                    width, height = pillow_img.size
-                    if width > 1024:
-                        scaler = ceil(width / 1024)
-                        new_width = int(width / scaler)
-                        new_height = int(height / scaler)
-                        pillow_img = pillow_img.resize((new_width, new_height))
-
-                    width, height = pillow_img.size
-                    if height > 2048:
-                        scaler = ceil(height / 2048)
-                        new_width = int(width / scaler)
-                        new_height = int(height / scaler)
-                        pillow_img = pillow_img.resize((new_width, new_height))
-
-                    for barcode_value in barcode_reader(pillow_img):
-                        detected_barcodes.append(Barcode(page_num, barcode_value))
-
-        return detected_barcodes
-
     def _pdf2image_barcode_scan(pdf_filepath: str) -> List[Barcode]:
         detected_barcodes = []
         # use a temporary directory in case the file is too big to handle in memory
         with tempfile.TemporaryDirectory() as path:
-            pages_from_path = convert_from_path(pdf_filepath, output_folder=path)
+            pages_from_path = convert_from_path(
+                pdf_filepath,
+                dpi=300,
+                output_folder=path,
+            )
             for current_page_number, page in enumerate(pages_from_path):
                 for barcode_value in barcode_reader(page):
                     detected_barcodes.append(
@@ -219,27 +180,19 @@ def _pdf2image_barcode_scan(pdf_filepath: str) -> List[Barcode]:
         # Always try pikepdf first, it's usually fine, faster and
         # uses less memory
         try:
-            barcodes = _pikepdf_barcode_scan(pdf_filepath)
+            barcodes = _pdf2image_barcode_scan(pdf_filepath)
         # Password protected files can't be checked
-        except PasswordError as e:
+        # This is the exception raised for those
+        except PDFPageCountError as e:
             logger.warning(
                 f"File is likely password protected, not checking for barcodes: {e}",
             )
-        # Handle pikepdf related image decoding issues with a fallback to page
-        # by page conversion to images in a temporary directory
-        except Exception as e:
+        # This file is really borked, allow the consumption to continue
+        # but it may fail further on
+        except Exception as e:  # pragma: no cover
             logger.warning(
-                f"Falling back to pdf2image because: {e}",
+                f"Exception during barcode scanning: {e}",
             )
-            try:
-                barcodes = _pdf2image_barcode_scan(pdf_filepath)
-            # This file is really borked, allow the consumption to continue
-            # but it may fail further on
-            except Exception as e:  # pragma: no cover
-                logger.warning(
-                    f"Exception during barcode scanning: {e}",
-                )
-
     else:
         logger.warning(
             f"Unsupported file format for barcode reader: {str(mime_type)}",


Test patch:
diff --git a/src/documents/tests/samples/barcodes/barcode-39-PATCHT-distorsion.png b/src/documents/tests/samples/barcodes/barcode-39-PATCHT-distortion.png
similarity index 100%
rename from src/documents/tests/samples/barcodes/barcode-39-PATCHT-distorsion.png
rename to src/documents/tests/samples/barcodes/barcode-39-PATCHT-distortion.png
diff --git a/src/documents/tests/samples/barcodes/barcode-39-PATCHT-distorsion2.png b/src/documents/tests/samples/barcodes/barcode-39-PATCHT-distortion2.png
similarity index 100%
rename from src/documents/tests/samples/barcodes/barcode-39-PATCHT-distorsion2.png
rename to src/documents/tests/samples/barcodes/barcode-39-PATCHT-distortion2.png
diff --git a/src/documents/tests/test_barcodes.py b/src/documents/tests/test_barcodes.py
--- a/src/documents/tests/test_barcodes.py
+++ b/src/documents/tests/test_barcodes.py
@@ -1,9 +1,7 @@
 import os
 import shutil
-import tempfile
 from unittest import mock
 
-import pikepdf
 from django.conf import settings
 from django.test import override_settings
 from django.test import TestCase
@@ -23,13 +21,29 @@ class TestBarcode(DirectoriesMixin, TestCase):
 
     BARCODE_SAMPLE_DIR = os.path.join(SAMPLE_DIR, "barcodes")
 
-    def test_barcode_reader(self):
+    def test_barcode_reader_png(self):
+        """
+        GIVEN:
+            - PNG file with separator barcode
+        WHEN:
+            - Image is scanned for codes
+        THEN:
+            - The barcode is detected
+        """
         test_file = os.path.join(self.BARCODE_SAMPLE_DIR, "barcode-39-PATCHT.png")
         img = Image.open(test_file)
-        separator_barcode = str(settings.CONSUMER_BARCODE_STRING)
+        separator_barcode = settings.CONSUMER_BARCODE_STRING
         self.assertEqual(barcodes.barcode_reader(img), [separator_barcode])
 
-    def test_barcode_reader2(self):
+    def test_barcode_reader_pbm(self):
+        """
+        GIVEN:
+            - Netpbm bitmap file with separator barcode
+        WHEN:
+            - Image is scanned for codes
+        THEN:
+            - The barcode is detected
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "patch-code-t.pbm",
@@ -38,25 +52,49 @@ def test_barcode_reader2(self):
         separator_barcode = str(settings.CONSUMER_BARCODE_STRING)
         self.assertEqual(barcodes.barcode_reader(img), [separator_barcode])
 
-    def test_barcode_reader_distorsion(self):
+    def test_barcode_reader_distortion_scratchy(self):
+        """
+        GIVEN:
+            - Image containing high noise
+        WHEN:
+            - Image is scanned for codes
+        THEN:
+            - The barcode is detected
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
-            "barcode-39-PATCHT-distorsion.png",
+            "barcode-39-PATCHT-distortion.png",
         )
         img = Image.open(test_file)
         separator_barcode = str(settings.CONSUMER_BARCODE_STRING)
         self.assertEqual(barcodes.barcode_reader(img), [separator_barcode])
 
-    def test_barcode_reader_distorsion2(self):
+    def test_barcode_reader_distortion_stretched(self):
+        """
+        GIVEN:
+            - Image with a stretched barcode
+        WHEN:
+            - Image is scanned for codes
+        THEN:
+            - The barcode is detected
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
-            "barcode-39-PATCHT-distorsion2.png",
+            "barcode-39-PATCHT-distortion2.png",
         )
         img = Image.open(test_file)
         separator_barcode = str(settings.CONSUMER_BARCODE_STRING)
         self.assertEqual(barcodes.barcode_reader(img), [separator_barcode])
 
     def test_barcode_reader_unreadable(self):
+        """
+        GIVEN:
+            - Image with a truly unreadable barcode
+        WHEN:
+            - Image is scanned for codes
+        THEN:
+            - No barcode is detected
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "barcode-39-PATCHT-unreadable.png",
@@ -65,6 +103,14 @@ def test_barcode_reader_unreadable(self):
         self.assertEqual(barcodes.barcode_reader(img), [])
 
     def test_barcode_reader_qr(self):
+        """
+        GIVEN:
+            - Image file with QR separator barcode
+        WHEN:
+            - Image is scanned for codes
+        THEN:
+            - The barcode is detected
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "qr-code-PATCHT.png",
@@ -74,6 +120,14 @@ def test_barcode_reader_qr(self):
         self.assertEqual(barcodes.barcode_reader(img), [separator_barcode])
 
     def test_barcode_reader_128(self):
+        """
+        GIVEN:
+            - Image file with 128 style separator barcode
+        WHEN:
+            - Image is scanned for codes
+        THEN:
+            - The barcode is detected
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "barcode-128-PATCHT.png",
@@ -83,11 +137,27 @@ def test_barcode_reader_128(self):
         self.assertEqual(barcodes.barcode_reader(img), [separator_barcode])
 
     def test_barcode_reader_no_barcode(self):
+        """
+        GIVEN:
+            - Image file with no barcode
+        WHEN:
+            - Image is scanned for codes
+        THEN:
+            - No barcode is detected
+        """
         test_file = os.path.join(self.SAMPLE_DIR, "simple.png")
         img = Image.open(test_file)
-        self.assertEqual(barcodes.barcode_reader(img), [])
+        self.assertListEqual(barcodes.barcode_reader(img), [])
 
     def test_barcode_reader_custom_separator(self):
+        """
+        GIVEN:
+            - Image file with custom separator barcode value
+        WHEN:
+            - Image is scanned for codes
+        THEN:
+            - The barcode is detected
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "barcode-39-custom.png",
@@ -96,74 +166,46 @@ def test_barcode_reader_custom_separator(self):
         self.assertEqual(barcodes.barcode_reader(img), ["CUSTOM BARCODE"])
 
     def test_barcode_reader_custom_qr_separator(self):
-        test_file = os.path.join(
-            self.BARCODE_SAMPLE_DIR,
-            "barcode-qr-custom.png",
-        )
-        img = Image.open(test_file)
-        self.assertEqual(barcodes.barcode_reader(img), ["CUSTOM BARCODE"])
-
-    def test_barcode_reader_custom_128_separator(self):
-        test_file = os.path.join(
-            self.BARCODE_SAMPLE_DIR,
-            "barcode-128-custom.png",
-        )
-        img = Image.open(test_file)
-        self.assertEqual(barcodes.barcode_reader(img), ["CUSTOM BARCODE"])
-
-    def test_barcode_reader_asn_normal(self):
         """
         GIVEN:
-            - Image containing standard ASNxxxxx barcode
+            - Image file with custom separator barcode value as a QR code
         WHEN:
-            - Image is scanned for barcodes
+            - Image is scanned for codes
         THEN:
-            - The barcode is located
-            - The barcode value is correct
+            - The barcode is detected
         """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
-            "barcode-39-asn-123.png",
+            "barcode-qr-custom.png",
         )
         img = Image.open(test_file)
-        self.assertEqual(barcodes.barcode_reader(img), ["ASN00123"])
+        self.assertEqual(barcodes.barcode_reader(img), ["CUSTOM BARCODE"])
 
-    def test_barcode_reader_asn_invalid(self):
+    def test_barcode_reader_custom_128_separator(self):
         """
         GIVEN:
-            - Image containing invalid ASNxxxxx barcode
-            - The number portion of the ASN is not a number
+            - Image file with custom separator 128 barcode value
         WHEN:
-            - Image is scanned for barcodes
+            - Image is scanned for codes
         THEN:
-            - The barcode is located
-            - The barcode value is correct
+            - The barcode is detected
         """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
-            "barcode-39-asn-invalid.png",
+            "barcode-128-custom.png",
         )
         img = Image.open(test_file)
-        self.assertEqual(barcodes.barcode_reader(img), ["ASNXYZXYZ"])
+        self.assertEqual(barcodes.barcode_reader(img), ["CUSTOM BARCODE"])
 
-    def test_barcode_reader_asn_custom_prefix(self):
+    def test_get_mime_type(self):
         """
         GIVEN:
-            - Image containing custom prefix barcode
+            -
         WHEN:
-            - Image is scanned for barcodes
+            -
         THEN:
-            - The barcode is located
-            - The barcode value is correct
+            -
         """
-        test_file = os.path.join(
-            self.BARCODE_SAMPLE_DIR,
-            "barcode-39-asn-custom-prefix.png",
-        )
-        img = Image.open(test_file)
-        self.assertEqual(barcodes.barcode_reader(img), ["CUSTOM-PREFIX-00123"])
-
-    def test_get_mime_type(self):
         tiff_file = os.path.join(
             self.SAMPLE_DIR,
             "simple.tiff",
@@ -194,6 +236,14 @@ def test_get_mime_type(self):
         self.assertEqual(barcodes.get_file_mime_type(png_file), "image/png")
 
     def test_convert_from_tiff_to_pdf(self):
+        """
+        GIVEN:
+            -
+        WHEN:
+            -
+        THEN:
+            -
+        """
         test_file = os.path.join(
             os.path.dirname(__file__),
             "samples",
@@ -207,6 +257,14 @@ def test_convert_from_tiff_to_pdf(self):
         self.assertEqual(file_extension, ".pdf")
 
     def test_convert_error_from_pdf_to_pdf(self):
+        """
+        GIVEN:
+            -
+        WHEN:
+            -
+        THEN:
+            -
+        """
         test_file = os.path.join(
             self.SAMPLE_DIR,
             "simple.pdf",
@@ -216,6 +274,14 @@ def test_convert_error_from_pdf_to_pdf(self):
         self.assertIsNone(barcodes.convert_from_tiff_to_pdf(dst))
 
     def test_scan_file_for_separating_barcodes(self):
+        """
+        GIVEN:
+            -
+        WHEN:
+            -
+        THEN:
+            -
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "patch-code-t.pdf",
@@ -231,6 +297,14 @@ def test_scan_file_for_separating_barcodes(self):
         self.assertListEqual(separator_page_numbers, [0])
 
     def test_scan_file_for_separating_barcodes_none_present(self):
+        """
+        GIVEN:
+            -
+        WHEN:
+            -
+        THEN:
+            -
+        """
         test_file = os.path.join(self.SAMPLE_DIR, "simple.pdf")
         doc_barcode_info = barcodes.scan_file_for_barcodes(
             test_file,
@@ -242,7 +316,15 @@ def test_scan_file_for_separating_barcodes_none_present(self):
         self.assertEqual(doc_barcode_info.pdf_path, test_file)
         self.assertListEqual(separator_page_numbers, [])
 
-    def test_scan_file_for_separating_barcodes3(self):
+    def test_scan_file_for_separating_barcodes_middle_page(self):
+        """
+        GIVEN:
+            - PDF file containing a separator on page 1 (zero indexed)
+        WHEN:
+            - File is scanned for barcodes
+        THEN:
+            - Barcode is detected on page 1 (zero indexed)
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "patch-code-t-middle.pdf",
@@ -257,7 +339,15 @@ def test_scan_file_for_separating_barcodes3(self):
         self.assertEqual(doc_barcode_info.pdf_path, test_file)
         self.assertListEqual(separator_page_numbers, [1])
 
-    def test_scan_file_for_separating_barcodes4(self):
+    def test_scan_file_for_separating_barcodes_multiple_pages(self):
+        """
+        GIVEN:
+            - PDF file containing a separator on pages 2 and 5 (zero indexed)
+        WHEN:
+            - File is scanned for barcodes
+        THEN:
+            - Barcode is detected on pages 2 and 5 (zero indexed)
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "several-patcht-codes.pdf",
@@ -272,7 +362,16 @@ def test_scan_file_for_separating_barcodes4(self):
         self.assertEqual(doc_barcode_info.pdf_path, test_file)
         self.assertListEqual(separator_page_numbers, [2, 5])
 
-    def test_scan_file_for_separating_barcodes_upsidedown(self):
+    def test_scan_file_for_separating_barcodes_upside_down(self):
+        """
+        GIVEN:
+            - PDF file containing a separator on page 1 (zero indexed)
+            - The barcode is upside down
+        WHEN:
+            - File is scanned for barcodes
+        THEN:
+            - Barcode is detected on page 1 (zero indexed)
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "patch-code-t-middle_reverse.pdf",
@@ -287,66 +386,6 @@ def test_scan_file_for_separating_barcodes_upsidedown(self):
         self.assertEqual(doc_barcode_info.pdf_path, test_file)
         self.assertListEqual(separator_page_numbers, [1])
 
-    def test_scan_file_for_barcodes_pillow_transcode_error(self):
-        """
-        GIVEN:
-            - A PDF containing an image which cannot be transcoded to a PIL image
-        WHEN:
-            - The image tries to be transcoded to a PIL image, but fails
-        THEN:
-            - The barcode reader is still called
-        """
-
-        def _build_device_n_pdf(self, save_path: str):
-            # Based on the pikepdf tests
-            # https://github.com/pikepdf/pikepdf/blob/abb35ebe17d579d76abe08265e00cf8890a12a95/tests/test_image_access.py
-            pdf = pikepdf.new()
-            pdf.add_blank_page(page_size=(72, 72))
-            imobj = pikepdf.Stream(
-                pdf,
-                bytes(range(0, 256)),
-                BitsPerComponent=8,
-                ColorSpace=pikepdf.Array(
-                    [
-                        pikepdf.Name.DeviceN,
-                        pikepdf.Array([pikepdf.Name.Black]),
-                        pikepdf.Name.DeviceCMYK,
-                        pikepdf.Stream(
-                            pdf,
-                            b"{0 0 0 4 -1 roll}",  # Colorspace conversion function
-                            FunctionType=4,
-                            Domain=[0.0, 1.0],
-                            Range=[0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0],
-                        ),
-                    ],
-                ),
-                Width=16,
-                Height=16,
-                Type=pikepdf.Name.XObject,
-                Subtype=pikepdf.Name.Image,
-            )
-            pim = pikepdf.PdfImage(imobj)
-            self.assertEqual(pim.mode, "DeviceN")
-            self.assertTrue(pim.is_device_n)
-
-            pdf.pages[0].Contents = pikepdf.Stream(pdf, b"72 0 0 72 0 0 cm /Im0 Do")
-            pdf.pages[0].Resources = pikepdf.Dictionary(
-                XObject=pikepdf.Dictionary(Im0=imobj),
-            )
-            pdf.save(save_path)
-
-        with tempfile.NamedTemporaryFile(suffix="pdf") as device_n_pdf:
-            # Build an offending file
-            _build_device_n_pdf(self, str(device_n_pdf.name))
-            with mock.patch("documents.barcodes.barcode_reader") as reader:
-                reader.return_value = list()
-
-                _ = barcodes.scan_file_for_barcodes(
-                    str(device_n_pdf.name),
-                )
-
-                reader.assert_called()
-
     def test_scan_file_for_separating_barcodes_fax_decode(self):
         """
         GIVEN:
@@ -371,6 +410,15 @@ def test_scan_file_for_separating_barcodes_fax_decode(self):
         self.assertListEqual(separator_page_numbers, [1])
 
     def test_scan_file_for_separating_qr_barcodes(self):
+        """
+        GIVEN:
+            - PDF file containing a separator on page 0 (zero indexed)
+            - The barcode is a QR code
+        WHEN:
+            - File is scanned for barcodes
+        THEN:
+            - Barcode is detected on page 0 (zero indexed)
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "patch-code-t-qr.pdf",
@@ -387,6 +435,15 @@ def test_scan_file_for_separating_qr_barcodes(self):
 
     @override_settings(CONSUMER_BARCODE_STRING="CUSTOM BARCODE")
     def test_scan_file_for_separating_custom_barcodes(self):
+        """
+        GIVEN:
+            - PDF file containing a separator on page 0 (zero indexed)
+            - The barcode separation value is customized
+        WHEN:
+            - File is scanned for barcodes
+        THEN:
+            - Barcode is detected on page 0 (zero indexed)
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "barcode-39-custom.pdf",
@@ -403,6 +460,16 @@ def test_scan_file_for_separating_custom_barcodes(self):
 
     @override_settings(CONSUMER_BARCODE_STRING="CUSTOM BARCODE")
     def test_scan_file_for_separating_custom_qr_barcodes(self):
+        """
+        GIVEN:
+            - PDF file containing a separator on page 0 (zero indexed)
+            - The barcode separation value is customized
+            - The barcode is a QR code
+        WHEN:
+            - File is scanned for barcodes
+        THEN:
+            - Barcode is detected on page 0 (zero indexed)
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "barcode-qr-custom.pdf",
@@ -419,6 +486,16 @@ def test_scan_file_for_separating_custom_qr_barcodes(self):
 
     @override_settings(CONSUMER_BARCODE_STRING="CUSTOM BARCODE")
     def test_scan_file_for_separating_custom_128_barcodes(self):
+        """
+        GIVEN:
+            - PDF file containing a separator on page 0 (zero indexed)
+            - The barcode separation value is customized
+            - The barcode is a 128 code
+        WHEN:
+            - File is scanned for barcodes
+        THEN:
+            - Barcode is detected on page 0 (zero indexed)
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "barcode-128-custom.pdf",
@@ -434,6 +511,16 @@ def test_scan_file_for_separating_custom_128_barcodes(self):
         self.assertListEqual(separator_page_numbers, [0])
 
     def test_scan_file_for_separating_wrong_qr_barcodes(self):
+        """
+        GIVEN:
+            - PDF file containing a separator on page 0 (zero indexed)
+            - The barcode value is customized
+            - The separation value is NOT customized
+        WHEN:
+            - File is scanned for barcodes
+        THEN:
+            - No split pages are detected
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "barcode-39-custom.pdf",
@@ -474,13 +561,21 @@ def test_scan_file_for_separating_qr_barcodes(self):
         self.assertListEqual(separator_page_numbers, [1])
 
     def test_separate_pages(self):
+        """
+        GIVEN:
+            - Input PDF 2 pages after separation
+        WHEN:
+            - The input file separated at the barcode
+        THEN:
+            - Two new documents are produced
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "patch-code-t-middle.pdf",
         )
-        pages = barcodes.separate_pages(test_file, [1])
+        documents = barcodes.separate_pages(test_file, [1])
 
-        self.assertEqual(len(pages), 2)
+        self.assertEqual(len(documents), 2)
 
     def test_separate_pages_double_code(self):
         """
@@ -493,8 +588,7 @@ def test_separate_pages_double_code(self):
         """
         test_file = os.path.join(
             os.path.dirname(__file__),
-            "samples",
-            "barcodes",
+            self.BARCODE_SAMPLE_DIR,
             "patch-code-t-double.pdf",
         )
         pages = barcodes.separate_pages(test_file, [1, 2])
@@ -502,6 +596,15 @@ def test_separate_pages_double_code(self):
         self.assertEqual(len(pages), 2)
 
     def test_separate_pages_no_list(self):
+        """
+        GIVEN:
+            - Input file to separate
+        WHEN:
+            - No separation pages are provided
+        THEN:
+            - No new documents are produced
+            - A warning is logged
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "patch-code-t-middle.pdf",
@@ -517,16 +620,32 @@ def test_separate_pages_no_list(self):
             )
 
     def test_save_to_dir(self):
+        """
+        GIVEN:
+            - File to save to a directory
+        WHEN:
+            - The file is saved
+        THEN:
+            - The file exists
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "patch-code-t.pdf",
         )
-        tempdir = tempfile.mkdtemp(prefix="paperless-", dir=settings.SCRATCH_DIR)
-        barcodes.save_to_dir(test_file, target_dir=tempdir)
-        target_file = os.path.join(tempdir, "patch-code-t.pdf")
+        barcodes.save_to_dir(test_file, target_dir=settings.SCRATCH_DIR)
+        target_file = os.path.join(settings.SCRATCH_DIR, "patch-code-t.pdf")
         self.assertTrue(os.path.isfile(target_file))
 
-    def test_save_to_dir2(self):
+    def test_save_to_dir_not_existing(self):
+        """
+        GIVEN:
+            - File to save to a directory
+            - The directory doesn't exist
+        WHEN:
+            - The file is saved
+        THEN:
+            - The file exists
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "patch-code-t.pdf",
@@ -534,32 +653,51 @@ def test_save_to_dir2(self):
         nonexistingdir = "/nowhere"
         if os.path.isdir(nonexistingdir):
             self.fail("non-existing dir exists")
-        else:
-            with self.assertLogs("paperless.barcodes", level="WARNING") as cm:
-                barcodes.save_to_dir(test_file, target_dir=nonexistingdir)
-            self.assertEqual(
-                cm.output,
-                [
-                    f"WARNING:paperless.barcodes:{str(test_file)} or {str(nonexistingdir)} don't exist.",
-                ],
-            )
 
-    def test_save_to_dir3(self):
+        with self.assertLogs("paperless.barcodes", level="WARNING") as cm:
+            barcodes.save_to_dir(test_file, target_dir=nonexistingdir)
+        self.assertEqual(
+            cm.output,
+            [
+                f"WARNING:paperless.barcodes:{str(test_file)} or {str(nonexistingdir)} don't exist.",
+            ],
+        )
+
+    def test_save_to_dir_given_name(self):
+        """
+        GIVEN:
+            - File to save to a directory
+            - There is a name override
+        WHEN:
+            - The file is saved
+        THEN:
+            - The file exists
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "patch-code-t.pdf",
         )
-        tempdir = tempfile.mkdtemp(prefix="paperless-", dir=settings.SCRATCH_DIR)
-        barcodes.save_to_dir(test_file, newname="newname.pdf", target_dir=tempdir)
-        target_file = os.path.join(tempdir, "newname.pdf")
+        barcodes.save_to_dir(
+            test_file,
+            newname="newname.pdf",
+            target_dir=settings.SCRATCH_DIR,
+        )
+        target_file = os.path.join(settings.SCRATCH_DIR, "newname.pdf")
         self.assertTrue(os.path.isfile(target_file))
 
     def test_barcode_splitter(self):
+        """
+        GIVEN:
+            - Input file containing barcodes
+        WHEN:
+            - Input file is split on barcodes
+        THEN:
+            - Correct number of files produced
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "patch-code-t-middle.pdf",
         )
-        tempdir = tempfile.mkdtemp(prefix="paperless-", dir=settings.SCRATCH_DIR)
 
         doc_barcode_info = barcodes.scan_file_for_barcodes(
             test_file,
@@ -572,18 +710,33 @@ def test_barcode_splitter(self):
         self.assertTrue(len(separator_page_numbers) > 0)
 
         document_list = barcodes.separate_pages(test_file, separator_page_numbers)
-        self.assertTrue(document_list)
+        self.assertGreater(len(document_list), 0)
+
         for document in document_list:
-            barcodes.save_to_dir(document, target_dir=tempdir)
+            barcodes.save_to_dir(document, target_dir=settings.SCRATCH_DIR)
 
-        target_file1 = os.path.join(tempdir, "patch-code-t-middle_document_0.pdf")
-        target_file2 = os.path.join(tempdir, "patch-code-t-middle_document_1.pdf")
+        target_file1 = os.path.join(
+            settings.SCRATCH_DIR,
+            "patch-code-t-middle_document_0.pdf",
+        )
+        target_file2 = os.path.join(
+            settings.SCRATCH_DIR,
+            "patch-code-t-middle_document_1.pdf",
+        )
 
         self.assertTrue(os.path.isfile(target_file1))
         self.assertTrue(os.path.isfile(target_file2))
 
     @override_settings(CONSUMER_ENABLE_BARCODES=True)
     def test_consume_barcode_file(self):
+        """
+        GIVEN:
+            - Input file with barcodes given to consume task
+        WHEN:
+            - Consume task returns
+        THEN:
+            - The file was split
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "patch-code-t-middle.pdf",
@@ -600,6 +753,14 @@ def test_consume_barcode_file(self):
         CONSUMER_BARCODE_TIFF_SUPPORT=True,
     )
     def test_consume_barcode_tiff_file(self):
+        """
+        GIVEN:
+            - TIFF image containing barcodes
+        WHEN:
+            - Consume task returns
+        THEN:
+            - The file was split
+        """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "patch-code-t-middle.tiff",
@@ -617,11 +778,13 @@ def test_consume_barcode_tiff_file(self):
     @mock.patch("documents.consumer.Consumer.try_consume_file")
     def test_consume_barcode_unsupported_jpg_file(self, m):
         """
-        This test assumes barcode and TIFF support are enabled and
-        the user uploads an unsupported image file (e.g. jpg)
-
-        The function shouldn't try to scan for separating barcodes
-        and continue archiving the file as is.
+        GIVEN:
+            - JPEG image as input
+        WHEN:
+            - Consume task returns
+        THEN:
+            - Barcode reader reported warning
+            - Consumption continued with the file
         """
         test_file = os.path.join(
             self.SAMPLE_DIR,
@@ -629,8 +792,10 @@ def test_consume_barcode_unsupported_jpg_file(self, m):
         )
         dst = os.path.join(settings.SCRATCH_DIR, "simple.jpg")
         shutil.copy(test_file, dst)
+
         with self.assertLogs("paperless.barcodes", level="WARNING") as cm:
             self.assertIn("Success", tasks.consume_file(dst))
+
         self.assertListEqual(
             cm.output,
             [
@@ -652,8 +817,13 @@ def test_consume_barcode_unsupported_jpg_file(self, m):
     )
     def test_consume_barcode_supported_no_extension_file(self):
         """
-        This test assumes barcode and TIFF support are enabled and
-        the user uploads a supported image file, but without extension
+        GIVEN:
+            - TIFF image containing barcodes
+            - TIFF file is given without extension
+        WHEN:
+            - Consume task returns
+        THEN:
+            - The file was split
         """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
@@ -669,16 +839,20 @@ def test_scan_file_for_separating_barcodes_password(self):
         """
         GIVEN:
             - Password protected PDF
-            - pikepdf based scanning
         WHEN:
             - File is scanned for barcode
         THEN:
-            - Scanning handles the exception without exception
+            - Scanning handles the exception without crashing
         """
         test_file = os.path.join(self.SAMPLE_DIR, "password-is-test.pdf")
-        doc_barcode_info = barcodes.scan_file_for_barcodes(
-            test_file,
-        )
+        with self.assertLogs("paperless.barcodes", level="WARNING") as cm:
+            doc_barcode_info = barcodes.scan_file_for_barcodes(
+                test_file,
+            )
+            warning = cm.output[0]
+            expected_str = "WARNING:paperless.barcodes:File is likely password protected, not checking for barcodes"
+            self.assertTrue(warning.startswith(expected_str))
+
         separator_page_numbers = barcodes.get_separating_barcodes(
             doc_barcode_info.barcodes,
         )
@@ -686,41 +860,83 @@ def test_scan_file_for_separating_barcodes_password(self):
         self.assertEqual(doc_barcode_info.pdf_path, test_file)
         self.assertListEqual(separator_page_numbers, [])
 
-    def test_scan_file_for_asn_barcode(self):
+
+class TestAsnBarcodes(DirectoriesMixin, TestCase):
+
+    SAMPLE_DIR = os.path.join(
+        os.path.dirname(__file__),
+        "samples",
+    )
+
+    BARCODE_SAMPLE_DIR = os.path.join(SAMPLE_DIR, "barcodes")
+
+    def test_barcode_reader_asn_normal(self):
         """
         GIVEN:
-            - PDF containing an ASN barcode
-            - The ASN value is 123
+            - Image containing standard ASNxxxxx barcode
         WHEN:
-            - File is scanned for barcodes
+            - Image is scanned for barcodes
         THEN:
-            - The ASN is located
-            - The ASN integer value is correct
+            - The barcode is located
+            - The barcode value is correct
         """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
-            "barcode-39-asn-123.pdf",
+            "barcode-39-asn-123.png",
         )
-        doc_barcode_info = barcodes.scan_file_for_barcodes(
-            test_file,
+        img = Image.open(test_file)
+        self.assertEqual(barcodes.barcode_reader(img), ["ASN00123"])
+
+    def test_barcode_reader_asn_invalid(self):
+        """
+        GIVEN:
+            - Image containing invalid ASNxxxxx barcode
+            - The number portion of the ASN is not a number
+        WHEN:
+            - Image is scanned for barcodes
+        THEN:
+            - The barcode is located
+            - The barcode value is correct
+        """
+        test_file = os.path.join(
+            self.BARCODE_SAMPLE_DIR,
+            "barcode-39-asn-invalid.png",
         )
-        asn = barcodes.get_asn_from_barcodes(doc_barcode_info.barcodes)
+        img = Image.open(test_file)
+        self.assertEqual(barcodes.barcode_reader(img), ["ASNXYZXYZ"])
 
-        self.assertEqual(doc_barcode_info.pdf_path, test_file)
-        self.assertEqual(asn, 123)
+    def test_barcode_reader_asn_custom_prefix(self):
+        """
+        GIVEN:
+            - Image containing custom prefix barcode
+        WHEN:
+            - Image is scanned for barcodes
+        THEN:
+            - The barcode is located
+            - The barcode value is correct
+        """
+        test_file = os.path.join(
+            self.BARCODE_SAMPLE_DIR,
+            "barcode-39-asn-custom-prefix.png",
+        )
+        img = Image.open(test_file)
+        self.assertEqual(barcodes.barcode_reader(img), ["CUSTOM-PREFIX-00123"])
 
-    def test_scan_file_for_asn_not_existing(self):
+    @override_settings(CONSUMER_ASN_BARCODE_PREFIX="CUSTOM-PREFIX-")
+    def test_scan_file_for_asn_custom_prefix(self):
         """
         GIVEN:
-            - PDF without an ASN barcode
+            - PDF containing an ASN barcode with custom prefix
+            - The ASN value is 123
         WHEN:
             - File is scanned for barcodes
         THEN:
-            - No ASN is retrieved from the document
+            - The ASN is located
+            - The ASN integer value is correct
         """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
-            "patch-code-t.pdf",
+            "barcode-39-asn-custom-prefix.pdf",
         )
         doc_barcode_info = barcodes.scan_file_for_barcodes(
             test_file,
@@ -728,7 +944,7 @@ def test_scan_file_for_asn_not_existing(self):
         asn = barcodes.get_asn_from_barcodes(doc_barcode_info.barcodes)
 
         self.assertEqual(doc_barcode_info.pdf_path, test_file)
-        self.assertEqual(asn, None)
+        self.assertEqual(asn, 123)
 
     def test_scan_file_for_asn_barcode_invalid(self):
         """
@@ -754,32 +970,35 @@ def test_scan_file_for_asn_barcode_invalid(self):
         self.assertEqual(doc_barcode_info.pdf_path, test_file)
         self.assertEqual(asn, None)
 
-    @override_settings(CONSUMER_ASN_BARCODE_PREFIX="CUSTOM-PREFIX-")
-    def test_scan_file_for_asn_custom_prefix(self):
+    @override_settings(CONSUMER_ENABLE_ASN_BARCODE=True)
+    def test_consume_barcode_file_asn_assignment(self):
         """
         GIVEN:
-            - PDF containing an ASN barcode with custom prefix
+            - PDF containing an ASN barcode
             - The ASN value is 123
         WHEN:
             - File is scanned for barcodes
         THEN:
             - The ASN is located
             - The ASN integer value is correct
+            - The ASN is provided as the override value to the consumer
         """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
-            "barcode-39-asn-custom-prefix.pdf",
-        )
-        doc_barcode_info = barcodes.scan_file_for_barcodes(
-            test_file,
+            "barcode-39-asn-123.pdf",
         )
-        asn = barcodes.get_asn_from_barcodes(doc_barcode_info.barcodes)
 
-        self.assertEqual(doc_barcode_info.pdf_path, test_file)
-        self.assertEqual(asn, 123)
+        dst = os.path.join(settings.SCRATCH_DIR, "barcode-39-asn-123.pdf")
+        shutil.copy(test_file, dst)
 
-    @override_settings(CONSUMER_ENABLE_ASN_BARCODE=True)
-    def test_consume_barcode_file_asn_assignment(self):
+        with mock.patch("documents.consumer.Consumer.try_consume_file") as mocked_call:
+            tasks.consume_file(dst)
+
+            args, kwargs = mocked_call.call_args
+
+            self.assertEqual(kwargs["override_asn"], 123)
+
+    def test_scan_file_for_asn_barcode(self):
         """
         GIVEN:
             - PDF containing an ASN barcode
@@ -789,26 +1008,51 @@ def test_consume_barcode_file_asn_assignment(self):
         THEN:
             - The ASN is located
             - The ASN integer value is correct
-            - The ASN is provided as the override value to the consumer
         """
         test_file = os.path.join(
             self.BARCODE_SAMPLE_DIR,
             "barcode-39-asn-123.pdf",
         )
+        doc_barcode_info = barcodes.scan_file_for_barcodes(
+            test_file,
+        )
+        asn = barcodes.get_asn_from_barcodes(doc_barcode_info.barcodes)
 
-        dst = os.path.join(settings.SCRATCH_DIR, "barcode-39-asn-123.pdf")
-        shutil.copy(test_file, dst)
-
-        with mock.patch("documents.consumer.Consumer.try_consume_file") as mocked_call:
-            tasks.consume_file(dst)
+        self.assertEqual(doc_barcode_info.pdf_path, test_file)
+        self.assertEqual(asn, 123)
 
-            args, kwargs = mocked_call.call_args
+    def test_scan_file_for_asn_not_existing(self):
+        """
+        GIVEN:
+            - PDF without an ASN barcode
+        WHEN:
+            - File is scanned for barcodes
+        THEN:
+            - No ASN is retrieved from the document
+        """
+        test_file = os.path.join(
+            self.BARCODE_SAMPLE_DIR,
+            "patch-code-t.pdf",
+        )
+        doc_barcode_info = barcodes.scan_file_for_barcodes(
+            test_file,
+        )
+        asn = barcodes.get_asn_from_barcodes(doc_barcode_info.barcodes)
 
-            self.assertEqual(kwargs["override_asn"], 123)
+        self.assertEqual(doc_barcode_info.pdf_path, test_file)
+        self.assertEqual(asn, None)
 
     @override_settings(CONSUMER_ENABLE_ASN_BARCODE=True)
     def test_asn_too_large(self):
-
+        """
+        GIVEN:
+            - ASN from barcode enabled
+            - Barcode contains too large an ASN value
+        WHEN:
+            - ASN from barcode checked for correctness
+        THEN:
+            - Exception is raised regarding size limits
+        """
         src = os.path.join(
             os.path.dirname(__file__),
             "samples",
diff --git a/src/documents/tests/utils.py b/src/documents/tests/utils.py
--- a/src/documents/tests/utils.py
+++ b/src/documents/tests/utils.py
@@ -3,6 +3,7 @@
 import tempfile
 from collections import namedtuple
 from contextlib import contextmanager
+from unittest import mock
 
 from django.apps import apps
 from django.db import connection
@@ -86,6 +87,30 @@ def tearDown(self) -> None:
         remove_dirs(self.dirs)
 
 
+class ConsumerProgressMixin:
+    def setUp(self) -> None:
+        self.send_progress_patcher = mock.patch(
+            "documents.consumer.Consumer._send_progress",
+        )
+        self.send_progress_mock = self.send_progress_patcher.start()
+        super().setUp()
+
+    def tearDown(self) -> None:
+        super().tearDown()
+        self.send_progress_patcher.stop()
+
+
+class DocumentConsumeDelayMixin:
+    def setUp(self) -> None:
+        self.consume_file_patcher = mock.patch("documents.tasks.consume_file.delay")
+        self.consume_file_mock = self.consume_file_patcher.start()
+        super().setUp()
+
+    def tearDown(self) -> None:
+        super().tearDown()
+        self.consume_file_patcher.stop()
+
+
 class TestMigrations(TransactionTestCase):
     @property
     def app(self):