Repository: FOLIO-FSE/folio_migration_tools (language: python)

Issue:
Map 86[6-8] $x to staff notes



Discussion / hints:

Fix patch:
diff --git a/generated_schemas/AuthorityTransformerSchema.json b/generated_schemas/AuthorityTransformerSchema.json
new file mode 100644
index 00000000..94bfcdeb
--- /dev/null
+++ b/generated_schemas/AuthorityTransformerSchema.json
@@ -0,0 +1,93 @@
+{
+    "title": "TaskConfiguration",
+    "type": "object",
+    "properties": {
+        "name": {
+            "title": "Name",
+            "description": "Name of this task",
+            "type": "string"
+        },
+        "migrationTaskType": {
+            "title": "Migrationtasktype",
+            "description": "The string represenation of this class. Do not set",
+            "type": "string"
+        },
+        "files": {
+            "title": "Files",
+            "description": "List of MARC21 files with authority records",
+            "type": "array",
+            "items": {
+                "$ref": "#/definitions/FileDefinition"
+            }
+        },
+        "ilsFlavour": {
+            "$ref": "#/definitions/IlsFlavour"
+        },
+        "tagsToDelete": {
+            "title": "Tagstodelete",
+            "default": [],
+            "type": "array",
+            "items": {
+                "type": "string"
+            }
+        },
+        "createSourceRecords": {
+            "title": "Createsourcerecords",
+            "description": "Controls wheter or not to retain the MARC records in SRS.",
+            "default": true,
+            "type": "boolean"
+        }
+    },
+    "required": [
+        "name",
+        "migrationTaskType",
+        "files",
+        "ilsFlavour"
+    ],
+    "definitions": {
+        "FileDefinition": {
+            "title": "FileDefinition",
+            "type": "object",
+            "properties": {
+                "file_name": {
+                    "title": "File Name",
+                    "type": "string"
+                },
+                "suppressed": {
+                    "title": "Suppressed",
+                    "default": false,
+                    "type": "boolean"
+                },
+                "staff_suppressed": {
+                    "title": "Staff Suppressed",
+                    "default": false,
+                    "type": "boolean"
+                },
+                "service_point_id": {
+                    "title": "Service Point Id",
+                    "default": "",
+                    "type": "string"
+                }
+            },
+            "required": [
+                "file_name"
+            ]
+        },
+        "IlsFlavour": {
+            "title": "IlsFlavour",
+            "description": " ",
+            "enum": [
+                "aleph",
+                "voyager",
+                "sierra",
+                "millennium",
+                "koha",
+                "tag907y",
+                "tag001",
+                "tagf990a",
+                "none"
+            ],
+            "type": "string"
+        }
+    }
+}
diff --git a/generated_schemas/BibsTransformerSchema.json b/generated_schemas/BibsTransformerSchema.json
index c3bce753..c53305ee 100644
--- a/generated_schemas/BibsTransformerSchema.json
+++ b/generated_schemas/BibsTransformerSchema.json
@@ -61,6 +61,12 @@
             "title": "Neverupdatehridsettings",
             "default": false,
             "type": "boolean"
+        },
+        "createSourceRecords": {
+            "title": "Createsourcerecords",
+            "description": "Controls wheter or not to retain the MARC records in SRS.",
+            "default": true,
+            "type": "boolean"
         }
     },
     "required": [
diff --git a/generated_schemas/LibraryConfigurationSchema.json b/generated_schemas/LibraryConfigurationSchema.json
index 992b0496..d72ec488 100644
--- a/generated_schemas/LibraryConfigurationSchema.json
+++ b/generated_schemas/LibraryConfigurationSchema.json
@@ -31,11 +31,13 @@
         },
         "failed_records_threshold": {
             "title": "Failed Records Threshold",
+            "description": "Number of failed records until the process shuts down",
             "default": 5000,
             "type": "integer"
         },
         "failed_percentage_threshold": {
             "title": "Failed Percentage Threshold",
+            "description": "Percentage of failed records until the process shuts down",
             "default": 20,
             "type": "integer"
         },
diff --git a/pyproject.toml b/pyproject.toml
index 2dc348be..e639cc3d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "folio_migration_tools"
-version = "1.6.4"
+version = "1.7.0.rc17"
 description =  "A tool allowing you to migrate data from legacy ILS:s (Library systems) into FOLIO LSP"
 authors = ["Theodor Tolstoy <github.teddes@tolstoy.se>", "Lisa Sjögren", "Brooks Travis"]
 license = "MIT"
@@ -37,7 +37,7 @@ folioclient = "^0.42.0"
 pyhumps = "^3.7.3"
 defusedxml = "^0.7.1"
 python-dateutil = "^2.8.2"
-folio-uuid = "^0.2.6"
+folio-uuid = "^0.2.7"
 pymarc = "^4.2.1"
 pydantic = "^1.10.2"
 argparse-prompt = "^0.0.5"
diff --git a/requirements.txt b/requirements.txt
index 5313d9e6..7488d819 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,9 +7,9 @@ argparse-prompt==0.0.5 ; python_version >= "3.9" and python_version < "4.0" \
 attrs==22.1.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6 \
     --hash=sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c
-babel==2.10.3 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51 \
-    --hash=sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb
+babel==2.11.0 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:1ad3eca1c885218f6dce2ab67291178944f810a10a9b5f3cb8382a5a232b64fe \
+    --hash=sha256:5ef4b3226b0180dedded4229651c8b0e1a3a6a2837d45a073272f313e4cf97f6
 bandit==1.7.4 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:2d63a8c573417bae338962d4b9b06fbc6080f74ecd955a092849e1e65c717bd2 \
     --hash=sha256:412d3f259dab4077d0e7f0c11f50f650cc7d10db905d98f6520a95a18049658a
@@ -44,9 +44,9 @@ charset-normalizer==2.0.12 ; python_version >= "3.9" and python_version < "4.0"
 click==8.1.3 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e \
     --hash=sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48
-colorama==0.4.5 ; python_version >= "3.9" and python_version < "4.0" and sys_platform == "win32" or python_version >= "3.9" and python_version < "4.0" and platform_system == "Windows" \
-    --hash=sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da \
-    --hash=sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4
+colorama==0.4.6 ; python_version >= "3.9" and python_version < "4.0" and sys_platform == "win32" or python_version >= "3.9" and python_version < "4.0" and platform_system == "Windows" \
+    --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
+    --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
 coverage[toml]==6.5.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79 \
     --hash=sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a \
@@ -107,33 +107,36 @@ defusedxml==0.7.1 ; python_version >= "3.9" and python_version < "4.0" \
 docutils==0.19 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6 \
     --hash=sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc
+exceptiongroup==1.0.4 ; python_version >= "3.9" and python_version < "3.11" \
+    --hash=sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828 \
+    --hash=sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec
 flake8==5.0.4 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db \
     --hash=sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248
 flake8-bandit==4.1.1 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:068e09287189cbfd7f986e92605adea2067630b75380c6b5733dab7d87f9a84e \
     --hash=sha256:4c8a53eb48f23d4ef1e59293657181a3c989d0077c9952717e98a0eace43e06d
-flake8-black==0.3.3 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:7d667d0059fd1aa468de1669d77cc934b7f1feeac258d57bdae69a8e73c4cd90 \
-    --hash=sha256:8211f5e20e954cb57c709acccf2f3281ce27016d4c4b989c3e51f878bb7ce12a
-flake8-bugbear==22.9.23 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:17b9623325e6e0dcdcc80ed9e4aa811287fcc81d7e03313b8736ea5733759937 \
-    --hash=sha256:cd2779b2b7ada212d7a322814a1e5651f1868ab0d3f24cc9da66169ab8fda474
+flake8-black==0.3.5 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:4948a579fdddd98fbf935fd94255dfcfce560c4ddc1ceee08e3f12d6114c8619 \
+    --hash=sha256:9e93252b1314a8eb3c2f55dec54a07239e502b12f57567f2c105f2202714b15e
+flake8-bugbear==22.10.27 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:6ad0ab754507319060695e2f2be80e6d8977cfcea082293089a9226276bd825d \
+    --hash=sha256:a6708608965c9e0de5fff13904fed82e0ba21ac929fe4896459226a797e11cd5
 flake8-docstrings==1.6.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde \
     --hash=sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b
-flake8-isort==5.0.0 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:c73f9cbd1bf209887f602a27b827164ccfeba1676801b2aa23cb49051a1be79c \
-    --hash=sha256:e336f928c7edc509684930ab124414194b7f4e237c712af8fcbdf49d8747b10c
-folio-uuid==0.2.6 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:54ae99f95a5da7c8bf634ba4bd0b23f87c029b1730afea3f12f5333df93fd472 \
-    --hash=sha256:70e440ab345c250e717b5aa4da9d4be575472b6c2adc4e4119f083f259b8a4f1
+flake8-isort==5.0.3 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:0951398c343c67f4933407adbbfb495d4df7c038650c5d05753a006efcfeb390 \
+    --hash=sha256:8c4ab431d87780d0c8336e9614e50ef11201bc848ef64ca017532dec39d4bf49
+folio-uuid==0.2.7 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:1106c6db13bc2e87a95cd993f5c42864736402bd859cb0ccd69a183766c56a98 \
+    --hash=sha256:2075e0b0ef827b5877bc5f5369d1024991cbc40bcdd2f5a344ea44ec099c6ca5
 folioclient==0.42.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:ba9590b3142e62f0088237a3e4d0217f33481d972bde2cda2b463dc648f1a966 \
     --hash=sha256:cf4fbeef11907ba490fd0b7f052fd620a72ae88890ddf032535c20d9d3bb85ea
-gitdb==4.0.9 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd \
-    --hash=sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa
+gitdb==4.0.10 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:6eb990b69df4e15bad899ea868dc46572c3f75339735663b81de79b06f17eb9a \
+    --hash=sha256:c286cf298426064079ed96a9e4a9d39e7f3e9bf15ba60701e95f5492f28415c7
 gitpython==3.1.29 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:41eea0deec2deea139b459ac03656f0dd28fc4a3387240ec1d3c259a2c47850f \
     --hash=sha256:cc36bfc4a3f913e66805a28e84703e419d9c264c1077e537b54f0e1af85dbefd
@@ -143,9 +146,9 @@ idna==3.3 ; python_version >= "3.9" and python_version < "4.0" \
 imagesize==1.4.1 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b \
     --hash=sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a
-importlib-metadata==5.0.0 ; python_version >= "3.9" and python_version < "3.10" \
-    --hash=sha256:da31db32b304314d044d3c12c79bd59e307889b287ad12ff387b3500835fc2ab \
-    --hash=sha256:ddb0e35065e8938f867ed4928d0ae5bf2a53b7773871bfe6bcc7e4fcdc7dea43
+importlib-metadata==5.1.0 ; python_version >= "3.9" and python_version < "3.10" \
+    --hash=sha256:d5059f9f1e8e41f80e9c56c2ee58811450c31984dfa625329ffd7c0dad88a73b \
+    --hash=sha256:d84d17e21670ec07990e1044a99efe8d615d860fd176fc29ef5c306068fda313
 iniconfig==1.1.1 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3 \
     --hash=sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32
@@ -313,21 +316,18 @@ myst-parser==0.18.1 ; python_version >= "3.9" and python_version < "4.0" \
 packaging==21.3 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb \
     --hash=sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522
-pathspec==0.10.1 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93 \
-    --hash=sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d
+pathspec==0.10.2 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:88c2606f2c1e818b978540f73ecc908e13999c6c3a383daf3705652ae79807a5 \
+    --hash=sha256:8f6bf73e5758fd365ef5d58ce09ac7c27d2833a8d7da51712eac6e27e35141b0
 pbr==5.11.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:b97bc6695b2aff02144133c2e7399d5885223d42b7912ffaec2ca3898e673bfe \
     --hash=sha256:db2317ff07c84c4c63648c9064a79fe9d9f5c7ce85a9099d4b6258b3db83225a
-platformdirs==2.5.2 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788 \
-    --hash=sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19
+platformdirs==2.5.4 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:1006647646d80f16130f052404c6b901e80ee4ed6bef6792e1f238a8969106f7 \
+    --hash=sha256:af0276409f9a02373d540bf8480021a048711d572745aef4b7842dad245eba10
 pluggy==1.0.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159 \
     --hash=sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3
-py==1.11.0 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719 \
-    --hash=sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378
 pycodestyle==2.9.1 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785 \
     --hash=sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b
@@ -386,18 +386,18 @@ pymarc==4.2.1 ; python_version >= "3.9" and python_version < "4.0" \
 pyparsing==3.0.9 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb \
     --hash=sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc
-pytest==7.1.3 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7 \
-    --hash=sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39
+pytest==7.2.0 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71 \
+    --hash=sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59
 pytest-cov==4.0.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b \
     --hash=sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470
 python-dateutil==2.8.2 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \
     --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9
-pytz==2022.5 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:335ab46900b1465e714b4fda4963d87363264eb662aab5e65da039c25f1f5b22 \
-    --hash=sha256:c4d88f472f54d615e9cd582a5004d1e5f624854a6a27a6211591c251f22a6914
+pytz==2022.6 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:222439474e9c98fced559f1709d89e6c9cbf8d79c794ff3eb9f8800064291427 \
+    --hash=sha256:e89512406b793ca39f5971bc999cc538ce125c0e51c27941bef4568b460095e2
 pyyaml==6.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf \
     --hash=sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293 \
@@ -454,9 +454,9 @@ snowballstemmer==2.2.0 ; python_version >= "3.9" and python_version < "4.0" \
 sphinx==5.3.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d \
     --hash=sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5
-sphinx-autodoc-typehints==1.19.4 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:e190d8ee8204c3de05a64f41cf10e592e987e4063c8ec0de7e4b11f6e036b2e2 \
-    --hash=sha256:ffd8e710f6757471b5c831c7ece88f52a9ff15f27836f4ef1c8695a64f8dcca8
+sphinx-autodoc-typehints==1.19.5 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:38a227378e2bc15c84e29af8cb1d7581182da1107111fd1c88b19b5eb7076205 \
+    --hash=sha256:ea55b3cc3f485e3a53668bcdd08de78121ab759f9724392fdb5bf3483d786328
 sphinxcontrib-applehelp==1.0.2 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a \
     --hash=sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58
@@ -475,9 +475,9 @@ sphinxcontrib-qthelp==1.0.3 ; python_version >= "3.9" and python_version < "4.0"
 sphinxcontrib-serializinghtml==1.1.5 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd \
     --hash=sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952
-stevedore==4.1.0 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:02518a8f0d6d29be8a445b7f2ac63753ff29e8f2a2faa01777568d5500d777a6 \
-    --hash=sha256:3b1cbd592a87315f000d05164941ee5e164899f8fc0ce9a00bb0f321f40ef93e
+stevedore==4.1.1 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:7f8aeb6e3f90f96832c301bff21a7eb5eefbe894c88c506483d355565d88cc1a \
+    --hash=sha256:aa6436565c069b2946fe4ebff07f5041e0c8bf18c7376dd29edf80cf7d524e4e
 tomli==2.0.1 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \
     --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f
@@ -487,6 +487,6 @@ typing-extensions==4.4.0 ; python_version >= "3.9" and python_version < "4.0" \
 urllib3==1.26.9 ; python_version >= "3.9" and python_version < "4" \
     --hash=sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14 \
     --hash=sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e
