Repository: bo4e/BO4E-python (language: python)

Issue:
Implement COM RegionalePreisstaffel
https://www.bo4e.de/dokumentation/komponenten/com-regionalepreisstaffel

Requires
- #140 
- #151
Implement COM RegionalePreisgarantie
https://www.bo4e.de/dokumentation/komponenten/com-regionalepreisgarantie
requires
- #163 
- #172 
Implement COM RegionaleTarifpreisPosition
review #292 first, then merge updates to here.
Fixes #173


Discussion / hints:

Fix patch:
diff --git a/src/bo4e/com/regionalepreisstaffel.py b/src/bo4e/com/regionalepreisstaffel.py
new file mode 100644
index 000000000..11ff72335
--- /dev/null
+++ b/src/bo4e/com/regionalepreisstaffel.py
@@ -0,0 +1,36 @@
+"""
+Contains RegionalePreisstaffel class and corresponding marshmallow schema for de-/serialization
+"""
+
+import attr
+from marshmallow import fields, post_load
+
+from bo4e.com.preisstaffel import Preisstaffel, PreisstaffelSchema
+from bo4e.com.regionalegueltigkeit import RegionaleGueltigkeit, RegionaleGueltigkeitSchema
+
+
+# pylint: disable=too-few-public-methods
+@attr.s(auto_attribs=True, kw_only=True)
+class RegionalePreisstaffel(Preisstaffel):
+    """
+    Abbildung einer Preisstaffel mit regionaler Abgrenzung
+    """
+
+    # required attributes
+    #: Regionale Eingrenzung der Preisstaffel
+    regionale_gueltigkeit: RegionaleGueltigkeit = attr.ib(validator=attr.validators.instance_of(RegionaleGueltigkeit))
+
+
+class RegionalePreisstaffelSchema(PreisstaffelSchema):
+    """
+    Schema for de-/serialization of RegionalePreisgarantie.
+    """
+
+    # required attributes
+    regionale_gueltigkeit = fields.Nested(RegionaleGueltigkeitSchema)
+
+    # pylint: disable=no-self-use, unused-argument
+    @post_load
+    def deserialize(self, data, **kwargs) -> RegionalePreisstaffel:
+        """Deserialize JSON to Energiemix object"""
+        return RegionalePreisstaffel(**data)


Test patch:
diff --git a/tests/test_fremdkostenposition.py b/tests/test_fremdkostenposition.py
index fabf099eb..754115ee3 100644
--- a/tests/test_fremdkostenposition.py
+++ b/tests/test_fremdkostenposition.py
@@ -6,7 +6,6 @@
 from bo4e.com.betrag import Betrag
 from bo4e.com.fremdkostenposition import Fremdkostenposition, FremdkostenpositionSchema
 from bo4e.com.preis import Preis
-from bo4e.com.preisstaffel import Preisstaffel
 from bo4e.enum.mengeneinheit import Mengeneinheit
 from bo4e.enum.preisstatus import Preisstatus
 from bo4e.enum.waehrungscode import Waehrungscode
diff --git a/tests/test_preisstaffel.py b/tests/test_preisstaffel.py
index 634958212..7ba5fc317 100644
--- a/tests/test_preisstaffel.py
+++ b/tests/test_preisstaffel.py
@@ -24,6 +24,19 @@ class TestPreisstaffel:
                     "staffelgrenzeVon": "12.5",
                     "staffelgrenzeBis": "25",
                 },
+                id="all attributes",
+            ),
+            pytest.param(
+                Preisstaffel(
+                    einheitspreis=Decimal(40.0), staffelgrenze_von=Decimal(12.5), staffelgrenze_bis=Decimal(25.0)
+                ),
+                {
+                    "einheitspreis": "40",
+                    "staffelgrenzeVon": "12.5",
+                    "staffelgrenzeBis": "25",
+                    "sigmoidparameter": None,
+                },
+                id="only required params",
             ),
         ],
     )
diff --git a/tests/test_regionalepreisstaffel.py b/tests/test_regionalepreisstaffel.py
new file mode 100644
index 000000000..b2a8ba5d5
--- /dev/null
+++ b/tests/test_regionalepreisstaffel.py
@@ -0,0 +1,54 @@
+from decimal import Decimal
+
+import pytest  # type:ignore[import]
+
+from bo4e.com.kriteriumwert import KriteriumWert
+from bo4e.com.regionalegueltigkeit import RegionaleGueltigkeit
+from bo4e.com.regionalepreisgarantie import RegionalePreisgarantie
+from bo4e.com.regionalepreisstaffel import RegionalePreisstaffel, RegionalePreisstaffelSchema
+from bo4e.enum.gueltigkeitstyp import Gueltigkeitstyp
+from bo4e.enum.tarifregionskriterium import Tarifregionskriterium
+from tests.serialization_helper import assert_serialization_roundtrip  # type:ignore[import]
+from tests.test_sigmoidparameter import example_sigmoidparameter  # type:ignore[import]
+
+
+class TestRegionalePreisstaffel:
+    @pytest.mark.parametrize(
+        "regionale_preisstaffel, expected_json_dict",
+        [
+            pytest.param(
+                RegionalePreisstaffel(
+                    einheitspreis=Decimal(40.0),
+                    staffelgrenze_von=Decimal(12.5),
+                    staffelgrenze_bis=Decimal(25.0),
+                    sigmoidparameter=example_sigmoidparameter,
+                    regionale_gueltigkeit=RegionaleGueltigkeit(
+                        gueltigkeitstyp=Gueltigkeitstyp.NUR_IN,
+                        kriteriums_werte=[KriteriumWert(kriterium=Tarifregionskriterium.POSTLEITZAHL, wert="01069")],
+                    ),
+                ),
+                {
+                    "regionaleGueltigkeit": {
+                        "gueltigkeitstyp": "NUR_IN",
+                        "kriteriumsWerte": [{"kriterium": "POSTLEITZAHL", "wert": "01069"}],
+                    },
+                    "einheitspreis": "40",
+                    "sigmoidparameter": {"a": "1", "b": "2", "c": "3", "d": "4"},
+                    "staffelgrenzeVon": "12.5",
+                    "staffelgrenzeBis": "25",
+                },
+                id="maximal attributes",  # the messing sigmoidparameter is tested in the Preisstaffel tests
+            ),
+        ],
+    )
+    def test_serialization_roundtrip(self, regionale_preisstaffel: RegionalePreisstaffel, expected_json_dict):
+        """
+        Test de-/serialisation of RegionalePreisgarantie with maximal attributes.
+        """
+        assert_serialization_roundtrip(regionale_preisstaffel, RegionalePreisstaffelSchema(), expected_json_dict)
+
+    def test_missing_required_attribute(self):
+        with pytest.raises(TypeError) as excinfo:
+            _ = RegionalePreisgarantie()
+
+        assert "missing 3 required" in str(excinfo.value)