diff --git a/soitool/inline_editable_soi_view.py b/soitool/inline_editable_soi_view.py
index 93941ece68f28062a99d8a57d0eb5d8795ceb46c..8a6c9a85e417cb5b00e799a00efb341e2390328f 100644
--- a/soitool/inline_editable_soi_view.py
+++ b/soitool/inline_editable_soi_view.py
@@ -1,4 +1,5 @@
 """Includes functionality for inline editing of SOI."""
+import string
 from datetime import datetime
 from PySide2.QtCore import Qt, QRectF, QTimer, QPoint, QMarginsF
 from PySide2.QtWidgets import (
@@ -15,6 +16,11 @@ from soitool.soi import ModuleLargerThanBinError
 from soitool.dialog_wrappers import exec_warning_dialog
 from soitool.serialize_export_import_soi import generate_soi_filename
 
+# How attachment pages should be numbered. The first page should be numbered
+# by the value of ATTACHMENT_NUMBERING_SCHEME[0], the second page
+# ATTACHMENT_NUMBERING_SCHEME[1], and so on.
+ATTACHMENT_NUMBERING_SCHEME = list(string.ascii_uppercase)
+
 
 class InlineEditableSOIView(QScrollArea):
     """Widget som kan byttes ut med view, edit etc."""
@@ -28,7 +34,7 @@ class InlineEditableSOIView(QScrollArea):
 
     def ensure_proxies(self):
         """Make sure all modules of the SOI have a proxy inside the scene."""
-        for module in self.soi.modules:
+        for module in self.soi.modules + self.soi.attachments:
             if not self.is_widget_in_scene(module["widget"]):
                 proxy = self.scene.addWidget(module["widget"])
                 self.proxies.add(proxy)
@@ -61,7 +67,11 @@ class InlineEditableSOIView(QScrollArea):
 
         self.soi = soi
 
-        self.number_of_pages = 1
+        # The following variables are updated by a call to `update_pages` later
+        # in this `__init__`. Therefore the values given here are in practice
+        # never used
+        self.number_of_pages_total = 1
+        self.number_of_non_attachment_pages = 1
         self.proxies = set()
 
         # Necessary to make the scroll area fill the space it's given
@@ -132,7 +142,7 @@ class InlineEditableSOIView(QScrollArea):
                 )
 
             # Render each page to own PDF page
-            for i in range(self.number_of_pages):
+            for i in range(self.number_of_pages_total):
 
                 x = 0
                 y = self.soi.HEIGHT * i + self.soi.PADDING * i
@@ -143,7 +153,7 @@ class InlineEditableSOIView(QScrollArea):
                 )
 
                 # If there are more pages, newPage
-                if i + 1 < self.number_of_pages:
+                if i + 1 < self.number_of_pages_total:
                     printer.newPage()
         finally:
             painter.end()
@@ -153,15 +163,18 @@ class InlineEditableSOIView(QScrollArea):
 
         The minimum page count is 1.
         """
-        required_pages = 1
-        for module in self.soi.modules:
-            if module["meta"]["page"] > required_pages:
-                required_pages += 1
-        self.number_of_pages = required_pages
+        self.number_of_non_attachment_pages = (
+            self.soi.get_number_of_non_attachment_pages()
+        )
+        # Each attachment module requires it's own page
+        required_pages = self.number_of_non_attachment_pages + len(
+            self.soi.attachments
+        )
+        self.number_of_pages_total = required_pages
 
     def draw_pages(self):
-        """Draw self.number_of_pages pages."""
-        for i in range(self.number_of_pages):
+        """Draw self.number_of_pages_total pages."""
+        for i in range(self.number_of_pages_total):
 
             x = 0
             y = self.soi.HEIGHT * i + self.soi.PADDING * i
@@ -173,7 +186,20 @@ class InlineEditableSOIView(QScrollArea):
             )
 
             self.draw_page(x, y)
-            self.draw_header(x + self.soi.PADDING, y + self.soi.PADDING, i + 1)
+            page_number = i + 1
+            header_x = x + self.soi.PADDING
+            header_y = y + self.soi.PADDING
+            # If not an attachment page: draw as normal
+            # If attachment page: draw with page number starting from 1 again
+            if page_number <= self.number_of_non_attachment_pages:
+                self.draw_header(header_x, header_y, page_number, False)
+            else:
+                self.draw_header(
+                    header_x,
+                    header_y,
+                    page_number - self.number_of_non_attachment_pages,
+                    True,
+                )
 
         for proxy in self.proxies:
             # Redraw of pages requires modules to be moved to front again
@@ -188,7 +214,7 @@ class InlineEditableSOIView(QScrollArea):
     # "Too many statements" for this function because it's doing GUI layout
     # work, which in nature is tedious and repetitive.
     # pylint: disable=R0914,R0915
-    def draw_header(self, x, y, page_number):
+    def draw_header(self, x, y, page_number, is_attachment_page):
         """Draw header staring at given position.
 
         Source for rotation approach:
