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

Issue:
Implement BO Tarifinfo
https://www.bo4e.de/dokumentation/geschaeftsobjekte/bo-tarifinfo/15-11-2021
- #112 
- #92 
- #120 
- #158 
- #164 


Discussion / hints:

Fix patch:
diff --git a/src/bo4e/bo/tarifinfo.py b/src/bo4e/bo/tarifinfo.py
new file mode 100644
index 000000000..be7a579e2
--- /dev/null
+++ b/src/bo4e/bo/tarifinfo.py
@@ -0,0 +1,105 @@
+"""
+Contains Tarifinfo 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.bo.geschaeftsobjekt import Geschaeftsobjekt, GeschaeftsobjektSchema
+from bo4e.bo.marktteilnehmer import Marktteilnehmer, MarktteilnehmerSchema
+from bo4e.com.energiemix import Energiemix, EnergiemixSchema
+from bo4e.com.vertragskonditionen import Vertragskonditionen, VertragskonditionenSchema
+from bo4e.com.zeitraum import Zeitraum, ZeitraumSchema
+from bo4e.enum.botyp import BoTyp
+from bo4e.enum.kundentyp import Kundentyp
+from bo4e.enum.sparte import Sparte
+from bo4e.enum.tarifart import Tarifart
+from bo4e.enum.tarifmerkmal import Tarifmerkmal
+from bo4e.enum.tariftyp import Tariftyp
+from bo4e.validators import check_list_length_at_least_one
+
+
+# pylint: disable=too-many-instance-attributes, too-few-public-methods
+@attr.s(auto_attribs=True, kw_only=True)
+class Tarifinfo(Geschaeftsobjekt):
+    """
+    Das BO Tarifinfo liefert die Merkmale, die einen Endkundentarif identifizierbar machen.
+    Dieses BO dient als Basis für weitere BOs mit erweiterten Anwendungsmöglichkeiten.
+    """
+
+    # required attributes
+    bo_typ: BoTyp = attr.ib(default=BoTyp.TARIFINFO)
+    #: Name des Tarifs
+    bezeichnung: str = attr.ib(validator=attr.validators.instance_of(str))
+    #: Der Name des Marktpartners, der den Tarif anbietet
+    anbietername: str = attr.ib(validator=attr.validators.instance_of(str))
+    #: Strom oder Gas, etc.
+    sparte: Sparte = attr.ib(validator=attr.validators.instance_of(Sparte))
+    #: Kundentypen für den der Tarif gilt, z.B. Privatkunden
+    kundentypen: List[Kundentyp] = attr.ib(
+        validator=attr.validators.deep_iterable(
+            member_validator=attr.validators.instance_of(Kundentyp),
+            iterable_validator=check_list_length_at_least_one,
+        )
+    )
+    #: Die Art des Tarifes, z.B. Eintarif oder Mehrtarif
+    tarifart: Tarifart = attr.ib(validator=attr.validators.instance_of(Tarifart))
+    #: Hinweis auf den Tariftyp, z.B. Grundversorgung oder Sondertarif
+    tariftyp: Tariftyp = attr.ib(validator=attr.validators.instance_of(Tariftyp))
+    #: Weitere Merkmale des Tarifs, z.B. Festpreis oder Vorkasse
+    tarifmerkmale: List[Tarifmerkmal] = attr.ib(
+        validator=attr.validators.deep_iterable(
+            member_validator=attr.validators.instance_of(Tarifmerkmal),
+            iterable_validator=check_list_length_at_least_one,
+        )
+    )
+    #: Der Marktteilnehmer (Lieferant), der diesen Tarif anbietet
+    anbieter: Marktteilnehmer = attr.ib(validator=attr.validators.instance_of(Marktteilnehmer))
+
+    # optional attributes
+    #: Internetseite auf dem der Tarif zu finden ist
+    website: Optional[str] = attr.ib(default=None, validator=attr.validators.optional(attr.validators.instance_of(str)))
+    #: Freitext
+    bemerkung: Optional[str] = attr.ib(
+        default=None, validator=attr.validators.optional(attr.validators.instance_of(str))
+    )
+
+    #: Angabe, in welchem Zeitraum der Tarif gültig ist
+    zeitliche_gueltigkeit: Optional[Zeitraum] = attr.ib(
+        default=None, validator=attr.validators.optional(attr.validators.instance_of(Zeitraum))
+    )
+    #: Der Energiemix, der für diesen Tarif gilt
+    energiemix: Optional[Energiemix] = attr.ib(
+        default=None, validator=attr.validators.optional(attr.validators.instance_of(Energiemix))
+    )
+    #: Mindestlaufzeiten und Kündigungsfristen zusammengefasst
+    vertragskonditionen: Optional[Vertragskonditionen] = attr.ib(
+        default=None, validator=attr.validators.optional(attr.validators.instance_of(Vertragskonditionen))
+    )
+
+
+class TarifinfoSchema(GeschaeftsobjektSchema):
+    """
+    Schema for de-/serialization of Tarifinfo
+    """
+
+    class_name = Tarifinfo
+
+    bezeichnung = fields.Str()
+    anbietername = fields.Str()
+    sparte = EnumField(Sparte)
+    kundentypen = fields.List(EnumField(Kundentyp))
+    tarifart = EnumField(Tarifart)
+    tariftyp = EnumField(Tariftyp)
+    tarifmerkmale = fields.List(EnumField(Tarifmerkmal))
+    anbieter = fields.Nested(MarktteilnehmerSchema)
+
+    # optional attributes
+    website = fields.Str(load_default=None)
+    bemerkung = fields.Str(load_default=None)
+    zeitliche_gueltigkeit = fields.Nested(ZeitraumSchema, load_default=None)
+    energiemix = fields.Nested(EnergiemixSchema, load_default=None)
+    vertragskonditionen = fields.Nested(VertragskonditionenSchema, load_default=None)


Test patch:
diff --git a/tests/test_marktteilnehmer.py b/tests/test_marktteilnehmer.py
index 5cbc9489d..7f19a6949 100644
--- a/tests/test_marktteilnehmer.py
+++ b/tests/test_marktteilnehmer.py
@@ -7,25 +7,27 @@
 from bo4e.enum.marktrolle import Marktrolle
 from bo4e.enum.rollencodetyp import Rollencodetyp
 
+example_marktteilnehmer = Marktteilnehmer(
+    # required attributes of Marktteilnehmer only
+    marktrolle=Marktrolle.DL,
+    rollencodenummer="9903916000000",
+    rollencodetyp=Rollencodetyp.BDEW,
+    # required attributes inherited from Geschaeftspartner
+    name1="Netze BW GmbH",
+    gewerbekennzeichnung=True,
+    geschaeftspartnerrolle=[Geschaeftspartnerrolle.DIENSTLEISTER],
+    partneradresse=Adresse(
+        strasse="Schelmenwasenstraße",
+        hausnummer="15",
+        postleitzahl="70567",
+        ort="Stuttgart",
+    ),
+)
+
 
 class TestMarktteilnehmer:
     def test_serialization(self):
-        mt = Marktteilnehmer(
-            # required attributes of Marktteilnehmer only
-            marktrolle=Marktrolle.DL,
-            rollencodenummer="9903916000000",
-            rollencodetyp=Rollencodetyp.BDEW,
-            # required attributes inherited from Geschaeftspartner
-            name1="Netze BW GmbH",
-            gewerbekennzeichnung=True,
-            geschaeftspartnerrolle=[Geschaeftspartnerrolle.DIENSTLEISTER],
-            partneradresse=Adresse(
-                strasse="Schelmenwasenstraße",
-                hausnummer="15",
-                postleitzahl="70567",
-                ort="Stuttgart",
-            ),
-        )
+        mt = example_marktteilnehmer
 
         assert mt.versionstruktur == "2", "versionstruktur was not automatically set"
         assert mt.bo_typ == BoTyp.MARKTTEILNEHMER, "boTyp was not automatically set"
diff --git a/tests/test_tarifinfo.py b/tests/test_tarifinfo.py
new file mode 100644
index 000000000..3e9ff3de7
--- /dev/null
+++ b/tests/test_tarifinfo.py
@@ -0,0 +1,49 @@
+import pytest  # type:ignore[import]
+
+from bo4e.bo.tarifinfo import Tarifinfo, TarifinfoSchema
+from bo4e.enum.kundentyp import Kundentyp
+from bo4e.enum.sparte import Sparte
+from bo4e.enum.tarifart import Tarifart
+from bo4e.enum.tarifmerkmal import Tarifmerkmal
+from bo4e.enum.tariftyp import Tariftyp
+from tests.serialization_helper import assert_serialization_roundtrip  # type:ignore[import]
+from tests.test_energiemix import example_energiemix  # type:ignore[import]
+from tests.test_marktteilnehmer import example_marktteilnehmer  # type:ignore[import]
+from tests.test_vertragskonditionen import example_vertragskonditionen  # type:ignore[import]
+from tests.test_zeitraum import example_zeitraum  # type:ignore[import]
+
+
+class TestTarifinfo:
+    @pytest.mark.parametrize(
+        "tarifinfo",
+        [
+            pytest.param(
+                Tarifinfo(
+                    bezeichnung="foo",
+                    anbietername="der beste stromanbieter",
+                    sparte=Sparte.STROM,
+                    kundentypen=[Kundentyp.PRIVAT, Kundentyp.GEWERBE],
+                    tarifart=Tarifart.MEHRTARIF,
+                    tariftyp=Tariftyp.GRUND_ERSATZVERSORGUNG,
+                    tarifmerkmale=[Tarifmerkmal.HEIZSTROM],
+                    website="https://foo.inv",
+                    bemerkung="super billig aber auch super dreckig",
+                    vertragskonditionen=example_vertragskonditionen,
+                    zeitliche_gueltigkeit=example_zeitraum,
+                    energiemix=example_energiemix,
+                    anbieter=example_marktteilnehmer,
+                )
+            ),
+        ],
+    )
+    def test_serialization_roundtrip(self, tarifinfo: Tarifinfo):
+        """
+        Test de-/serialisation
+        """
+        assert_serialization_roundtrip(tarifinfo, TarifinfoSchema())
+
+    def test_missing_required_attribute(self):
+        with pytest.raises(TypeError) as excinfo:
+            _ = Tarifinfo()
+
+        assert "missing 8 required" in str(excinfo.value)