diff --git a/src/bo4e/bo/preisblattmessung.py b/src/bo4e/bo/preisblattmessung.py new file mode 100644 index 000000000..94d012fb7 --- /dev/null +++ b/src/bo4e/bo/preisblattmessung.py @@ -0,0 +1,72 @@ +""" +Contains PreisblattMessung 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.preisblatt import Preisblatt, PreisblattSchema +from bo4e.com.geraeteeigenschaften import Geraeteeigenschaften, GeraeteeigenschaftenSchema +from bo4e.enum.bilanzierungsmethode import Bilanzierungsmethode +from bo4e.enum.botyp import BoTyp +from bo4e.enum.dienstleistungstyp import Dienstleistungstyp +from bo4e.enum.netzebene import Netzebene + + +# pylint: disable=too-few-public-methods +@attr.s(auto_attribs=True, kw_only=True) +class PreisblattMessung(Preisblatt): + """ + Variante des Preisblattmodells zur Abbildung der Preise des Messstellenbetriebs und damit verbundener Leistungen + """ + + bo_typ: BoTyp = attr.ib(default=BoTyp.PREISBLATTMESSUNG) + # required attributes (additional to those of Preisblatt) + #: Die Preise gelten für Marktlokationen der angebebenen Bilanzierungsmethode + bilanzierungsmethode: Bilanzierungsmethode = attr.ib(validator=attr.validators.instance_of(Bilanzierungsmethode)) + #: Die Preise gelten für Messlokationen in der angebebenen Netzebene + messebene: Netzebene = attr.ib(validator=attr.validators.instance_of(Netzebene)) + + #: Der Preis betrifft den hier angegebenen Zähler, z.B. einen Drehstromzähler + zaehler: Geraeteeigenschaften = attr.ib(validator=attr.validators.instance_of(Geraeteeigenschaften)) + # todo: https://github.com/Hochfrequenz/BO4E-python/issues/333 + + # optional attributes + #: Im Preis sind die hier angegebenen Dienstleistungen enthalten, z.B. Jährliche Ablesung + inklusive_dienstleistungen: Optional[List[Dienstleistungstyp]] = attr.ib( + default=None, + validator=attr.validators.optional( + attr.validators.deep_iterable( + member_validator=attr.validators.instance_of(Dienstleistungstyp), + iterable_validator=attr.validators.instance_of(list), + ) + ), + ) + + #: Im Preis sind die hier angebebenen Geräte mit enthalten, z.B. ein Wandler + inklusive_geraete: Optional[List[Geraeteeigenschaften]] = attr.ib( + default=None, + validator=attr.validators.optional( + attr.validators.deep_iterable( + member_validator=attr.validators.instance_of(Geraeteeigenschaften), + iterable_validator=attr.validators.instance_of(list), + ) + ), + ) + + +class PreisblattMessungSchema(PreisblattSchema): + """ + Schema for de-/serialization of PreisblattMessung + """ + + class_name = PreisblattMessung # type:ignore[assignment] + # required attributes + bilanzierungsmethode = EnumField(Bilanzierungsmethode) + messebene = EnumField(Netzebene) + zaehler = fields.Nested(GeraeteeigenschaftenSchema) + # optional attributes + inklusive_dienstleistungen = fields.List(EnumField(Dienstleistungstyp)) + inklusive_geraete = fields.List(fields.Nested(GeraeteeigenschaftenSchema)) diff --git a/tests/test_geraeteeigenschaften.py b/tests/test_geraeteeigenschaften.py index 6e466ec64..737da20d9 100644 --- a/tests/test_geraeteeigenschaften.py +++ b/tests/test_geraeteeigenschaften.py @@ -5,13 +5,17 @@ from bo4e.enum.geraetetyp import Geraetetyp from tests.serialization_helper import assert_serialization_roundtrip # type:ignore[import] +example_geraeteeigenschaften = Geraeteeigenschaften( + geraetemerkmal=Geraetemerkmal.GAS_G1000, geraetetyp=Geraetetyp.MULTIPLEXANLAGE +) + class TestGeraeteeigenschaften: @pytest.mark.parametrize( "geraeteeigenschaften, expected_json_dict", [ pytest.param( - Geraeteeigenschaften(geraetemerkmal=Geraetemerkmal.GAS_G1000, geraetetyp=Geraetetyp.MULTIPLEXANLAGE), + example_geraeteeigenschaften, {"geraetemerkmal": "GAS_G1000", "geraetetyp": "MULTIPLEXANLAGE"}, ), ], diff --git a/tests/test_preisblatt_messung.py b/tests/test_preisblatt_messung.py new file mode 100644 index 000000000..d495880ad --- /dev/null +++ b/tests/test_preisblatt_messung.py @@ -0,0 +1,46 @@ +import pytest # type:ignore[import] + +from bo4e.bo.preisblattmessung import PreisblattMessung, PreisblattMessungSchema +from bo4e.enum.bilanzierungsmethode import Bilanzierungsmethode +from bo4e.enum.dienstleistungstyp import Dienstleistungstyp +from bo4e.enum.netzebene import Netzebene +from bo4e.enum.preisstatus import Preisstatus +from bo4e.enum.sparte import Sparte +from tests.serialization_helper import assert_serialization_roundtrip # type:ignore[import] +from tests.test_geraeteeigenschaften import example_geraeteeigenschaften # type:ignore[import] +from tests.test_marktteilnehmer import example_marktteilnehmer # type:ignore[import] +from tests.test_preisposition import example_preisposition # type:ignore[import] +from tests.test_zeitraum import example_zeitraum # type:ignore[import] + + +class TestPreisblatt: + @pytest.mark.parametrize( + "preisblatt_messung", + [ + pytest.param( + PreisblattMessung( + bezeichnung="foo", + sparte=Sparte.STROM, + preisstatus=Preisstatus.ENDGUELTIG, + preispositionen=[example_preisposition], + gueltigkeit=example_zeitraum, + herausgeber=example_marktteilnehmer, + bilanzierungsmethode=Bilanzierungsmethode.TLP_GEMEINSAM, + messebene=Netzebene.MSP, + inklusive_dienstleistungen=[Dienstleistungstyp.AUSLESUNG_FERNAUSLESUNG_ZUSAETZLICH_MSB], + zaehler=example_geraeteeigenschaften, + inklusive_geraete=[example_geraeteeigenschaften], + ) + ), + ], + ) + def test_serialization_roundtrip(self, preisblatt_messung: PreisblattMessung): + """ + Test de-/serialisation + """ + assert_serialization_roundtrip(preisblatt_messung, PreisblattMessungSchema()) + + def test_missing_required_attribute(self): + with pytest.raises(TypeError) as excinfo: + _ = PreisblattMessung() + assert "missing 8 required" in str(excinfo.value) # 5 from preisblatt + 3 from preisblatt messung