@@ -198,6 +224,15 @@ class InlineEditableSOIView(QScrollArea):
         ----------
         x : int
         y : int
+        page_number : int
+            Page number of page to draw. 'is_attachment_page' affects how the
+            page number is drawn.
+        is_attachment_page : bool
+            If True the page will indicate that it is an attachment, and the
+            page numbering will follow a convention for page numbering defined
+            in 'ATTACHMENT_NUMBERING_SCHEME'. If False the page will indicate
+            that the page is part of the main page, and the page numbering will
+            use 1,2,3,....
         """
         # Title
         label_title = QLabel(self.soi.title)
@@ -254,7 +289,7 @@ class InlineEditableSOIView(QScrollArea):
         )
         # Store for usage when placing copy number title and page number
         copy_number_y_pos = y + self.soi.HEADER_HEIGHT / 2 - 100
-        copy_number.move(x + 18, copy_number_y_pos + label_width / 2)
+        copy_number.move(x + 15, copy_number_y_pos + label_width / 2)
         proxy = self.scene.addWidget(copy_number)
         proxy.setRotation(-90)
 
@@ -276,9 +311,18 @@ class InlineEditableSOIView(QScrollArea):
         proxy.setRotation(-90)
 
         # Page numbering
-        page_number = QLabel(
-            "Side {} av {}".format(page_number, self.number_of_pages)
-        )
+        if is_attachment_page:
+            page_number = QLabel(
+                "Vedlegg {}".format(
+                    ATTACHMENT_NUMBERING_SCHEME[page_number - 1]
+                )
+            )
+        else:
+            page_number = QLabel(
+                "Side {} av {}".format(
+                    page_number, self.number_of_non_attachment_pages
+                )
+            )
         page_number.setStyleSheet("background-color: rgba(0,0,0,0%)")
         page_number.setFont(QFont("Times New Roman", 15))
         # Source: https://stackoverflow.com/a/8638114/3545896
@@ -286,7 +330,7 @@ class InlineEditableSOIView(QScrollArea):
         label_width = (
             page_number.fontMetrics().boundingRect(page_number.text()).width()
         )
-        page_number.move(x + 85, copy_number_y_pos + label_width / 2)
+        page_number.move(x + 80, copy_number_y_pos + label_width / 2)
         proxy = self.scene.addWidget(page_number)
         proxy.setRotation(-90)
 
diff --git a/soitool/module_list.py b/soitool/module_list.py
index 64c62e84200bf56fad6bb145357433b1bf463fff..c30fbcd636d733869a9c0b943ce7807c9c8fde4b 100644
--- a/soitool/module_list.py
+++ b/soitool/module_list.py
@@ -15,7 +15,7 @@ class ModuleList(QListWidget):
 
     List elements are names of modules or attachment-modules from soitool.SOI.
     List elements are editable, drag-and-droppable and unique (no duplicates).
-    Makes changes in soitool.SOI lists through its parent-variable.
+    Makes changes in soitool.SOI lists through its soi property.
 
     Parameters
     ----------
@@ -32,9 +32,9 @@ class ModuleList(QListWidget):
 
         if not isinstance(soi, SOI):
             raise RuntimeError(
-                "Only soitool.SOIWorkspaceWidget is "
-                "acceptable type for parent-variable "
-                "in class Module_list."
+                "Only soitool.SOI is "
+                "acceptable type for the soi property "
+                "in class ModuleList."
             )
         self.type = module_type
         self.soi = soi
@@ -142,7 +142,8 @@ class ModuleList(QListWidget):
         """Notify parent when an element is drag-and-dropped.
 
         Note that if the SOI is not prepared for manual priorization an error
-        message will be displayed, and nothing else will be done.
+        message will be displayed, and nothing else will be done. This only
+        applies to uses of this class with non-attachment modules.
 
         https://doc.qt.io/qt-5/qabstractitemview.html#dropEvent.
 
