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

Issue:
Implement COM RegionalePreisstaffel
Fixes #152
very similar to #183 

⚠️ before merging to main please update the dependent branch of #293 with changes
Implement COM RegionaleTarifPreisPosition
https://www.bo4e.de/dokumentation/komponenten/com-regionaletarifpreisposition
requires 
- #134 
- #125 
- #152 


Discussion / hints:

Fix patch:
diff --git a/src/bo4e/com/regionaletarifpreisposition.py b/src/bo4e/com/regionaletarifpreisposition.py
new file mode 100644
index 000000000..5bbee5ceb
--- /dev/null
+++ b/src/bo4e/com/regionaletarifpreisposition.py
@@ -0,0 +1,64 @@
+"""
+Contains RegionaleTarifpreisposition class and corresponding marshmallow schema for de-/serialization
+"""
+from typing import List, Optional
+
+import attr
+from marshmallow import fields
+from marshmallow_enum import EnumField  # type:ignore[import]
+
+from bo4e.com.com import COM, COMSchema
+from bo4e.com.regionalepreisstaffel import RegionalePreisstaffel, RegionalePreisstaffelSchema
+from bo4e.enum.mengeneinheit import Mengeneinheit
+from bo4e.enum.preistyp import Preistyp
+from bo4e.enum.waehrungseinheit import Waehrungseinheit
+from bo4e.validators import check_list_length_at_least_one
+
+
+# pylint: disable=too-few-public-methods
+@attr.s(auto_attribs=True, kw_only=True)
+class RegionaleTarifpreisposition(COM):
+    """
+    Mit dieser Komponente können Tarifpreise verschiedener Typen im Zusammenhang mit regionalen Gültigkeiten abgebildet
+    werden.
+    """
+
+    # required attributes
+    #: Angabe des Preistypes (z.B. Grundpreis)
+    preistyp: Preistyp = attr.ib(validator=attr.validators.instance_of(Preistyp))
+    #: Einheit des Preises (z.B. EURO)
+    einheit: Waehrungseinheit = attr.ib(validator=attr.validators.instance_of(Waehrungseinheit))
+    #: Größe, auf die sich die Einheit bezieht, beispielsweise kWh, Jahr
+    bezugseinheit: Mengeneinheit = attr.ib(validator=attr.validators.instance_of(Mengeneinheit))
+    #: Hier sind die Staffeln mit ihren Preisangaben und regionalen Gültigkeiten definiert
+    preisstaffeln: List[RegionalePreisstaffel] = attr.ib(
+        validator=[
+            attr.validators.deep_iterable(
+                member_validator=attr.validators.instance_of(RegionalePreisstaffel),
+                iterable_validator=attr.validators.instance_of(list),
+            ),
+            check_list_length_at_least_one,
+        ]
+    )
+
+    # optional attributes
+    #: Gibt an, nach welcher Menge die vorgenannte Einschränkung erfolgt (z.B. Jahresstromverbrauch in kWh)
+    mengeneinheitstaffel: Optional[Mengeneinheit] = attr.ib(
+        default=None, validator=attr.validators.optional(attr.validators.instance_of(Mengeneinheit))
+    )
+
+
+class RegionaleTarifpreispositionSchema(COMSchema):
+    """
+    Schema for de-/serialization of RegionaleTarifpreisposition
+    """
+
+    class_name = RegionaleTarifpreisposition
+    # required attributes
+    preistyp = EnumField(Preistyp)
+    einheit = EnumField(Waehrungseinheit)
+    bezugseinheit = EnumField(Mengeneinheit)
+    preisstaffeln = fields.List(fields.Nested(RegionalePreisstaffelSchema))
+
+    # optional attributes
+    mengeneinheitstaffel = EnumField(Mengeneinheit, load_default=None)


Test patch:
diff --git a/tests/test_regionaletarifpreisposition.py b/tests/test_regionaletarifpreisposition.py
new file mode 100644
index 000000000..9b2d1a811
--- /dev/null
+++ b/tests/test_regionaletarifpreisposition.py
@@ -0,0 +1,76 @@
+from decimal import Decimal
+
+import pytest  # type:ignore[import]
+
+from bo4e.com.kriteriumwert import KriteriumWert
+from bo4e.com.regionalegueltigkeit import RegionaleGueltigkeit
+from bo4e.com.regionalepreisstaffel import RegionalePreisstaffel
+from bo4e.com.regionaletarifpreisposition import RegionaleTarifpreisposition, RegionaleTarifpreispositionSchema
+from bo4e.enum.gueltigkeitstyp import Gueltigkeitstyp
+from bo4e.enum.mengeneinheit import Mengeneinheit
+from bo4e.enum.preistyp import Preistyp
+from bo4e.enum.tarifregionskriterium import Tarifregionskriterium
+from bo4e.enum.waehrungseinheit import Waehrungseinheit
+from tests.serialization_helper import assert_serialization_roundtrip  # type:ignore[import]
+from tests.test_sigmoidparameter import example_sigmoidparameter  # type:ignore[import]
+
+
+class TestRegionaleTarifpreisPosition:
+    @pytest.mark.parametrize(
+        "regionale_tarifpreis_position, expected_json_dict",
+        [
+            pytest.param(
+                RegionaleTarifpreisposition(
+                    preistyp=Preistyp.ARBEITSPREIS_NT,
+                    einheit=Waehrungseinheit.EUR,
+                    bezugseinheit=Mengeneinheit.KWH,
+                    mengeneinheitstaffel=Mengeneinheit.WH,
+                    preisstaffeln=[
+                        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")
+                                ],
+                            ),
+                        ),
+                    ],
+                ),
+                {
+                    "bezugseinheit": "KWH",
+                    "preisstaffeln": [
+                        {
+                            "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",
+                        }
+                    ],
+                    "einheit": "EUR",
+                    "mengeneinheitstaffel": "WH",
+                    "preistyp": "ARBEITSPREIS_NT",
+                },
+                id="all attributes",
+            ),
+        ],
+    )
+    def test_serialization_roundtrip(
+        self, regionale_tarifpreis_position: RegionaleTarifpreisposition, expected_json_dict: dict
+    ):
+        assert_serialization_roundtrip(
+            regionale_tarifpreis_position, RegionaleTarifpreispositionSchema(), expected_json_dict
+        )
+
+    def test_missing_required_attribute(self):
+        with pytest.raises(TypeError) as excinfo:
+            _ = RegionaleTarifpreisposition()
+
+        assert "missing 4 required" in str(excinfo.value)