-zipp==3.9.0 ; python_version >= "3.9" and python_version < "3.10" \
-    --hash=sha256:3a7af91c3db40ec72dd9d154ae18e008c69efe8ca88dde4f9a731bb82fe2f9eb \
-    --hash=sha256:972cfa31bc2fedd3fa838a51e9bc7e64b7fb725a8c00e7431554311f180e9980
+zipp==3.11.0 ; python_version >= "3.9" and python_version < "3.10" \
+    --hash=sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa \
+    --hash=sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766
diff --git a/src/fetch_default_ref_data.py b/src/fetch_default_ref_data.py
index f46ad31f..ac488559 100644
--- a/src/fetch_default_ref_data.py
+++ b/src/fetch_default_ref_data.py
@@ -6,6 +6,8 @@
 import requests
 
 super_schema = {
+    "/location-units": {},
+    "/locations": {},
     "/alternative-title-types": {},
     "/authority-note-types": {},
     "/call-number-types": {},
@@ -26,8 +28,6 @@
     "/item-damaged-statuses": {},
     "/item-note-types": {},
     "/loan-types": {},
-    "/location-units": {},
-    "/locations": {},
     "/material-types": {},
     "/modes-of-issuance": {},
     "/nature-of-content-terms": {},
@@ -45,7 +45,26 @@ def main():
         res = get_ref_data_from_github_folder(
             "folio-org", "mod-inventory-storage", f"/reference-data{entry}"
         )
-        print(f"fetched {entry}")
+        if not res:
+            res = get_ref_data_from_github_folder(
+                "folio-org", "mod-inventory-storage", f"/sample-data{entry}"
+            )
+        print(f"fetched {entry}: {any(res)}")
+        if entry == "/holdings-types":
+            res.append(
+                {
+                    "id": "b92480eb-210d-442e-a6f7-4043fe7f0f24",
+                    "name": "Unknown",
+                    "source": "local",
+                    "metadata": {
+                        "createdDate": "2022-12-08T09:55:58.067+00:00",
+                        "createdByUserId": "7cee216a-b298-4e7c-b678-8917e6b8edd3",
+                        "updatedDate": "2022-12-08T09:55:58.067+00:00",
+                        "updatedByUserId": "7cee216a-b298-4e7c-b678-8917e6b8edd3",
+                    },
+                }
+            )
+
         super_schema[entry] = res
     rules = get_ref_data_from_github_folder(
         "folio-org",
@@ -65,31 +84,37 @@ def main():
 
 def get_ref_data_from_github_folder(owner, repo, folder_path: str):
     ret_arr = []
-    logging.info("Using GITHB_TOKEN environment variable for Gihub API Access")
-    github_headers = {
-        "content-type": "application/json",
-        "User-Agent": "Folio Client (https://github.com/FOLIO-FSE/FolioClient)",
-        "authorization": f"token {os.environ.get('GITHUB_TOKEN')}",
-    }
+    try:
+
+        logging.info("Using GITHB_TOKEN environment variable for Gihub API Access")
+        github_headers = {
+            "content-type": "application/json",
+            "User-Agent": "Folio Client (https://github.com/FOLIO-FSE/FolioClient)",
+            "authorization": f"token {os.environ.get('GITHUB_TOKEN')}",
+        }
 
-    latest_path = f"https://api.github.com/repos/{owner}/{repo}/releases/latest"
-    req = requests.get(latest_path, headers=github_headers)
-    req.raise_for_status()
-    latest = json.loads(req.text)
-    # print(json.dumps(latest, indent=4))
-    latest_tag = latest["tag_name"]
-    path = f"https://api.github.com/repos/{owner}/{repo}/contents{folder_path}?ref={latest_tag}"
-    # print(latest_path)
-    req = requests.get(path, headers=github_headers)
-    req.raise_for_status()
-    file_list = [x["name"] for x in json.loads(req.text) if x["type"] == "file"]
-    github_path = "https://raw.githubusercontent.com"
-    for file_name in file_list:
-        file_path = f"{github_path}/{owner}/{repo}/{latest_tag}{folder_path}/{file_name}"
-        resp = requests.get(file_path, headers=github_headers)
-        resp.raise_for_status()
-        ret_arr.append(json.loads(resp.text))
-    logging.debug(folder_path)
+        latest_path = f"https://api.github.com/repos/{owner}/{repo}/releases/latest"
+        req = requests.get(latest_path, headers=github_headers)
+        req.raise_for_status()
+        latest = json.loads(req.text)
+        # print(json.dumps(latest, indent=4))
+        latest_tag = latest["tag_name"]
+        path = (
+            f"https://api.github.com/repos/{owner}/{repo}/contents{folder_path}?ref={latest_tag}"
+        )
+        # print(latest_path)
+        req = requests.get(path, headers=github_headers)
+        req.raise_for_status()
+        file_list = [x["name"] for x in json.loads(req.text) if x["type"] == "file"]
+        github_path = "https://raw.githubusercontent.com"
+        for file_name in file_list:
+            file_path = f"{github_path}/{owner}/{repo}/{latest_tag}{folder_path}/{file_name}"
+            resp = requests.get(file_path, headers=github_headers)
+            resp.raise_for_status()
+            ret_arr.append(json.loads(resp.text))
+        logging.debug(folder_path)
+    except Exception:
+        logging.error("Something went wrong")
     return ret_arr
 
 
diff --git a/src/folio_migration_tools/__main__.py b/src/folio_migration_tools/__main__.py
index b3ca329e..475a2216 100644
--- a/src/folio_migration_tools/__main__.py
+++ b/src/folio_migration_tools/__main__.py
@@ -95,11 +95,14 @@ def main():
                 print("Halting")
 
             # task_obj.do_work()
-            logging.info("Work done, wrapping up")
+            logging.info("Work done. Shutting down")
             sys.exit(0)
             # task_obj.wrap_up()
     except requests.exceptions.SSLError:
         print("\nSSL error. Are you connected to the Internet and the VPN?")
+    except Exception as ee:
+        logging.exception("Unhandled exception")
+        raise TransformationProcessError("", "unhandled exception", "") from ee
 
 
 def inheritors(base_class):
diff --git a/src/folio_migration_tools/circulation_helper.py b/src/folio_migration_tools/circulation_helper.py
index 41afbbdb..3cc749e4 100644
--- a/src/folio_migration_tools/circulation_helper.py
+++ b/src/folio_migration_tools/circulation_helper.py
@@ -271,7 +271,6 @@ def load_migrated_item_barcodes(self, item_barcodes, item_files, folder_structur
 
     @staticmethod
     def extend_open_loan(folio_client: FolioClient, loan, extension_due_date, extend_out_date):
-        # TODO: add logging instead of print out
         try:
             loan_to_put = copy.deepcopy(loan)
             del loan_to_put["metadata"]
diff --git a/src/folio_migration_tools/folder_structure.py b/src/folio_migration_tools/folder_structure.py
index c98cac59..3aa52c40 100644
--- a/src/folio_migration_tools/folder_structure.py
+++ b/src/folio_migration_tools/folder_structure.py
@@ -92,10 +92,8 @@ def setup_migration_file_structure(self, source_file_type: str = ""):
         self.created_objects_path = (
             self.results_folder / f"folio_{object_type_string}{self.file_template}.json"
         )
-
-        self.failed_bibs_file = self.results_folder / f"failed_bib_records{self.file_template}.mrc"
-        self.failed_mfhds_file = (
-            self.results_folder / f"failed_mfhd_records{self.file_template}.mrc"
+        self.failed_marc_recs_file = (
+            self.results_folder / f"failed_records{self.file_template}.mrc"
         )
 
         self.migration_reports_file = self.reports_folder / f"report{self.file_template}.md"
@@ -104,10 +102,19 @@ def setup_migration_file_structure(self, source_file_type: str = ""):
             self.results_folder / f"folio_srs_{object_type_string}{self.file_template}.json"
         )
 
-        self.instance_id_map_path = self.results_folder / "instance_id_map.json"
-
-        self.holdings_id_map_path = self.results_folder / "holdings_id_map.json"
+        self.instance_id_map_path = (
+            self.results_folder / f"{str(FOLIONamespaces.instances.name).lower()}_id_map.json"
+        )
+        self.auth_id_map_path = (
+            self.results_folder / f"{str(FOLIONamespaces.authorities.name).lower()}_id_map.json"
+        )
 
+        self.holdings_id_map_path = (
+            self.results_folder / f"{str(FOLIONamespaces.holdings.name).lower()}_id_map.json"
+        )
+        self.id_map_path = (
+            self.results_folder / f"{str(self.object_type.name).lower()}_id_map.json"
+        )
         # Mapping files
         self.material_type_map_path = self.mapping_files_folder / "material_types.tsv"
         self.loan_type_map_path = self.mapping_files_folder / "loan_types.tsv"
diff --git a/src/folio_migration_tools/helper.py b/src/folio_migration_tools/helper.py
index ef5789f0..e6c03f26 100644
--- a/src/folio_migration_tools/helper.py
+++ b/src/folio_migration_tools/helper.py
@@ -22,7 +22,11 @@ def print_mapping_report(
             mapped = v[0]
             mp = mapped / total_records if total_records else 0
             mapped_per = "{:.0%}".format(max(mp, 0))
-            report_file.write(f"{k} | {max(mapped, 0):,} ({mapped_per}) | {unmapped:,}  \n")
+            up = unmapped / total_records if total_records else 0
+            unmapped_per = "{:.0%}".format(max(up, 0))
+            report_file.write(
+                f"{k} | {max(mapped, 0):,} ({mapped_per}) | {unmapped:,} ({unmapped_per}) \n"
+            )
         report_file.write(details_end)
 
         report_file.write("\n## Mapped Legacy fields\n")
@@ -50,16 +54,11 @@ def log_data_issue(index_or_id, message, legacy_value):
         logging.log(26, "DATA ISSUE\t%s\t%s\t%s", index_or_id, message, legacy_value)
 
     @staticmethod
-    def write_to_file(file, folio_record, pg_dump=False):
-        """Writes record to file. pg_dump=true for importing directly via the
-        psql copy command
+    def write_to_file(file, folio_record):
+        """Writes record to file.
 
         Args:
             file (_type_): _description_
             folio_record (_type_): _description_
-            pg_dump (bool, optional): _description_. Defaults to False.
         """
-        if pg_dump:
-            file.write(f'{folio_record["id"]}\t{json.dumps(folio_record)}\n')
-        else:
-            file.write(f"{json.dumps(folio_record)}\n")
+        file.write(f"{json.dumps(folio_record)}\n")
diff --git a/src/folio_migration_tools/holdings_helper.py b/src/folio_migration_tools/holdings_helper.py
index 411f2728..93245b0d 100644
--- a/src/folio_migration_tools/holdings_helper.py
+++ b/src/folio_migration_tools/holdings_helper.py
@@ -166,5 +166,4 @@ def extend_list(
 
 
 def dedupe(list_of_dicts):
-    # TODO: Move to interface or parent class
     return [dict(t) for t in {tuple(d.items()) for d in list_of_dicts}]
diff --git a/src/folio_migration_tools/library_configuration.py b/src/folio_migration_tools/library_configuration.py
index 903b8300..a9c23e2e 100644
--- a/src/folio_migration_tools/library_configuration.py
+++ b/src/folio_migration_tools/library_configuration.py
@@ -1,4 +1,5 @@
 from enum import Enum
+from typing import Annotated
 from typing import Optional
 
 from pydantic import BaseModel
@@ -58,8 +59,12 @@ class LibraryConfiguration(BaseModel):
         )
     )
     multi_field_delimiter: Optional[str] = "<delimiter>"
-    failed_records_threshold: Optional[int] = 5000
-    failed_percentage_threshold: Optional[int] = 20
+    failed_records_threshold: Annotated[
+        int, Field(description=("Number of failed records until the process shuts down"))
+    ] = 5000
+    failed_percentage_threshold: Annotated[
+        int, Field(description=("Percentage of failed records until the process shuts down"))
+    ] = 20
     library_name: str
     log_level_debug: bool
     folio_release: FolioRelease = Field(
diff --git a/src/folio_migration_tools/mapper_base.py b/src/folio_migration_tools/mapper_base.py
index 90cfd1b8..265d0622 100644
--- a/src/folio_migration_tools/mapper_base.py
+++ b/src/folio_migration_tools/mapper_base.py
@@ -24,28 +24,20 @@
 class MapperBase:
 
     legacy_id_template = "Identifier(s) from previous system:"
+    bib_id_template = "Bib id: "
 
-    def __init__(self, library_configuration: LibraryConfiguration, folio_client: FolioClient):
+    def __init__(
+        self,
+        library_configuration: LibraryConfiguration,
+        folio_client: FolioClient,
+        parent_id_map: dict[str, tuple] = None,
+    ):
         logging.info("MapperBase initiating")
+        self.parent_id_map: dict[str, tuple] = parent_id_map
         self.extradata_writer: ExtradataWriter = ExtradataWriter(Path(""))
         self.start_datetime = datetime.now(timezone.utc)
         self.folio_client: FolioClient = folio_client
-        self.hrid_path = "/hrid-settings-storage/hrid-settings"
         self.library_configuration: LibraryConfiguration = library_configuration
-        self.hrid_settings = self.folio_client.folio_get_single_object(self.hrid_path)
-        self.instance_hrid_prefix = self.hrid_settings["instances"]["prefix"]
-        self.instance_hrid_counter = self.hrid_settings["instances"]["startNumber"]
-        self.holdings_hrid_prefix = self.hrid_settings["holdings"]["prefix"]
-        self.holdings_hrid_counter = self.hrid_settings["holdings"]["startNumber"]
-        self.items_hrid_prefix = self.hrid_settings["items"]["prefix"]
-        self.items_hrid_counter = self.hrid_settings["items"]["startNumber"]
-        self.common_retain_leading_zeroes: bool = self.hrid_settings["commonRetainLeadingZeroes"]
-
-        logging.info("Fetched HRID settings.")
-        logging.info("Instance HRID prefix is %s", self.instance_hrid_prefix)
-        logging.info("Instance start number is %s", self.instance_hrid_counter)
-        logging.info("Holdings HRID prefix is %s", self.instance_hrid_prefix)
-        logging.info("Holdings start number is %s", self.holdings_hrid_counter)
 
         self.mapped_folio_fields: dict = {}
         self.migration_report = MigrationReport()
@@ -64,7 +56,7 @@ def report_legacy_mapping(self, field_name, present, mapped):
 
     def report_folio_mapping(self, folio_record, schema):
         try:
-            for field_name in flatten(folio_record):
+            for field_name in set(flatten(folio_record)):
                 try:
                     self.mapped_folio_fields[field_name][0] += 1
                 except KeyError:
@@ -92,7 +84,7 @@ def report_legacy_mapping_no_schema(self, legacy_object):
                 self.mapped_legacy_fields[field_name][1] += v
 
     def report_folio_mapping_no_schema(self, folio_object):
-        for field_name in flatten(folio_object):
+        for field_name in set(flatten(folio_object)):
             if field_name not in self.mapped_folio_fields:
                 self.mapped_folio_fields[field_name] = [1, 1]
             else:
@@ -137,7 +129,6 @@ def get_mapped_name(
 
             # Gets the first line in the map satisfying all legacy mapping values.
             # Case insensitive, strips away whitespace
-            # TODO: add option for Wild card matching in individual columns
             right_mapping = ref_data_mapping.get_ref_data_mapping(legacy_object)
 
             if not right_mapping:
@@ -209,7 +200,6 @@ def get_mapped_value(
 
             # Gets the first line in the map satisfying all legacy mapping values.
             # Case insensitive, strips away whitespace
-            # TODO: add option for Wild card matching in individual columns
             right_mapping = ref_data_mapping.get_ref_data_mapping(legacy_object)
             if not right_mapping:
                 # Not all fields matched. Could it be a hybrid wildcard map?
@@ -296,9 +286,10 @@ def handle_transformation_record_failed_error(
             )
             sys.exit(1)
 
-    @staticmethod
-    def get_id_map_dict(legacy_id, folio_record):
-        return {"legacy_id": legacy_id, "folio_id": folio_record["id"]}
+    def get_id_map_tuple(self, legacy_id: str, folio_record: dict, object_type: FOLIONamespaces):
+        if object_type == FOLIONamespaces.instances:
+            return (legacy_id, folio_record["id"], folio_record["hrid"])
+        return (legacy_id, folio_record["id"])
 
     def handle_generic_exception(self, idx, excepion: Exception):
         self.num_exeptions += 1
@@ -315,12 +306,14 @@ def handle_generic_exception(self, idx, excepion: Exception):
             )
             sys.exit(1)
 
-    @staticmethod
-    def save_id_map_file(path, legacy_map: dict):
+    def save_id_map_file(self, path, legacy_map: dict):
         with open(path, "w") as legacy_map_file:
             for id_string in legacy_map.values():
                 legacy_map_file.write(f"{json.dumps(id_string)}\n")
-            logging.info("Wrote %s id:s to legacy map", len(legacy_map))
+                self.migration_report.add(
+                    Blurbs.GeneralStatistics, "Unique ID:s written to legacy map"
+                )
+        logging.info("Wrote legacy id map to %s", path)
 
     def store_hrid_settings(self):
         logging.info("Setting HRID counter to current")
@@ -414,13 +407,11 @@ def add_legacy_id_to_admin_note(self, folio_record: dict, legacy_id: str):
             None,
         ):
             if legacy_id not in id_string:
-                folio_record["administrativeNotes"] = list(
-                    [
-                        f
-                        for f in folio_record["administrativeNotes"]
-                        if MapperBase.legacy_id_template not in f
-                    ]
-                )
+                folio_record["administrativeNotes"] = [
+                    f
+                    for f in folio_record["administrativeNotes"]
+                    if MapperBase.legacy_id_template not in f
+                ]
 
                 folio_record["administrativeNotes"].append(f"{id_string}, {legacy_id}")
         else:
@@ -430,13 +421,27 @@ def add_legacy_id_to_admin_note(self, folio_record: dict, legacy_id: str):
 
 
 def flatten(my_dict: dict, path=""):
+
     for k, v in iter(my_dict.items()):
+        if not path:
+            yield k
         if v:
             if isinstance(v, list):
+                if path and check_if_list_with_dict_keys(v):
+                    yield f"{path}.{k}".strip(".")
                 for e in v:
                     if isinstance(e, dict):
                         yield from flatten(dict(e), f"{path}.{k}")
+                    elif isinstance(e, str) and path:
+                        yield f"{path}.{k}".strip(".")
+
             elif isinstance(v, dict):
+                if path:
+                    yield f"{path}.{k}".strip(".")
                 yield from flatten(dict(v), f"{path}.{k}")
-            else:
+            elif path:
                 yield f"{path}.{k}".strip(".")
+
+
+def check_if_list_with_dict_keys(data):
+    return isinstance(data, list) and all(isinstance(x, dict) for x in data)
diff --git a/src/folio_migration_tools/mapping_file_transformation/holdings_mapper.py b/src/folio_migration_tools/mapping_file_transformation/holdings_mapper.py
index 17d35d20..50c5b2cf 100644
--- a/src/folio_migration_tools/mapping_file_transformation/holdings_mapper.py
+++ b/src/folio_migration_tools/mapping_file_transformation/holdings_mapper.py
@@ -148,7 +148,7 @@ def get_instance_ids(self, legacy_value: str, index_or_id: str):
                 entry = self.instance_id_map.get(new_legacy_value, "") or self.instance_id_map.get(
                     legacy_instance_id
                 )
-                return_ids.append(entry["folio_id"])
+                return_ids.append(entry[1])
         if any(return_ids):
             return return_ids
         else:
diff --git a/src/folio_migration_tools/mapping_file_transformation/item_mapper.py b/src/folio_migration_tools/mapping_file_transformation/item_mapper.py
index 2f1a5421..a5bb13d1 100644
--- a/src/folio_migration_tools/mapping_file_transformation/item_mapper.py
+++ b/src/folio_migration_tools/mapping_file_transformation/item_mapper.py
@@ -230,7 +230,9 @@ def get_prop(self, legacy_item, folio_prop_name, index_or_id):
             return statistical_code_id
         elif folio_prop_name == "holdingsRecordId":
             if legacy_value in self.holdings_id_map:
-                return self.holdings_id_map[legacy_value]["folio_id"]
+                return self.holdings_id_map[legacy_value][1]
+            elif f"{self.bib_id_template}{legacy_value}" in self.holdings_id_map:
+                return self.holdings_id_map[f"{self.bib_id_template}{legacy_value}"][1]
             self.migration_report.add_general_statistics(
                 "Records failed because of failed holdings",
             )
diff --git a/src/folio_migration_tools/mapping_file_transformation/ref_data_mapping.py b/src/folio_migration_tools/mapping_file_transformation/ref_data_mapping.py
index 344d0adc..03404823 100644
--- a/src/folio_migration_tools/mapping_file_transformation/ref_data_mapping.py
+++ b/src/folio_migration_tools/mapping_file_transformation/ref_data_mapping.py
@@ -5,7 +5,6 @@
 from folioclient import FolioClient
 
 from folio_migration_tools.custom_exceptions import TransformationProcessError
-from folio_migration_tools.report_blurbs import Blurbs
 
 
 class RefDataMapping(object):
@@ -16,16 +15,16 @@ def __init__(
         array_name,
         the_map,
         key_type,
-        blurb: Blurbs,
+        blurb,
     ):
         self.name = array_name
-        self.cache = {}
+        self.cache: dict = {}
         self.blurb = blurb
         logging.info("%s reference data mapping. Initializing", self.name)
         logging.info("Fetching %s reference data from FOLIO", self.name)
         self.ref_data = list(folio_client.folio_get_all(ref_data_path, array_name, "", 1000))
         self.map = the_map
-        self.regular_mappings = []
+        self.regular_mappings: list = []
         self.key_type = key_type
         self.hybrid_mappings = []
         self.mapped_legacy_keys = []
diff --git a/src/folio_migration_tools/marc_rules_transformation/bibs_processor.py b/src/folio_migration_tools/marc_rules_transformation/bibs_processor.py
deleted file mode 100644
index 2cbfece1..00000000
--- a/src/folio_migration_tools/marc_rules_transformation/bibs_processor.py
+++ /dev/null
@@ -1,172 +0,0 @@
-""" Class that processes each MARC record """
-import json
-import logging
-
-from folio_uuid.folio_uuid import FOLIONamespaces
-from folioclient import FolioClient
-from pymarc.record import Record
-
-from folio_migration_tools.custom_exceptions import TransformationRecordFailedError
-from folio_migration_tools.folder_structure import FolderStructure
-from folio_migration_tools.helper import Helper
-from folio_migration_tools.library_configuration import FileDefinition
-from folio_migration_tools.marc_rules_transformation.rules_mapper_bibs import (
-    BibsRulesMapper,
-)
-from folio_migration_tools.migration_report import MigrationReport
-from folio_migration_tools.report_blurbs import Blurbs
-
-
-class BibsProcessor:
-    """the processor"""
-
-    def __init__(
-        self,
-        mapper: BibsRulesMapper,
-        folio_client: FolioClient,
-        results_file,
-        folder_structure: FolderStructure,
-    ):
-        self.results_file = results_file
-        self.folio_client = folio_client
-        self.instance_schema = folio_client.get_instance_json_schema()
-        self.mapper: BibsRulesMapper = mapper
-        self.folders = folder_structure
-        self.srs_records_file = open(self.folders.srs_records_path, "w+")
-        self.instance_id_map_file = open(self.folders.instance_id_map_path, "w+")
-        self.instance_identifiers = set()
-
-    def process_record(self, idx, marc_record: Record, file_definition: FileDefinition):
-        """processes a marc record and saves it
-
-
-        Args:
-            idx (_type_): _description_
-            marc_record (Record): _description_
-            file_definition (FileDefinition): _description_
-
-        Raises:
-            Exception: _description_
-        """
-        folio_rec = None
-        legacy_ids = []
-        try:
-            legacy_ids = self.mapper.get_legacy_ids(
-                marc_record, self.mapper.task_configuration.ils_flavour, idx
-            )
-            if not legacy_ids:
-                raise TransformationRecordFailedError(
-                    f"Index in file: {idx}", "No legacy id found", idx
-                )
-            # Transform the MARC21 to a FOLIO record
-            folio_rec = self.mapper.parse_bib(legacy_ids, marc_record, file_definition)
-            if prec_titles := folio_rec.get("precedingTitles", []):
-                self.mapper.migration_report.add(
-                    Blurbs.PrecedingSuccedingTitles, f"{len(prec_titles)}"
-                )
-                del folio_rec["precedingTitles"]
-            if succ_titles := folio_rec.get("succeedingTitles", []):
-                del folio_rec["succeedingTitles"]
-                self.mapper.migration_report.add(
-                    Blurbs.PrecedingSuccedingTitles, f"{len(succ_titles)}"
-                )
-            filtered_legacy_ids = self.get_valid_instance_ids(
-                folio_rec,
-                legacy_ids,
-                self.instance_identifiers,
-                self.mapper.migration_report,
-            )
-            self.save_instance_ids_to_file(file_definition, folio_rec, filtered_legacy_ids)
-            Helper.write_to_file(self.results_file, folio_rec)
-            self.mapper.save_source_record(
-                self.srs_records_file,
-                FOLIONamespaces.instances,
-                self.folio_client,
-                marc_record,
-                folio_rec,
-                legacy_ids[0],
-                file_definition.suppressed,
-            )
-            self.mapper.migration_report.add_general_statistics(
-                "Records successfully transformed into FOLIO objects"
-            )
-        except ValueError as value_error:
-            self.mapper.migration_report.add(
-                Blurbs.FieldMappingErrors,
-                f"{value_error} for {legacy_ids} ",
-            )
-            self.mapper.migration_report.add_general_statistics(
-                "Records that failed transformation. Check log for details",
-            )
-        except TransformationRecordFailedError as error:
-            self.mapper.migration_report.add_general_statistics(
-                "Records that failed transformation. Check log for details",
-            )
-            error.id = legacy_ids
-            error.log_it()
-        except Exception as inst:
-            self.mapper.migration_report.add_general_statistics(
-                "Records that failed Due to a unhandled exception",
-            )
-            self.mapper.migration_report.add_general_statistics(
-                f"Transformation exceptions: {inst.__class__.__name__}",
-            )
-            logging.error(type(inst))
-            logging.error(inst.args)
-            logging.error(inst)
-            logging.error(marc_record)
-            if folio_rec:
-                logging.error(folio_rec)
-            raise inst from inst
-
-    def save_instance_ids_to_file(self, file_def: FileDefinition, folio_rec, filtered_legacy_ids):
-        for legacy_id in filtered_legacy_ids:
-            self.instance_identifiers.add(legacy_id)
-            s = json.dumps(
-                {
-                    "legacy_id": legacy_id,
-                    "folio_id": folio_rec["id"],
-                    "instance_hrid": folio_rec["hrid"],
-                    "suppressed": file_def.suppressed,
-                }
-            )
-            self.instance_id_map_file.write(f"{s}\n")
-            self.mapper.migration_report.add_general_statistics("Lines written to identifier map")
-
-    @staticmethod
-    def get_valid_instance_ids(
-        folio_rec, legacy_ids, instance_identifiers, migration_report: MigrationReport
-    ):
-        new_ids = set()
-        for legacy_id in legacy_ids:
-            if legacy_id not in instance_identifiers:
-                new_ids.add(legacy_id)
-            else:
-                s = "Duplicate Instance identifiers "
-                migration_report.add_general_statistics(s)
-                Helper.log_data_issue(legacy_id, s, "-".join(legacy_ids))
-                logging.error(s)
-        if not any(new_ids):
-            s = "Failed records. No unique bib identifiers in legacy record"
-            migration_report.add_general_statistics(s)
-            raise TransformationRecordFailedError(
-                "-".join(legacy_ids),
-                "Duplicate recod identifier(s). See logs. Record Failed",
-                "-".join(legacy_ids),
-            )
-        return list(new_ids)
-
-    def wrap_up(self):  # sourcery skip: remove-redundant-fstring
-        """Finalizes the mapping by writing things out."""
-        try:
-            self.mapper.wrap_up()
-        except Exception:
-            logging.exception("error during wrap up")
-        if any(self.mapper.holdings_map):
-            logging.info("Saving holdings created from bibs")
-            holdings_path = self.folders.data_folder / "folio_holdings_from_bibs.json"
-            with open(holdings_path, "w+") as holdings_file:
-                for _key, holding in self.mapper.holdings_map.items():
-                    Helper.write_to_file(holdings_file, holding)
-        self.srs_records_file.close()
-        self.instance_id_map_file.close()
diff --git a/src/folio_migration_tools/marc_rules_transformation/conditions.py b/src/folio_migration_tools/marc_rules_transformation/conditions.py
index b8ffc980..789bec93 100644
--- a/src/folio_migration_tools/marc_rules_transformation/conditions.py
+++ b/src/folio_migration_tools/marc_rules_transformation/conditions.py
@@ -9,6 +9,7 @@
 from folio_migration_tools.custom_exceptions import TransformationProcessError
 from folio_migration_tools.custom_exceptions import TransformationRecordFailedError
 from folio_migration_tools.helper import Helper
+from folio_migration_tools.library_configuration import FolioRelease
 from folio_migration_tools.marc_rules_transformation.rules_mapper_base import (
     RulesMapperBase,
 )
@@ -18,26 +19,38 @@
 
 
 class Conditions:
+    holdings_type_map = {
+        "u": "Unknown",
+        "v": "Multi-part monograph",
+        "x": "Monograph",
+        "y": "Serial",
+    }
+
     def __init__(
         self,
         folio: FolioClient,
         mapper: RulesMapperBase,
         object_type,
+        folio_release: FolioRelease,
         default_call_number_type_name="",
     ):
         self.filter_chars = r"[.,\/#!$%\^&\*;:{}=\-_`~()]"
         self.filter_chars_dop = r"[.,\/#!$%\^&\*;:{}=\_`~()]"
+        self.folio_release: FolioRelease = folio_release
         self.filter_last_chars = r",$"
         self.folio = folio
         self.default_contributor_type = ""
         self.mapper = mapper
         self.ref_data_dicts = {}
-        self.setup_reference_data_for_all()
         if object_type == "bibs":
+            self.setup_reference_data_for_all()
             self.setup_reference_data_for_bibs()
+        elif object_type == "auth":
+            self.setup_reference_data_for_auth()
         else:
+            self.setup_reference_data_for_all()
             self.setup_reference_data_for_items_and_holdings(default_call_number_type_name)
-        self.condition_cache = {}
+        self.condition_cache: dict = {}
 
     def setup_reference_data_for_bibs(self):
         logging.info("Setting up reference data for bib transformation")
@@ -69,17 +82,12 @@ def setup_reference_data_for_items_and_holdings(self, default_call_number_type_n
         self.default_call_number_type = {}
         logging.info("%s\tholding_note_types", len(self.folio.holding_note_types))
         logging.info("%s\tcall_number_types", len(self.folio.call_number_types))
-        self.holdings_types = list(
-            self.folio.folio_get_all("/holdings-types", "holdingsTypes", "", 1000)
-        )
-        logging.info("%s\tholdings types", len(self.holdings_types))
+        self.setup_and_validate_holdings_types()
         # Raise for empty settings
         if not self.folio.holding_note_types:
             raise TransformationProcessError("", "No holding_note_types in FOLIO")
         if not self.folio.call_number_types:
             raise TransformationProcessError("", "No call_number_types in FOLIO")
-        if not self.holdings_types:
-            raise TransformationProcessError("", "No holdings_types in FOLIO")
         if not self.folio.locations:
             raise TransformationProcessError("", "No locations in FOLIO")
 
@@ -104,11 +112,31 @@ def setup_reference_data_for_items_and_holdings(self, default_call_number_type_n
             )
         logging.info("Default Callnumber type Name:\t%s", self.default_call_number_type["name"])
 
+    def setup_and_validate_holdings_types(self):
+        self.holdings_types = list(
+            self.folio.folio_get_all("/holdings-types", "holdingsTypes", "", 1000)
+        )
+        if not self.holdings_types:
+            raise TransformationProcessError("", "No holdings_types in FOLIO")
+        missing_holdings_types = [
+            ht
+            for ht in self.holdings_type_map.values()
+            if ht not in [ht_ref["name"] for ht_ref in self.holdings_types]
+        ]
+        if any(missing_holdings_types):
+            raise TransformationProcessError(
+                "",
+                "Holdings types are missing from the tenant. Please set them up",
+                missing_holdings_types,
+            )
+        logging.info("%s\tholdings types", len(self.holdings_types))
+
     def setup_reference_data_for_all(self):
+
+        logging.info(f"{len(self.folio.class_types)}\tclass_types")
         logging.info(
             f"{len(self.folio.electronic_access_relationships)}\telectronic_access_relationships"
         )
-        logging.info(f"{len(self.folio.class_types)}\tclass_types")
         self.statistical_codes = self.folio.statistical_codes
         logging.info(f"{len(self.statistical_codes)} \tstatistical_codes")
 
@@ -116,6 +144,22 @@ def setup_reference_data_for_all(self):
         if not self.folio.class_types:
             raise TransformationProcessError("", "No class_types in FOLIO")
 
+    def setup_reference_data_for_auth(self):
+        if self.folio_release not in [FolioRelease.morning_glory, FolioRelease.lotus]:
+            self.authority_source_files = list(
+                self.folio.folio_get_all(
+                    "/authority-source-files", "authoritySourceFiles", self.folio.cql_all, 1000
+                )
+            )
+            logging.info(f"{len(self.authority_source_files)} \tAuthority source files")
+        self.authority_note_types = list(
+            self.folio.folio_get_all(
+                "/authority-note-types", "authorityNoteTypes", self.folio.cql_all, 1000
+            )
+        )
+        logging.info(f"{len(self.authority_note_types)} \tAuthority note types")
+        logging.info(f"{len(self.folio.identifier_types)} \tidentifier types")
+
     def get_condition(
         self, name, legacy_id, value, parameter=None, marc_field: field.Field = None
     ):
@@ -134,6 +178,10 @@ def condition_trim_period(self, legacy_id, value, parameter, marc_field: field.F
     def condition_trim(self, legacy_id, value, parameter, marc_field: field.Field):
         return value.strip()
 
+    def condition_set_holdings_type_id(self, legacy_id, value, parameter, marc_field: field.Field):
+        self.mapper.migration_report.add(Blurbs.HoldingsTypeMapping, "Condition in rules hit")
+        return ""
+
     def condition_concat_subfields_by_name(
         self, legacy_id, value, parameter, marc_field: field.Field
     ):
@@ -239,7 +287,14 @@ def condition_set_identifier_type_id_by_value(
             )
             return t[0]
         identifier_type = next(
-            (f for f in self.folio.identifier_types if f["name"] in parameter["names"]),
+            (
+                f
+                for f in self.folio.identifier_types
+                if (
+                    f["name"] in parameter.get("names", "non existant")
+                    or f["name"] in parameter.get("name", "non existant")
+                )
+            ),
             None,
         )
         self.mapper.migration_report.add(Blurbs.MappedIdentifierTypes, identifier_type["name"])
@@ -254,6 +309,18 @@ def condition_set_identifier_type_id_by_value(
 
     def condition_set_holding_note_type_id_by_name(
         self, legacy_id, value, parameter, marc_field: field.Field
+    ):
+        self.mapper.migration_report.add(
+            Blurbs.Exceptions,
+            (
+                "Condition set_holding_note_type_id_by_name is deprecated. "
+                "Use set_holdings_note_type_id instead"
+            ),
+        )
+        return self.condition_set_holdings_note_type_id(legacy_id, value, parameter, marc_field)
+
+    def condition_set_holdings_note_type_id(
+        self, legacy_id, value, parameter, marc_field: field.Field
     ):
         try:
             t = self.get_ref_data_tuple_by_name(
@@ -270,6 +337,24 @@ def condition_set_holding_note_type_id_by_name(
                 parameter.get("name", ""),
             ) from ee
 
+    def condition_set_authority_note_type_id(
+        self, legacy_id, _, parameter, marc_field: field.Field
+    ):
+        try:
+            t = self.get_ref_data_tuple_by_name(
+                self.authority_note_types, "authority_note_types", parameter["name"]
+            )
+            self.mapper.migration_report.add(Blurbs.MappedNoteTypes, t[1])
+            return t[0]
+        except Exception as ee:
+            logging.error(ee)
+            raise TransformationProcessError(
+                legacy_id,
+                f'Authority note type mapping error.\tParameter: {parameter.get("name", "")}\t'
+                f"MARC Field: {marc_field}. Is mapping rules and ref data aligned?",
+                parameter.get("name", ""),
+            ) from ee
+
     def condition_set_classification_type_id(
         self, legacy_id, value, parameter, marc_field: field.Field
     ):
@@ -417,28 +502,23 @@ def condition_set_contributor_type_id(
                 return t[0]
         return self.default_contributor_type["id"]
 
-    def condition_set_instance_id_by_map(
-        self, legacy_id, value, parameter, marc_field: field.Field
-    ):
-        try:
-            if value:
-                if value.strip() not in self.mapper.instance_id_map:
-                    raise ValueError()
-                return self.mapper.instance_id_map[value.strip()]["folio_id"]
-            Helper.log_data_issue("", "No instance id provided", marc_field.format_field())
-            return ""
-        except Exception:
-            raise TransformationRecordFailedError(
-                legacy_id,
-                "Old instance id not in map",
-                f"{marc_field.format_field()}",
-            )
-
     def condition_set_url_relationship(self, legacy_id, value, parameter, marc_field: field.Field):
         return self._extracted_from_condition_set_electronic_access_relations_id_2("8", marc_field)
 
     def condition_set_call_number_type_by_indicator(
         self, legacy_id, value, parameter, marc_field: pymarc.Field
+    ):
+        self.mapper.migration_report.add(
+            Blurbs.Exceptions,
+            (
+                "Condition set_call_number_type_by_indicator is deprecated. "
+                "Change to set_call_number_type_id"
+            ),
+        )
+        return self.condition_set_call_number_type_id(legacy_id, value, parameter, marc_field)
+
+    def condition_set_call_number_type_id(
+        self, legacy_id, value, parameter, marc_field: pymarc.Field
     ):
         first_level_map = {
             "0": "Library of Congress classification",
@@ -514,23 +594,6 @@ def condition_set_alternative_title_type_id(self, legacy_id, value, parameter, m
                 f"Alternative title type not found for {parameter['name']} {marc_field}",
             )
 
-    def setup_location_code_from_second_column(self):
-        try:
-            other_columns = [
-                f for f in self.mapper.location_map[0].keys() if f not in ["folio_code"]
-            ]
-            if len(other_columns) > 1:
-                raise TransformationProcessError(
-                    "",
-                    "Other location map columns could not be used since more than one",
-                    other_columns,
-                )
-            elif len(other_columns) == 1:
-                logging.info(f"{other_columns[0]} will be used for location mapping")
-                return {lm[other_columns[0]]: lm["folio_code"] for lm in self.mapper.location_map}
-        except Exception as ee:
-            raise TransformationProcessError("", f"{ee}", self.mapper.location_map)
-
     def condition_set_location_id_by_code(
         self, legacy_id, value, parameter, marc_field: field.Field
     ):
@@ -546,29 +609,46 @@ def condition_set_location_id_by_code(
     def condition_set_permanent_location_id(
         self, legacy_id, value, parameter, marc_field: field.Field
     ):
-        # Setup mapping if not already set up
+
         if "legacy_locations" not in self.ref_data_dicts:
             try:
                 d = {lm["legacy_code"]: lm["folio_code"] for lm in self.mapper.location_map}
                 self.ref_data_dicts["legacy_locations"] = d
+                for folio_code in d.values():
+                    t = self.get_ref_data_tuple_by_code(
+                        self.folio.locations, "locations", folio_code
+                    )
+                    if not t:
+                        raise TransformationProcessError(
+                            "", "No FOLIO location found for code", folio_code
+                        )
+                if "*" not in d:
+                    raise TransformationProcessError(
+                        "",
+                        (
+                            "Fallback location mapping missing. Add a row with a * in the "
+                            "legacy_code column and a location code to map unmapped locations to"
+                        ),
+                        "",
+                    )
             except KeyError as ke:
                 if "folio_code" in str(ke):
                     raise TransformationProcessError(
                         legacy_id, "Your location map lacks the column folio_code"
-                    )
+                    ) from ke
                 if "legacy_code" in str(ke):
-                    logging.info(
-                        "legacy_code column not found. "
-                        "Trying to use other columns from location map."
-                    )
-                    self.ref_data_dicts[
-                        "legacy_locations"
-                    ] = self.setup_location_code_from_second_column()
+                    raise TransformationProcessError(
+                        legacy_id, "Your location map lacks the column legacy_code"
+                    ) from ke
+
         # Get the right code from the location map
-        if self.mapper.location_map and any(self.mapper.location_map):
-            mapped_code = self.ref_data_dicts["legacy_locations"].get(value.strip(), "").strip()
-        else:  # IF there is no map, assume legacy code is the same as FOLIO code
-            mapped_code = value.strip()
+        mapped_code = self.ref_data_dicts["legacy_locations"].get(value.strip(), "").strip()
+        if not mapped_code:
+            mapped_code = self.ref_data_dicts["legacy_locations"].get("*", "").strip()
+            if mapped_code:
+                self.mapper.migration_report.add(
+                    Blurbs.LocationMapping, f"Fallback mapping: {value}->{mapped_code}"
+                )
         # Get the FOLIO UUID for the code and return it
         t = self.get_ref_data_tuple_by_code(self.folio.locations, "locations", mapped_code)
         if not t:
diff --git a/src/folio_migration_tools/marc_rules_transformation/holdings_processor.py b/src/folio_migration_tools/marc_rules_transformation/holdings_processor.py
deleted file mode 100644
index 9e2dd9e3..00000000
--- a/src/folio_migration_tools/marc_rules_transformation/holdings_processor.py
+++ /dev/null
@@ -1,269 +0,0 @@
-""" Class that processes each MARC record """
-import logging
-import sys
-import time
-import traceback
-
-from folio_uuid.folio_namespaces import FOLIONamespaces
-from pymarc import Field
-from pymarc import Record
-
-from folio_migration_tools.custom_exceptions import TransformationProcessError
-from folio_migration_tools.custom_exceptions import TransformationRecordFailedError
-from folio_migration_tools.folder_structure import FolderStructure
-from folio_migration_tools.helper import Helper
-from folio_migration_tools.holdings_helper import HoldingsHelper
-from folio_migration_tools.library_configuration import FileDefinition
-from folio_migration_tools.library_configuration import HridHandling
-from folio_migration_tools.marc_rules_transformation.rules_mapper_holdings import (
-    RulesMapperHoldings,
-)
-from folio_migration_tools.report_blurbs import Blurbs
-
-
-class HoldingsProcessor:
-    """the processor"""
-
-    def __init__(self, mapper, folder_structure: FolderStructure):
-        self.folder_structure: FolderStructure = folder_structure
-        self.records_count = 0
-        self.unique_001s = set()
-        self.failed_records_count = 0
-        self.mapper: RulesMapperHoldings = mapper
-        self.start = time.time()
-        self.created_objects_file = open(self.folder_structure.created_objects_path, "w+")
-        self.srs_records_file = open(self.folder_structure.srs_records_path, "w+")
-        self.setup_holdings_sources()
-
-    def setup_holdings_sources(self):
-        holdings_sources = list(
-            self.mapper.folio_client.folio_get_all("/holdings-sources", "holdingsRecordsSources")
-        )
-        logging.info("Fetched %s holdingsRecordsSources from tenant", len(holdings_sources))
-        self.holdingssources = {n["name"].upper(): n["id"] for n in holdings_sources}
-        if "FOLIO" not in self.holdingssources:
-            raise TransformationProcessError("", "No holdings source with name FOLIO in tenant")
-        if "MARC" not in self.holdingssources:
-            raise TransformationProcessError("", "No holdings source with name MARC in tenant")
-
-    def exit_on_too_many_exceptions(self):
-        if (
-            self.failed_records_count / (self.records_count + 1)
-            > (self.mapper.library_configuration.failed_percentage_threshold / 100)
-            and self.failed_records_count
-            > self.mapper.library_configuration.failed_records_threshold
-        ):
-            logging.critical("More than 20 percent of the records have failed. Halting")
-            sys.exit(1)
-
-    def process_record(self, marc_record: Record, file_def: FileDefinition):
-        """processes a marc holdings record and saves it
-
-        Args:
-            marc_record (Record): _description_
-            file_def (FileDefinition): _description_
-
-        Raises:
-            TransformationProcessError: _description_
-            Exception: _description_
-        """
-        success = True
-        folio_rec = {}
-        try:
-            self.records_count += 1
-            # Transform the MARC21 to a FOLIO record
-            legacy_id = self.get_legacy_id(
-                self.mapper.task_configuration.legacy_id_marc_path, marc_record
-            )
-            if not legacy_id:
-                raise TransformationRecordFailedError(
-                    self.records_count,
-                    "legacy_id was empty",
-                    self.mapper.task_configuration.legacy_id_marc_path,
-                )
-            folio_rec = self.mapper.parse_hold(marc_record, legacy_id)
-            HoldingsHelper.handle_notes(folio_rec)
-            if not folio_rec.get("instanceId", ""):
-                raise TransformationRecordFailedError(
-                    "".join(folio_rec.get("formerIds", [])),
-                    "Missing instance ids. Something is wrong.",
-                    "",
-                )
-            self.mapper.handle_suppression(folio_rec, file_def, True)
-            self.set_source_id(self.mapper.task_configuration, folio_rec, self.holdingssources)
-            self.set_hrid(marc_record, folio_rec)
-            self.save_srs_record(marc_record, file_def, folio_rec, legacy_id)
-            Helper.write_to_file(self.created_objects_file, folio_rec)
-            self.mapper.migration_report.add_general_statistics("Holdings records written to disk")
-
-            self.exit_on_too_many_exceptions()
-        except TransformationRecordFailedError as error:
-            success = False
-            error.log_it()
-            self.mapper.migration_report.add_general_statistics(
-                "Records that failed transformation. Check log for details",
-            )
-        except TransformationProcessError as tpe:
-            raise tpe from tpe
-        except Exception as inst:
-            success = False
-            traceback.print_exc()
-            logging.error(type(inst))
-            logging.error(inst.args)
-            logging.error(inst)
-            logging.error(marc_record)
-            logging.error(folio_rec)
-            raise inst from inst
-        finally:
-            if not success:
-                self.failed_records_count += 1
-                remove_from_id_map = getattr(self.mapper, "remove_from_id_map", None)
-                if (
-                    callable(remove_from_id_map)
-                    and "folio_rec" in locals()
-                    and folio_rec.get("formerIds", "")
-                ):
-                    self.mapper.remove_from_id_map(folio_rec["formerIds"])
-
-    @staticmethod
-    def get_legacy_id(marc_path: str, marc_record: Record):
-        split = marc_path.split("$", maxsplit=1)
-        if not (split[0].isnumeric() and len(split[0]) == 3):
-            raise TransformationProcessError(
-                "",
-                (
-                    "the marc field used for determining the legacy id is not numeric "
-                    "or does not have the stipulated lenght of 3."
-                    "Make sure the task configuration setting for 'legacyIdMarcPath' "
-                    "is correct or make this piece of code more allowing"
-                ),
-                marc_path,
-            )
-        elif len(split) == 1:
-            return marc_record[split[0]].value()
-        elif len(split) == 2 and len(split[1]) == 1:
-            for field in marc_record.get_fields(split[0]):
-                if sf := field.get_subfields(split[1]):
-                    return sf[0]
-            raise TransformationRecordFailedError("", "Subfield not found in record", split[1])
-
-        else:
-            raise TransformationProcessError(
-                "",
-                ("Something is wrong with 'legacyIdMarcPath' property in the settings"),
-                marc_path,
-            )
-
-    def save_srs_record(self, marc_record, file_def, folio_rec, legacy_id: str):
-        if self.mapper.task_configuration.create_source_records:
-            self.add_hrid_to_records(folio_rec, marc_record)
-            if "008" in marc_record and len(marc_record["008"].data) > 32:
-                remain, rest = (
-                    marc_record["008"].data[:32],
-                    marc_record["008"].data[32:],
-                )
-                marc_record["008"].data = remain
-                self.mapper.migration_report.add(
-                    Blurbs.MarcValidation,
-                    f"008 lenght invalid. '{rest}' was stripped out",
-                )
-            for former_id in folio_rec["formerIds"]:
-                if map_entity := self.mapper.instance_id_map.get(former_id, ""):
-                    new_004 = Field(tag="004", data=map_entity["instance_hrid"])
-                    marc_record.remove_fields("004")
-                    marc_record.add_ordered_field(new_004)
-                if self.mapper.task_configuration.hrid_handling == HridHandling.default:
-                    new_035 = Field(
-                        tag="035",
-                        indicators=[" ", " "],
-                        subfields=["a", former_id],
-                    )
-                    marc_record.add_ordered_field(new_035)
-            self.mapper.save_source_record(
-                self.srs_records_file,
-                FOLIONamespaces.holdings,
-                self.mapper.folio_client,
-                marc_record,
-                folio_rec,
-                legacy_id,
-                file_def.suppressed,
-            )
-            self.mapper.migration_report.add_general_statistics("SRS records written to disk")
-
-    def set_hrid(self, marc_record, folio_rec):
-        if self.mapper.task_configuration.hrid_handling == HridHandling.preserve001:
-            value = marc_record["001"].value()
-            if value in self.unique_001s:
-                self.mapper.migration_report.add(
-                    Blurbs.HridHandling, "Duplicate 001. Creating HRID instead"
-                )
-                Helper.log_data_issue(
-                    folio_rec["formerIds"],
-                    "Duplicate 001 for record. HRID created for record",
-                    value,
-                )
-                num_part = self.generate_num_part()
-                folio_rec["hrid"] = f"{self.mapper.holdings_hrid_prefix}{num_part}"
-                new_001 = Field(tag="001", data=folio_rec["hrid"])
-                marc_record.add_ordered_field(new_001)
-                self.mapper.holdings_hrid_counter += 1
-            else:
-                self.unique_001s.add(value)
-                folio_rec["hrid"] = value
-                self.mapper.migration_report.add(Blurbs.HridHandling, "Took HRID from 001")
-
-    def add_hrid_to_records(self, folio_record: dict, marc_record: Record):
-        if (
-            "hrid" in folio_record
-            and "001" in marc_record
-            and marc_record["001"].value() == folio_record["hrid"]
-        ):
-            return
-        num_part = self.generate_num_part()
-        folio_record["hrid"] = f"{self.mapper.holdings_hrid_prefix}{num_part}"
-        new_001 = Field(tag="001", data=folio_record["hrid"])
-        marc_record.remove_fields("001")
-        marc_record.add_ordered_field(new_001)
-        self.mapper.holdings_hrid_counter += 1
-
-    def generate_num_part(self):
-        return (
-            str(self.mapper.holdings_hrid_counter).zfill(11)
-            if self.mapper.common_retain_leading_zeroes
-            else str(self.mapper.holdings_hrid_counter)
-        )
-
-    @staticmethod
-    def set_source_id(task_configuration, folio_rec, holdingssources):
-        if task_configuration.create_source_records:
-            folio_rec["sourceId"] = holdingssources.get("MARC")
-        else:
-            folio_rec["sourceId"] = holdingssources.get("FOLIO")
-
-    def wrap_up(self):
-        """Finalizes the mapping by writing things out."""
-        self.created_objects_file.close()
-        logging.info(
-            "Saving map of %s old and new IDs to %s",
-            len(self.mapper.holdings_id_map),
-            self.folder_structure.holdings_id_map_path,
-        )
-        self.mapper.save_id_map_file(
-            self.folder_structure.holdings_id_map_path, self.mapper.holdings_id_map
-        )
-        logging.info("%s records processed", self.records_count)
-        with open(self.folder_structure.migration_reports_file, "w+") as report_file:
-            self.mapper.migration_report.write_migration_report(
-                "MFHD records transformation report", report_file, self.mapper.start_datetime
-            )
-            Helper.print_mapping_report(
-                report_file,
-                self.mapper.parsed_records,
-                self.mapper.mapped_folio_fields,
-                self.mapper.mapped_legacy_fields,
-            )
-        self.srs_records_file.close()
-        self.mapper.wrap_up()
-
-        logging.info("Done. Transformation report written to %s", report_file.name)
-        logging.info("Done.")
diff --git a/src/folio_migration_tools/marc_rules_transformation/holdings_statementsparser.py b/src/folio_migration_tools/marc_rules_transformation/holdings_statementsparser.py
index 0777f509..57bc62e5 100644
--- a/src/folio_migration_tools/marc_rules_transformation/holdings_statementsparser.py
+++ b/src/folio_migration_tools/marc_rules_transformation/holdings_statementsparser.py
@@ -2,6 +2,7 @@
 import contextlib
 import logging
 import re
+from typing import List
 
 from pymarc import Field
 from pymarc import Record
@@ -16,17 +17,17 @@ def get_holdings_statements(
         pattern_tag: str,
         value_tag: str,
         field_textual: str,
-        legacy_id: str,
+        legacy_ids: List[str],
         dedupe_results: bool = True,
     ) -> dict:
-        """The main method
+        """_summary_
 
         Args:
             marc_record (Record): _description_
             pattern_tag (str): _description_
             value_tag (str): _description_
             field_textual (str): _description_
-            legacy_id (str): _description_
+            legacy_ids (List[str]): _description_
             dedupe_results (bool): _description_. Defaults to True.
 
         Raises:
@@ -39,16 +40,16 @@ def get_holdings_statements(
         # Textual holdings statements
         return_dict: dict = {"statements": [], "migration_report": [], "hlm_stmts": []}
         HoldingsStatementsParser.get_textual_statements(
-            marc_record, field_textual, return_dict, legacy_id
+            marc_record, field_textual, return_dict, legacy_ids
         )
 
         value_fields = marc_record.get_fields(value_tag)
         for pattern_field in marc_record.get_fields(pattern_tag):
             if "8" not in pattern_field:
                 raise TransformationFieldMappingError(
-                    legacy_id,
+                    legacy_ids,
                     f"{pattern_tag} subfield 8 not in field",
-                    pattern_field.format_field(),
+                    pattern_field,
                 )
             linked_value_fields = [
                 value_field
@@ -73,7 +74,7 @@ def get_holdings_statements(
                         return_dict["hlm_stmts"].append(parsed_dict["hlm_stmt"])
                     if parsed_dict["statement"]:
                         logging.info(
-                            f"HOLDINGS STATEMENT PATTERN\t{legacy_id}\t{pattern_field}"
+                            f"HOLDINGS STATEMENT PATTERN\t{'-'.join(legacy_ids)}\t{pattern_field}"
                             f"\t{linked_value_field}"
                             f"\t{parsed_dict['statement']['statement']}"
                             f"\t{parsed_dict['statement']['note']}"
@@ -134,7 +135,7 @@ def parse_linked_field(pattern_field: Field, linked_value_fields: Field):
 
     @staticmethod
     def get_textual_statements(
-        marc_record: Record, field_textual: str, return_dict: dict, legacy_id: str
+        marc_record: Record, field_textual: str, return_dict: dict, legacy_ids: List[str]
     ):
         """Returns the textual statements from the relevant marc fields
 
@@ -142,27 +143,27 @@ def get_textual_statements(
             marc_record (Record): _description_
             field_textual (str): _description_
             return_dict (dict): _description_
-            legacy_id (str): _description_
+            legacy_ids (List[str]): List of legacy ids in record
 
         Raises:
             TransformationFieldMappingError: _description_
             TransformationFieldMappingError: _description_
         """
         for f in marc_record.get_fields(field_textual):
-            if "a" not in f and "z" not in f:
+            if "a" not in f and "z" not in f and "x" not in f:
                 raise TransformationFieldMappingError(
-                    legacy_id,
-                    f"{field_textual} subfield a or z not in field",
-                    f.format_field(),
+                    legacy_ids,
+                    f"{field_textual} subfields a, x, and z missing from field",
+                    f,
                 )
-            if not (f["a"] or f["z"]):
+            if not (f["a"] or f["z"] or f["x"]):
                 raise TransformationFieldMappingError(
-                    legacy_id,
-                    f"{field_textual} Both a or z are empty",
-                    f.format_field(),
+                    legacy_ids,
+                    f"{field_textual} a,x and z are all empty",
+                    f,
                 )
             return_dict["statements"].append(
-                {"statement": (f["a"] or ""), "note": (f["z"] or ""), "staffNote": ""}
+                {"statement": (f["a"] or ""), "note": (f["z"] or ""), "staffNote": (f["x"] or "")}
             )
             return_dict["migration_report"].append(
                 ("Holdings statements", f"From {field_textual}")
diff --git a/src/folio_migration_tools/marc_rules_transformation/hrid_handler.py b/src/folio_migration_tools/marc_rules_transformation/hrid_handler.py
index 609c762e..b4c8603a 100644
--- a/src/folio_migration_tools/marc_rules_transformation/hrid_handler.py
+++ b/src/folio_migration_tools/marc_rules_transformation/hrid_handler.py
@@ -71,7 +71,9 @@ def generate_enumerated_hrid(
     ):
         folio_record["hrid"] = self.get_next_hrid(namespace)
         new_001 = Field(tag="001", data=folio_record["hrid"])
-        self.handle_035_generation(marc_record, legacy_ids)
+        self.handle_035_generation(
+            marc_record, legacy_ids, self.migration_report, self.deactivate035_from001
+        )
         marc_record.add_ordered_field(new_001)
         self.migration_report.add(Blurbs.HridHandling, "Created HRID using default settings")
 
@@ -99,16 +101,21 @@ def get_next_hrid(self, namespace: FOLIONamespaces):
     def generate_numeric_part(self, counter):
         return str(counter).zfill(11) if self.common_retain_leading_zeroes else str(counter)
 
-    def handle_035_generation(self, marc_record, legacy_ids):
+    @staticmethod
+    def handle_035_generation(
+        marc_record: Record,
+        legacy_ids,
+        migration_report: MigrationReport,
+        deactivate035_from001: bool,
+        remove_001: bool = True,
+    ):
         try:
             f_001 = marc_record["001"].value()
             f_003 = marc_record["003"].value().strip() if "003" in marc_record else ""
-            self.migration_report.add(Blurbs.HridHandling, f'Values in 003: {f_003 or "Empty"}')
+            migration_report.add(Blurbs.HridHandling, f'Values in 003: {f_003 or "Empty"}')
 
-            if self.deactivate035_from001:
-                self.migration_report.add(
-                    Blurbs.HridHandling, "035 generation from 001 turned off"
-                )
+            if deactivate035_from001:
+                migration_report.add(Blurbs.HridHandling, "035 generation from 001 turned off")
             else:
                 str_035 = f"({f_003}){f_001}" if f_003 else f"{f_001}"
                 new_035 = Field(
@@ -117,16 +124,17 @@ def handle_035_generation(self, marc_record, legacy_ids):
                     subfields=["a", str_035],
                 )
                 marc_record.add_ordered_field(new_035)
-                self.migration_report.add(Blurbs.HridHandling, "Added 035 from 001")
-            marc_record.remove_fields("001")
+                migration_report.add(Blurbs.HridHandling, "Added 035 from 001")
+            if remove_001:
+                marc_record.remove_fields("001")
 
         except Exception:
             if "001" in marc_record:
                 s = "Failed to create 035 from 001"
-                self.migration_report.add(Blurbs.HridHandling, s)
+                migration_report.add(Blurbs.HridHandling, s)
                 Helper.log_data_issue(legacy_ids, s, marc_record["001"])
             else:
-                self.migration_report.add(Blurbs.HridHandling, "Legacy bib records without 001")
+                migration_report.add(Blurbs.HridHandling, "Legacy bib records without 001")
 
     def preserve_001_as_hrid(
         self,
@@ -142,7 +150,9 @@ def preserve_001_as_hrid(
                 "Duplicate 001. Creating HRID instead. "
                 "Previous 001 will be stored in a new 035 field",
             )
-            self.handle_035_generation(marc_record, legacy_ids)
+            self.handle_035_generation(
+                marc_record, legacy_ids, self.migration_report, self.deactivate035_from001
+            )
             Helper.log_data_issue(
                 legacy_ids,
                 "Duplicate 001 for record. HRID created for record",
diff --git a/src/folio_migration_tools/marc_rules_transformation/marc_file_processor.py b/src/folio_migration_tools/marc_rules_transformation/marc_file_processor.py
new file mode 100644
index 00000000..967e9d89
--- /dev/null
+++ b/src/folio_migration_tools/marc_rules_transformation/marc_file_processor.py
@@ -0,0 +1,264 @@
+import logging
+import sys
+import time
+import traceback
+from typing import List
+
+from folio_uuid.folio_namespaces import FOLIONamespaces
+from pymarc import Field
+from pymarc import Record
+
+from folio_migration_tools.custom_exceptions import TransformationProcessError
+from folio_migration_tools.custom_exceptions import TransformationRecordFailedError
+from folio_migration_tools.folder_structure import FolderStructure
+from folio_migration_tools.helper import Helper
+from folio_migration_tools.library_configuration import FileDefinition
+from folio_migration_tools.library_configuration import HridHandling
+from folio_migration_tools.marc_rules_transformation.rules_mapper_base import (
+    RulesMapperBase,
+)
+from folio_migration_tools.migration_report import MigrationReport
+from folio_migration_tools.report_blurbs import Blurbs
+
+
+class MarcFileProcessor:
+    def __init__(
+        self, mapper: RulesMapperBase, folder_structure: FolderStructure, created_objects_file
+    ):
+        self.object_type: FOLIONamespaces = folder_structure.object_type
+        self.folder_structure: FolderStructure = folder_structure
+        self.mapper: RulesMapperBase = mapper
+        self.created_objects_file = created_objects_file
+        self.srs_records_file = open(self.folder_structure.srs_records_path, "w+")
+        self.unique_001s: set = set()
+        self.failed_records_count: int = 0
+        self.records_count: int = 0
+        self.start: float = time.time()
+        self.legacy_ids: set = set()
+        if (
+            self.object_type == FOLIONamespaces.holdings
+            and self.mapper.task_configuration.create_source_records
+        ):
+            logging.info("Loading Parent HRID map for SRS creation")
+            self.parent_hrids = {entity[1]: entity[2] for entity in mapper.parent_id_map.values()}
+
+    def process_record(self, idx: int, marc_record: Record, file_def: FileDefinition):
+        """processes a marc holdings record and saves it
+
+        Args:
+            idx (int): Index in file being parsed
+            marc_record (Record): _description_
+            file_def (FileDefinition): _description_
+
+        Raises:
+            TransformationProcessError: _description_
+            TransformationRecordFailedError: _description_
+        """
+        success = True
+        folio_rec = {}
+        self.records_count += 1
+        try:
+            # Transform the MARC21 to a FOLIO record
+            legacy_ids = self.mapper.get_legacy_ids(marc_record, idx)
+            if not legacy_ids:
+                raise TransformationRecordFailedError(
+                    f"Index in file: {idx}", "No legacy id found", idx
+                )
+            folio_rec = self.mapper.parse_record(marc_record, file_def, legacy_ids)
+            filtered_legacy_ids = self.get_valid_folio_record_ids(
+                legacy_ids,
+                self.legacy_ids,
+                self.mapper.migration_report,
+            )
+            self.add_legacy_ids_to_map(folio_rec, filtered_legacy_ids)
+
+            self.save_srs_record(
+                marc_record,
+                file_def,
+                folio_rec,
+                legacy_ids,
+                self.object_type,
+            )
+            Helper.write_to_file(self.created_objects_file, folio_rec)
+            self.mapper.migration_report.add_general_statistics(
+                "Inventory records written to disk"
+            )
+
+            self.exit_on_too_many_exceptions()
+        except TransformationRecordFailedError as error:
+            success = False
+            raise TransformationRecordFailedError(
+                f"{error.index_or_id} in {file_def.file_name}", error.message, error.data_value
+            ) from error
+        except TransformationProcessError as tpe:
+            raise TransformationProcessError(
+                f"{tpe.index_or_id} in {file_def.file_name}", tpe.message, tpe.data_value
+            ) from tpe
+        except Exception as inst:
+            success = False
+            traceback.print_exc()
+            logging.error(type(inst))
+            logging.error(inst.args)
+            logging.error(inst)
+            logging.error(marc_record)
+            logging.error(folio_rec)
+            raise TransformationProcessError("", inst.args, "") from inst
+        finally:
+            if not success:
+                self.failed_records_count += 1
+                remove_from_id_map = getattr(self.mapper, "remove_from_id_map", None)
+                if (
+                    callable(remove_from_id_map)
+                    and "folio_rec" in locals()
+                    and folio_rec.get("formerIds", "")
+                ):
+                    self.mapper.remove_from_id_map(folio_rec.get("formerIds", []))
+
+    def save_srs_record(
+        self,
+        marc_record: Record,
+        file_def,
+        folio_rec,
+        legacy_ids: List[str],
+        object_type: FOLIONamespaces,
+    ):
+        if not self.mapper.task_configuration.create_source_records:
+            return
+        if object_type in [FOLIONamespaces.holdings, FOLIONamespaces.instances]:
+            self.add_hrid_to_records(folio_rec, marc_record)
+        if object_type in [FOLIONamespaces.holdings]:
+            if "008" in marc_record and len(marc_record["008"].data) > 32:
+                remain, rest = (
+                    marc_record["008"].data[:32],
+                    marc_record["008"].data[32:],
+                )
+                marc_record["008"].data = remain
+                self.mapper.migration_report.add(
+                    Blurbs.MarcValidation,
+                    f"008 lenght invalid. '{rest}' was stripped out",
+                )
+            self.add_mapped_location_code_to_record(marc_record, folio_rec)
+            new_004 = Field(tag="004", data=self.parent_hrids[folio_rec["instanceId"]])
+            marc_record.remove_fields("004")
+            marc_record.add_ordered_field(new_004)
+            for former_id in legacy_ids:
+                if self.mapper.task_configuration.hrid_handling == HridHandling.default:
+                    new_035 = Field(
+                        tag="035",
+                        indicators=[" ", " "],
+                        subfields=["a", former_id],
+                    )
+                    marc_record.add_ordered_field(new_035)
+        self.mapper.save_source_record(
+            self.srs_records_file,
+            self.object_type,
+            self.mapper.folio_client,
+            marc_record,
+            folio_rec,
+            legacy_ids,
+            file_def.suppressed,
+        )
+        self.mapper.migration_report.add_general_statistics("SRS records written to disk")
+
+    def add_mapped_location_code_to_record(self, marc_record, folio_rec):
+        location_code = next(
+            (
+                location["code"]
+                for location in self.mapper.folio_client.locations
+                if location["id"] == folio_rec["permanentLocationId"]
+            ),
+            None,
+        )
+        marc_record["852"]["b"] = location_code
+        self.mapper.migration_report.add(Blurbs.LocationMapping, "Set 852 to FOLIO location code")
+
+    def add_hrid_to_records(self, folio_record: dict, marc_record: Record):
+        if (
+            "hrid" in folio_record
+            and "001" in marc_record
+            and marc_record["001"].value() == folio_record["hrid"]
+        ):
+            return
+        num_part = self.generate_num_part()
+        folio_record["hrid"] = f"{self.mapper.holdings_hrid_prefix}{num_part}"
+        new_001 = Field(tag="001", data=folio_record["hrid"])
+        marc_record.remove_fields("001")
+        marc_record.add_ordered_field(new_001)
+        self.mapper.holdings_hrid_counter += 1
+
+    def exit_on_too_many_exceptions(self):
+        if (
+            self.failed_records_count / (self.records_count + 1)
+            > (self.mapper.library_configuration.failed_percentage_threshold / 100)
+            and self.failed_records_count
+            > self.mapper.library_configuration.failed_records_threshold
+        ):
+            logging.critical("More than 20 percent of the records have failed. Halting")
+            sys.exit(1)
+
+    @staticmethod
+    def get_valid_folio_record_ids(
+        legacy_ids, folio_record_identifiers, migration_report: MigrationReport
+    ):
+        new_ids = set()
+        for legacy_id in legacy_ids:
+            if legacy_id not in folio_record_identifiers:
+                new_ids.add(legacy_id)
+            else:
+                migration_report.add_general_statistics("Duplicate MARC record identifiers ")
+        if not any(new_ids):
+            s = "Failed records. No unique record identifiers in legacy record"
+            migration_report.add_general_statistics(s)
+            raise TransformationRecordFailedError(
+                "-".join(legacy_ids),
+                "Duplicate recod identifier(s). See logs. Record Failed",
+                "-".join(legacy_ids),
+            )
+        return list(new_ids)
+
+    def generate_num_part(self):
+        return (
+            str(self.mapper.holdings_hrid_counter).zfill(11)
+            if self.mapper.common_retain_leading_zeroes
+            else str(self.mapper.holdings_hrid_counter)
+        )
+
+    def wrap_up(self):
+        """Finalizes the mapping by writing things out."""
+        logging.info(
+            "Saving map of %s old and new IDs to %s",
+            len(self.mapper.id_map),
+            self.folder_structure.id_map_path,
+        )
+        self.mapper.save_id_map_file(self.folder_structure.id_map_path, self.mapper.id_map)
+        logging.info("%s records processed", self.records_count)
+        with open(self.folder_structure.migration_reports_file, "w+") as report_file:
+            self.mapper.migration_report.write_migration_report(
+                "MFHD records transformation report", report_file, self.mapper.start_datetime
+            )
+            Helper.print_mapping_report(
+                report_file,
+                self.mapper.parsed_records,
+                self.mapper.mapped_folio_fields,
+                self.mapper.mapped_legacy_fields,
+            )
+        self.srs_records_file.close()
+        self.mapper.wrap_up()
+
+        logging.info("Transformation report written to %s", report_file.name)
+        logging.info("Processor is done.")
+
+    def add_legacy_ids_to_map(self, folio_rec, filtered_legacy_ids):
+        for legacy_id in filtered_legacy_ids:
+            self.legacy_ids.add(legacy_id)
+            if legacy_id not in self.mapper.id_map:
+                self.mapper.id_map[legacy_id] = self.mapper.get_id_map_tuple(
+                    legacy_id, folio_rec, self.object_type
+                )
+
+            else:
+                raise TransformationRecordFailedError(
+                    legacy_id,
+                    "Legacy ID already added to Legacy Id map.",
+                    ",".join(filtered_legacy_ids),
+                )
diff --git a/src/folio_migration_tools/marc_rules_transformation/marc_reader_wrapper.py b/src/folio_migration_tools/marc_rules_transformation/marc_reader_wrapper.py
new file mode 100644
index 00000000..43823f00
--- /dev/null
+++ b/src/folio_migration_tools/marc_rules_transformation/marc_reader_wrapper.py
@@ -0,0 +1,123 @@
+import logging
+import sys
+from io import IOBase
+from pathlib import Path
+
+from pymarc import MARCReader
+from pymarc import Record
+
+from folio_migration_tools.custom_exceptions import TransformationProcessError
+from folio_migration_tools.custom_exceptions import TransformationRecordFailedError
+from folio_migration_tools.folder_structure import FolderStructure
+from folio_migration_tools.library_configuration import FileDefinition
+from folio_migration_tools.migration_report import MigrationReport
+from folio_migration_tools.report_blurbs import Blurbs
+
+
+class MARCReaderWrapper:
+    @staticmethod
+    def process_single_file(
+        file_def: FileDefinition,
+        processor,
+        failed_records_path: Path,
+        folder_structure: FolderStructure,
+    ):
+        try:
+            with open(failed_records_path, "ab") as failed_marc_records_file:
+                with open(
+                    folder_structure.legacy_records_folder / file_def.file_name,
+                    "rb",
+                ) as marc_file:
+                    reader = MARCReader(marc_file, to_unicode=True, permissive=True)
+                    reader.hide_utf8_warnings = True
+                    reader.force_utf8 = False
+                    logging.info("Running %s", file_def.file_name)
+                    MARCReaderWrapper.read_records(
+                        reader, file_def, failed_marc_records_file, processor
+                    )
+        except TransformationProcessError as tpe:
+            logging.critical(tpe)
+            sys.exit(1)
+        except Exception:
+            logging.exception("Failure in Main: %s", file_def.file_name, stack_info=True)
+
+    @staticmethod
+    def read_records(
+        reader,
+        source_file: FileDefinition,
+        failed_records_file: IOBase,
+        processor,
+    ):
+        for idx, record in enumerate(reader):
+            processor.mapper.migration_report.add_general_statistics(
+                "Records in file before parsing"
+            )
+            try:
+                # None = Something bad happened
+                if record is None:
+                    report_failed_parsing(
+                        reader,
+                        source_file,
+                        failed_records_file,
+                        idx,
+                        processor.mapper.migration_report,
+                    )
+                # The normal case
+                else:
+                    MARCReaderWrapper.set_leader(record, processor.mapper.migration_report)
+                    processor.mapper.migration_report.add_general_statistics(
+                        "Records successfully decoded from MARC21",
+                    )
+                    processor.process_record(idx, record, source_file)
+            except TransformationRecordFailedError as error:
+                error.log_it()
+                processor.mapper.migration_report.add_general_statistics(
+                    "Records that failed transformation. Check log for details",
+                )
+            except ValueError as error:
+                logging.error(error)
+        logging.info("Done reading %s records from file", idx + 1)
+
+    @staticmethod
+    def set_leader(marc_record: Record, migration_report: MigrationReport):
+        if marc_record.leader[9] != "a":
+            migration_report.add(
+                Blurbs.LeaderManipulation,
+                f"Set leader 09 (Character coding scheme) from {marc_record.leader[9]} to a",
+            )
+            marc_record.leader = f"{marc_record.leader[:9]}a{marc_record.leader[10:]}"
+
+        if not marc_record.leader.endswith("4500"):
+            migration_report.add(
+                Blurbs.LeaderManipulation,
+                f"Set leader 20-23 from {marc_record.leader[-4:]} to 4500",
+            )
+            marc_record.leader = f"{marc_record.leader[:-4]}4500"
+
+        if marc_record.leader[10] != "2":
+            migration_report.add(
+                Blurbs.LeaderManipulation,
+                f"Set leader 10 (Indicator count) from {marc_record.leader[10]} to 2",
+            )
+            marc_record.leader = f"{marc_record.leader[:10]}2{marc_record.leader[11:]}"
+
+        if marc_record.leader[11] != "2":
+            migration_report.add(
+                Blurbs.LeaderManipulation,
+                f"Set leader 10 (Subfield code count) from {marc_record.leader[11]} to 2",
+            )
+            marc_record.leader = f"{marc_record.leader[:11]}2{marc_record.leader[12:]}"
+
+
+def report_failed_parsing(
+    reader, source_file, failed_bibs_file, idx, migration_report: MigrationReport
+):
+    migration_report.add_general_statistics(
+        "Records with encoding errors - parsing failed",
+    )
+    failed_bibs_file.write(reader.current_chunk)
+    raise TransformationRecordFailedError(
+        f"Index in {source_file.file_name}:{idx}",
+        f"MARC parsing error: {reader.current_exception}",
+        "Failed records stored in results/failed_bib_records.mrc",
+    )
diff --git a/src/folio_migration_tools/marc_rules_transformation/rules_mapper_autorities.py b/src/folio_migration_tools/marc_rules_transformation/rules_mapper_autorities.py
new file mode 100644
index 00000000..a2a92201
--- /dev/null
+++ b/src/folio_migration_tools/marc_rules_transformation/rules_mapper_autorities.py
@@ -0,0 +1,160 @@
+"""The default mapper, responsible for parsing MARC21 records acording to the
+FOLIO community specifications"""
+import logging
+import time
+import uuid
+from typing import List
+
+import pymarc
+from folio_uuid.folio_namespaces import FOLIONamespaces
+from folio_uuid.folio_uuid import FolioUUID
+from folioclient import FolioClient
+from pymarc import Record
+
+from folio_migration_tools.custom_exceptions import TransformationProcessError
+from folio_migration_tools.library_configuration import FileDefinition
+from folio_migration_tools.library_configuration import IlsFlavour
+from folio_migration_tools.library_configuration import LibraryConfiguration
+from folio_migration_tools.marc_rules_transformation.conditions import Conditions
+from folio_migration_tools.marc_rules_transformation.hrid_handler import HRIDHandler
+from folio_migration_tools.marc_rules_transformation.rules_mapper_base import (
+    RulesMapperBase,
+)
+
+
+class AuthorityMapper(RulesMapperBase):
+    non_repatable_fields = [
+        "100",
+        "110",
+        "111",
+        "130",
+        "147",
+        "148",
+        "150",
+        "151",
+        "155",
+        "162",
+        "180",
+        "181",
+        "182",
+        "185",
+        "378",
+        "384",
+    ]
+    """_summary_
+
+    Args:
+        RulesMapperBase (_type_): _description_
+    """
+
+    def __init__(
+        self,
+        folio_client,
+        library_configuration: LibraryConfiguration,
+        task_configuration,
+    ):
+        super().__init__(
+            folio_client,
+            library_configuration,
+            task_configuration,
+            self.get_autority_json_schema(),
+            Conditions(folio_client, self, "auth", library_configuration.folio_release),
+        )
+        self.srs_recs: list = []
+        logging.info("Fetching mapping rules from the tenant")
+        rules_endpoint = "/mapping-rules/marc-authority"
+        self.mappings = self.folio_client.folio_get_single_object(rules_endpoint)
+        self.start = time.time()
+
+    def get_legacy_ids(self, marc_record: Record, idx: int) -> List[str]:
+        ils_flavour: IlsFlavour = self.task_configuration.ils_flavour
+        if ils_flavour in {IlsFlavour.sierra, IlsFlavour.millennium}:
+            raise TransformationProcessError("", f"ILS {ils_flavour} not configured")
+        elif ils_flavour == IlsFlavour.tag907y:
+            return RulesMapperBase.get_bib_id_from_907y(marc_record, idx)
+        elif ils_flavour == IlsFlavour.tagf990a:
+            return RulesMapperBase.get_bib_id_from_990a(marc_record, idx)
+        elif ils_flavour == IlsFlavour.aleph:
+            raise TransformationProcessError("", f"ILS {ils_flavour} not configured")
+        elif ils_flavour in {IlsFlavour.voyager, "voyager", IlsFlavour.tag001}:
+            return RulesMapperBase.get_bib_id_from_001(marc_record, idx)
+        elif ils_flavour == IlsFlavour.koha:
+            raise TransformationProcessError("", f"ILS {ils_flavour} not configured")
+        elif ils_flavour == IlsFlavour.none:
+            return [str(uuid.uuid4())]
+        else:
+            raise TransformationProcessError("", f"ILS {ils_flavour} not configured")
+
+    def parse_record(
+        self, marc_record: pymarc.Record, file_def: FileDefinition, legacy_ids: List[str]
+    ) -> dict:
+        """Parses an auth recod into a FOLIO Authority object
+         This is the main function
+
+        Args:
+            legacy_ids (_type_): _description_
+            marc_record (Record): _description_
+            file_def (FileDefinition): _description_
+
+        Returns:
+            dict: _description_
+        """
+        self.print_progress()
+        ignored_subsequent_fields: set = set()
+        bad_tags = set(self.task_configuration.tags_to_delete)  # "907"
+        folio_authority = self.perform_initial_preparation(marc_record, legacy_ids)
+        for marc_field in marc_record:
+            self.report_marc_stats(marc_field, bad_tags, legacy_ids, ignored_subsequent_fields)
+            if marc_field.tag not in ignored_subsequent_fields:
+                self.process_marc_field(
+                    folio_authority,
+                    marc_field,
+                    ignored_subsequent_fields,
+                    legacy_ids,
+                )
+
+        self.perform_additional_parsing(folio_authority)
+        clean_folio_authority = self.validate_required_properties(
+            "-".join(legacy_ids), folio_authority, self.schema, FOLIONamespaces.instances
+        )
+        self.dedupe_rec(clean_folio_authority)
+        marc_record.remove_fields(*list(bad_tags))
+        self.report_folio_mapping(clean_folio_authority, self.schema)
+        return clean_folio_authority
+
+    def perform_initial_preparation(self, marc_record: pymarc.Record, legacy_ids):
+        folio_authority = {
+            "metadata": self.folio_client.get_metadata_construct(),
+        }
+        folio_authority["id"] = str(
+            FolioUUID(
+                str(self.folio_client.okapi_url),
+                FOLIONamespaces.authorities,
+                str(legacy_ids[-1]),
+            )
+        )
+        HRIDHandler.handle_035_generation(
+            marc_record, legacy_ids, self.migration_report, False, False
+        )
+
+        return folio_authority
+
+    def perform_additional_parsing(
+        self,
+        folio_authority: dict,
+    ) -> None:
+        """Do stuff not easily captured by the mapping rules
+
+        Args:
+            folio_authority (dict): _description_
+        """
+        folio_authority["source"] = "MARC"
+
+    def get_autority_json_schema(self):
+        """Fetches the JSON Schema for autorities"""
+        return FolioClient.get_latest_from_github(
+            "folio-org", "mod-inventory-storage", "/ramls/authorities/authority.json"
+        )
+
+    def wrap_up(self):
+        logging.info("Mapper wrapping up")
diff --git a/src/folio_migration_tools/marc_rules_transformation/rules_mapper_base.py b/src/folio_migration_tools/marc_rules_transformation/rules_mapper_base.py
index 9f52718c..58fa3401 100644
--- a/src/folio_migration_tools/marc_rules_transformation/rules_mapper_base.py
+++ b/src/folio_migration_tools/marc_rules_transformation/rules_mapper_base.py
@@ -2,8 +2,11 @@
 import json
 import logging
 import time
+import urllib.parse
 import uuid
+from abc import abstractmethod
 from textwrap import wrap
+from typing import List
 
 import pymarc
 from folio_uuid.folio_uuid import FOLIONamespaces
@@ -30,30 +33,31 @@ def __init__(
         folio_client: FolioClient,
         library_configuration: LibraryConfiguration,
         task_configuration,
+        schema: dict,
         conditions=None,
+        parent_id_map: dict[str, tuple] = None,
     ):
-        super().__init__(library_configuration, folio_client)
+        super().__init__(library_configuration, folio_client, parent_id_map)
         self.parsed_records = 0
+        self.id_map: dict[str, tuple] = {}
         self.start = time.time()
         self.last_batch_time = time.time()
         self.folio_client: FolioClient = folio_client
-        self.holdings_json_schema = self.fetch_holdings_schema()
-        self.instance_json_schema = self.get_instance_schema()
-        self.schema: dict = {}
+        self.schema: dict = schema
         self.task_configuration = task_configuration
         self.conditions = conditions
         self.item_json_schema = ""
         self.mappings: dict = {}
         self.schema_properties = None
-        self.hrid_handler = HRIDHandler(
-            folio_client,
-            self.task_configuration.hrid_handling,
-            self.migration_report,
-            self.task_configuration.deactivate035_from001,
-        )
+        if hasattr(self.task_configuration, "hrid_handling"):
+            self.hrid_handler = HRIDHandler(
+                folio_client,
+                self.task_configuration.hrid_handling,
+                self.migration_report,
+                self.task_configuration.deactivate035_from001,
+            )
         logging.info("Current user id is %s", self.folio_client.current_user)
 
-    # TODO: Rebuild and move
     def print_progress(self):
         self.parsed_records += 1
         num_recs = 5000
@@ -68,6 +72,10 @@ def print_progress(self):
             )
             self.last_batch_time = time.time()
 
+    @abstractmethod
+    def get_legacy_ids(self, marc_record: Record, idx: int):
+        raise NotImplementedError()
+
     @staticmethod
     def dedupe_rec(rec, props_to_not_dedupe=None):
         if props_to_not_dedupe is None:
@@ -101,7 +109,6 @@ def handle_normal_mapping(self, mapping, marc_field: pymarc.Field, folio_record,
             marc_field = self.remove_repeated_subfields(marc_field)
         if has_conditions(mapping):
             values = self.apply_rules(marc_field, mapping, legacy_ids)
-            # TODO: add condition to customize this hardcoded thing
             if marc_field.tag == "655":
                 values[0] = f"Genre: {values[0]}"
             self.add_value_to_target(folio_record, target, values)
@@ -137,6 +144,12 @@ def set_005_as_updated_date(marc_record: Record, folio_object: dict, legacy_ids)
                     marc_record["005"].data,
                 )
 
+    @abstractmethod
+    def parse_record(
+        self, marc_record: Record, file_def: FileDefinition, legacy_ids: List[str]
+    ) -> dict:
+        raise NotImplementedError()
+
     @staticmethod
     def use_008_for_dates(marc_record: Record, folio_object: dict, legacy_ids):
         try:
@@ -176,6 +189,109 @@ def get_value_from_condition(
             value1 = marc_field.format_field() if marc_field else ""
             return self.apply_rule(legacy_id, value1, condition_types, marc_field, parameter)
 
+    def process_marc_field(
+        self,
+        folio_record: dict,
+        marc_field: Field,
+        ignored_subsequent_fields,
+        legacy_ids,
+    ):
+        if marc_field.tag == "880":
+            mappings = self.perform_proxy_mapping(marc_field)
+        else:
+            tags_to_ignore = {"880", "001", "008"}
+            mappings = (
+                self.mappings.get(marc_field.tag, {})
+                if marc_field.tag not in tags_to_ignore
+                else []
+            )
+        if mappings:
+            try:
+                self.map_field_according_to_mapping(marc_field, mappings, folio_record, legacy_ids)
+                if any(m.get("ignoreSubsequentFields", False) for m in mappings):
+                    ignored_subsequent_fields.add(marc_field.tag)
+            except TransformationFieldMappingError as tre:
+                tre.log_it()
+                logging.error(
+                    "map_field_according_to_mapping %s %s %s %s %s",
+                    "-".join(legacy_ids),
+                    marc_field.tag,
+                    marc_field.format_field(),
+                    json.dumps(mappings),
+                    tre.message,
+                )
+            except Exception as ee:
+                logging.error(
+                    "map_field_according_to_mapping %s %s %s",
+                    marc_field.tag,
+                    marc_field.format_field(),
+                    json.dumps(mappings),
+                )
+                raise ee
+
+    def perform_proxy_mapping(self, marc_field):
+        proxy_mapping = next(iter(self.mappings.get("880", [])), [])
+        if "6" not in marc_field:
+            self.migration_report.add(Blurbs.Field880Mappings, "Records without $6")
+            return None
+        if not proxy_mapping or not proxy_mapping.get("fieldReplacementBy3Digits", False):
+            return None
+        if not marc_field["6"][:3] or len(marc_field["6"][:3]) != 3:
+            self.migration_report.add(
+                Blurbs.Field880Mappings, "Records with unexpected length in $6"
+            )
+            return None
+        first_three = marc_field["6"][:3]
+
+        target_field = next(
+            (
+                r.get("targetField", "")
+                for r in proxy_mapping.get("fieldReplacementRule", [])
+                if r["sourceDigits"] == first_three
+            ),
+            first_three,
+        )
+        self.migration_report.add(
+            Blurbs.Field880Mappings,
+            f"Source digits: {marc_field['6']} Target field: {target_field}",
+        )
+        mappings = self.mappings.get(target_field, {})
+        if not mappings:
+            self.migration_report.add(
+                Blurbs.Field880Mappings,
+                f"Mapping not set up for target field: {target_field} ({marc_field['6']})",
+            )
+        return mappings
+
+    def report_marc_stats(
+        self, marc_field: Field, bad_tags, legacy_ids, ignored_subsequent_fields
+    ):
+        self.migration_report.add(Blurbs.Trivia, "Total number of Tags processed")
+        self.report_source_and_links(marc_field)
+        self.report_bad_tags(marc_field, bad_tags, legacy_ids)
+        mapped = marc_field.tag in self.mappings
+        if marc_field.tag in ignored_subsequent_fields:
+            mapped = False
+        self.report_legacy_mapping(marc_field.tag, True, mapped)
+
+    def report_source_and_links(self, marc_field: Field):
+        for subfield_2 in marc_field.get_subfields("2"):
+            self.migration_report.add(
+                Blurbs.AuthoritySources, f"Source of heading or term: {subfield_2.split(' ')[0]}"
+            )
+        for subfield_0 in marc_field.get_subfields("0"):
+            code = ""
+            if "(" in subfield_0 and ")" in subfield_0:
+                code = subfield_0[subfield_0.find("(") + 1 : subfield_0.find(")")]
+                code = code.split(" ")[0]
+            elif url := urllib.parse.urlparse(subfield_0):
+                if url.hostname:
+                    code = subfield_0[: subfield_0.find(url.path)]
+            if code:
+                self.migration_report.add(
+                    Blurbs.AuthoritySources, f"$0 base uri or source code: {code}"
+                )
+
     def apply_rules(self, marc_field: pymarc.Field, mapping, legacy_ids):
         try:
             values = []
@@ -214,6 +330,17 @@ def apply_rules(self, marc_field: pymarc.Field, mapping, legacy_ids):
         except Exception as exception:
             self.handle_generic_exception(self.parsed_records, exception)
 
+    def report_bad_tags(self, marc_field, bad_tags, legacy_ids):
+        if (
+            (not marc_field.tag.isnumeric())
+            and marc_field.tag != "LDR"
+            and marc_field.tag not in bad_tags
+        ):
+            self.migration_report.add(Blurbs.NonNumericTagsInRecord, marc_field.tag)
+            message = "Non-numeric tags in records"
+            Helper.log_data_issue(legacy_ids, message, marc_field.tag)
+            bad_tags.add(marc_field.tag)
+
     def add_value_to_target(self, rec, target_string, value):
         if not value:
             return
@@ -287,10 +414,10 @@ def add_value_to_first_level_target(self, rec, target_string, value):
         sch = self.schema["properties"]
 
         if not target_string or target_string not in sch:
-            raise TransformationProcessError(
+            raise TransformationFieldMappingError(
                 "",
                 (
-                    f"Target string {target_string} not in Schema! "
+                    f"Target string '{target_string}' not in Schema! "
                     "Check mapping file against the schema. "
                     f"Target type: {sch.get(target_string,{}).get('type','')} Value: {value}"
                 ),
@@ -320,6 +447,16 @@ def add_value_to_first_level_target(self, rec, target_string, value):
                 ),
             )
 
+    def remove_from_id_map(self, former_ids: List[str]):
+        """removes the ID from the map in case parsing failed
+
+        Args:
+            former_ids (_type_): _description_
+        """
+        for former_id in [id for id in former_ids if id]:
+            if former_id in self.id_map:
+                del self.id_map[former_id]
+
     def create_entity(self, entity_mappings, marc_field, entity_parent_key, index_or_legacy_id):
         entity = {}
         for entity_mapping in entity_mappings:
@@ -429,7 +566,14 @@ def create_preceding_succeeding_titles(self, entity, e_parent, identifier):
     def apply_rule(self, legacy_id, value, condition_types, marc_field, parameter):
         v = value
         for condition_type in iter(condition_types):
-            v = self.conditions.get_condition(condition_type, legacy_id, v, parameter, marc_field)
+            try:
+                v = self.conditions.get_condition(
+                    condition_type, legacy_id, v, parameter, marc_field
+                )
+            except AttributeError as attr_error:
+                raise TransformationProcessError(
+                    legacy_id, attr_error, condition_type
+                ) from attr_error
         return v
 
     @staticmethod
@@ -443,24 +587,6 @@ def add_entity_to_record(entity, entity_parent_key, rec, schema):
         else:
             rec[entity_parent_key] = entity
 
-    @staticmethod
-    def get_instance_schema():
-        logging.info("Fetching Instance schema...")
-        instance_schema = FolioClient.get_latest_from_github(
-            "folio-org", "mod-inventory-storage", "ramls/instance.json"
-        )
-        logging.info("done")
-        return instance_schema
-
-    @staticmethod
-    def fetch_holdings_schema():
-        logging.info("Fetching HoldingsRecord schema...")
-        holdings_record_schema = FolioClient.get_latest_from_github(
-            "folio-org", "mod-inventory-storage", "ramls/holdingsrecord.json"
-        )
-        logging.info("done")
-        return holdings_record_schema
-
     @staticmethod
     def grouped(marc_field: Field):
         """Groups the subfields
@@ -526,7 +652,7 @@ def save_source_record(
         folio_client: FolioClient,
         marc_record: Record,
         folio_record,
-        legacy_id: str,
+        legacy_ids: List[str],
         suppress: bool,
     ):
         """Saves the source Marc_record to the Source record Storage module
@@ -537,10 +663,10 @@ def save_source_record(
             folio_client (FolioClient): _description_
             marc_record (Record): _description_
             folio_record (_type_): _description_
-            legacy_id (str): _description_
+            legacy_ids (List[str]): _description_
             suppress (bool): _description_
         """
-        srs_id = RulesMapperBase.create_srs_id(record_type, folio_client.okapi_url, legacy_id)
+        srs_id = RulesMapperBase.create_srs_id(record_type, folio_client.okapi_url, legacy_ids[-1])
 
         marc_record.add_ordered_field(
             Field(
@@ -578,13 +704,46 @@ def create_srs_id(record_type, okapi_url: str, legacy_id: str):
             FOLIONamespaces.edifact: FOLIONamespaces.srs_records_edifact,
         }
 
-        return str(
-            FolioUUID(
-                okapi_url,
-                srs_types.get(record_type),
-                str(legacy_id),
+        return str(FolioUUID(okapi_url, srs_types.get(record_type), legacy_id))
+
+    @staticmethod
+    def get_bib_id_from_907y(marc_record: Record, index_or_legacy_id):
+        try:
+            return list(set(marc_record["907"].get_subfields("a", "y")))
+        except Exception as e:
+            raise TransformationRecordFailedError(
+                index_or_legacy_id,
+                (
+                    "907 $y and $a is missing is missing, although they is "
+                    "required for this legacy ILS choice"
+                ),
+                marc_record.as_json(),
+            ) from e
+
+    @staticmethod
+    def get_bib_id_from_990a(marc_record: Record, index_or_legacy_id):
+        res = {f["a"].strip() for f in marc_record.get_fields("990") if "a" in f}
+        if marc_record["001"].format_field().strip():
+            res.add(marc_record["001"].format_field().strip())
+        if any(res):
+            return list(res)
+        else:
+            raise TransformationRecordFailedError(
+                index_or_legacy_id,
+                "neither 990$a or 001 found in record.",
+                marc_record.as_json(),
             )
-        )
+
+    @staticmethod
+    def get_bib_id_from_001(marc_record: Record, index_or_legacy_id):
+        try:
+            return [marc_record["001"].format_field().strip()]
+        except Exception as e:
+            raise TransformationRecordFailedError(
+                index_or_legacy_id,
+                "001 is missing, although it is required for Voyager migrations",
+                marc_record.as_json(),
+            ) from e
 
     @staticmethod
     def get_srs_string(
@@ -605,13 +764,16 @@ def get_srs_string(
         id_holders = {
             FOLIONamespaces.instances: {
                 "instanceId": folio_object["id"],
-                "instanceHrid": folio_object["hrid"],
+                "instanceHrid": folio_object.get("hrid", ""),
             },
             FOLIONamespaces.holdings: {
                 "holdingsId": folio_object["id"],
-                "holdingsHrid": folio_object["hrid"],
+                "holdingsHrid": folio_object.get("hrid", ""),
+            },
+            FOLIONamespaces.authorities: {
+                "authorityId": folio_object["id"],
+                "authorityHrid": marc_record["001"].data,
             },
-            FOLIONamespaces.authorities: {},
             FOLIONamespaces.edifact: {},
         }
 
diff --git a/src/folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py b/src/folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py
index 68e0137e..9e49f7a0 100644
--- a/src/folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py
+++ b/src/folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py
@@ -1,6 +1,5 @@
 """The default mapper, responsible for parsing MARC21 records acording to the
 FOLIO community specifications"""
-import json
 import logging
 import sys
 import time
@@ -44,35 +43,23 @@ def __init__(
             folio_client,
             library_configuration,
             task_configuration,
-            Conditions(folio_client, self, "bibs"),
+            self.get_instance_schema(),
+            Conditions(folio_client, self, "bibs", library_configuration.folio_release),
         )
-        self.folio: FolioClient = folio_client
-        self.task_configuration = task_configuration
-        self.record_status: dict = {}
-        self.holdings_map: dict = {}
-        self.id_map: dict = {}
-        self.srs_recs: list = []
-        self.schema = self.instance_json_schema
-        self.contrib_name_types: dict = {}
-        self.mapped_folio_fields: dict = {}
-        self.unmapped_folio_fields: dict = {}
-        self.alt_title_map: dict = {}
         logging.info("Fetching mapping rules from the tenant")
         rules_endpoint = "/mapping-rules/marc-bib"
-        self.mappings = self.folio.folio_get_single_object(rules_endpoint)
+        self.mappings = self.folio_client.folio_get_single_object(rules_endpoint)
         logging.info("Fetching valid language codes...")
         self.language_codes = list(self.fetch_language_codes())
-        self.unmapped_tags: dict = {}
-        self.unmapped_conditions: dict = {}
         self.instance_relationships: dict = {}
         self.instance_relationship_types: dict = {}
-        self.other_mode_of_issuance_id = get_unspecified_mode_of_issuance(self.folio)
+        self.other_mode_of_issuance_id = get_unspecified_mode_of_issuance(self.folio_client)
 
         self.start = time.time()
 
     def perform_initial_preparation(self, marc_record: pymarc.Record, legacy_ids):
         folio_instance = {
-            "metadata": self.folio.get_metadata_construct(),
+            "metadata": self.folio_client.get_metadata_construct(),
         }
         folio_instance["id"] = str(
             FolioUUID(
@@ -103,18 +90,20 @@ def handle_leader_05(self, marc_record, legacy_ids):
         if leader_05 == "d":
             Helper.log_data_issue(legacy_ids, "d in leader. Is this correct?", marc_record.leader)
 
-    def parse_bib(self, legacy_ids, marc_record: pymarc.Record, file_def: FileDefinition):
+    def parse_record(
+        self, marc_record: pymarc.Record, file_def: FileDefinition, legacy_ids: List[str]
+    ) -> dict:
         """Parses a bib recod into a FOLIO Inventory instance object
         Community mapping suggestion: https://bit.ly/2S7Gyp3
          This is the main function
 
         Args:
-            legacy_ids (_type_): _description_
             marc_record (pymarc.Record): _description_
             file_def (FileDefinition): _description_
+            legacy_ids (List[str]): List of legacy ids in record
 
         Returns:
-            _type_: _description_
+            dict: _description_
         """
         self.print_progress()
         ignored_subsequent_fields: set = set()
@@ -136,78 +125,9 @@ def parse_bib(self, legacy_ids, marc_record: pymarc.Record, file_def: FileDefini
         )
         self.dedupe_rec(clean_folio_instance)
         marc_record.remove_fields(*list(bad_tags))
-        self.report_folio_mapping(clean_folio_instance, self.instance_json_schema)
-        # TODO: trim away multiple whitespace and newlines..
-        # TODO: createDate and update date and catalogeddate
+        self.report_folio_mapping(clean_folio_instance, self.schema)
         return clean_folio_instance
 
-    def process_marc_field(
-        self,
-        folio_instance,
-        marc_field,
-        ignored_subsequent_fields,
-        legacy_ids,
-    ):
-        if marc_field.tag == "880" and "6" in marc_field:
-            mappings = self.perform_proxy_mapping(marc_field)
-        else:
-            tags_to_ignore = {"880", "001", "008"}
-            mappings = (
-                self.mappings.get(marc_field.tag, {})
-                if marc_field.tag not in tags_to_ignore
-                else []
-            )
-        if mappings:
-            try:
-                self.map_field_according_to_mapping(
-                    marc_field, mappings, folio_instance, legacy_ids
-                )
-                if any(m.get("ignoreSubsequentFields", False) for m in mappings):
-                    ignored_subsequent_fields.add(marc_field.tag)
-            except Exception as ee:
-                logging.error(
-                    "map_field_according_to_mapping %s %s %s",
-                    marc_field.tag,
-                    marc_field.format_field(),
-                    json.dumps(mappings),
-                )
-                raise ee
-
-    def report_marc_stats(self, marc_field, bad_tags, legacy_ids, ignored_subsequent_fields):
-        self.migration_report.add(Blurbs.Trivia, "Total number of Tags processed")
-        self.report_bad_tags(marc_field, bad_tags, legacy_ids)
-        mapped = marc_field.tag in self.mappings
-        if marc_field.tag in ignored_subsequent_fields:
-            mapped = False
-        self.report_legacy_mapping(marc_field.tag, True, mapped)
-
-    def perform_proxy_mapping(self, marc_field):
-        proxy_mapping = next(iter(self.mappings.get("880", [])), [])
-        if not proxy_mapping or not proxy_mapping.get("fieldReplacementBy3Digits", False):
-            return None
-        if "6" not in marc_field:
-            self.migration_report.add(Blurbs.Field880Mappings, "Records without $6")
-            return None
-        target_field = next(
-            (
-                r.get("targetField", "")
-                for r in proxy_mapping.get("fieldReplacementRule", [])
-                if r["sourceDigits"] == marc_field["6"][:3]
-            ),
-            marc_field["6"][:3],
-        )
-        self.migration_report.add(
-            Blurbs.Field880Mappings,
-            f"Source digits: {marc_field['6']} Target field: {target_field}",
-        )
-        mappings = self.mappings.get(target_field, {})
-        if not mappings:
-            self.migration_report.add(
-                Blurbs.Field880Mappings,
-                f"Mapping not set up for target field: {target_field} ({marc_field['6']})",
-            )
-        return mappings
-
     def perform_additional_parsing(
         self,
         folio_instance: dict,
@@ -233,6 +153,12 @@ def perform_additional_parsing(
         self.handle_languages(folio_instance, marc_record, legacy_ids)
         self.handle_suppression(folio_instance, file_def)
         self.handle_holdings(marc_record)
+        if prec_titles := folio_instance.get("precedingTitles", []):
+            self.migration_report.add(Blurbs.PrecedingSuccedingTitles, f"{len(prec_titles)}")
+            del folio_instance["precedingTitles"]
+        if succ_titles := folio_instance.get("succeedingTitles", []):
+            del folio_instance["succeedingTitles"]
+            self.migration_report.add(Blurbs.PrecedingSuccedingTitles, f"{len(succ_titles)}")
 
     def handle_languages(self, folio_instance, marc_record, legacy_ids):
         if "languages" in folio_instance:
@@ -243,6 +169,14 @@ def handle_languages(self, folio_instance, marc_record, legacy_ids):
             self.filter_langs(folio_instance["languages"], marc_record, legacy_ids)
         )
 
+    @staticmethod
+    def get_instance_schema():
+        logging.info("Fetching Instance schema...")
+        instance_schema = FolioClient.get_latest_from_github(
+            "folio-org", "mod-inventory-storage", "ramls/instance.json"
+        )
+        return instance_schema
+
     def handle_holdings(self, marc_record: Record):
         if "852" in marc_record:
             holdingsfields = marc_record.get_fields(
@@ -274,17 +208,6 @@ def wrap_up(self):
         if not self.task_configuration.never_update_hrid_settings:
             self.store_hrid_settings()
 
-    def report_bad_tags(self, marc_field, bad_tags, legacy_ids):
-        if (
-            (not marc_field.tag.isnumeric())
-            and marc_field.tag != "LDR"
-            and marc_field.tag not in bad_tags
-        ):
-            self.migration_report.add(Blurbs.NonNumericTagsInRecord, marc_field.tag)
-            message = "Non-numeric tags in records"
-            Helper.log_data_issue(legacy_ids, message, marc_field.tag)
-            bad_tags.add(marc_field.tag)
-
     def get_instance_type_id(self, marc_record, legacy_id):
         return_id = ""
 
@@ -293,7 +216,7 @@ def get_folio_id_by_name(f336a: str):
             match = next(
                 (
                     f["id"]
-                    for f in self.folio.instance_types
+                    for f in self.folio_client.instance_types
                     if f["name"].lower().replace(" ", "") == match_template
                 ),
                 "",
@@ -315,7 +238,7 @@ def get_folio_id_by_name(f336a: str):
                 )
             return match
 
-        if not self.folio.instance_types:
+        if not self.folio_client.instance_types:
             raise TransformationProcessError("", "No instance_types setup in tenant")
 
         if "336" in marc_record and "b" not in marc_record["336"]:
@@ -327,7 +250,7 @@ def get_folio_id_by_name(f336a: str):
             f336_b = marc_record["336"]["b"].lower().replace(" ", "")
             f336_b_norm = f336_b.lower().replace(" ", "")
             t = self.conditions.get_ref_data_tuple_by_code(
-                self.folio.instance_types,
+                self.folio_client.instance_types,
                 "instance_types",
                 f336_b_norm,
             )
@@ -350,14 +273,14 @@ def get_folio_id_by_name(f336a: str):
 
         if not return_id:
             t = self.conditions.get_ref_data_tuple_by_code(
-                self.folio.instance_types, "instance_types", "zzz"
+                self.folio_client.instance_types, "instance_types", "zzz"
             )
             return_id = t[0]
         return return_id
 
     def get_instance_format_id_by_code(self, legacy_id: str, code: str):
         try:
-            match = next(f for f in self.folio.instance_formats if f["code"] == code)
+            match = next(f for f in self.folio_client.instance_formats if f["code"] == code)
             self.migration_report.add(
                 Blurbs.InstanceFormat,
                 f"Successful match  - {code}->{match['name']}",
@@ -378,7 +301,9 @@ def get_instance_format_id_by_name(self, f337a: str, f338a: str, legacy_id: str)
         match_template = f"{f337a} -- {f338a}"
         try:
             match = next(
-                f for f in self.folio.instance_formats if f["name"].lower() == match_template
+                f
+                for f in self.folio_client.instance_formats
+                if f["name"].lower() == match_template
             )
             self.migration_report.add(
                 Blurbs.InstanceFormat,
@@ -483,7 +408,7 @@ def get_mode_of_issuance_id(self, marc_record: Record, legacy_id: List[str]) ->
             ret = next(
                 (
                     i["id"]
-                    for i in self.folio.modes_of_issuance
+                    for i in self.folio_client.modes_of_issuance
                     if name.lower() == i["name"].lower()
                 ),
                 "",
@@ -503,7 +428,7 @@ def get_mode_of_issuance_id(self, marc_record: Record, legacy_id: List[str]) ->
 
             return self.other_mode_of_issuance_id
         except StopIteration as ee:
-            logging.exception(f"{marc_record.leader} {list(self.folio.modes_of_issuance)}")
+            logging.exception(f"{marc_record.leader} {list(self.folio_client.modes_of_issuance)}")
             raise ee from ee
 
     def get_nature_of_content(self, marc_record: Record) -> List[str]:
@@ -523,10 +448,8 @@ def get_languages_041(self, marc_record, legacy_id):
         for lang_tag in lang_fields:
             if "2" in lang_tag:
                 self.migration_report.add(Blurbs.LanguageCodeSources, lang_tag["2"])
-                logging.info(
-                    "Field with other Language code\t%s\t%s",
-                    marc_record["001"],
-                    lang_tag.value(),
+                Helper.log_data_issue(
+                    legacy_id, "Field with other Language code", lang_tag.value()
                 )
             lang_codes = lang_tag.get_subfields(*list(subfields))
             for lang_code in lang_codes:
@@ -601,36 +524,24 @@ def filter_langs(
                     f"{m}: {language_value}",
                 )
 
-    def get_legacy_ids(
-        self, marc_record: Record, ils_flavour: IlsFlavour, index_or_legacy_id: str
-    ) -> List[str]:
+    def get_legacy_ids(self, marc_record: Record, idx: int) -> List[str]:
+        ils_flavour: IlsFlavour = self.task_configuration.ils_flavour
         if ils_flavour in {IlsFlavour.sierra, IlsFlavour.millennium}:
             return get_iii_bib_id(marc_record)
         elif ils_flavour == IlsFlavour.tag907y:
-            return get_bib_id_from_907y(marc_record, index_or_legacy_id)
+            return RulesMapperBase.get_bib_id_from_907y(marc_record, idx)
         elif ils_flavour == IlsFlavour.tagf990a:
-            res = {f["a"].strip() for f in marc_record.get_fields("990") if "a" in f}
-            if marc_record["001"].format_field().strip():
-                res.add(marc_record["001"].format_field().strip())
-            if any(res):
-                self.migration_report.add_general_statistics("legacy id from 990$a")
-                return list(res)
-            else:
-                raise TransformationRecordFailedError(
-                    index_or_legacy_id,
-                    "neither 990$a or 001 found in record.",
-                    marc_record.as_json(),
-                )
+            return RulesMapperBase.get_bib_id_from_990a(marc_record, idx)
         elif ils_flavour == IlsFlavour.aleph:
             return self.get_aleph_bib_id(marc_record)
         elif ils_flavour in {IlsFlavour.voyager, "voyager", IlsFlavour.tag001}:
-            return get_bib_id_from_001(marc_record, index_or_legacy_id)
+            return RulesMapperBase.get_bib_id_from_001(marc_record, idx)
         elif ils_flavour == IlsFlavour.koha:
             try:
                 return [marc_record["999"]["c"]]
             except Exception as e:
                 raise TransformationRecordFailedError(
-                    index_or_legacy_id,
+                    idx,
                     "999 $c is missing, although it is required for this legacy ILS choice",
                     marc_record.as_json(),
                 ) from e
@@ -680,28 +591,3 @@ def get_iii_bib_id(marc_record: Record):
             "907 $a is missing, although it is required for Sierra/iii migrations",
             marc_record.as_json(),
         ) from e
-
-
-def get_bib_id_from_001(marc_record: Record, index_or_legacy_id):
-    try:
-        return [marc_record["001"].format_field().strip()]
-    except Exception as e:
-        raise TransformationRecordFailedError(
-            index_or_legacy_id,
-            "001 is missing, although it is required for Voyager migrations",
-            marc_record.as_json(),
-        ) from e
-
-
-def get_bib_id_from_907y(marc_record: Record, index_or_legacy_id):
-    try:
-        return list(set(marc_record["907"].get_subfields("a", "y")))
-    except Exception as e:
-        raise TransformationRecordFailedError(
-            index_or_legacy_id,
-            (
-                "907 $y and $a is missing is missing, although they is "
-                "required for this legacy ILS choice"
-            ),
-            marc_record.as_json(),
-        ) from e
diff --git a/src/folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py b/src/folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py
index dfcf484a..5b50dc2a 100644
--- a/src/folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py
+++ b/src/folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py
@@ -1,7 +1,11 @@
+import copy
+import json
 import logging
+from typing import List
 
 from folio_uuid.folio_namespaces import FOLIONamespaces
 from folio_uuid.folio_uuid import FolioUUID
+from folioclient import FolioClient
 from pymarc.field import Field
 from pymarc.record import Record
 
@@ -9,6 +13,8 @@
 from folio_migration_tools.custom_exceptions import TransformationProcessError
 from folio_migration_tools.custom_exceptions import TransformationRecordFailedError
 from folio_migration_tools.helper import Helper
+from folio_migration_tools.holdings_helper import HoldingsHelper
+from folio_migration_tools.library_configuration import FileDefinition
 from folio_migration_tools.library_configuration import LibraryConfiguration
 from folio_migration_tools.marc_rules_transformation.conditions import Conditions
 from folio_migration_tools.marc_rules_transformation.holdings_statementsparser import (
@@ -24,85 +30,112 @@ class RulesMapperHoldings(RulesMapperBase):
     def __init__(
         self,
         folio,
-        instance_id_map,
         location_map,
         task_configuration,
         library_configuration: LibraryConfiguration,
+        parent_id_map: dict,
     ):
-        self.instance_id_map = instance_id_map
         self.task_configuration = task_configuration
         self.conditions = Conditions(
             folio,
             self,
             "holdings",
+            library_configuration.folio_release,
             self.task_configuration.default_call_number_type_name,
         )
         self.folio = folio
-        super().__init__(folio, library_configuration, task_configuration, self.conditions)
+        super().__init__(
+            folio,
+            library_configuration,
+            task_configuration,
+            self.fetch_holdings_schema(),
+            self.conditions,
+            parent_id_map,
+        )
         self.location_map = location_map
-        self.schema = self.holdings_json_schema
-        self.holdings_id_map = {}
-        self.ref_data_dicts = {}
+        self.holdings_id_map: dict = {}
+        self.ref_data_dicts: dict = {}
         self.fallback_holdings_type_id = self.task_configuration.fallback_holdings_type_id
+        self.setup_holdings_sources()
+        logging.info("Fetching mapping rules from the tenant")
+        rules_endpoint = "/mapping-rules/marc-holdings"
+        self.mappings = self.folio_client.folio_get_single_object(rules_endpoint)
+        self.fix_853_bug_in_rules()
 
-    def parse_hold(self, marc_record, legacy_id):
-        """Parses a mfhd recod into a FOLIO Inventory instance object
+    def fix_853_bug_in_rules(self):
+        f852_mappings = self.mappings["852"]
+        new_852_mapping = []
+        for mapping in f852_mappings:
+            if "entity" in mapping:
+                for entity_mapping in mapping["entity"]:
+                    if "." not in entity_mapping["target"]:
+                        new_852_mapping.append(entity_mapping)
+                    else:
+                        raise TransformationProcessError(
+                            "",
+                            (
+                                "Actual entity mapping found in 852 mappings. "
+                                "Report this to the maintainers of this codebase"
+                            ),
+                            json.dumps(entity_mapping),
+                        )
+        self.mappings["852"] = new_852_mapping
+
+    def parse_record(
+        self, marc_record: Record, file_def: FileDefinition, legacy_ids: List[str]
+    ) -> dict:
+        """Parses a mfhd recod into a FOLIO Inventory holdings object
         Community mapping suggestion: https://tinyurl.com/3rh52e2x
          This is the main function
 
         Args:
-            marc_record (_type_): _description_
-            legacy_id (_type_): _description_
+            marc_record (Record): _description_
+            file_def (FileDefinition): _description_
+            legacy_ids (List[str]): _description_
 
         Raises:
             TransformationRecordFailedError: _description_
 
         Returns:
-            _type_: _description_
+            dict: _description_
         """
+
         self.print_progress()
-        folio_holding = {
-            "metadata": self.folio_client.get_metadata_construct(),
-        }
+        folio_holding = self.perform_initial_preparation(marc_record, legacy_ids)
+
         self.migration_report.add(Blurbs.RecordStatus, marc_record.leader[5])
-        ignored_subsequent_fields = set()
+        ignored_subsequent_fields: set = set()
         num_852s = 0
         for marc_field in marc_record:
             try:
                 if marc_field.tag == "852":
                     num_852s += 1
+                    if num_852s > 1:
+                        continue
                 self.process_marc_field(
+                    folio_holding,
                     marc_field,
                     ignored_subsequent_fields,
-                    folio_holding,
-                    legacy_id,
+                    legacy_ids,
                 )
             except TransformationFieldMappingError as tfme:
                 tfme.log_it()
         if num_852s > 1:
-            Helper.log_data_issue(legacy_id, "More than 1 852 found", "")
-
-        folio_holding["id"] = str(
-            FolioUUID(
-                self.folio_client.okapi_url,
-                FOLIONamespaces.holdings,
-                legacy_id,
-            )
-        )
+            Helper.log_data_issue(legacy_ids, "More than 1 852 found", "")
 
-        if not folio_holding.get("instanceId", ""):
-            raise TransformationRecordFailedError(
-                legacy_id,
-                "No Instance id mapped. ",
-                folio_holding["formerIds"],
-            )
-        self.perform_additional_mapping(marc_record, folio_holding, legacy_id)
+        self.perform_additional_mapping(marc_record, folio_holding, legacy_ids, file_def)
         cleaned_folio_holding = self.validate_required_properties(
             "-".join(folio_holding.get("formerIds")),
             folio_holding,
-            self.holdings_json_schema,
+            self.schema,
             FOLIONamespaces.holdings,
         )
+        if not folio_holding.get("instanceId", ""):
+            raise TransformationRecordFailedError(
+                legacy_ids,
+                "No Instance id mapped. ",
+                folio_holding.get("formerIds", ["No former ids"]),
+            )
         props_to_not_dedupe = (
             []
             if self.task_configuration.deduplicate_holdings_statements
@@ -113,18 +146,70 @@ def parse_hold(self, marc_record, legacy_id):
             ]
         )
         self.dedupe_rec(cleaned_folio_holding, props_to_not_dedupe)
-        self.holdings_id_map[legacy_id] = self.get_id_map_dict(legacy_id, cleaned_folio_holding)
-
         self.report_folio_mapping(cleaned_folio_holding, self.schema)
         return cleaned_folio_holding
 
+    def set_instance_id_by_map(self, legacy_ids: list, folio_holding: dict, marc_record: Record):
+        if "004" not in marc_record:
+            raise TransformationProcessError(
+                "",
+                ("No 004 in record. The tools only support bib-mfhd linking throuh 004"),
+                legacy_ids,
+            )
+        legacy_instance_id = marc_record["004"].data
+        folio_holding["formerIds"].append(f"{self.bib_id_template}{legacy_instance_id}")
+        if legacy_instance_id in self.parent_id_map:
+            folio_holding["instanceId"] = self.parent_id_map[legacy_instance_id][1]
+        else:
+            raise TransformationRecordFailedError(
+                legacy_ids,
+                "Old instance id not in map",
+                marc_record["004"],
+            )
+
+    def perform_initial_preparation(self, marc_record: Record, legacy_ids):
+        folio_holding: dict = {
+            "metadata": self.folio_client.get_metadata_construct(),
+        }
+        folio_holding["id"] = str(
+            FolioUUID(
+                str(self.folio_client.okapi_url),
+                FOLIONamespaces.holdings,
+                str(legacy_ids[0]),
+            )
+        )
+        for legacy_id in legacy_ids:
+            self.add_legacy_id_to_admin_note(folio_holding, legacy_id)
+        folio_holding["formerIds"] = copy.copy(legacy_ids)
+        self.set_instance_id_by_map(legacy_ids, folio_holding, marc_record)
+        return folio_holding
+
+    def setup_holdings_sources(self):
+        holdings_sources = list(
+            self.folio_client.folio_get_all("/holdings-sources", "holdingsRecordsSources")
+        )
+        logging.info("Fetched %s holdingsRecordsSources from tenant", len(holdings_sources))
+        self.holdingssources = {n["name"].upper(): n["id"] for n in holdings_sources}
+        if "FOLIO" not in self.holdingssources:
+            raise TransformationProcessError("", "No holdings source with name FOLIO in tenant")
+        if "MARC" not in self.holdingssources:
+            raise TransformationProcessError("", "No holdings source with name MARC in tenant")
+
     def process_marc_field(
         self,
+        folio_holding: dict,
         marc_field: Field,
         ignored_subsequent_fields,
-        folio_holding,
         index_or_legacy_ids,
     ):
+        """This overwrites the implementation for Auth and instances
+
+        Args:
+            folio_holding (dict): _description_
+            marc_field (Field): _description_
+            ignored_subsequent_fields (_type_): _description_
+            index_or_legacy_ids (_type_): _description_
+        """
         self.migration_report.add(Blurbs.Trivia, "Total number of Tags processed")
         if marc_field.tag not in self.mappings:
             self.report_legacy_mapping(marc_field.tag, True, False)
@@ -137,26 +222,41 @@ def process_marc_field(
             if any(m.get("ignoreSubsequentFields", False) for m in mappings):
                 ignored_subsequent_fields.add(marc_field.tag)
 
-    def perform_additional_mapping(self, marc_record: Record, folio_holding, legacy_id: str):
-        """Perform additional tasks not easily handled in the mapping rules
+    def perform_additional_mapping(
+        self, marc_record: Record, folio_holding, legacy_ids: List[str], file_def: FileDefinition
+    ):
+        """_summary_
 
         Args:
             marc_record (Record): _description_
             folio_holding (_type_): _description_
-            legacy_id (str): _description_
+            legacy_ids (List[str]): _description_
+            file_def (FileDefinition): _description_
+
+        Raises:
+            TransformationRecordFailedError: _description_
         """
-        self.set_holdings_type(marc_record, folio_holding, legacy_id)
+        self.set_holdings_type(marc_record, folio_holding, legacy_ids)
         self.set_default_call_number_type_if_empty(folio_holding)
-        self.pick_first_location_if_many(folio_holding, legacy_id)
-        self.parse_coded_holdings_statements(marc_record, folio_holding, legacy_id)
+        self.pick_first_location_if_many(folio_holding, legacy_ids)
+        self.parse_coded_holdings_statements(marc_record, folio_holding, legacy_ids)
+        HoldingsHelper.handle_notes(folio_holding)
         self.hrid_handler.handle_hrid(
-            FOLIONamespaces.holdings, folio_holding, marc_record, [legacy_id]
+            FOLIONamespaces.holdings, folio_holding, marc_record, legacy_ids
         )
+        if not folio_holding.get("instanceId", ""):
+            raise TransformationRecordFailedError(
+                "".join(folio_holding.get("formerIds", [])),
+                "Missing instance ids. Something is wrong.",
+                "",
+            )
+        self.handle_suppression(folio_holding, file_def, True)
+        self.set_source_id(self.task_configuration, folio_holding, self.holdingssources)
 
-    def pick_first_location_if_many(self, folio_holding, legacy_id: str):
+    def pick_first_location_if_many(self, folio_holding, legacy_ids: List[str]):
         if " " in folio_holding.get("permanentLocationId", ""):
             Helper.log_data_issue(
-                legacy_id,
+                legacy_ids,
                 "Space in permanentLocationId. Was this MFHD attached to multiple holdings?",
                 folio_holding["permanentLocationId"],
             )
@@ -164,7 +264,16 @@ def pick_first_location_if_many(self, folio_holding, legacy_id: str):
                 0
             ]
 
-    def parse_coded_holdings_statements(self, marc_record: Record, folio_holding, legacy_id):
+    @staticmethod
+    def set_source_id(task_configuration, folio_rec, holdingssources):
+        if task_configuration.create_source_records:
+            folio_rec["sourceId"] = holdingssources.get("MARC")
+        else:
+            folio_rec["sourceId"] = holdingssources.get("FOLIO")
+
+    def parse_coded_holdings_statements(
+        self, marc_record: Record, folio_holding, legacy_ids: List[str]
+    ):
         # TODO: Should one be able to switch these things off?
         a = {
             "holdingsStatements": ("853", "863", "866"),
@@ -178,7 +287,7 @@ def parse_coded_holdings_statements(self, marc_record: Record, folio_holding, le
                     v[0],
                     v[1],
                     v[2],
-                    legacy_id,
+                    legacy_ids,
                     self.task_configuration.deduplicate_holdings_statements,
                 )
                 if res["statements"]:
@@ -199,7 +308,15 @@ def wrap_up(self):
             else:
                 logging.info("NOT storing HRID settings since that is managed by FOLIO")
 
-    def set_holdings_type(self, marc_record: Record, folio_holding, legacy_id: str):
+    @staticmethod
+    def fetch_holdings_schema():
+        logging.info("Fetching HoldingsRecord schema...")
+        holdings_record_schema = FolioClient.get_latest_from_github(
+            "folio-org", "mod-inventory-storage", "ramls/holdingsrecord.json"
+        )
+        return holdings_record_schema
+
+    def set_holdings_type(self, marc_record: Record, folio_holding, legacy_ids: List[str]):
         # Holdings type mapping
         ldr06 = marc_record.leader[6]
         # TODO: map this better
@@ -210,13 +327,8 @@ def set_holdings_type(self, marc_record: Record, folio_holding, legacy_id: str):
                 f"Already set to {folio_holding.get('holdingsTypeId')}. LDR[06] was {ldr06}",
             )
         else:
-            holdings_type_map = {
-                "u": "Unknown",
-                "v": "Multi-part monograph",
-                "x": "Monograph",
-                "y": "Serial",
-            }
-            holdings_type = holdings_type_map.get(ldr06, "")
+
+            holdings_type = self.conditions.holdings_type_map.get(ldr06, "")
             if t := self.conditions.get_ref_data_tuple_by_name(
                 self.conditions.holdings_types, "hold_types", holdings_type
             ):
@@ -227,7 +339,7 @@ def set_holdings_type(self, marc_record: Record, folio_holding, legacy_id: str):
                 )
                 if holdings_type == "Unknown":
                     Helper.log_data_issue(
-                        legacy_id,
+                        legacy_ids,
                         (
                             f"{Blurbs.HoldingsTypeMapping[0]} is 'unknown'. "
                             "(leader 06 is set to 'u') Check if this is correct"
@@ -246,7 +358,7 @@ def set_holdings_type(self, marc_record: Record, folio_holding, legacy_id: str):
                     f"A Unmapped {ldr06} -> {holdings_type} -> Unmapped",
                 )
                 Helper.log_data_issue(
-                    legacy_id,
+                    legacy_ids,
                     (f"{Blurbs.HoldingsTypeMapping[0]}. leader 06 was unmapped."),
                     ldr06,
                 )
@@ -255,12 +367,35 @@ def set_default_call_number_type_if_empty(self, folio_holding):
         if not folio_holding.get("callNumberTypeId", ""):
             folio_holding["callNumberTypeId"] = self.conditions.default_call_number_type["id"]
 
-    def remove_from_id_map(self, former_ids):
-        """removes the ID from the map in case parsing failed
-
-        Args:
-            former_ids (_type_): _description_
-        """
-        for former_id in [id for id in former_ids if id]:
-            if former_id in self.holdings_id_map:
-                del self.holdings_id_map[former_id]
+    def get_legacy_ids(self, marc_record: Record, idx: int) -> List[str]:
+        marc_path = self.task_configuration.legacy_id_marc_path
+        split = marc_path.split("$", maxsplit=1)
+        results = []
+        if not (split[0].isnumeric() and len(split[0]) == 3):
+            raise TransformationProcessError(
+                "",
+                (
+                    "the marc field used for determining the legacy id is not numeric "
+                    "or does not have the stipulated lenght of 3."
+                    "Make sure the task configuration setting for 'legacyIdMarcPath' "
+                    "is correct or make this piece of code more allowing"
+                ),
+                marc_path,
+            )
+        elif len(split) == 1:
+            results.append(marc_record[split[0]].value())
+        elif len(split) == 2 and len(split[1]) == 1:
+            for field in marc_record.get_fields(split[0]):
+                if sf := field.get_subfields(split[1]):
+                    results.append(sf[0])
+        else:
+            raise TransformationProcessError(
+                "",
+                ("Something is wrong with 'legacyIdMarcPath' property in the settings"),
+                marc_path,
+            )
+        if not any(results):
+            raise TransformationRecordFailedError(
+                idx, f"No legacy id found in record from {marc_path}", ""
+            )
+        return results
diff --git a/src/folio_migration_tools/migration_report.py b/src/folio_migration_tools/migration_report.py
index d525c925..527a5ecd 100644
--- a/src/folio_migration_tools/migration_report.py
+++ b/src/folio_migration_tools/migration_report.py
@@ -24,7 +24,6 @@ def add(self, blurb_tuple: tuple, measure_to_add, number=1):
             self.report[blurb_tuple[0]][measure_to_add] += number
         except KeyError:
             if blurb_tuple[0] not in self.report:
-                # TODO: Is this correct?
                 self.report[blurb_tuple[0]] = {"blurb_tuple": blurb_tuple}
             if measure_to_add not in self.report[blurb_tuple[0]]:
                 self.report[blurb_tuple[0]][measure_to_add] = number
diff --git a/src/folio_migration_tools/migration_tasks/authority_transformer.py b/src/folio_migration_tools/migration_tasks/authority_transformer.py
new file mode 100644
index 00000000..ea269466
--- /dev/null
+++ b/src/folio_migration_tools/migration_tasks/authority_transformer.py
@@ -0,0 +1,83 @@
+import logging
+from typing import Annotated
+from typing import List
+from typing import Optional
+
+from folio_uuid.folio_namespaces import FOLIONamespaces
+from pydantic import Field
+
+from folio_migration_tools.helper import Helper
+from folio_migration_tools.library_configuration import FileDefinition
+from folio_migration_tools.library_configuration import IlsFlavour
+from folio_migration_tools.library_configuration import LibraryConfiguration
+from folio_migration_tools.marc_rules_transformation.marc_file_processor import (
+    MarcFileProcessor,
+)
+from folio_migration_tools.marc_rules_transformation.rules_mapper_autorities import (
+    AuthorityMapper,
+)
+from folio_migration_tools.migration_tasks.migration_task_base import MigrationTaskBase
+from folio_migration_tools.task_configuration import AbstractTaskConfiguration
+
+
+class AuthorityTransformer(MigrationTaskBase):
+    class TaskConfiguration(AbstractTaskConfiguration):
+        name: Annotated[str, Field(description=("Name of this task"))]
+        migration_task_type: Annotated[
+            str, Field(description=("The string represenation of this class. Do not set"))
+        ]
+        files: Annotated[
+            List[FileDefinition],
+            Field(description=("List of MARC21 files with authority records")),
+        ]
+        ils_flavour: IlsFlavour
+        tags_to_delete: Optional[List[str]] = []
+        create_source_records: Annotated[
+            bool, Field(description="Controls wheter or not to retain the MARC records in SRS.")
+        ] = True
+
+    @staticmethod
+    def get_object_type() -> FOLIONamespaces:
+        return FOLIONamespaces.authorities
+
+    def __init__(
+        self,
+        task_config: TaskConfiguration,
+        library_config: LibraryConfiguration,
+        use_logging: bool = True,
+    ):
+        super().__init__(library_config, task_config, use_logging)
+        self.processor: MarcFileProcessor
+        self.check_source_files(
+            self.folder_structure.legacy_records_folder, self.task_configuration.files
+        )
+        self.mapper: AuthorityMapper = AuthorityMapper(
+            self.folio_client, library_config, task_config
+        )
+        self.auth_ids: set = set()
+        logging.info("Init done")
+
+    def do_work(self):
+        self.do_work_marc_transformer()
+
+    def wrap_up(self):
+        logging.info("Done. Transformer Wrapping up...")
+        self.extradata_writer.flush()
+        self.processor.wrap_up()
+        with open(self.folder_structure.migration_reports_file, "w+") as report_file:
+            self.mapper.migration_report.write_migration_report(
+                "Autority records transformation report",
+                report_file,
+                self.start_datetime,
+            )
+            Helper.print_mapping_report(
+                report_file,
+                self.mapper.parsed_records,
+                self.mapper.mapped_folio_fields,
+                self.mapper.mapped_legacy_fields,
+            )
+        logging.info(
+            "Done. Transformation report written to %s",
+            self.folder_structure.migration_reports_file.name,
+        )
+        self.clean_out_empty_logs()
diff --git a/src/folio_migration_tools/migration_tasks/batch_poster.py b/src/folio_migration_tools/migration_tasks/batch_poster.py
index 616fe331..b5fba4d7 100644
--- a/src/folio_migration_tools/migration_tasks/batch_poster.py
+++ b/src/folio_migration_tools/migration_tasks/batch_poster.py
@@ -99,6 +99,10 @@ def do_work(self):
                                 try:
                                     if self.task_configuration.object_type == "Extradata":
                                         self.post_extra_data(row, self.processed, failed_recs_file)
+                                    elif self.task_configuration.object_type == "Authorities":
+                                        self.post_authorities(
+                                            row, self.processed, failed_recs_file
+                                        )
                                     else:
                                         json_rec = json.loads(row.split("\t")[-1])
                                         if self.task_configuration.object_type == "SRS":
@@ -170,6 +174,28 @@ def post_extra_data(self, row: str, num_records: int, failed_recs_file):
                 self.num_failures,
             )
 
+    def post_authorities(self, row: str, num_records: int, failed_recs_file):
+        url = f"{self.folio_client.okapi_url}/authority-storage/authorities"
+        response = self.post_objects(url, row)
+        if response.status_code == 201:
+            self.num_posted += 1
+        elif response.status_code == 422:
+            self.num_failures += 1
+            error_msg = json.loads(response.text)["errors"][0]["message"]
+            logging.error("Row %s\tHTTP %s\t %s", num_records, response.status_code, error_msg)
+            if "id value already exists" not in json.loads(response.text)["errors"][0]["message"]:
+                failed_recs_file.write(row)
+        else:
+            self.num_failures += 1
+            logging.error("Row %s\tHTTP %s\t%s", num_records, response.status_code, response.text)
+            failed_recs_file.write(row)
+        if num_records % 50 == 0:
+            logging.info(
+                "%s records posted successfully. %s failed",
+                self.num_posted,
+                self.num_failures,
+            )
+
     def post_objects(self, url, body):
         return requests.post(
             url, headers=self.folio_client.okapi_headers, data=body.encode("utf-8")
@@ -270,7 +296,7 @@ def post_batch(self, batch, failed_recs_file, num_records, recursion_depth=0):
             # Likely a json parsing error
             logging.error(response.text)
             raise TransformationProcessError("", "HTTP 400. Somehting is wrong. Quitting")
-        elif self.task_configuration.object_type == "SRS" and response.status_code == 500:
+        elif self.task_configuration.object_type == "SRS" and response.status_code >= 500:
             logging.info(
                 "Post failed. Size: %s Waiting 30s until reposting. Number of tries: %s of 5",
                 get_req_size(response),
@@ -354,6 +380,7 @@ def wrap_up(self):
                 report_file,
                 self.start_datetime,
             )
+        self.clean_out_empty_logs()
 
     def rerun_run(self):
         if self.task_configuration.rerun_failed_records and (self.num_failures > 0):
@@ -452,6 +479,12 @@ def list_objects(object_type: str):
             "total_records": False,
             "addSnapshotId": False,
         },
+        "Authorities": {
+            "object_name": "",
+            "api_endpoint": "/authority-storage/authorities",
+            "total_records": False,
+            "addSnapshotId": False,
+        },
         "SRS": {
             "object_name": "records",
             "api_endpoint": "/source-storage/batch/records",
diff --git a/src/folio_migration_tools/migration_tasks/bibs_transformer.py b/src/folio_migration_tools/migration_tasks/bibs_transformer.py
index a9f12eec..0602a169 100644
--- a/src/folio_migration_tools/migration_tasks/bibs_transformer.py
+++ b/src/folio_migration_tools/migration_tasks/bibs_transformer.py
@@ -1,29 +1,23 @@
 import logging
-import sys
-from io import IOBase
 from typing import Annotated
 from typing import List
 from typing import Optional
 
 from folio_uuid.folio_namespaces import FOLIONamespaces
 from pydantic import Field
-from pymarc import MARCReader
-from pymarc.record import Record
 
-from folio_migration_tools.custom_exceptions import TransformationProcessError
-from folio_migration_tools.custom_exceptions import TransformationRecordFailedError
 from folio_migration_tools.helper import Helper
 from folio_migration_tools.library_configuration import FileDefinition
 from folio_migration_tools.library_configuration import HridHandling
 from folio_migration_tools.library_configuration import IlsFlavour
 from folio_migration_tools.library_configuration import LibraryConfiguration
-from folio_migration_tools.marc_rules_transformation.bibs_processor import BibsProcessor
+from folio_migration_tools.marc_rules_transformation.marc_file_processor import (
+    MarcFileProcessor,
+)
 from folio_migration_tools.marc_rules_transformation.rules_mapper_bibs import (
     BibsRulesMapper,
 )
-from folio_migration_tools.migration_report import MigrationReport
 from folio_migration_tools.migration_tasks.migration_task_base import MigrationTaskBase
-from folio_migration_tools.report_blurbs import Blurbs
 from folio_migration_tools.task_configuration import AbstractTaskConfiguration
 
 
@@ -41,7 +35,6 @@ class TaskConfiguration(AbstractTaskConfiguration):
             ),
         ] = True
         migration_task_type: str
-        use_tenant_mapping_rules: Optional[bool] = True
         hrid_handling: Optional[HridHandling] = HridHandling.default
         deactivate035_from001: Optional[bool] = False
         files: List[FileDefinition]
@@ -49,6 +42,9 @@ class TaskConfiguration(AbstractTaskConfiguration):
         tags_to_delete: Optional[List[str]] = []
         reset_hrid_settings: Optional[bool] = False
         never_update_hrid_settings: Optional[bool] = False
+        create_source_records: Annotated[
+            bool, Field(description="Controls wheter or not to retain the MARC records in SRS.")
+        ] = True
 
     @staticmethod
     def get_object_type() -> FOLIONamespaces:
@@ -62,54 +58,24 @@ def __init__(
     ):
 
         super().__init__(library_config, task_config, use_logging)
-        self.processor: BibsProcessor = None
+        self.processor: MarcFileProcessor
         self.check_source_files(
             self.folder_structure.legacy_records_folder, self.task_configuration.files
         )
-        logging.info(self.task_configuration.json(indent=4))
         self.mapper = BibsRulesMapper(self.folio_client, library_config, self.task_configuration)
         self.bib_ids: set = set()
-        logging.info("Init done")
         if (
             self.task_configuration.reset_hrid_settings
             and not self.task_configuration.never_update_hrid_settings
         ):
             self.mapper.reset_instance_hrid_counter()
+        logging.info("Init done")
 
     def do_work(self):
-        logging.info("Starting....")
-
-        with open(self.folder_structure.created_objects_path, "w+") as created_records_file:
-            self.processor = BibsProcessor(
-                self.mapper,
-                self.folio_client,
-                created_records_file,
-                self.folder_structure,
-            )
-            with open(self.folder_structure.failed_bibs_file, "wb") as failed_bibs_file:
-                for file_obj in self.task_configuration.files:
-                    try:
-                        with open(
-                            self.folder_structure.legacy_records_folder / file_obj.file_name,
-                            "rb",
-                        ) as marc_file:
-                            reader = MARCReader(marc_file, to_unicode=True, permissive=True)
-                            reader.hide_utf8_warnings = True
-                            reader.force_utf8 = False
-                            logging.info("running %s", file_obj.file_name)
-                            self.read_records(reader, file_obj, failed_bibs_file)
-                    except TransformationProcessError as tpe:
-                        logging.critical(tpe)
-                        sys.exit(1)
-                    except Exception:
-                        logging.exception(file_obj, stack_info=True)
-                        logging.critical(
-                            "File %s failed for unknown reason. Halting", file_obj.file_name
-                        )
-                        sys.exit(1)
+        self.do_work_marc_transformer()
 
     def wrap_up(self):
-        logging.info("Done. Wrapping up...")
+        logging.info("Done. Transformer wrapping up...")
         self.extradata_writer.flush()
         self.processor.wrap_up()
         with open(self.folder_structure.migration_reports_file, "w+") as report_file:
@@ -129,62 +95,4 @@ def wrap_up(self):
             "Done. Transformation report written to %s",
             self.folder_structure.migration_reports_file.name,
         )
-
-    def read_records(self, reader, source_file: FileDefinition, failed_bibs_file: IOBase):
-        for idx, record in enumerate(reader):
-            self.mapper.migration_report.add_general_statistics("Records in file before parsing")
-            try:
-                # None = Something bad happened
-                if record is None:
-                    self.report_failed_parsing(reader, source_file, failed_bibs_file, idx)
-                # The normal case
-                else:
-                    self.set_leader(record, self.mapper.migration_report)
-                    self.mapper.migration_report.add_general_statistics(
-                        "Records successfully decoded from MARC21",
-                    )
-                    self.processor.process_record(idx, record, source_file)
-            except TransformationRecordFailedError as error:
-                error.log_it()
-        logging.info("Done reading %s records from file", idx + 1)
-
-    def report_failed_parsing(self, reader, source_file, failed_bibs_file, idx):
-        self.mapper.migration_report.add_general_statistics(
-            "Records with encoding errors - parsing failed",
-        )
-        failed_bibs_file.write(reader.current_chunk)
-        raise TransformationRecordFailedError(
-            f"Index in {source_file.file_name}:{idx}",
-            f"MARC parsing error: {reader.current_exception}",
-            "Failed records stored in results/failed_bib_records.mrc",
-        )
-
-    @staticmethod
-    def set_leader(marc_record: Record, migration_report: MigrationReport):
-        if marc_record.leader[9] != "a":
-            migration_report.add(
-                Blurbs.LeaderManipulation,
-                f"Set leader 09 (Character coding scheme) from {marc_record.leader[9]} to a",
-            )
-            marc_record.leader = f"{marc_record.leader[:9]}a{marc_record.leader[10:]}"
-
-        if not marc_record.leader.endswith("4500"):
-            migration_report.add(
-                Blurbs.LeaderManipulation,
-                f"Set leader 20-23 from {marc_record.leader[-4:]} to 4500",
-            )
-            marc_record.leader = f"{marc_record.leader[:-4]}4500"
-
-        if marc_record.leader[10] != "2":
-            migration_report.add(
-                Blurbs.LeaderManipulation,
-                f"Set leader 10 (Indicator count) from {marc_record.leader[10]} to 2",
-            )
-            marc_record.leader = f"{marc_record.leader[:10]}2{marc_record.leader[11:]}"
-
-        if marc_record.leader[11] != "2":
-            migration_report.add(
-                Blurbs.LeaderManipulation,
-                f"Set leader 10 (Subfield code count) from {marc_record.leader[11]} to 2",
-            )
-            marc_record.leader = f"{marc_record.leader[:11]}2{marc_record.leader[12:]}"
+        self.clean_out_empty_logs()
diff --git a/src/folio_migration_tools/migration_tasks/courses_migrator.py b/src/folio_migration_tools/migration_tasks/courses_migrator.py
index 8b3d3a45..4a86d511 100644
--- a/src/folio_migration_tools/migration_tasks/courses_migrator.py
+++ b/src/folio_migration_tools/migration_tasks/courses_migrator.py
@@ -123,12 +123,12 @@ def do_work(self):
                 self.print_progress(idx, start)
 
     def wrap_up(self):
-
         self.extradata_writer.flush()
         with open(self.folder_structure.migration_reports_file, "w+") as report_file:
             self.mapper.migration_report.write_migration_report(
                 "Courses migration report", report_file, self.mapper.start_datetime
             )
+        self.clean_out_empty_logs()
 
 
 def timings(t0, t0func, num_objects):
diff --git a/src/folio_migration_tools/migration_tasks/holdings_csv_transformer.py b/src/folio_migration_tools/migration_tasks/holdings_csv_transformer.py
index 37dbb0ae..722ed5f4 100644
--- a/src/folio_migration_tools/migration_tasks/holdings_csv_transformer.py
+++ b/src/folio_migration_tools/migration_tasks/holdings_csv_transformer.py
@@ -191,7 +191,7 @@ def load_instance_id_map(self):
         with open(self.folder_structure.instance_id_map_path, "r") as instance_id_map_file:
             for index, json_string in enumerate(instance_id_map_file):
                 # Format:{"legacy_id", "folio_id","instanceLevelCallNumber"}
-                if index % 100000 == 0:
+                if index % 500000 == 0:
                     print(f"{index} instance ids loaded to map", end="\r")
                 map_object = json.loads(json_string)
                 res[map_object["legacy_id"]] = map_object
@@ -217,7 +217,7 @@ def do_work(self):
         )
 
     def wrap_up(self):
-        logging.info("Work done. Wrapping up...")
+        logging.info("Done. Transformer wrapping up...")
         self.extradata_writer.flush()
         if any(self.holdings):
             logging.info(
@@ -230,8 +230,8 @@ def wrap_up(self):
                         # Prevent the first item in a boundwith to be overwritten
                         # TODO: Find out why not
                         # if legacy_id not in self.holdings_id_map:
-                        self.holdings_id_map[legacy_id] = self.mapper.get_id_map_dict(
-                            legacy_id, holding
+                        self.holdings_id_map[legacy_id] = self.mapper.get_id_map_tuple(
+                            legacy_id, holding, self.object_type
                         )
                     Helper.write_to_file(holdings_file, holding)
                     self.mapper.migration_report.add_general_statistics(
@@ -253,6 +253,7 @@ def wrap_up(self):
                 self.mapper.mapped_legacy_fields,
             )
         logging.info("All done!")
+        self.clean_out_empty_logs()
 
     def validate_merge_criterias(self):
         holdings_schema = self.folio_client.get_holdings_schema()
@@ -424,8 +425,8 @@ def merge_holding_in(
                 self.generate_boundwith_part(
                     self.folio_client, legacy_item_id, self.holdings[bw_key]
                 )
-                self.holdings_id_map[legacy_item_id] = self.mapper.get_id_map_dict(
-                    legacy_item_id, self.holdings[bw_key]
+                self.holdings_id_map[legacy_item_id] = self.mapper.get_id_map_tuple(
+                    legacy_item_id, self.holdings[bw_key], self.object_type
                 )
                 self.mapper.migration_report.add_general_statistics(
                     "BW Items found tied to previously created BW Holdings"
@@ -467,8 +468,3 @@ def get_holdings_sources(self):
             raise TransformationProcessError("", "No holdings source with name MARC in tenant")
         logging.info(json.dumps(res, indent=4))
         return res
-
-
-def dedupe(list_of_dicts):
-    # TODO: Move to interface or parent class
-    return [dict(t) for t in {tuple(d.items()) for d in list_of_dicts}]
diff --git a/src/folio_migration_tools/migration_tasks/holdings_marc_transformer.py b/src/folio_migration_tools/migration_tasks/holdings_marc_transformer.py
index 4208142c..2a194230 100644
--- a/src/folio_migration_tools/migration_tasks/holdings_marc_transformer.py
+++ b/src/folio_migration_tools/migration_tasks/holdings_marc_transformer.py
@@ -1,22 +1,18 @@
 '''Main "script."'''
 import csv
-import json
 import logging
-import sys
+from typing import Annotated
 from typing import List
 from typing import Optional
 
 from folio_uuid.folio_namespaces import FOLIONamespaces
-from pymarc import MARCReader
+from pydantic import Field
 
 from folio_migration_tools.custom_exceptions import TransformationProcessError
-from folio_migration_tools.custom_exceptions import TransformationRecordFailedError
+from folio_migration_tools.helper import Helper
 from folio_migration_tools.library_configuration import FileDefinition
 from folio_migration_tools.library_configuration import HridHandling
 from folio_migration_tools.library_configuration import LibraryConfiguration
-from folio_migration_tools.marc_rules_transformation.holdings_processor import (
-    HoldingsProcessor,
-)
 from folio_migration_tools.marc_rules_transformation.rules_mapper_holdings import (
     RulesMapperHoldings,
 )
@@ -30,15 +26,15 @@ class TaskConfiguration(AbstractTaskConfiguration):
         legacy_id_marc_path: str
         deduplicate_holdings_statements: Optional[bool] = True
         migration_task_type: str
-        use_tenant_mapping_rules: bool
         hrid_handling: Optional[HridHandling] = HridHandling.default
         deactivate035_from001: Optional[bool] = False
         files: List[FileDefinition]
-        mfhd_mapping_file_name: str
         location_map_file_name: str
         default_call_number_type_name: str
         fallback_holdings_type_id: str
-        create_source_records: Optional[bool] = False
+        create_source_records: Annotated[
+            bool, Field(description="Controls wheter or not to retain the MARC records in SRS.")
+        ] = True
         reset_hrid_settings: Optional[bool] = False
         never_update_hrid_settings: Optional[bool] = False
 
@@ -78,80 +74,55 @@ def __init__(
             "%s will be used as default holdings type",
             self.default_holdings_type.get("name", ""),
         )
+        location_map_path = (
+            self.folder_structure.mapping_files_folder / self.task_config.location_map_file_name
+        )
+        with open(location_map_path) as location_map_file:
+            self.location_map = list(csv.DictReader(location_map_file, dialect="tsv"))
+            logging.info("Locations in map: %s", len(self.location_map))
+
         self.check_source_files(
             self.folder_structure.legacy_records_folder, self.task_config.files
         )
         self.instance_id_map = self.load_id_map(self.folder_structure.instance_id_map_path, True)
+        self.mapper = RulesMapperHoldings(
+            self.folio_client,
+            self.location_map,
+            self.task_config,
+            self.library_configuration,
+            self.instance_id_map,
+        )
+        if (
+            self.task_configuration.reset_hrid_settings
+            and not self.task_configuration.never_update_hrid_settings
+        ):
+            self.mapper.reset_holdings_hrid_counter()
         logging.info("%s Instance ids in map", len(self.instance_id_map))
         logging.info("Init done")
 
     def do_work(self):
-        loc_map_path = (
-            self.folder_structure.mapping_files_folder / self.task_config.location_map_file_name
-        )
-        map_path = (
-            self.folder_structure.mapping_files_folder / self.task_config.mfhd_mapping_file_name
-        )
-        with open(loc_map_path) as loc_map_f, open(map_path) as map_f:
-            location_map = list(csv.DictReader(loc_map_f, dialect="tsv"))
-            logging.info("Locations in map: %s", len(location_map))
-            rules_file = json.load(map_f)
-            logging.info("Default location code %s", rules_file["defaultLocationCode"])
-            mapper = RulesMapperHoldings(
-                self.folio_client,
-                self.instance_id_map,
-                location_map,
-                self.task_config,
-                self.library_configuration,
-            )
-            mapper.mappings = rules_file["rules"]
-            if (
-                self.task_configuration.reset_hrid_settings
-                and not self.task_configuration.never_update_hrid_settings
-            ):
-                mapper.reset_holdings_hrid_counter()
-            processor = HoldingsProcessor(mapper, self.folder_structure)
-            for file_def in self.task_config.files:
-                self.process_single_file(file_def, processor)
-            processor.wrap_up()
-
-    def process_single_file(self, file_def: FileDefinition, processor: HoldingsProcessor):
-        try:
-            with open(
-                self.folder_structure.legacy_records_folder / file_def.file_name,
-                "rb",
-            ) as marc_file:
-                reader = MARCReader(marc_file, to_unicode=True, permissive=True)
-                reader.hide_utf8_warnings = True
-                reader.force_utf8 = False
-                logging.info("Running %s", file_def.file_name)
-                read_records(reader, processor, file_def)
-        except TransformationProcessError as tpe:
-            logging.critical(tpe)
-            sys.exit(1)
-        except Exception:
-            logging.exception("Failure in Main: %s", file_def.file_name, stack_info=True)
+        self.do_work_marc_transformer()
 
     def wrap_up(self):
-        logging.info("wapping up")
+        logging.info("Done. Transformer Wrapping up...")
         self.extradata_writer.flush()
+        self.processor.wrap_up()
+        with open(self.folder_structure.migration_reports_file, "w+") as report_file:
+            self.mapper.migration_report.write_migration_report(
+                "Bibliographic records transformation report",
+                report_file,
+                self.start_datetime,
+            )
+            Helper.print_mapping_report(
+                report_file,
+                self.mapper.parsed_records,
+                self.mapper.mapped_folio_fields,
+                self.mapper.mapped_legacy_fields,
+            )
 
+        logging.info(
+            "Done. Transformation report written to %s",
+            self.folder_structure.migration_reports_file.name,
+        )
 
-def read_records(reader, processor: HoldingsProcessor, file_def: FileDefinition):
-    for idx, record in enumerate(reader):
-        try:
-            if record is None:
-                processor.mapper.migration_report.add_general_statistics(
-                    "Records with encoding errors. See data issues log for details"
-                )
-                raise TransformationRecordFailedError(
-                    f"Index in file:{idx}",
-                    f"MARC parsing error: {reader.current_exception}",
-                    f"{reader.current_chunk}",
-                )
-            else:
-                processor.process_record(record, file_def)
-        except TransformationRecordFailedError as error:
-            error.log_it()
-        except ValueError as error:
-            logging.error(error)
+        self.clean_out_empty_logs()
diff --git a/src/folio_migration_tools/migration_tasks/items_transformer.py b/src/folio_migration_tools/migration_tasks/items_transformer.py
index d9afac06..656e3b47 100644
--- a/src/folio_migration_tools/migration_tasks/items_transformer.py
+++ b/src/folio_migration_tools/migration_tasks/items_transformer.py
@@ -278,7 +278,7 @@ def handle_circiulation_notes(folio_rec, current_user_uuid):
             del folio_rec["circulationNotes"]
 
     def wrap_up(self):
-        logging.info("Work done. Wrapping up...")
+        logging.info("Done. Transformer wrapping up...")
         self.extradata_writer.flush()
         with open(self.folder_structure.migration_reports_file, "w") as migration_report_file:
             self.mapper.migration_report.write_migration_report(
@@ -290,4 +290,5 @@ def wrap_up(self):
                 self.mapper.mapped_folio_fields,
                 self.mapper.mapped_legacy_fields,
             )
+        self.clean_out_empty_logs()
         logging.info("All done!")
diff --git a/src/folio_migration_tools/migration_tasks/loans_migrator.py b/src/folio_migration_tools/migration_tasks/loans_migrator.py
index 549e3619..a5f23eee 100644
--- a/src/folio_migration_tools/migration_tasks/loans_migrator.py
+++ b/src/folio_migration_tools/migration_tasks/loans_migrator.py
@@ -226,6 +226,7 @@ def wrap_up(self):
             self.migration_report.write_migration_report(
                 "Loans migration report", report_file, self.start_datetime
             )
+        self.clean_out_empty_logs()
 
     def write_failed_loans_to_file(self):
         csv_columns = [
@@ -404,7 +405,6 @@ def update_open_loan(self, folio_loan: dict, legacy_loan: LegacyLoan):
         due_date = du_parser.isoparse(str(legacy_loan.due_date))
         out_date = du_parser.isoparse(str(legacy_loan.out_date))
         renewal_count = legacy_loan.renewal_count
-        # TODO: add logging instead of print out
         try:
             loan_to_put = copy.deepcopy(folio_loan)
             del loan_to_put["metadata"]
@@ -467,7 +467,6 @@ def declare_lost(self, folio_loan):
         else:
             logging.error(f"Unsuccessfully declared loan {folio_loan} as lost")
             self.migration_report.add(Blurbs.Details, "Unsuccessfully declared loan as lost")
-        # TODO: Exception handling
 
     def claim_returned(self, folio_loan):
         claim_returned_url = f"/circulation/loans/{folio_loan['id']}/claim-item-returned"
@@ -488,7 +487,6 @@ def claim_returned(self, folio_loan):
                 Blurbs.Details,
                 f"Unsuccessfully declared loan {folio_loan} as Claimed returned",
             )
-        # TODO: Exception handling
 
     def set_item_status(self, legacy_loan: LegacyLoan):
         try:
diff --git a/src/folio_migration_tools/migration_tasks/migration_task_base.py b/src/folio_migration_tools/migration_tasks/migration_task_base.py
index f0e1450c..50eaed5e 100644
--- a/src/folio_migration_tools/migration_tasks/migration_task_base.py
+++ b/src/folio_migration_tools/migration_tasks/migration_task_base.py
@@ -1,6 +1,7 @@
 import csv
 import json
 import logging
+import os
 import sys
 import time
 from abc import abstractmethod
@@ -17,6 +18,12 @@
 from folio_migration_tools.custom_exceptions import TransformationRecordFailedError
 from folio_migration_tools.extradata_writer import ExtradataWriter
 from folio_migration_tools.folder_structure import FolderStructure
+from folio_migration_tools.marc_rules_transformation.marc_file_processor import (
+    MarcFileProcessor,
+)
+from folio_migration_tools.marc_rules_transformation.marc_reader_wrapper import (
+    MARCReaderWrapper,
+)
 
 
 class MigrationTaskBase:
@@ -35,6 +42,7 @@ def __init__(
         logging.info("MigrationTaskBase init")
         self.start_datetime = datetime.now(timezone.utc)
         self.task_configuration = task_configuration
+        logging.info(self.task_configuration.json(indent=4))
         self.folio_client: FolioClient = FolioClient(
             library_configuration.okapi_url,
             library_configuration.tenant_id,
@@ -73,6 +81,22 @@ def __init__(
     def wrap_up(self):
         raise NotImplementedError()
 
+    def clean_out_empty_logs(self):
+        if (
+            self.folder_structure.data_issue_file_path.is_file()
+            and os.stat(self.folder_structure.data_issue_file_path).st_size == 0
+        ):
+            logging.info("Removing data issues file since it is empty")
+            os.remove(self.folder_structure.data_issue_file_path)
+            logging.info("Removed data issues file since it was empty")
+
+        if (
+            self.folder_structure.failed_marc_recs_file.is_file()
+            and os.stat(self.folder_structure.failed_marc_recs_file).st_size == 0
+        ):
+            os.remove(self.folder_structure.failed_marc_recs_file)
+            logging.info("Removed empty failed marc records file since it was empty")
+
     @abstractmethod
     def do_work(self):
         raise NotImplementedError
@@ -118,14 +142,14 @@ def load_id_map(map_path, raise_if_empty=False):
             for index, json_string in enumerate(id_map_file, start=1):
                 loaded_rows = index
                 # {"legacy_id", "folio_id","suppressed"}
-                map_object = json.loads(json_string)
-                if loaded_rows % 50000 == 0:
+                map_tuple = json.loads(json_string)
+                if loaded_rows % 500000 == 0:
                     print(
-                        f"{loaded_rows + 1} ids loaded to map {map_object['legacy_id']}",
+                        f"{loaded_rows + 1} ids loaded to map. Last Id: {map_tuple[0]}",
                         end="\r",
                     )
 
-                id_map[map_object["legacy_id"]] = map_object
+                id_map[map_tuple[0]] = map_tuple
         logging.info("Loaded %s migrated IDs", loaded_rows)
         if not any(id_map) and raise_if_empty:
             raise TransformationProcessError("", "Legacy id map is empty", map_path)
@@ -137,15 +161,6 @@ def add_argument(parser, destination, help, **kwargs):
 
     def setup_logging(self):
         debug = self.library_configuration.log_level_debug
-        DATA_OUTPUT_LVL_NUM = 25
-        logging.addLevelName(DATA_OUTPUT_LVL_NUM, "DATA_OUTPUT")
-
-        def data_output(self, message, *args, **kws):
-            if self.isEnabledFor(DATA_OUTPUT_LVL_NUM):
-                # Yes, logger takes its '*args' as 'args'.
-                self._log(DATA_OUTPUT_LVL_NUM, message, args, **kws)
-
-        logging.Logger.data_output = data_output
 
         DATA_ISSUE_LVL_NUM = 26
         logging.addLevelName(DATA_ISSUE_LVL_NUM, "DATA_ISSUES")
@@ -228,6 +243,25 @@ def print_progress(num_processed, start_time):
             elapsed_formatted = "{0:.4g}".format(elapsed)
             logging.info(f"{num_processed:,} records processed. Recs/sec: {elapsed_formatted} ")
 
+    def do_work_marc_transformer(
+        self,
+    ):
+        logging.info("Starting....")
+        if self.folder_structure.failed_marc_recs_file.is_file():
+            os.remove(self.folder_structure.failed_marc_recs_file)
+            logging.info("Removed failed marc records file to prevent duplicating data")
+        with open(self.folder_structure.created_objects_path, "w+") as created_records_file:
+            self.processor = MarcFileProcessor(
+                self.mapper, self.folder_structure, created_records_file
+            )
+            for file_def in self.task_configuration.files:
+                MARCReaderWrapper.process_single_file(
+                    file_def,
+                    self.processor,
+                    self.folder_structure.failed_marc_recs_file,
+                    self.folder_structure,
+                )
+
     def load_ref_data_mapping_file(
         self,
         folio_property_name: str,
diff --git a/src/folio_migration_tools/migration_tasks/organization_transformer.py b/src/folio_migration_tools/migration_tasks/organization_transformer.py
index 0e898b54..a9e695a8 100644
--- a/src/folio_migration_tools/migration_tasks/organization_transformer.py
+++ b/src/folio_migration_tools/migration_tasks/organization_transformer.py
@@ -192,7 +192,7 @@ def do_work(self):
                 sys.exit()
 
     def wrap_up(self):
-        logging.info("Done. Wrapping up...")
+        logging.info("Done. Transformer wrapping up...")
         self.extradata_writer.flush()
         with open(self.folder_structure.migration_reports_file, "w") as migration_report_file:
             logging.info(
@@ -209,6 +209,8 @@ def wrap_up(self):
                 self.mapper.mapped_folio_fields,
                 self.mapper.mapped_legacy_fields,
             )
+        self.clean_out_empty_logs()
+
         logging.info("All done!")
 
     def clean_org(self, folio_rec):
diff --git a/src/folio_migration_tools/migration_tasks/requests_migrator.py b/src/folio_migration_tools/migration_tasks/requests_migrator.py
index 637baf55..2e72dc35 100644
--- a/src/folio_migration_tools/migration_tasks/requests_migrator.py
+++ b/src/folio_migration_tools/migration_tasks/requests_migrator.py
@@ -182,6 +182,7 @@ def wrap_up(self):
             self.migration_report.write_migration_report(
                 "Requests migration report", report_file, self.start_datetime
             )
+        self.clean_out_empty_logs()
 
     def write_failed_request_to_file(self):
         csv_columns = [
diff --git a/src/folio_migration_tools/migration_tasks/reserves_migrator.py b/src/folio_migration_tools/migration_tasks/reserves_migrator.py
index 09e69186..9e741deb 100644
--- a/src/folio_migration_tools/migration_tasks/reserves_migrator.py
+++ b/src/folio_migration_tools/migration_tasks/reserves_migrator.py
@@ -97,6 +97,7 @@ def wrap_up(self):
             self.migration_report.write_migration_report(
                 "Reserves migration report", report_file, self.start_datetime
             )
+        self.clean_out_empty_logs()
 
     def write_failed_reserves_to_file(self):
         # POST /coursereserves/courselistings/40a085bd-b44b-42b3-b92f-61894a75e3ce/reserves
diff --git a/src/folio_migration_tools/migration_tasks/user_transformer.py b/src/folio_migration_tools/migration_tasks/user_transformer.py
index b8c2299a..837f2f3a 100644
--- a/src/folio_migration_tools/migration_tasks/user_transformer.py
+++ b/src/folio_migration_tools/migration_tasks/user_transformer.py
@@ -177,6 +177,7 @@ def wrap_up(self):
                 self.mapper.mapped_legacy_fields,
             )
         logging.info("All done!")
+        self.clean_out_empty_logs()
 
     @staticmethod
     def clean_user(folio_user, index_or_id):
diff --git a/src/folio_migration_tools/report_blurbs.py b/src/folio_migration_tools/report_blurbs.py
index c601aa99..e6da52a2 100644
--- a/src/folio_migration_tools/report_blurbs.py
+++ b/src/folio_migration_tools/report_blurbs.py
@@ -9,7 +9,9 @@ class ReportSection(Enum):
 
 
 class Blurbs:
+    AuthoritySources = ("Authorization sources and related information", "")
     FieldMappingDetails = ("Mapping details", "")
+    CatalogingAgency = ("Cataloging sources", "")
     Trivia = ("Trivia", "")
     LeaderManipulation = ("Leader manipulation", "")
     TermsMapping = ("Terms Mapping", "Reference data mapping for Terms.")
@@ -158,7 +160,7 @@ class Blurbs:
     Section3 = ("__Section 3: items", "The entries below seem to be related to items")
     Exceptions = ("Exceptions", "")
     HridHandling = (
-        "HRID Handling",
+        "HRID and 001/035 handling",
         "There are two ways of handling HRIDs. The default behaviour is to take the current 001 and move that to a new 035. This will also emerge as an Identifier on the Inventory Instances. The 001 and Instance HRID will be generated from the HRID settings in FOLIO. The second option is to maintain the 001s in the records, and also add this as the Instance HRID",
     )
     PrecedingSuccedingTitles = (
diff --git a/src/folio_migration_tools/transaction_migration/legacy_course.py b/src/folio_migration_tools/transaction_migration/legacy_course.py
deleted file mode 100644
index ad434a52..00000000
--- a/src/folio_migration_tools/transaction_migration/legacy_course.py
+++ /dev/null
@@ -1,8 +0,0 @@
-class LegacyCourse(object):
-    correct_headers = [
-        "course_title",
-        "patron_barcode",
-        "create_date",
-        "remaining",
-        "amount",
-    ]
diff --git a/src/folio_migration_tools/transaction_migration/legacy_fee_fine.py b/src/folio_migration_tools/transaction_migration/legacy_fee_fine.py
deleted file mode 100644
index 010bfd0b..00000000
--- a/src/folio_migration_tools/transaction_migration/legacy_fee_fine.py
+++ /dev/null
@@ -1,36 +0,0 @@
-from datetime import datetime
-import logging
-from datetime import timezone
-from dateutil.parser import parse
-
-
-class LegacyFeeFine(object):
-    def __init__(self, legacy_fee_fine_dict, row=0):
-        # validate
-        correct_headers = [
-            "item_barcode",
-            "patron_barcode",
-            "create_date",
-            "remaining",
-            "amount",
-        ]
-        self.errors = []
-        for prop in correct_headers:
-            if prop not in legacy_fee_fine_dict:
-                self.errors.append(("Missing properties in legacy data", prop))
-            elif not legacy_fee_fine_dict[prop]:
-                self.errors.append(("Empty properties in legacy data", prop))
-        try:
-            temp_created_date: datetime = parse(legacy_fee_fine_dict["create_date"])
-        except Exception as ee:
-            logging.error(ee)
-            self.errors.append(("Dates that failed to parse", prop))
-            temp_created_date = datetime.now(timezone.utc)
-
-        # good to go, set properties
-        self.item_barcode = legacy_fee_fine_dict.get("item_barcode", "")
-        self.patron_barcode = legacy_fee_fine_dict.get("patron_barcode", "")
-        self.created_date = temp_created_date
-        self.remaining = legacy_fee_fine_dict.get("remaining", "")
-        self.amount = legacy_fee_fine_dict.get("amount", "")
-        self.source_dict = legacy_fee_fine_dict
diff --git a/static/reference_data.json b/static/reference_data.json
index b30e3feb..0d83d5ec 100644
--- a/static/reference_data.json
+++ b/static/reference_data.json
@@ -1915,6 +1915,17 @@
             "id": "e6da6c98-6dd0-41bc-8b4b-cfd4bbd9c3ae",
             "name": "Serial",
             "source": "folio"
+        },
+        {
+            "id": "b92480eb-210d-442e-a6f7-4043fe7f0f24",
+            "metadata": {
+                "createdByUserId": "7cee216a-b298-4e7c-b678-8917e6b8edd3",
+                "createdDate": "2022-12-08T09:55:58.067+00:00",
+                "updatedByUserId": "7cee216a-b298-4e7c-b678-8917e6b8edd3",
+                "updatedDate": "2022-12-08T09:55:58.067+00:00"
+            },
+            "name": "Unknown",
+            "source": "local"
         }
     ],
     "/identifier-types": [


Test patch:
diff --git a/src/folio_migration_tools/test_infrastructure/mocked_classes.py b/src/folio_migration_tools/test_infrastructure/mocked_classes.py
index ef67cde3..4649dfb5 100644
--- a/src/folio_migration_tools/test_infrastructure/mocked_classes.py
+++ b/src/folio_migration_tools/test_infrastructure/mocked_classes.py
@@ -35,7 +35,7 @@ def mocked_folio_client() -> FolioClient:
         raise ee
 
 
-def folio_get_all_mocked(ref_data_path, array_name, query, limit=10):
+def folio_get_all_mocked(ref_data_path, array_name, query="", limit=10):
     with open("./static/reference_data.json", "r") as super_schema_file:
         super_schema = json.load(super_schema_file)
     if ref_data_path == "/coursereserves/terms":
diff --git a/tests/test_basic.py b/tests/test_basic.py
index e1472dc9..6083110f 100644
--- a/tests/test_basic.py
+++ b/tests/test_basic.py
@@ -71,12 +71,37 @@ def test_flatten():
     instance_str = '{"id": "11af72aa-5921-46c4-8fa3-55b481849948", "metadata": {"createdDate": "2021-10-14T13:40:48.848", "createdByUserId": "f446ed29-2dac-436a-b1bc-8ebe5ac7ea76", "updatedDate": "2021-10-14T13:40:48.848", "updatedByUserId": "f446ed29-2dac-436a-b1bc-8ebe5ac7ea76"}, "hrid": "2471459", "identifiers": [{"identifierTypeId": "c858e4f2-2b6b-4385-842b-60732ee14abb", "value": "2018027930"}, {"identifierTypeId": "439bfbae-75bc-4f74-9fc7-b2a2d47ce3ef", "value": "(OCoLC)on1040079128"}, {"identifierTypeId": "8261054f-be78-422d-bd51-4ed9f33c3422", "value": "1541541839"}, {"identifierTypeId": "8261054f-be78-422d-bd51-4ed9f33c3422", "value": "9781541546745 (electronic bk.)"}, {"identifierTypeId": "8261054f-be78-422d-bd51-4ed9f33c3422", "value": "1541546741 (electronic bk.)"}, {"identifierTypeId": "8261054f-be78-422d-bd51-4ed9f33c3422", "value": "9781541541832 (electronic bk.)"}, {"identifierTypeId": "8261054f-be78-422d-bd51-4ed9f33c3422", "value": "9781541546752 (e-book)"}, {"identifierTypeId": "8261054f-be78-422d-bd51-4ed9f33c3422", "value": "154154675X"}], "classifications": [{"classificationTypeId": "ce176ace-a53e-4b4d-aa89-725ed7b2edac", "classificationNumber": "PZ7.1.H433"}], "contributors": [{"contributorNameTypeId": "2b94c631-fca9-4892-a730-03ee529ffe2a", "contributorTypeId": "6e09d47d-95e2-4d8a-831b-f777b8ef6d81", "contributorTypeText": "Contributor", "primary": true, "name": "Heathfield, Lisa"}, {"contributorNameTypeId": "2e48e713-17f3-4c13-a9f8-23845bb210aa", "contributorTypeId": "9f0a2cf0-7a9b-45a2-a403-f68d2850d07c", "contributorTypeText": "Contributor", "primary": false, "name": "PALCI EBSCO books"}], "title": "Flight of a starling / Lisa Heathfield.", "indexTitle": "Flight of a starling", "publication": [{"place": "Minneapolis", "publisher": "Carolrhoda Lab", "dateOfPublication": "[2019]", "role": "Publication"}], "physicalDescriptions": ["1 online resource"], "instanceFormatIds": ["8d511d33-5e85-4c5d-9bce-6e3c9cd0c324"], "notes": [{"instanceNoteTypeId": "6a2533a7-4de2-4e64-8466-074c2fa9308c", "note": "Originally published: London : Electric Monkey, 2017", "staffOnly": false}, {"instanceNoteTypeId": "e814a32e-02da-4773-8f3a-6629cdb7ecdf", "note": "Electronic access restricted to Villanova University patrons", "staffOnly": false}, {"instanceNoteTypeId": "10e2e11b-450f-45c8-b09b-0f819999966e", "note": "Told from two viewpoints, sisters Lo and Rita spend their lives flying high on the trapeze, but real danger comes as secrets begin to unravel the tightknit circus community and Lo finds love with a ", "staffOnly": false}, {"instanceNoteTypeId": "66ea8f28-d5da-426a-a7c9-739a5d676347", "note": "Description based on print version record and CIP data provided by publisher", "staffOnly": false}], "subjects": ["Sisters Fiction", "Circus Fiction", "Family life Fiction", "Dating (Social customs) Fiction", "Aerialists Fiction", "Families Fiction", "Aerialists", "Circus", "Dating (Social customs)", "Families", "Sisters", "Genre: Electronic books", "Genre: Fiction"], "electronicAccess": [{"relationshipId": "f5d0068e-6272-458e-8a81-b85e7b9a14aa", "uri": "http://ezproxy.villanova.edu/login?URL=http://search.ebscohost.com/login.aspx?direct=true&scope=site&db=nlebk&AN=1947377", "linkText": "", "materialsSpecification": "", "publicNote": "Online version"}], "source": "MARC", "instanceTypeId": "6312d172-f0cf-40f6-b27d-9fa8feaf332f", "modeOfIssuanceId": "9d18a02f-5897-4c31-9106-c9abb5c7ae8b", "languages": ["eng"], "discoverySuppress": false, "staffSuppress": false}'
     instance = json.loads(instance_str)
 
-    flat = list(mapper_base.flatten(instance))
+    flat = set(mapper_base.flatten(instance))
     assert "id" in flat
     assert "metadata.createdDate" in flat
     assert "contributors.name" in flat
 
 
+def test_flatten2():
+    my_dict = {
+        "a": {
+            "b": {"c": "95"},
+            "d": "apa",
+            "e": ["papa", "lapa"],
+            "f": [{"g": "another string"}, {"g": "a string"}],
+        },
+        "h": [{"g": "another string"}, {"g": "a string"}],
+        "i": ["papa", "lapa"],
+    }
+    flat = list(mapper_base.flatten(my_dict))
+
+    assert "a" in flat
+    assert "a.b" in flat
+    assert "a.d" in flat
+    assert "a.e" in flat
+    assert "a.f" in flat
+    assert "a.b.c" in flat
+    assert "a.f.g" in flat
+    assert len([a for a in flat if a == "a.f.g"]) == 2
+    assert len([a for a in flat if a == "a.e"]) == 2
+    assert len([a for a in flat if a == "h"]) == 1
+
+
 def test_udec():
     path = "./tests/test_data/msplit00000005.mrc"
     with open(path, "rb") as marc_file:
diff --git a/tests/test_bibs_transformer.py b/tests/test_bibs_transformer.py
index 42192aba..1fb95cab 100644
--- a/tests/test_bibs_transformer.py
+++ b/tests/test_bibs_transformer.py
@@ -2,6 +2,9 @@
 from pymarc import MARCReader
 from pymarc import Record
 
+from folio_migration_tools.marc_rules_transformation.marc_reader_wrapper import (
+    MARCReaderWrapper,
+)
 from folio_migration_tools.migration_report import MigrationReport
 from folio_migration_tools.migration_tasks.bibs_transformer import BibsTransformer
 from folio_migration_tools.report_blurbs import Blurbs
@@ -20,7 +23,7 @@ def test_set_leader():
         reader.force_utf8 = True
         record: Record = None
         record = next(reader)
-        BibsTransformer.set_leader(record, migration_report)
+        MARCReaderWrapper.set_leader(record, migration_report)
         assert record.leader.endswith("4500")
         assert record.leader[9] == "a"
         assert record.leader[10] == "2"
diff --git a/tests/test_colors.py b/tests/test_colors.py
new file mode 100644
index 00000000..a3d62ee5
--- /dev/null
+++ b/tests/test_colors.py
@@ -0,0 +1,13 @@
+from folio_migration_tools.colors import Bcolors
+
+
+def test_all_colors():
+    assert Bcolors.HEADER == "\033[95m"
+    assert Bcolors.OKBLUE == "\033[94m"
+    assert Bcolors.OKCYAN == "\033[96m"
+    assert Bcolors.OKGREEN == "\033[92m"
+    assert Bcolors.WARNING == "\033[93m"
+    assert Bcolors.FAIL == "\033[91m"
+    assert Bcolors.ENDC == "\033[0m"
+    assert Bcolors.BOLD == "\033[1m"
+    assert Bcolors.UNDERLINE == "\033[4m"
diff --git a/tests/test_conditions.py b/tests/test_conditions.py
index fd0373c8..a7073b77 100644
--- a/tests/test_conditions.py
+++ b/tests/test_conditions.py
@@ -12,7 +12,6 @@ def test_condition_trim_period():
 
 
 def test_condition():
-
     mock = Mock(spec=Conditions)
     parameter = {"subfieldsToConcat": ["q"]}
     legacy_id = "legacy_id"
diff --git a/tests/test_data/auth_363723.mrc b/tests/test_data/auth_363723.mrc
new file mode 100644
index 00000000..61ef207c
--- /dev/null
+++ b/tests/test_data/auth_363723.mrc
@@ -0,0 +1,1 @@
+00509cz  a  00145n  450000100070000000500170000700800410002402400270006504000150009204300120010710000300011967000840014967800260023388701040025936372320130527105911.0130527nb ac|nnaabn          |n aaa    |d7 a00000004096946272isni  aKdbsweec  ae-sw---1 aEricsson, Leif KE,d1964-  aGrowth and characterization of ZnO nanocrystals, 2013:bt.p. (Leif KE Ericsson)  aDoktorerat inom fysik  a{"@id":"khw06k731bb0clj","modified":"2013-05-27T10:59:11+02:00","checksum":"46067695014"}2librisxl
\ No newline at end of file
diff --git a/tests/test_data/auth_918643.mrc b/tests/test_data/auth_918643.mrc
new file mode 100644
index 00000000..56156f92
--- /dev/null
+++ b/tests/test_data/auth_918643.mrc
@@ -0,0 +1,1 @@
+00967cz  a2200193n  450000100070000000500170000700800410002401000170006504000280008210000160011037200320012637400270015840000210018540000140020667001790022067000640039967002320046367000780069591864320190102133608.0080418n| azannaabn          |a aaa        an 2008028538  aDLCbengerdacDLCdDLC1 aYu, Danling  aLawaEnglish language2lcsh  aCollege teachers2lcsh1 wnneaYu, Tanling1 a于丹翎  aYu, Tanling. American contract law, 2006:bt.p. (Yu Tanling; Yu Danling [in Chi.]) About the author (LLB and Masters, China Foreign Affairs Univ. and JD, Stanford Law School)  aChinese business law, 2015:bt.p. (Yu Danling = 于丹翎 )  aAuthor's letter dated June 4, 2016 attached to the book "Chinese business Law":bp.3-4 (Yu Danling; JD, Stanford University; Professor of law and Professor of English Language and Literature at China Foreign Affairs University)  aLC database, Nov. 17, 2016:b(usage: Yu, Danling; Yu, Tanling; 于丹翎)
\ No newline at end of file
diff --git a/tests/test_data/diacritics/test-880.mrc b/tests/test_data/diacritics/test-880.mrc
index 22925e9a..2bfd258c 100644
--- a/tests/test_data/diacritics/test-880.mrc
+++ b/tests/test_data/diacritics/test-880.mrc
@@ -1,1 +1,1 @@
-07819cam a22003493  45 0001001000000003000600010008004100016245005900057264002400116300003100140336002100171337002500192338002300217500003200240505336800272610003403640650002103674710003403695940002403729596000703753690005203760690003303812880005903845880002003904880339603924880002907320880002907349910002907378910002907407910002007436918001307456a12196508SIRSI171027m19591973ja            000 0 jpn d006880-05a[Tōkyō Takarazuka gekijō kōen panfuretto.] 16880-02aTakarazuka.  a43 volumes:billustrations  atext2rdacontent  aunmediated2rdamedia  avolume2rdacarrier  aTitle devised by cataloger.006880-01g1.t7-gatsu Hoshigumi kōen.tYodogimi, Shanson Damūru (1959) --g2.t8-gatsu Hanagumi kōen.tHanataue --tArupusu e no shōtai (1959) --g3.t10-gatsu Yukigumi kōen.tYumiharizuki (1959) --g4.t11-gatsu Tsukigumi kōen. tAsama no tonosama --tDaru Rēku no koi (1959) --g5.t1-gatsu Hoshigumi kōen.tKuroazahime to sumiyaki --tKimi arite koso (1960) --g6.t3-gatsu Tsukigumi kōen.tYukihime --tUi ui Pari (1960) --g7.t8-gatsu Hanagumi kōen. tShanson do Pari --tTōkyō no sora no shita (1960) --g8. t7-gatsu Yukigumi kōen.tŌgi --tRōzu Damūru (1960) --g9.t1-gatsu Tsukigumi kōen. tBishō no kuni --tShō izu on (1961) --g10.t3-gatsu Hoshigumi kōen.tOboroyo zukiyo --tKareinaru senbyōshi (1961) --g11.t4-gatsu Yukigumi kōen.tZansetsu --tKareinaru senbyōshi (1961) --g12.t7-gatsu Tsukigumi kōen.tTenshu monogatari --tSarutan banku (1961) --g13.t8-gatsu Hanagumi kōen.tPoniii Redī (1961) --g14.t11-gatsu Yukigumi kōen.tHi no shima --tSabaku ni kieru (1961) --g15.t1-gatsu Hoshigumi kōen.tKengō to botanmochi --tForute de ikō (1962) --g16.t3-gatsu Yukigumi kōen.tHi no shima --tShakunetsu no Karibia (1962) --g17.t4-gatsu Hanagumi kōen.tYo wa kagerō no monogatari --tShiawase ga ippai (1962) --g18.t7-gatsu Hoshigumi kōen.tMeido in Nippon (1962) -- g19.t8-gatsu Tsukigumi kōen.tOdori mandara --tRe gāruzu (1962) -- g20.t11-gatsu Hana Hoshi gōdō kōen.tHimegumo --tTakarajennu ni eikō are (1962) --g21.t1-gatsu Tsukigumi Kōen.tKiyohime --tBoku wa kimi (1963) --g22.t3-gatsu Hanagumi kōen.tOmine-daiko --tKōtei to majo (1963) --g23.t4-gatsu Yukigumi kōen.tBurakku kyandoru --tHana no Oranda-zaka (1963) --g24.t6-gatsu Hoshigumi kōen.tNanbā o dōzo --tKachūsha monogatari (1963) --g25.t8-gatsu Yukigumi kōen.tShiroi tenshitachi no uta --tFushichō no tsubasa moyutomo (1963) --g26.t11-gatsu Tsuki, Hoshigumi gōdō kōen. tNiji no orugōru kōjō --tRebyū e no shōtai (1963) --g27.t4-gatsu Hoshigumi kōen Shangurira (1964) --g28.t7-gatsu Senka Yukigumi gōdō kōen.tHōju --tKureopatora (1964) --g29.t8-gatsu Hoshi, Yukigumi gōdō kōen.tSuna ni egakouyo --tRebyū obu rebyūzu (1964) --g30.t10-gatsu Tsukigumi kōen.tYunta --tNihon no senritsu (1964) --g31.t11-gatsu Hanagumi kōen. tRakuyō ni hana chiredo --tSekai wa ichinichi (1964) --g32.t7-gatsu Tsukigumi kōen.tUtakurabe --tSupēdo no joō (1965) --g33.t3-gatsu Hoshigumi kōen. tYogiri no shiro no koi no monogatari --tEsukaiya gāruzu (1966) --g34.t11-gatsu Hanagumi kōen Nihon no gensō --tYume o uru yōseitachi (1966) --g35.t8-gatsu Hanagumi kōen.tUni no hanatennyo --tKirameku hoshi no shita ni (1967) --g36.t8-gatsu Yukigumi kōen.tShiki no odori emaki --t Harō Takarazuka (1970) --g37.t9-gatsu Hanagumi kōen.tHonoo --tDorīmu a dorīmu (1970) --g38.t3-gatsu Yukigumi kōen.tKōbai hakubai --tShingāzu shingā (1971) --g39.t4-gatsu Hoshigumi kōen. tHoshi no makiba --tŌ byūtifuru (1971) --g40.t3-gatsu Hoshigumi kōen.tItsuno hika awan --tNoba bosa noba (1972) --g41.t7-gatsu Hanagumi kōen.tChiisana hana ga hiraita --tAishū no Nairu (1972) --g42.t4-gatsu Yukigumi kōen.tHanafubuki --tAi no rapusodi (1973) -- g43.t8-gatsu Tsukigumi kōen.tKiri fukaki Erube no hotori --tFanī fīringu (1973)206880-03aTakarazuka Kagekidan. 0aMusicalszJapan.2 6880-04aTakarazuka Kagekidan.  aOrder ID: 310229F18  a26 4aTakarazuka collection at the East Asia Library. 4aJapanese theatre collection.006245-05a[東京宝塚劇場公演パンフレット. ] 16264-02a宝塚.006505-01g1.t7月星組公演. t淀君, シャンソン・ダムール (1959) --g2.t8月花組公演.t花田植 --tアルプスへの招待 (1959) --g3.t10月雪組公演.t弓張月 (1959) --g4.t11月月組公演.t浅間の殿様 --tダル・レークの恋 (1959) --g5.t1月星組公演.t黒あざ姫と炭焼 --t君ありてこそ (1960) --g6.t3月月組公演.t雪姫 --tウイ・ウイ・パリ (1960) --g7.t8月花組公演.tシャンソン・ド・パリ --t東京の空の下 (1960) --g8. t7月雪組公演.t扇 --tローズ・ダムール (1960) --g9.t1月月組公演.t微笑の国 --tショウ・イズ・オン (1961) --g10.t3月星組公演. t朧夜源氏 --t華麗なる千拍子 (1961) -- g11.t4月雪組公演. t残雪 --t華麗なる千拍子 (1961) --g12.t7月月組公演.t天守物語  --tサルタンバンク (1961) --g13.t8月花組公演.tポニィ・レディー (1961) --g14.t1月雪組公演.t火の島 --t砂漠に消える (1961) --g15.t1月星組公演.t剣豪と牡丹餅 --tフォルテで行こう (1962) --g16.t3月雪組公演. t火の島 --t灼熱のカリビア (1962) --g17.t4月花組公演.t世はかげろうの物語 --t幸せがいっぱい (1962) --g18.t7月星組公演.tメイド・イン・ニッポン (1962) --g19.t8月月組公演.tおどり曼荼羅 --tレ・ガールズ (1962) --g20. t11月花・星合同公演.t姫蜘蛛 --tタカラジェンヌに栄光あれ (1962) --g21.t1月月組公演.t清姫 --t僕は君 (1963) --g22.t3月花組公演.tおみね太鼓 --t皇帝と魔女 (1963) --g23.t4月雪組公演.tブラックキャンドル --t花のオランダ坂 --g24.t6月星組公演. tナンバーをどうぞ --tカチューシャ物語 (1963) --g25.t8月雪組公演.t白い天使たちの歌 --t不死鳥のつばさ燃ゆとも (1963) --g26.t11月月・星組合同公演.t虹のオルゴール工場 --tレビューへの招待 (1963) --g27.t4月星組公演.tシャングリラ (1964) --g28.t7月専科・雪組合同公演.t宝寿 --tクレオパトラ (1964) --g29.t8月星・雪組み合同公演.t砂に描こうよ --tレビュー・オブ・レビューズ (1964) --g30.t10月月組公演.tユンタ --t日本の旋律 (1964) --g31.t11月花組公演.t洛陽に花散れど --t世界は一日 (1964) --g32.t7月月組公演.t歌くらべ --tスペードの女王 (1965) --g33.t3月星組公演.t夜霧の城の恋の物語 --tエスカイヤ・ガールズ (1966) --g34.t11月花組公演.t日本の幻想 --t夢を売る妖精たち (1966) --g35.t8月花組公演.t海の花天女 --t燦く星の下に (1967) --g36.t8月雪組公演.t四季の踊り絵巻 --tハロー!タカラズカ (1970) --g37.t8月花組公演.t炎 --tドリーム・ア・ドリーム (1970) --g38.t3月雪組公演.t紅梅白梅 --tシンガース・シンガー (1971) --g39.t4月星組公演.t星の牧場 --tオー！ビューティフル (1971) --g40.t3月星組公演.tいつの日か逢わん --tノバ・ボサ・ノバ (1972) --g41.t7月花組公演.t小さな花がひらいた --t哀愁のナイル (1972) --g42.t4月雪組公演.t花吹雪 --t愛のラプソディ (1973) --g43.t8月月組公演.t霧深きエルベのほとり --tファニーフィーリング (1973)246610-03a宝塚歌劇団.2 6710-04a宝塚歌劇団.  aNONLATINCJK/20171031/ma1  aNONLATINCJK/20171031/mm1  aEAL/20220210/mm  a1219650806122cpc a2200325Ki 45 0001001000000003000600010008004100016040002300057043001200080049000900092090001500101245007900116300001800195336002100213337002500234338002300259520020400282651007700486650008700563650008900650651009300739856005700832994001200889910003000901910001900931596000700950035002400957880480200981918001305783a12257361SIRSI171128nuuuuuuuucc                  chi d  aSTFbengerdacSTF  aa-cc---  aSTFA  aEALCHN000900aCollection of Chinese working unit documents and personal files, 1949-1985  a97 containers  atext2rdacontent  aunmediated2rdamedia  avolume2rdacarrier  aArchival materials that include "danwei" reports, individual dossiers, personal correspondence, legal briefs, and miscellaneous materials of ordinary Chinese workers, low-ranking cadres, and workers. 0aChinaxHistoryy1949-0http://id.loc.gov/authorities/subjects/sh85024123 0aPublic administrationzChina.0http://id.loc.gov/authorities/subjects/sh2008109092 0aAdministrative agencieszChina.0http://id.loc.gov/authorities/subjects/sh2009113589 0aChinaxPolitics and governmenty1949-0http://id.loc.gov/authorities/subjects/sh85024174413Content list.uhttps://purl.stanford.edu/th607zg5176  aC0bSTF  aNONLATINCJK/20171128/aly1  aEAL/2021113/hl  a26  a(OCoLC-M)1013208921006505-00gBox 1.t北京市纺织工业部设计院文书档案 -- gBox 2 - 5.t北京市纺织工业部设计院干部档案 -- gBox 6 - 9.t北京市纺织工业部设计院干部个人档案 --gBox 10.t北京市纺织工业部设计院文书档案 -- gBox 11 - 13.t北京市建筑工程部第六工程局文书档案 -- gBox 14.t北京市建筑工程部第一，六工程局文书档案 --gBox 15 - 16.t北京市建筑工程部第一工程局文书档案 -- gBox 17.t北京市铁道部第十六工程局四处第一工程段文书档案 -- gBox 18 - 19.t北京市农业科学院土壤肥料研究所文书档案 -- gBox 20 - 21.t北京市农业科学院土壤肥料研究所干部个人档案 -- gBox 22.t北京市中央气象局观象台文书档案 -- tBox 23 - 27.t北京市地质局文书档案 -- gBox 28.t北京市劳改局文书档案及干部个人档案 -- gBox 29 - 30.t北京市公安局朝阳分局三里屯派出所文书档案 -- gBox 31 - 33.t北京市公共汽车公司四场文书档案 -- gBox 34.t北京市对外贸易包装运输公司，第一建筑公司，朝阳区商业局等，个人档案 -- gBox 35.t北京市食品公司东城分公司，北京市煤业建筑材料公司文书挡案 -- gBox 36.t北京市棉纺织厂储备处青岛训练班文档 -- gBox 37.t北京市棉纺厂筹备处青岛训练班文书档案 -- gBox 38.t北京市八宝山砂石厂人保科 -- gBox 39.t北京市铁道兵学校及北京市外贸仓储分公司个人档案 -- gBox 40.t北京市丰台区和昌平县文书档案 -- gBox 41.t北京市朝阳县十八里店公社老君堂大队文书档案 -- gBox 42.t北京市某派出所，山西省平遥县文书档案 -- gBox 43.t山西省离山县，方山县，离石县，界休县文书档案 -- gBox 44.t山西省机要通新局文书档案 -- gBox 45 - 46.t山西省太原清徐县洋房公社文书档案 -- gBox 47.t山西省沁县杨安公社文书档案 -- gBox 48.t河北省石家庄市桥西区革委会文书档案 -- gBox 49.t东北铁路公安局文书档案 -- gBox 50.t东北铁道部公安局团委，东北铁路公安局文书档案 -- gBox 51.t吉林铁路局公安处吉林段文书档案 -- gBox 52.t各地出版物 -- gBox 53.t单位不详文书档案 -- gBox 54 - 58.t河北省怀来县党员个人档案 -- gBox 59.t河北省赤城县党员个人档案 -- gBox 60.t河北省赤城县个人档案 -- gBox 61.t河北省安新县粮食局文档资料： 财务资料 -- gBox 62.t河北省人民政府商业厅内部刊物； 无名人士工作笔记 -- gBox 63.t湖北省武汉文化大革命政治历史审核材料： 人事档案 -- gBox 64.t湖南长沙法院案卷： 文化大革命中一六三医院材料 -- gBox 65.t湖南常德法院案卷 -- gBox 66.t 济南市人民政府税务局第四分局个人费用报销表 -- gBox 67.t内蒙四子王旗第九区财务资料 -- gBox 68.t内蒙四子王旗公文资料：会议资料 -- gBox 69.t内蒙阿其图乌拉公社介绍信 -- gBox 70.t51 份入党入团自愿书 （地区不详） -- gBox 71.tCD (Box 54 - Box 70 原始电子资料）及CD明细表 -- gBox 72.t四川旺苍县干部档案 -- gBox 73.t四川, 贵州，湖南等地个人档案 -- gBox 74.t证书，单据和其他 -- gBox 75.t四川省医药公司有关检查揭发材料 -- gBox 76 - 77.t四川省医药公司劳动工资资料 -- gBox 78 - 80.t王水资料集 -- gBox 81.t中共太原省市i机关文件， 山西其他地区资料 -- gBox 82.t各地资料： 辽宁， 内蒙古， 天津， 河北，黑龙江，湖南 -- gBox 83.t北京，天津， 山东等地个人档案 -- gBox 84.t内蒙古宁城地区文书档案 -- gBox 85.t生产建设兵团政治部政法部卷宗 -- gBox 86.t吉林省个人档案 -- tBox 87.t吉林省个人档案 -- gBox 88.t湖北省江陵县个人档案 -- gBox 89.t上海敌伪档案资料汇编 -- gBox 90.t河北武清县个人档案 -- tBox 91.t安徽怀宁县个人档案 -- gBox 92.t安徽贵池县机关人员个人档案 -- gBox 93.t安徽贵池县铜山公社长林大队个人档案 -- gBox 94.t安徽贵池县铜山公社长林大队个人档案 -- gBox 95.t安徽省安庆市个人档案 -- gBox 96.t安徽省其他地区个人档案：太湖县，芜湖市， 望江县等 -- gBox 97.t黑龙江省个人档案 --gBox98.t河北省个人档案 --gBox99.t内蒙古呼和浩特和其他地区档案 --gBox100.t河北省怀来县单位档案 --gBox101.t河北省沽源县单位档案 --gBox102.t河北省尚义县和固安县单位档案 --gBox103.t河北省保定地区单位档案 --gBox104.t山西省天镇县和繁峙县单位档案 --gBox105.t山西省怀仁县和定襄县单位档案  a1225736101378cam a2200337Mi 45 0001001000000003000600010005001700016008004100033040004200074049000900116050002300125245013100148264009600279300004000375336002600415337002800441338002700469610003100496650007400527710003100601830004100632880013300673880009500806880003500901880003500936994001200971001001300983596000700996035002401003918001301027a12698851SIRSI20180707013011.0171003s2017    bl            000 0 jpn d  aNHNbjpnencr/1987cNHNdHINdUtOrBLW  aHINA 4aF2659.J3bB87 2017006880-01aBurajiru rikkokai 100nen no ayumi :b1917 - 2017 /cBurajiru rikkokai sōritsu 100-shūnen kinenshi hensan iinkai hen. 16880-02aSão Paulo :bBurajiru rikkokai sōritsu 100-shūnen kinenshi hensan iinkai,c2017.  a341 pages :billustrations ;c27 cm  atextbtxt2rdacontent  aunmediatedbn2rdamedia  avolumebnc2rdacarrier206880-03aBurajiru Rikkokai. 0aJapanesezBrazil0http://id.loc.gov/authorities/subjects/sh20081059862 6880-04aBurajiru Rikkokai. 0aHoover Japanese Diaspora Collection.006245-01aブラジル力行会100年のあゆみ :b1917-2017 /cブラジル力行会創立100周年記念誌編纂委員会編. 16264-02aSão Paulo :bブラジル力行会創立100周年記念誌編纂委員会,c2017.206610-03aブラジル力行会.2 6710-04aブラジル力行会.  aC0bHINon1013998368  a25  a(OCoLC-M)1043443303  a1269885102821cpc a2200421Ii 45 0001001000000003000600010005001700016006001900033008004100052040003700093043001200130245009200142264002800234300001900262336002100281337002500302338002200327338002100349500003100370506010300401520066800504524019801172540044401370541008201814650002301896650006001919655002201979700009102001994001202092001001302104099001402117979010802131910003702239596000702276035002402283880007902307918001302386a13798527SIRSI20220115014323.0m     o  c        220112i19401950is      o           heb d  aSTFbengerdacSTFdSTFdUtOrBLW  aa-is---04a[Collection of invitations sent to Rabbi Nisan Tukachinsky from residents of Jerusalem] 1aJerusalem,c[1940-1950]  a36 invitations  atext2rdacontent  aunmediated2rdamedia  asheet2rdacarrier  acard2rdacarrier  aTitle devised by cataloger  aOpen for research. Note that material must be ordered at least 36 hours in advance of intended use2 aCollections consists of 36 invtations and addressed envelopes of various sizes sent to Rabbi Nisan Tukachinsky from residents of Jerusalem. These included rabbis, lawyers and homeowners such as the Salant family, Rabbi Arieli, Rabbi Shlomo Zalman Zeleznik (rosh yeshivah of Etz Chaim); his son Rabbi Avraham Ya'akov Zeleznik; Rabbi Alter Yavrov; Rabbi Elyakim Daburks; Rabbi Shmuel Aharon Yudelevitz; Rabbi Shlomo Zalman Solomon  known as "The Printer" ' grandson of Rabbi Yoel Moshe; Rabbi Moshe David Tennenbaum. Rabbi Binyamin Trachtenberg. Invitations were for a number of different occasions including Bar Matsvas, anniversaries, engagement parties, weddings  a[Collection of invitations sent to Rabbi Nisan Tukachinsky from residents of Jerusalem. Dept. of Special Collections and University Archives, Stanford University Libraries, Stanford, California  aWhile Special Collections is the owner of the physical and digital items, permission to examine collection materials is not an authorization to publish. These materials are made available for use in research, teaching and private study. Any transmission or reproduction beyond that for fair use requires permission from the owner of rights, heir(s) or assigns. See: https://library.stanford.edu/spc/using-our-collections/permission-publish  cPurchase;aReiss Antiquariat - Eran Reiss;d2021.eAccession no. MSS-2021-215 0aRabbiszJerusalem. 0aJewszJerusalemxSocial life and customsy20th century. 7aInvitations.2aat1 6880-01aṬuḳtsinsḳi, Nisan Aharon.0http://id.loc.gov/authorities/names/n95030666  aZ0bSTFon1291718766  aMISC 2703  fBARONJbdruid:cd966nb0770ccd966nb0770_00_0001.jp2dJeannette Meisel and Salo Wittmayer Baron Book Fund  aCatalogedbySPEC/hgl/date20220112  a21  a(OCoLC-M)12917187661 6700-01//raטוקצינסקי, ניסן אהרן בן יחיאל מיכל.  a13798527
\ No newline at end of file
+07948cam a22003613  45 0001001000000003000600010008004100016245006300057264002400120300003100144336002100175337002500196338002300221500003200244505346700276610003403743650002103777710003403798940002403832596000703856690005203863690003303915880005903948880002004007880339604027880002907423880001407452880002907466910002907495910002907524910002007553918001307573a12196508SIRSI171027m19591973ja            000 0 jpn d006880-05a[Tōkyō Takarazuka gekijō kōen panfuretto.] 16880-02aTakarazuka.  a43 volumes:billustrations  atext2rdacontent  aunmediated2rdamedia  avolume2rdacarrier  aTitle devised by cataloger.006880-01g1.t7-gatsu Hoshigumi kōen.tYodogimi, Shanson Damūru (1959) --g2.t8-gatsu Hanagumi kōen.tHanataue --tArupusu e no shōtai (1959) --g3.t10-gatsu Yukigumi kōen.tYumiharizuki (1959) --g4.t11-gatsu Tsukigumi kōen. tAsama no tonosama --tDaru Rēku no koi (1959) --g5.t1-gatsu Hoshigumi kōen.tKuroazahime to sumiyaki --tKimi arite koso (1960) --g6.t3-gatsu Tsukigumi kōen.tYukihime --tUi ui Pari (1960) --g7.t8-gatsu Hanagumi kōen. tShanson do Pari --tTōkyō no sora no shita (1960) --g8. t7-gatsu Yukigumi kōen.tŌgi --tRōzu Damūru (1960) --g9.t1-gatsu Tsukigumi kōen. tBishō no kuni --tShō izu on (1961) --g10.t3-gatsu Hoshigumi kōen.tOboroyo zukiyo --tKareinaru senbyōshi (1961) --g11.t4-gatsu Yukigumi kōen.tZansetsu --tKareinaru senbyōshi (1961) --g12.t7-gatsu Tsukigumi kōen.tTenshu monogatari --tSarutan banku (1961) --g13.t8-gatsu Hanagumi kōen.tPoniii Redī (1961) --g14.t11-gatsu Yukigumi kōen.tHi no shima --tSabaku ni kieru (1961) --g15.t1-gatsu Hoshigumi kōen.tKengō to botanmochi --tForute de ikō (1962) --g16.t3-gatsu Yukigumi kōen.tHi no shima --tShakunetsu no Karibia (1962) --g17.t4-gatsu Hanagumi kōen.tYo wa kagerō no monogatari --tShiawase ga ippai (1962) --g18.t7-gatsu Hoshigumi kōen.tMeido in Nippon (1962) -- g19.t8-gatsu Tsukigumi kōen.tOdori mandara --tRe gāruzu (1962) -- g20.t11-gatsu Hana Hoshi gōdō kōen.tHimegumo --tTakarajennu ni eikō are (1962) --g21.t1-gatsu Tsukigumi Kōen.tKiyohime --tBoku wa kimi (1963) --g22.t3-gatsu Hanagumi kōen.tOmine-daiko --tKōtei to majo (1963) --g23.t4-gatsu Yukigumi kōen.tBurakku kyandoru --tHana no Oranda-zaka (1963) --g24.t6-gatsu Hoshigumi kōen.tNanbā o dōzo --tKachūsha monogatari (1963) --g25.t8-gatsu Yukigumi kōen.tShiroi tenshitachi no uta --tFushichō no tsubasa moyutomo (1963) --g26.t11-gatsu Tsuki, Hoshigumi gōdō kōen. tNiji no orugōru kōjō --tRebyū e no shōtai (1963) --g27.t4-gatsu Hoshigumi kōen Shangurira (1964) --g28.t7-gatsu Senka Yukigumi gōdō kōen.tHōju --tKureopatora (1964) --g29.t8-gatsu Hoshi, Yukigumi gōdō kōen.tSuna ni egakouyo --tRebyū obu rebyūzu (1964) --g30.t10-gatsu Tsukigumi kōen.tYunta --tNihon no senritsu (1964) --g31.t11-gatsu Hanagumi kōen. tRakuyō ni hana chiredo --tSekai wa ichinichi (1964) --g32.t7-gatsu Tsukigumi kōen.tUtakurabe --tSupēdo no joō (1965) --g33.t3-gatsu Hoshigumi kōen. tYogiri no shiro no koi no monogatari --tEsukaiya gāruzu (1966) --g34.t11-gatsu Hanagumi kōen Nihon no gensō --tYume o uru yōseitachi (1966) --g35.t8-gatsu Hanagumi kōen.tUni no hanatennyo --tKirameku hoshi no shita ni (1967) --g36.t8-gatsu Yukigumi kōen.tShiki no odori emaki --t Harō Takarazuka (1970) --g37.t9-gatsu Hanagumi kōen.tHonoo --tDorīmu a dorīmu (1970) --g38.t3-gatsu Yukigumi kōen.tKōbai hakubai --tShingāzu shingā (1971) --g39.t4-gatsu Hoshigumi kōen. tHoshi no makiba --tŌ byūtifuru (1971) --g40.t3-gatsu Hoshigumi kōen.tItsuno hika awan --tNoba bosa noba (1972) --g41.t7-gatsu Hanagumi kōen.tChiisana hana ga hiraita --tAishū no Nairu (1972) --g42.t4-gatsu Yukigumi kōen.tHanafubuki --tAi no rapusodi (1973) -- g43.t8-gatsu Tsukigumi kōen.tKiri fukaki Erube no hotori --tFanī fīringu (1973)206880-03aTakarazuka Kagekidan. 0aMusicalszJapan.2 6880-04aTakarazuka Kagekidan.  aOrder ID: 310229F18  a26 4aTakarazuka collection at the East Asia Library. 4aJapanese theatre collection.006245-05a[東京宝塚劇場公演パンフレット. ] 16264-02a宝塚.006505-01g1.t7月星組公演. t淀君, シャンソン・ダムール (1959) --g2.t8月花組公演.t花田植 --tアルプスへの招待 (1959) --g3.t10月雪組公演.t弓張月 (1959) --g4.t11月月組公演.t浅間の殿様 --tダル・レークの恋 (1959) --g5.t1月星組公演.t黒あざ姫と炭焼 --t君ありてこそ (1960) --g6.t3月月組公演.t雪姫 --tウイ・ウイ・パリ (1960) --g7.t8月花組公演.tシャンソン・ド・パリ --t東京の空の下 (1960) --g8. t7月雪組公演.t扇 --tローズ・ダムール (1960) --g9.t1月月組公演.t微笑の国 --tショウ・イズ・オン (1961) --g10.t3月星組公演. t朧夜源氏 --t華麗なる千拍子 (1961) -- g11.t4月雪組公演. t残雪 --t華麗なる千拍子 (1961) --g12.t7月月組公演.t天守物語  --tサルタンバンク (1961) --g13.t8月花組公演.tポニィ・レディー (1961) --g14.t1月雪組公演.t火の島 --t砂漠に消える (1961) --g15.t1月星組公演.t剣豪と牡丹餅 --tフォルテで行こう (1962) --g16.t3月雪組公演. t火の島 --t灼熱のカリビア (1962) --g17.t4月花組公演.t世はかげろうの物語 --t幸せがいっぱい (1962) --g18.t7月星組公演.tメイド・イン・ニッポン (1962) --g19.t8月月組公演.tおどり曼荼羅 --tレ・ガールズ (1962) --g20. t11月花・星合同公演.t姫蜘蛛 --tタカラジェンヌに栄光あれ (1962) --g21.t1月月組公演.t清姫 --t僕は君 (1963) --g22.t3月花組公演.tおみね太鼓 --t皇帝と魔女 (1963) --g23.t4月雪組公演.tブラックキャンドル --t花のオランダ坂 --g24.t6月星組公演. tナンバーをどうぞ --tカチューシャ物語 (1963) --g25.t8月雪組公演.t白い天使たちの歌 --t不死鳥のつばさ燃ゆとも (1963) --g26.t11月月・星組合同公演.t虹のオルゴール工場 --tレビューへの招待 (1963) --g27.t4月星組公演.tシャングリラ (1964) --g28.t7月専科・雪組合同公演.t宝寿 --tクレオパトラ (1964) --g29.t8月星・雪組み合同公演.t砂に描こうよ --tレビュー・オブ・レビューズ (1964) --g30.t10月月組公演.tユンタ --t日本の旋律 (1964) --g31.t11月花組公演.t洛陽に花散れど --t世界は一日 (1964) --g32.t7月月組公演.t歌くらべ --tスペードの女王 (1965) --g33.t3月星組公演.t夜霧の城の恋の物語 --tエスカイヤ・ガールズ (1966) --g34.t11月花組公演.t日本の幻想 --t夢を売る妖精たち (1966) --g35.t8月花組公演.t海の花天女 --t燦く星の下に (1967) --g36.t8月雪組公演.t四季の踊り絵巻 --tハロー!タカラズカ (1970) --g37.t8月花組公演.t炎 --tドリーム・ア・ドリーム (1970) --g38.t3月雪組公演.t紅梅白梅 --tシンガース・シンガー (1971) --g39.t4月星組公演.t星の牧場 --tオー！ビューティフル (1971) --g40.t3月星組公演.tいつの日か逢わん --tノバ・ボサ・ノバ (1972) --g41.t7月花組公演.t小さな花がひらいた --t哀愁のナイル (1972) --g42.t4月雪組公演.t花吹雪 --t愛のラプソディ (1973) --g43.t8月月組公演.t霧深きエルベのほとり --tファニーフィーリング (1973)246610-03a宝塚歌劇団.24awithout 62 6710-04a宝塚歌劇団.  aNONLATINCJK/20171031/ma1  aNONLATINCJK/20171031/mm1  aEAL/20220210/mm  a1219650806122cpc a2200325Ki 45 0001001000000003000600010008004100016040002300057043001200080049000900092090001500101245007900116300001800195336002100213337002500234338002300259520020400282651007700486650008700563650008900650651009300739856005700832994001200889910003000901910001900931596000700950035002400957880480200981918001305783a12257361SIRSI171128nuuuuuuuucc                  chi d  aSTFbengerdacSTF  aa-cc---  aSTFA  aEALCHN000900aCollection of Chinese working unit documents and personal files, 1949-1985  a97 containers  atext2rdacontent  aunmediated2rdamedia  avolume2rdacarrier  aArchival materials that include "danwei" reports, individual dossiers, personal correspondence, legal briefs, and miscellaneous materials of ordinary Chinese workers, low-ranking cadres, and workers. 0aChinaxHistoryy1949-0http://id.loc.gov/authorities/subjects/sh85024123 0aPublic administrationzChina.0http://id.loc.gov/authorities/subjects/sh2008109092 0aAdministrative agencieszChina.0http://id.loc.gov/authorities/subjects/sh2009113589 0aChinaxPolitics and governmenty1949-0http://id.loc.gov/authorities/subjects/sh85024174413Content list.uhttps://purl.stanford.edu/th607zg5176  aC0bSTF  aNONLATINCJK/20171128/aly1  aEAL/2021113/hl  a26  a(OCoLC-M)1013208921006505-00gBox 1.t北京市纺织工业部设计院文书档案 -- gBox 2 - 5.t北京市纺织工业部设计院干部档案 -- gBox 6 - 9.t北京市纺织工业部设计院干部个人档案 --gBox 10.t北京市纺织工业部设计院文书档案 -- gBox 11 - 13.t北京市建筑工程部第六工程局文书档案 -- gBox 14.t北京市建筑工程部第一，六工程局文书档案 --gBox 15 - 16.t北京市建筑工程部第一工程局文书档案 -- gBox 17.t北京市铁道部第十六工程局四处第一工程段文书档案 -- gBox 18 - 19.t北京市农业科学院土壤肥料研究所文书档案 -- gBox 20 - 21.t北京市农业科学院土壤肥料研究所干部个人档案 -- gBox 22.t北京市中央气象局观象台文书档案 -- tBox 23 - 27.t北京市地质局文书档案 -- gBox 28.t北京市劳改局文书档案及干部个人档案 -- gBox 29 - 30.t北京市公安局朝阳分局三里屯派出所文书档案 -- gBox 31 - 33.t北京市公共汽车公司四场文书档案 -- gBox 34.t北京市对外贸易包装运输公司，第一建筑公司，朝阳区商业局等，个人档案 -- gBox 35.t北京市食品公司东城分公司，北京市煤业建筑材料公司文书挡案 -- gBox 36.t北京市棉纺织厂储备处青岛训练班文档 -- gBox 37.t北京市棉纺厂筹备处青岛训练班文书档案 -- gBox 38.t北京市八宝山砂石厂人保科 -- gBox 39.t北京市铁道兵学校及北京市外贸仓储分公司个人档案 -- gBox 40.t北京市丰台区和昌平县文书档案 -- gBox 41.t北京市朝阳县十八里店公社老君堂大队文书档案 -- gBox 42.t北京市某派出所，山西省平遥县文书档案 -- gBox 43.t山西省离山县，方山县，离石县，界休县文书档案 -- gBox 44.t山西省机要通新局文书档案 -- gBox 45 - 46.t山西省太原清徐县洋房公社文书档案 -- gBox 47.t山西省沁县杨安公社文书档案 -- gBox 48.t河北省石家庄市桥西区革委会文书档案 -- gBox 49.t东北铁路公安局文书档案 -- gBox 50.t东北铁道部公安局团委，东北铁路公安局文书档案 -- gBox 51.t吉林铁路局公安处吉林段文书档案 -- gBox 52.t各地出版物 -- gBox 53.t单位不详文书档案 -- gBox 54 - 58.t河北省怀来县党员个人档案 -- gBox 59.t河北省赤城县党员个人档案 -- gBox 60.t河北省赤城县个人档案 -- gBox 61.t河北省安新县粮食局文档资料： 财务资料 -- gBox 62.t河北省人民政府商业厅内部刊物； 无名人士工作笔记 -- gBox 63.t湖北省武汉文化大革命政治历史审核材料： 人事档案 -- gBox 64.t湖南长沙法院案卷： 文化大革命中一六三医院材料 -- gBox 65.t湖南常德法院案卷 -- gBox 66.t 济南市人民政府税务局第四分局个人费用报销表 -- gBox 67.t内蒙四子王旗第九区财务资料 -- gBox 68.t内蒙四子王旗公文资料：会议资料 -- gBox 69.t内蒙阿其图乌拉公社介绍信 -- gBox 70.t51 份入党入团自愿书 （地区不详） -- gBox 71.tCD (Box 54 - Box 70 原始电子资料）及CD明细表 -- gBox 72.t四川旺苍县干部档案 -- gBox 73.t四川, 贵州，湖南等地个人档案 -- gBox 74.t证书，单据和其他 -- gBox 75.t四川省医药公司有关检查揭发材料 -- gBox 76 - 77.t四川省医药公司劳动工资资料 -- gBox 78 - 80.t王水资料集 -- gBox 81.t中共太原省市i机关文件， 山西其他地区资料 -- gBox 82.t各地资料： 辽宁， 内蒙古， 天津， 河北，黑龙江，湖南 -- gBox 83.t北京，天津， 山东等地个人档案 -- gBox 84.t内蒙古宁城地区文书档案 -- gBox 85.t生产建设兵团政治部政法部卷宗 -- gBox 86.t吉林省个人档案 -- tBox 87.t吉林省个人档案 -- gBox 88.t湖北省江陵县个人档案 -- gBox 89.t上海敌伪档案资料汇编 -- gBox 90.t河北武清县个人档案 -- tBox 91.t安徽怀宁县个人档案 -- gBox 92.t安徽贵池县机关人员个人档案 -- gBox 93.t安徽贵池县铜山公社长林大队个人档案 -- gBox 94.t安徽贵池县铜山公社长林大队个人档案 -- gBox 95.t安徽省安庆市个人档案 -- gBox 96.t安徽省其他地区个人档案：太湖县，芜湖市， 望江县等 -- gBox 97.t黑龙江省个人档案 --gBox98.t河北省个人档案 --gBox99.t内蒙古呼和浩特和其他地区档案 --gBox100.t河北省怀来县单位档案 --gBox101.t河北省沽源县单位档案 --gBox102.t河北省尚义县和固安县单位档案 --gBox103.t河北省保定地区单位档案 --gBox104.t山西省天镇县和繁峙县单位档案 --gBox105.t山西省怀仁县和定襄县单位档案  a1225736101383cam a2200337Mi 45 0001001000000003000600010005001700016008004100033040004200074049000900116050002300125245013300148264009900281300004000380336002600420337002800446338002700474610003100501650007400532710003100606830004100637880013300678880009500811880003500906880003500941994001200976001001300988596000701001035002401008918001301032a12698851SIRSI20180707013011.0171003s2017    bl            000 0 jpn d  aNHNbjpnencr/1987cNHNdHINdUtOrBLW  aHINA 4aF2659.J3bB87 2017006880-01aBurajiru rikkokai 100nen no ayumi :b1917 - 2017 /cBurajiru rikkokai sōritsu 100-shūnen kinenshi hensan iinkai hen. 16880-02aSão Paulo :bBurajiru rikkokai sōritsu 100-shūnen kinenshi hensan iinkai,c2017.  a341 pages :billustrations ;c27 cm  atextbtxt2rdacontent  aunmediatedbn2rdamedia  avolumebnc2rdacarrier206880-03aBurajiru Rikkokai. 0aJapanesezBrazil0http://id.loc.gov/authorities/subjects/sh20081059862 6880-04aBurajiru Rikkokai. 0aHoover Japanese Diaspora Collection.006245-01aブラジル力行会100年のあゆみ :b1917-2017 /cブラジル力行会創立100周年記念誌編纂委員会編. 16264-02aSão Paulo :bブラジル力行会創立100周年記念誌編纂委員会,c2017.206610-03aブラジル力行会.2 6710-04aブラジル力行会.  aC0bHINon1013998368  a25  a(OCoLC-M)1043443303  a1269885102821cpc a2200421Ii 45 0001001000000003000600010005001700016006001900033008004100052040003700093043001200130245009200142264002800234300001900262336002100281337002500302338002200327338002100349500003100370506010300401520066800504524019801172540044401370541008201814650002301896650006001919655002201979700009102001994001202092001001302104099001402117979010802131910003702239596000702276035002402283880007902307918001302386a13798527SIRSI20220115014323.0m     o  c        220112i19401950is      o           heb d  aSTFbengerdacSTFdSTFdUtOrBLW  aa-is---04a[Collection of invitations sent to Rabbi Nisan Tukachinsky from residents of Jerusalem] 1aJerusalem,c[1940-1950]  a36 invitations  atext2rdacontent  aunmediated2rdamedia  asheet2rdacarrier  acard2rdacarrier  aTitle devised by cataloger  aOpen for research. Note that material must be ordered at least 36 hours in advance of intended use2 aCollections consists of 36 invtations and addressed envelopes of various sizes sent to Rabbi Nisan Tukachinsky from residents of Jerusalem. These included rabbis, lawyers and homeowners such as the Salant family, Rabbi Arieli, Rabbi Shlomo Zalman Zeleznik (rosh yeshivah of Etz Chaim); his son Rabbi Avraham Ya'akov Zeleznik; Rabbi Alter Yavrov; Rabbi Elyakim Daburks; Rabbi Shmuel Aharon Yudelevitz; Rabbi Shlomo Zalman Solomon  known as "The Printer" ' grandson of Rabbi Yoel Moshe; Rabbi Moshe David Tennenbaum. Rabbi Binyamin Trachtenberg. Invitations were for a number of different occasions including Bar Matsvas, anniversaries, engagement parties, weddings  a[Collection of invitations sent to Rabbi Nisan Tukachinsky from residents of Jerusalem. Dept. of Special Collections and University Archives, Stanford University Libraries, Stanford, California  aWhile Special Collections is the owner of the physical and digital items, permission to examine collection materials is not an authorization to publish. These materials are made available for use in research, teaching and private study. Any transmission or reproduction beyond that for fair use requires permission from the owner of rights, heir(s) or assigns. See: https://library.stanford.edu/spc/using-our-collections/permission-publish  cPurchase;aReiss Antiquariat - Eran Reiss;d2021.eAccession no. MSS-2021-215 0aRabbiszJerusalem. 0aJewszJerusalemxSocial life and customsy20th century. 7aInvitations.2aat1 6880-01aṬuḳtsinsḳi, Nisan Aharon.0http://id.loc.gov/authorities/names/n95030666  aZ0bSTFon1291718766  aMISC 2703  fBARONJbdruid:cd966nb0770ccd966nb0770_00_0001.jp2dJeannette Meisel and Salo Wittmayer Baron Book Fund  aCatalogedbySPEC/hgl/date20220112  a21  a(OCoLC-M)12917187661 6700-01//raטוקצינסקי, ניסן אהרן בן יחיאל מיכל.  a13798527
\ No newline at end of file
diff --git a/tests/test_data/mfhd/holding.mrc b/tests/test_data/mfhd/holding.mrc
new file mode 100644
index 00000000..c36167a9
--- /dev/null
+++ b/tests/test_data/mfhd/holding.mrc
@@ -0,0 +1,1 @@
+00179nx  a22000854n 4500001001000000004000800010005001700018008003300035852002500068000000167761178020190827122500.01601264|00008|||1001|||||09011280 bjnlDeskhQB611i.C4400187nx  a22000854n 4500001000900000004000900009005001700018008004100035852002500076436089571800687120150616151259.01506164|00008|||1001|||||0901128xxxxxxxx0 binfoOffhQB611i.C4400174nx  a22000854n 4500001000900000004000900009005001700018008003300035852002000068463615201800687120160126133215.01601264|00008|||1001|||||09011280 bcdhQB611i.C4400176nx  a22000854n 4500001000900000004000900009005001700018008003300035852002200068435000441800687120150603121003.01506034|00008|||1001|||||09011280 bmapshQB611i.C44
\ No newline at end of file
diff --git a/tests/test_data/test_circulation_helper.py b/tests/test_data/test_circulation_helper.py
new file mode 100644
index 00000000..f10d84f1
--- /dev/null
+++ b/tests/test_data/test_circulation_helper.py
@@ -0,0 +1,16 @@
+import uuid
+
+from folio_migration_tools.circulation_helper import CirculationHelper
+from folio_migration_tools.migration_report import MigrationReport
+from folio_migration_tools.test_infrastructure import mocked_classes
+
+
+def test_init():
+    mocked_folio = mocked_classes.mocked_folio_client()
+    sp_id = str(uuid.uuid4())
+    circ_helper = CirculationHelper(mocked_folio, sp_id, MigrationReport())
+    assert circ_helper.folio_client
+    assert circ_helper.service_point_id == sp_id
+    assert not any(circ_helper.missing_patron_barcodes)
+    assert not any(circ_helper.missing_item_barcodes)
+    assert not any(circ_helper.migration_report.report)
diff --git a/tests/test_folder_structure.py b/tests/test_folder_structure.py
index 6c7595e2..7d39f33b 100644
--- a/tests/test_folder_structure.py
+++ b/tests/test_folder_structure.py
@@ -43,13 +43,8 @@ def test_setup_migration_file_structure():
     )
 
     assert (
-        str(folder_structure.failed_bibs_file)
-        == "iterations/test_iteration/results/failed_bib_records_test_task.mrc"
-    )
-
-    assert (
-        str(folder_structure.failed_mfhds_file)
-        == "iterations/test_iteration/results/failed_mfhd_records_test_task.mrc"
+        str(folder_structure.failed_marc_recs_file)
+        == "iterations/test_iteration/results/failed_records_test_task.mrc"
     )
 
     assert str(folder_structure.failed_recs_path).startswith(
@@ -64,7 +59,7 @@ def test_setup_migration_file_structure():
 
     assert (
         str(folder_structure.instance_id_map_path)
-        == "iterations/test_iteration/results/instance_id_map.json"
+        == "iterations/test_iteration/results/instances_id_map.json"
     )
 
     assert str(folder_structure.item_statuses_map_path) == "mapping_files/item_statuses.tsv"
diff --git a/tests/test_holdings_csv_transformer.py b/tests/test_holdings_csv_transformer.py
index 3cd219da..1144e311 100644
--- a/tests/test_holdings_csv_transformer.py
+++ b/tests/test_holdings_csv_transformer.py
@@ -75,7 +75,7 @@ def test_merge_holding_in_second_boundwith_to_merge(caplog):
     mock_transformer.holdings_id_map = {}
     mock_transformer.folio_client = mock_folio
     mock_transformer.mapper = mock_mapper
-
+    mock_transformer.object_type = FOLIONamespaces.holdings
     new_holding = {"instanceId": "Instance_1"}
     instance_ids: list[str] = ["Instance_1", "Instance_2"]
     item_id: str = "item_1"
diff --git a/tests/test_holdings_processor.py b/tests/test_holdings_processor.py
index ba04071e..e8506778 100644
--- a/tests/test_holdings_processor.py
+++ b/tests/test_holdings_processor.py
@@ -1,9 +1,10 @@
 from unittest.mock import Mock
 
-from pymarc import Field, Record
+from pymarc import Field
+from pymarc import Record
 
-from folio_migration_tools.marc_rules_transformation.holdings_processor import (
-    HoldingsProcessor,
+from folio_migration_tools.marc_rules_transformation.marc_file_processor import (
+    MarcFileProcessor,
 )
 from folio_migration_tools.marc_rules_transformation.rules_mapper_holdings import (
     RulesMapperHoldings,
@@ -21,66 +22,19 @@ def test_chop_008():
     assert len(record["008"].data) == 32
 
 
-def test_get_legacy_id_001():
-    record = Record()
-    record.add_field(Field(tag="001", data="0001"))
-    legacy_id = HoldingsProcessor.get_legacy_id("001", record)
-    assert legacy_id == "0001"
-
-
-def test_get_legacy_id_001_wrong_order():
-    record = Record()
-    record.add_field(Field(tag="001", data="0001"))
-    record.add_field(
-        Field(
-            tag="951",
-            subfields=["b", "bid"],
-        )
-    )
-    record.add_field(
-        Field(
-            tag="951",
-            subfields=["c", "cid"],
-        )
-    )
-    record.add_field(Field(tag="001", data="0001"))
-    legacy_id = HoldingsProcessor.get_legacy_id("951$c", record)
-    assert legacy_id == "cid"
-
-
-def test_get_legacy_id_001_right_order():
-    record = Record()
-    record.add_field(Field(tag="001", data="0001"))
-    record.add_field(
-        Field(
-            tag="951",
-            subfields=["c", "cid"],
-        )
-    )
-    record.add_field(
-        Field(
-            tag="951",
-            subfields=["b", "bid"],
-        )
-    )
-    record.add_field(Field(tag="001", data="0001"))
-    legacy_id = HoldingsProcessor.get_legacy_id("951$c", record)
-    assert legacy_id == "cid"
-
-
 def test_generate_num_part_retain_leading_zeroes():
-    mock_processor = Mock(spec=HoldingsProcessor)
+    mock_processor = Mock(spec=MarcFileProcessor)
     mock_mapper = Mock(spec=RulesMapperHoldings)
     mock_mapper.holdings_hrid_counter = "1"
     mock_mapper.common_retain_leading_zeroes = True
     mock_processor.mapper = mock_mapper
-    assert HoldingsProcessor.generate_num_part(mock_processor) == "00000000001"
+    assert MarcFileProcessor.generate_num_part(mock_processor) == "00000000001"
 
 
 def test_generate_num_part_without_leading_zeroes():
-    mock_processor = Mock(spec=HoldingsProcessor)
+    mock_processor = Mock(spec=MarcFileProcessor)
     mock_mapper = Mock(spec=RulesMapperHoldings)
     mock_mapper.holdings_hrid_counter = "1"
     mock_mapper.common_retain_leading_zeroes = False
     mock_processor.mapper = mock_mapper
-    assert HoldingsProcessor.generate_num_part(mock_processor) == "1"
+    assert MarcFileProcessor.generate_num_part(mock_processor) == "1"
diff --git a/tests/test_main.py b/tests/test_main.py
new file mode 100644
index 00000000..8b94cf1e
--- /dev/null
+++ b/tests/test_main.py
@@ -0,0 +1,18 @@
+from folio_migration_tools import __main__
+from folio_migration_tools.migration_tasks.migration_task_base import MigrationTaskBase
+
+
+def test_inheritance():
+    inheritors = [f.__name__ for f in __main__.inheritors(MigrationTaskBase)]
+    assert "HoldingsMarcTransformer" in inheritors
+    assert "CoursesMigrator" in inheritors
+    assert "UserTransformer" in inheritors
+    assert "LoansMigrator" in inheritors
+    assert "ItemsTransformer" in inheritors
+    assert "HoldingsCsvTransformer" in inheritors
+    assert "RequestsMigrator" in inheritors
+    assert "OrganizationTransformer" in inheritors
+    assert "ReservesMigrator" in inheritors
+    assert "BibsTransformer" in inheritors
+    assert "BatchPoster" in inheritors
+    assert "AuthorityTransformer" in inheritors
diff --git a/tests/test_marc_file_processor.py b/tests/test_marc_file_processor.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/test_rules_mapper_auth.py b/tests/test_rules_mapper_auth.py
new file mode 100644
index 00000000..75183530
--- /dev/null
+++ b/tests/test_rules_mapper_auth.py
@@ -0,0 +1,99 @@
+import logging
+
+import pytest
+from pymarc import MARCReader
+from pymarc import Record
+
+from folio_migration_tools.library_configuration import FileDefinition
+from folio_migration_tools.library_configuration import FolioRelease
+from folio_migration_tools.library_configuration import HridHandling
+from folio_migration_tools.library_configuration import IlsFlavour
+from folio_migration_tools.library_configuration import LibraryConfiguration
+from folio_migration_tools.marc_rules_transformation.rules_mapper_autorities import (
+    AuthorityMapper,
+)
+from folio_migration_tools.migration_report import MigrationReport
+from folio_migration_tools.migration_tasks.authority_transformer import (
+    AuthorityTransformer,
+)
+from folio_migration_tools.test_infrastructure import mocked_classes
+
+LOGGER = logging.getLogger(__name__)
+LOGGER.propagate = True
+
+
+@pytest.fixture(scope="session", autouse=True)
+def mapper(pytestconfig) -> AuthorityMapper:
+    folio = mocked_classes.mocked_folio_client()
+    lib = LibraryConfiguration(
+        okapi_url=folio.okapi_url,
+        tenant_id=folio.tenant_id,
+        okapi_username=folio.username,
+        okapi_password=folio.password,
+        folio_release=FolioRelease.morning_glory,
+        library_name="Test Run Library",
+        log_level_debug=False,
+        iteration_identifier="I have no clue",
+        base_folder="/",
+    )
+    conf = AuthorityTransformer.TaskConfiguration(
+        name="test",
+        migration_task_type="AuthorityTransformer",
+        hrid_handling=HridHandling.default,
+        files=[],
+        ils_flavour=IlsFlavour.tag001,
+    )
+    # BibsRulesMapper.__init__ = MagicMock(name="__init__", return_value=None)
+    # BibsRulesMapper.get_instance_schema = MagicMock(name="get_instance_schema")
+    # Conditions.setup_reference_data_for_all = MagicMock(name="setup_reference_data_for_all")
+    # Conditions.setup_reference_data_for_bibs = MagicMock(name="setup_reference_data_for_bibs")
+    mapper = AuthorityMapper(folio, lib, conf)
+    mapper.folio_client = folio
+    mapper.migration_report = MigrationReport()
+    return mapper
+
+
+def test_basic(mapper: AuthorityMapper, caplog):
+    path = "./tests/test_data/auth_363723.mrc"
+    with open(path, "rb") as marc_file:
+        reader = MARCReader(marc_file, to_unicode=True, permissive=True)
+        reader.hide_utf8_warnings = True
+        reader.force_utf8 = True
+        record: Record = None
+        record = next(reader)
+        auth = mapper.parse_record(record, FileDefinition(file_name=""), ["ids"])
+        assert auth["personalName"] == "Ericsson, Leif KE, 1964-"
+        assert auth["personalNameTitle"] == "Ericsson, Leif KE, 1964-"
+        assert auth["id"] == "54ac1b25-aa36-566b-a688-030a745ae080"
+        assert all(id["identifierTypeId"] and id["value"] for id in auth["identifiers"])
+        assert len(auth["identifiers"]) == 2
+        assert auth["source"] == "MARC"
+        assert mapper.mapped_folio_fields["personalNameTitle"] == [1]
+        assert mapper.mapped_folio_fields["personalName"] == [1]
+        assert mapper.mapped_folio_fields["source"] == [1]
+        assert mapper.mapped_folio_fields["identifiers.value"] == [1]
+        assert mapper.mapped_folio_fields["identifiers.identifierTypeId"] == [1]
+
+
+def test_saft(mapper: AuthorityMapper, caplog):
+    path = "./tests/test_data/auth_918643.mrc"
+    with open(path, "rb") as marc_file:
+        mapper.mapped_folio_fields["sftPersonalName"] = [0]
+        mapper.mapped_folio_fields["personalNameTitle"] = [0]
+        mapper.mapped_folio_fields["personalName"] = [0]
+        mapper.mapped_folio_fields["identifiers"] = [0]
+        mapper.mapped_folio_fields["identifiers.identifierTypeId"] = [0]
+
+        reader = MARCReader(marc_file, to_unicode=True, permissive=True)
+        reader.hide_utf8_warnings = True
+        reader.force_utf8 = True
+        record: Record = None
+        record = next(reader)
+        auth = mapper.parse_record(record, FileDefinition(file_name=""), ["ids"])
+        assert "Yu, Tanling" in auth["sftPersonalName"]
+        assert "于丹翎" in auth["sftPersonalName"]
+        assert mapper.mapped_folio_fields["personalNameTitle"] == [1]
+        assert mapper.mapped_folio_fields["identifiers"] == [1]
+        assert mapper.mapped_folio_fields["identifiers.identifierTypeId"] == [1]
+        assert mapper.mapped_folio_fields["personalName"] == [1]
+        assert mapper.mapped_folio_fields["sftPersonalName"] == [1]
diff --git a/tests/test_rules_mapper_base.py b/tests/test_rules_mapper_base.py
index c9db37c3..2e263151 100644
--- a/tests/test_rules_mapper_base.py
+++ b/tests/test_rules_mapper_base.py
@@ -12,6 +12,9 @@
 from folio_migration_tools.marc_rules_transformation.rules_mapper_base import (
     RulesMapperBase,
 )
+from folio_migration_tools.marc_rules_transformation.rules_mapper_bibs import (
+    BibsRulesMapper,
+)
 
 # flake8: noqa: E501
 
@@ -87,16 +90,11 @@ def test_date_from_008_holding():
     # assert holding["metadata"]["createdDate"] == "2017-03-09T00:00:00"
 
 
-def test_get_holdings_schema():
-    schema = RulesMapperBase.fetch_holdings_schema()
-    assert schema["required"]
-
-
 def test_add_entity_to_record():
     entity = {"id": "id", "type": "type"}
     rec = {}
     RulesMapperBase.add_entity_to_record(
-        entity, "identifiers", rec, RulesMapperBase.get_instance_schema()
+        entity, "identifiers", rec, BibsRulesMapper.get_instance_schema()
     )
     assert rec == {"identifiers": [{"id": "id", "type": "type"}]}
 
@@ -191,35 +189,3 @@ def test_create_srs_uuid():
     assert str(created_id) == "6734f228-cba2-54c7-b129-c6437375a864"
     created_id_2 = RulesMapperBase.create_srs_id(FOLIONamespaces.instances, "some_url", "id_1")
     assert str(created_id) != str(created_id_2)
-
-
-def test_get_instance_schema():
-    path = "./tests/test_data/two020a.mrc"
-    with open(path, "rb") as marc_file:
-        reader = MARCReader(marc_file, to_unicode=True, permissive=True)
-        reader.hide_utf8_warnings = True
-        reader.force_utf8 = True
-        record1 = None
-        for record in reader:
-            record1 = record
-        entity_mapping = json.loads(
-            '[ { "rules": [ { "conditions": [ { "type": "set_identifier_type_id_by_name", "parameter": { "name": "ISBN" } } ] } ], "target": "identifiers.identifierTypeId", "subfield": [ "a" ], "requiredSubfield": [ "a" ], "description": "Type for Valid ISBN" }, { "rules": [ { "conditions": [ { "type": "remove_ending_punc, trim" } ] } ], "target": "identifiers.value", "subfield": [ "a", "c", "q" ], "description": "Valid ISBN", "requiredSubfield": [ "a" ], "applyRulesOnConcatenatedData": true } ]'
-        )
-        marc_field = record1["020"]
-        folio_record = {}
-        mock = Mock(spec=RulesMapperBase)
-        schema = RulesMapperBase.get_instance_schema()
-        assert record1["020"]["a"] == "0870990004 (v. 1)"
-        assert schema["required"]
-        # mock.mapped_legacy_keys = ["location", "loan_type", "material_type"]
-        # RulesMapperBase.handle_entity_mapping(
-        #    mock,
-        #    marc_field,
-        #    entity_mapping,
-        #    folio_record,
-        #    False,
-        #    [],
-        # )
-        # print("!")
-        # print(folio_record)
-        # assert folio_record != {}
diff --git a/tests/test_rules_mapper_bibs.py b/tests/test_rules_mapper_bibs.py
index 7cfaf658..1ec9a644 100644
--- a/tests/test_rules_mapper_bibs.py
+++ b/tests/test_rules_mapper_bibs.py
@@ -1,20 +1,11 @@
 import json
 import logging
 import re
-import unittest
-from unittest.mock import MagicMock
-from unittest.mock import Mock
-from unittest.mock import patch
-from uuid import uuid4
 
 import pymarc
 import pytest
-from folioclient import FolioClient
 from lxml import etree
-
-from folio_migration_tools.marc_rules_transformation.conditions import Conditions
-from folio_migration_tools.report_blurbs import Blurbs
-from folio_migration_tools.test_infrastructure.mocked_classes import mocked_folio_client
+from pymarc import MARCReader
 
 LOGGER = logging.getLogger(__name__)
 LOGGER.propagate = True
@@ -26,9 +17,8 @@
 from folio_migration_tools.library_configuration import IlsFlavour
 from folio_migration_tools.library_configuration import LibraryConfiguration
 from folio_migration_tools.mapper_base import MapperBase
-from folio_migration_tools.marc_rules_transformation.bibs_processor import BibsProcessor
-from folio_migration_tools.marc_rules_transformation.rules_mapper_base import (
-    RulesMapperBase,
+from folio_migration_tools.marc_rules_transformation.marc_file_processor import (
+    MarcFileProcessor,
 )
 from folio_migration_tools.marc_rules_transformation.rules_mapper_bibs import (
     BibsRulesMapper,
@@ -75,7 +65,7 @@ def default_map(file_name, xpath, the_mapper: BibsRulesMapper):
     file_path = f"./tests/test_data/default/{file_name}"
     record = pymarc.parse_xml_to_array(file_path)[0]
     file_def = FileDefinition(file_name="", suppressed=False, staff_suppressed=False)
-    result = the_mapper.parse_bib(["legacy_id"], record, file_def)
+    result = the_mapper.parse_record(record, file_def, ["legacy_id"])
     the_mapper.perform_additional_parsing(result, record, ["legacy_id"], file_def)
     root = etree.parse(file_path)
     data = ""
@@ -93,7 +83,7 @@ def default_map_suppression(file_name, xpath, the_mapper):
     file_path = f"./tests/test_data/default/{file_name}"
     record = pymarc.parse_xml_to_array(file_path)[0]
     file_def = FileDefinition(file_name="", suppressed=True, staff_suppressed=True)
-    result = the_mapper.parse_bib(["legacy_id"], record, file_def)
+    result = the_mapper.parse_record(record, file_def, ["legacy_id"])
     root = etree.parse(file_path)
     data = ""
     for element in root.xpath(xpath, namespaces=ns):
@@ -132,16 +122,16 @@ def test_simple_title(mapper):
     assert "Modern Electrosynthetic Methods in Organic Chemistry" == record[0]["title"]
 
     with pytest.raises(TransformationRecordFailedError):
-        BibsProcessor.get_valid_instance_ids(
-            record[0], ["a", "b"], instance_identifiers, MigrationReport()
+        MarcFileProcessor.get_valid_folio_record_ids(
+            ["a", "b"], instance_identifiers, MigrationReport()
         )
 
 
 def test_simple_title2(mapper):
     record = default_map("test1.xml", xpath_245, mapper)
     instance_identifiers = {"c", "d"}
-    ids = BibsProcessor.get_valid_instance_ids(
-        record[0], ["a", "b"], instance_identifiers, MigrationReport()
+    ids = MarcFileProcessor.get_valid_folio_record_ids(
+        ["a", "b"], instance_identifiers, MigrationReport()
     )
 
     assert "a" in ids
@@ -151,8 +141,8 @@ def test_simple_title2(mapper):
 def test_simple_title3(mapper):
     record = default_map("test1.xml", xpath_245, mapper)
     instance_identifiers = {"b", "c", "d"}
-    ids = BibsProcessor.get_valid_instance_ids(
-        record[0], ["a", "b"], instance_identifiers, MigrationReport()
+    ids = MarcFileProcessor.get_valid_folio_record_ids(
+        ["a", "b"], instance_identifiers, MigrationReport()
     )
 
     assert ids == ["a"]
@@ -164,6 +154,38 @@ def test_multiple336s(mapper):
     assert "8105bd44-e7bd-487e-a8f2-b804a361d92f" in record[0]["instanceTypeId"]
 
 
+def test_get_instance_schema():
+    path = "./tests/test_data/two020a.mrc"
+    with open(path, "rb") as marc_file:
+        reader = MARCReader(marc_file, to_unicode=True, permissive=True)
+        reader.hide_utf8_warnings = True
+        reader.force_utf8 = True
+        record1 = None
+        for record in reader:
+            record1 = record
+        entity_mapping = json.loads(
+            '[ { "rules": [ { "conditions": [ { "type": "set_identifier_type_id_by_name", "parameter": { "name": "ISBN" } } ] } ], "target": "identifiers.identifierTypeId", "subfield": [ "a" ], "requiredSubfield": [ "a" ], "description": "Type for Valid ISBN" }, { "rules": [ { "conditions": [ { "type": "remove_ending_punc, trim" } ] } ], "target": "identifiers.value", "subfield": [ "a", "c", "q" ], "description": "Valid ISBN", "requiredSubfield": [ "a" ], "applyRulesOnConcatenatedData": true } ]'
+        )
+        marc_field = record1["020"]
+        folio_record = {}
+        # mock = Mock(spec=RulesMapperBase)
+        schema = BibsRulesMapper.get_instance_schema()
+        assert record1["020"]["a"] == "0870990004 (v. 1)"
+        assert schema["required"]
+        # mock.mapped_legacy_keys = ["location", "loan_type", "material_type"]
+        # RulesMapperBase.handle_entity_mapping(
+        #    mock,
+        #    marc_field,
+        #    entity_mapping,
+        #    folio_record,
+        #    False,
+        #    [],
+        # )
+        # print("!")
+        # print(folio_record)
+        # assert folio_record != {}
+
+
 def test_strange_isbn(mapper):
     xpath = "//marc:datafield[@tag='020']"
     record = default_map("isbn_c.xml", xpath, mapper)
diff --git a/tests/test_rules_mapper_bibs_no_folio.py b/tests/test_rules_mapper_bibs_no_folio.py
index dbbd2c50..51341367 100644
--- a/tests/test_rules_mapper_bibs_no_folio.py
+++ b/tests/test_rules_mapper_bibs_no_folio.py
@@ -28,7 +28,9 @@
 from folio_migration_tools.library_configuration import IlsFlavour
 from folio_migration_tools.library_configuration import LibraryConfiguration
 from folio_migration_tools.mapper_base import MapperBase
-from folio_migration_tools.marc_rules_transformation.bibs_processor import BibsProcessor
+from folio_migration_tools.marc_rules_transformation.marc_file_processor import (
+    MarcFileProcessor,
+)
 from folio_migration_tools.marc_rules_transformation.rules_mapper_base import (
     RulesMapperBase,
 )
@@ -70,7 +72,7 @@ def mapper(pytestconfig) -> BibsRulesMapper:
     # Conditions.setup_reference_data_for_all = MagicMock(name="setup_reference_data_for_all")
     # Conditions.setup_reference_data_for_bibs = MagicMock(name="setup_reference_data_for_bibs")
     mapper = BibsRulesMapper(folio, lib, conf)
-    mapper.folio = folio
+    mapper.folio_client = folio
     mapper.migration_report = MigrationReport()
     return mapper
 
@@ -264,10 +266,11 @@ def test_fieldReplacementBy3Digits(mapper: BibsRulesMapper, caplog):
         reader.force_utf8 = True
         record: Record = None
         record = next(reader)
-        res = mapper.parse_bib(
-            ["ii"], record, FileDefinition(file_name="", suppressed=False, staff_suppressed=False)
+        res = mapper.parse_record(
+            record, FileDefinition(file_name="", suppressed=False, staff_suppressed=False), ["ii"]
         )
         assert "宝塚歌劇団" in res["subjects"]
         assert "[東京宝塚劇場公演パンフレット. ]" in res["alternativeTitles"][0]["alternativeTitle"]
         assert "1. 7月星組公演.  淀君, シャンソン・ダムール (1959)" in res["notes"][2]["note"]
         assert "宝塚" in res["publication"][1]["place"]
+        assert "Records without $6" in mapper.migration_report.report["880 mappings"]
diff --git a/tests/test_rules_mapper_holdings.py b/tests/test_rules_mapper_holdings.py
index 0aae362d..ff8fa8a6 100644
--- a/tests/test_rules_mapper_holdings.py
+++ b/tests/test_rules_mapper_holdings.py
@@ -1,7 +1,16 @@
 from unittest.mock import Mock
 
 import pymarc
+import pytest
+from pymarc import Field
+from pymarc import MARCReader
+from pymarc import Record
 
+from folio_migration_tools.library_configuration import FileDefinition
+from folio_migration_tools.library_configuration import FolioRelease
+from folio_migration_tools.library_configuration import HridHandling
+from folio_migration_tools.library_configuration import IlsFlavour
+from folio_migration_tools.library_configuration import LibraryConfiguration
 from folio_migration_tools.marc_rules_transformation.conditions import Conditions
 from folio_migration_tools.marc_rules_transformation.holdings_statementsparser import (
     HoldingsStatementsParser,
@@ -9,6 +18,153 @@
 from folio_migration_tools.marc_rules_transformation.rules_mapper_holdings import (
     RulesMapperHoldings,
 )
+from folio_migration_tools.migration_report import MigrationReport
+from folio_migration_tools.migration_tasks.holdings_marc_transformer import (
+    HoldingsMarcTransformer,
+)
+from folio_migration_tools.test_infrastructure import mocked_classes
+
+
+@pytest.fixture(scope="session", autouse=True)
+def mapper(pytestconfig) -> RulesMapperHoldings:
+    folio = mocked_classes.mocked_folio_client()
+    lib = LibraryConfiguration(
+        okapi_url=folio.okapi_url,
+        tenant_id=folio.tenant_id,
+        okapi_username=folio.username,
+        okapi_password=folio.password,
+        folio_release=FolioRelease.morning_glory,
+        library_name="Test Run Library",
+        log_level_debug=False,
+        iteration_identifier="I have no clue",
+        base_folder="/",
+    )
+    conf = HoldingsMarcTransformer.TaskConfiguration(
+        name="test",
+        migration_task_type="HoldingsTransformer",
+        hrid_handling=HridHandling.default,
+        files=[],
+        ils_flavour=IlsFlavour.voyager,
+        legacy_id_marc_path="001",
+        location_map_file_name="",
+        default_call_number_type_name="Dewey Decimal classification",
+        fallback_holdings_type_id="03c9c400-b9e3-4a07-ac0e-05ab470233ed",
+    )
+    parent_id_map: dict[str, tuple] = {}
+    location_map = [
+        {"legacy_code": "jnlDesk", "folio_code": "KU/CC/DI/2"},
+        {"legacy_code": "*", "folio_code": "KU/CC/DI/2"},
+    ]
+    mapper = RulesMapperHoldings(folio, location_map, conf, lib, parent_id_map)
+    mapper.folio_client = folio
+    mapper.migration_report = MigrationReport()
+    return mapper
+
+
+def test_basic(mapper: RulesMapperHoldings, caplog):
+    path = "./tests/test_data/mfhd/holding.mrc"
+    with open(path, "rb") as marc_file:
+        reader = MARCReader(marc_file, to_unicode=True, permissive=True)
+        reader.hide_utf8_warnings = True
+        reader.force_utf8 = True
+        record: Record = None
+        mapper.parent_id_map = {
+            "7611780": (
+                "7611780",
+                "9d1673a3-a546-5afa-b0fb-5ab971f73eca",
+                "in00000000005",
+            )
+        }
+        record = next(reader)
+        ids = RulesMapperHoldings.get_legacy_ids(mapper, record, 1)
+        res = mapper.parse_record(
+            record, FileDefinition(file_name="", suppressed=False, staff_suppressed=False), ids
+        )
+        assert res
+        assert res["permanentLocationId"] == "f34d27c6-a8eb-461b-acd6-5dea81771e70"
+        assert res["hrid"] == "pref00000000001"
+        assert len(res["administrativeNotes"]) > 0
+        assert res["callNumber"] == "QB611 .C44"
+        assert res["callNumberTypeId"] == "95467209-6d7b-468b-94df-0f5d7ad2747d"
+
+
+def test_edit852(mapper: RulesMapperHoldings, caplog):
+    path = "./tests/test_data/mfhd/holding.mrc"
+    with open(path, "rb") as marc_file:
+        reader = MARCReader(marc_file, to_unicode=True, permissive=True)
+        reader.hide_utf8_warnings = True
+        reader.force_utf8 = True
+        record: Record = None
+        record = next(reader)
+        new_code = "JOUR"
+        record["852"]["b"] = new_code
+        assert record["852"]["b"] == new_code
+
+
+def test_get_legacy_ids_001():
+    record = Record()
+    mock_mapper = Mock(spec=RulesMapperHoldings)
+    record.add_field(Field(tag="001", data="0001"))
+    mocked_config = Mock(spec=HoldingsMarcTransformer.TaskConfiguration)
+    mocked_config.legacy_id_marc_path = "001"
+    mock_mapper = Mock(spec=RulesMapperHoldings)
+    mock_mapper.task_configuration = mocked_config
+    legacy_ids = RulesMapperHoldings.get_legacy_ids(mock_mapper, record, 1)
+    assert legacy_ids == ["0001"]
+
+
+def test_get_legacy_id_001_wrong_order():
+    record = Record()
+    record.add_field(Field(tag="001", data="0001"))
+    record.add_field(
+        Field(
+            tag="951",
+            subfields=["b", "bid"],
+        )
+    )
+    record.add_field(
+        Field(
+            tag="951",
+            subfields=["c", "cid"],
+        )
+    )
+    record.add_field(Field(tag="001", data="0001"))
+    mocked_config = Mock(spec=HoldingsMarcTransformer.TaskConfiguration)
+    mocked_config.legacy_id_marc_path = "951$c"
+    mock_mapper = Mock(spec=RulesMapperHoldings)
+    mock_mapper.task_configuration = mocked_config
+
+    legacy_ids = RulesMapperHoldings.get_legacy_ids(mock_mapper, record, 1)
+    assert legacy_ids == ["cid"]
+
+
+def test_get_legacy_id_001_right_order():
+    record = Record()
+    record.add_field(Field(tag="001", data="0001"))
+    record.add_field(
+        Field(
+            tag="951",
+            subfields=["c", "cid"],
+        )
+    )
+    record.add_field(
+        Field(
+            tag="951",
+            subfields=["b", "bid"],
+        )
+    )
+    record.add_field(Field(tag="001", data="0001"))
+    mocked_config = Mock(spec=HoldingsMarcTransformer.TaskConfiguration)
+    mocked_config.legacy_id_marc_path = "951$c"
+    mock_mapper = Mock(spec=RulesMapperHoldings)
+    mock_mapper.task_configuration = mocked_config
+    legacy_ids = RulesMapperHoldings.get_legacy_ids(mock_mapper, record, 1)
+    assert legacy_ids == ["cid"]
+
+
+def test_get_holdings_schema():
+    schema = RulesMapperHoldings.fetch_holdings_schema()
+    assert schema["required"]
 
 
 def test_get_marc_textual_stmt_correct_order_and_not_deduped():
@@ -28,15 +184,14 @@ def test_get_marc_textual_stmt_correct_order_and_not_deduped():
             "holdingsStatementsForSupplements",
         ]
         RulesMapperHoldings.dedupe_rec(rec, props_to_not_dedupe)
-        stmt = "1994-1998."
         all_stmts = [f["statement"] for f in rec["holdingsStatements"]]
-        all_94s = [f for f in all_stmts if stmt == f]
+        all_94s = [f for f in all_stmts if f == "1994-1998."]
         assert len(all_94s) == 2
 
 
 def test_remove_from_id_map():
     mocked_rules_mapper_holdings = Mock(spec=RulesMapperHoldings)
-    mocked_rules_mapper_holdings.holdings_id_map = {
+    mocked_rules_mapper_holdings.id_map = {
         "h15066915": "5a0af31f-aa4a-5215-8a60-712b38cd6cb6",
         "h14554914": "c9c44650-11e2-5534-ae50-01a1aa0fbd66",
     }
@@ -46,8 +201,8 @@ def test_remove_from_id_map():
     RulesMapperHoldings.remove_from_id_map(mocked_rules_mapper_holdings, former_ids)
 
     # The ids in the former_ids have been removed, any others are still there
-    assert "h15066915" not in mocked_rules_mapper_holdings.holdings_id_map.keys()
-    assert "h14554914" in mocked_rules_mapper_holdings.holdings_id_map.keys()
+    assert "h15066915" not in mocked_rules_mapper_holdings.id_map.keys()
+    assert "h14554914" in mocked_rules_mapper_holdings.id_map.keys()
 
 
 def test_set_default_call_number_type_if_empty():