@@ -151,7 +152,10 @@ class ModuleList(QListWidget):
         event : QDropEvent
             Is sent to super().
         """
-        if self.soi.algorithm_sort != "none":
+        if (
+            self.soi.algorithm_sort != "none"
+            and ModuleType(self.type) == ModuleType.MAIN_MODULE
+        ):
             exec_warning_dialog(
                 text="SOI er ikke innstilt for automatisk plassering av "
                 "moduler!",
diff --git a/soitool/soi.py b/soitool/soi.py
index fcb67a2360e13a1ce094bcf7e864079ffd2494ed..bb776a51b5b81ea6c7c6fc1ead7a0fb573952935 100644
--- a/soitool/soi.py
+++ b/soitool/soi.py
@@ -469,10 +469,42 @@ class SOI:
                     self.placement_strategy
                 )
             )
+        self.reorganize_attachments()
         # Call listener functions
         for listener in self.reorganization_listeners:
             listener()
 
+    def reorganize_attachments(self):
+        """Reorganize attachments. Each on own page in correct order.
+
+        Order taken from order in attachment list directly. Attachments appear
+        at the top-left corner always.
+        """
+        for i, attachment in enumerate(self.attachments):
+            first_attachment_page = (
+                self.get_number_of_non_attachment_pages() + 1
+            )
+            attachment["meta"]["x"] = 0
+            attachment["meta"]["y"] = 0
+            attachment["meta"]["page"] = first_attachment_page + i
+            self.update_module_widget_position(attachment)
+
+    def get_number_of_non_attachment_pages(self):
+        """Calculate how many pages non-attachment modules require.
+
+        The minimum page count is 1.
+
+        Returns
+        -------
+        int
+            Number of pages required for non-attachment modules.
+        """
+        pages = 1
+        for module in self.modules:
+            if module["meta"]["page"] > pages:
+                pages += 1
+        return pages
+
     def get_rectpack_packer(self):
         """Return rectpack packer set up for this SOI.
 
diff --git a/test/test_serialize_export_import.py b/test/test_serialize_export_import.py
index 3a9e20815cf18a62ae65564c8731f17ffc68e838..143c31635110ed0a69270a28e3124aba99960d80 100644
--- a/test/test_serialize_export_import.py
+++ b/test/test_serialize_export_import.py
@@ -14,6 +14,12 @@ from soitool.serialize_export_import_soi import (
     SERIALIZED_SOI_SCHEMA,
 )
 
+# The error being ignored here is pylint telling us that 'test' is a standard
+# module, so the import should be placed further up. In our case we have an
+# actual custom module called 'test', so pylint is confused.
+# pylint: disable=C0411
+from test.test_soi import deep_copy_of_modules_list
+
 app = QApplication.instance()
 if app is None:
     app = QApplication([])
@@ -52,6 +58,27 @@ MODULES = [
 ]
 
 
+# We'd like to use the same list of modules for both main and attachment
+# modules, so we need this to help make a copy of widgets. Otherwise main and
+# attachment modules would be referencing the same underlying widgets, as Qt
+# does not support cloning of QWidgets by default.
+def tablemodule_cloner(module):
+    """Return new TableModule that is a clone of parameter module.
+
+    Parameters
+    ----------
+    module : TableModule
+        Module to clone.
+
+    Returns
+    -------
+    TableModule
+        New TableModule that is a clone of parameter module.
+    """
+    width, height = module.get_size()
+    return TableModule({"width": width, "height": height}, module.get_data())
+
+
 class SerializeTest(unittest.TestCase):
     """Testcase for functions in module 'serialize_export_import_soi.py'."""
 
@@ -71,8 +98,8 @@ class SerializeTest(unittest.TestCase):
             algorithm_bin=ALGORITHM_BIN,
             algorithm_pack=ALGORITHM_PACK,
             algorithm_sort=ALGORITHM_SORT,
-            modules=MODULES,
-            attachments=MODULES,
+            modules=deep_copy_of_modules_list(MODULES, tablemodule_cloner),
+            attachments=deep_copy_of_modules_list(MODULES, tablemodule_cloner),
         )
         date = datetime.strptime(DATE, "%Y-%m-%d")
         date_formatted_for_file_path = date.strftime("%Y_%m_%d")
diff --git a/test/test_soi.py b/test/test_soi.py
index 4045214d1af11cf35a64c47c32d087716a5dc891..d122741addd8ee8a7337d8532aa832aa08b0c57d 100644
--- a/test/test_soi.py
+++ b/test/test_soi.py
@@ -36,11 +36,15 @@ class TestModule(ModuleBase, QWidget, metaclass=Meta):
         QWidget.__init__(self, *args, **kwargs)
         ModuleBase.__init__(self)
 
+        self.color = color
+        self.width = width
+        self.height = height
+
         self.setAutoFillBackground(True)
         palette = self.palette()
-        palette.setColor(QPalette.Window, QColor(color))
+        palette.setColor(QPalette.Window, QColor(self.color))
         self.setPalette(palette)
-        self.setGeometry(0, 0, width, height)
+        self.setGeometry(0, 0, self.width, self.height)
 
     def get_size(self):
         """Override."""
@@ -60,6 +64,22 @@ class TestModule(ModuleBase, QWidget, metaclass=Meta):
         raise NotImplementedError()
 
 
+def testmodule_cloner(module):
+    """Return new TestModule that is a clone of parameter module.
+
+    Parameters
+    ----------
+    module : TestModule
+        Module to clone.
+
+    Returns
+    -------
+    TestModule
+        New TestModule that is a clone of parameter module.
+    """
+    return TestModule(module.color, module.width, module.height)
+
+
 # The modules below have sizes that make the ideal for testing.
 # Sorting them by width should yield
 #  1. wide_module
@@ -105,8 +125,10 @@ class TestSOI(unittest.TestCase):
         simply sets the default.
         """
         self.soi = SOI(
-            modules=list(TEST_MODULES),
-            attachments=list(TEST_MODULES),
+            modules=deep_copy_of_modules_list(TEST_MODULES, testmodule_cloner),
+            attachments=deep_copy_of_modules_list(
+                TEST_MODULES, testmodule_cloner
+            ),
             algorithm_sort="none",
             placement_strategy="auto",
         )
@@ -238,7 +260,9 @@ class TestSOI(unittest.TestCase):
         self.soi.modules.append(module_maximum_size)
 
         # Store modules so we can use the exact same input for both algorithms
-        input_modules = self.soi.modules
+        input_modules = deep_copy_of_modules_list(
+            self.soi.modules, testmodule_cloner
+        )
 
         # Test packing with Guillotine algorithm
         expected_module_packing_metadata_guillotinebssfsas = [
@@ -255,7 +279,9 @@ class TestSOI(unittest.TestCase):
         )
 
         # Restore modules and test packing with MaxRects
-        self.soi.modules = input_modules
+        self.soi.modules = deep_copy_of_modules_list(
+            input_modules, testmodule_cloner
+        )
         expected_module_packing_metadata_maxrectsbl = [
             {"x": 0, "y": 0, "page": 1, "name": "tall_module"},
             {"x": 100, "y": 0, "page": 1, "name": "wide_module"},
@@ -387,3 +413,70 @@ class TestSOI(unittest.TestCase):
             ValueError,
             lambda: self.soi.update_properties(invalid_key="garbage"),
         )
+
+    def test_reorganize_attachments(self):
+        """Test that calling reorganize properly reorganized attachments.
+
+        Attachments should appear on their own pages, starting after the last
+        non-attachment page. The attachments should be placed at the top-left
+        corner.
+        """
+        self.soi.reorganize()
+
+        # Modules of SOI from self.setUp will all be placed on first page, so
+        # expect the three attachment modules also from self.setUp on pages
+        # 2,3,4
+        self.assertEqual(
+            self.soi.attachments[0]["meta"],
+            {"x": 0, "y": 0, "page": 2, "name": "tall_module"},
+        )
+        self.assertEqual(
+            self.soi.attachments[1]["meta"],
+            {"x": 0, "y": 0, "page": 3, "name": "wide_module"},
+        )
+        self.assertEqual(
+            self.soi.attachments[2]["meta"],
+            {"x": 0, "y": 0, "page": 4, "name": "big_module"},
+        )
+
+
+def deep_copy_of_modules_list(modules, widget_cloner):
+    """Get deep copy of modules list. Works for both modules and attachments.
+
+    This is necessary because copying lists in Python only works on one level
+    of nesting, such that:
+
+    ```python
+    a = [[1,2,3]
+    b = a.copy()
+    b[0][0] = 9
+    print(
+        "nested list in 'a' now updated "
+        "to '{}'!! Not what we want! ".format(a[0][0])
+    )
+    ```
+
+    There is a package copy that contains deepcopy for this, but we can't use
+    it because some of our objects "Can't be pickled", quoting the error.
+
+    Parameters
+    ----------
+    modules : list
+        List of modules.
+    widget_cloner : function
+        Function that given a Qt widget as a parameter can return a clone of
+        that widget. Example usage: `widget_cloner(mywidget)` should return a
+        widget that is a clone of mywidget.
+
+    Returns
+    -------
+    list
+        List of modules, deep copied from input list.
+    """
+    return [
+        {
+            "meta": module["meta"].copy(),
+            "widget": widget_cloner(module["widget"]),
+        }
+        for module in modules
+    ]