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

Issue:
Implement BO Energiemenge
https://www.bo4e.de/dokumentation/geschaeftsobjekte/bo-energiemenge
- #178 


Discussion / hints:

Fix patch:
diff --git a/src/bo4e/bo/energiemenge.py b/src/bo4e/bo/energiemenge.py
new file mode 100644
index 000000000..d6826dda3
--- /dev/null
+++ b/src/bo4e/bo/energiemenge.py
@@ -0,0 +1,52 @@
+"""
+Contains Energiemenge class
+and corresponding marshmallow schema for de-/serialization
+"""
+from typing import List
+
+import attr
+from marshmallow import fields
+from marshmallow_enum import EnumField  # type:ignore[import]
+
+from bo4e.bo.geschaeftsobjekt import Geschaeftsobjekt, GeschaeftsobjektSchema
+from bo4e.com.verbrauch import Verbrauch, VerbrauchSchema
+from bo4e.enum.botyp import BoTyp
+from bo4e.enum.lokationstyp import Lokationstyp
+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 Energiemenge(Geschaeftsobjekt):
+    """
+    Abbildung von Mengen, die Lokationen zugeordnet sind
+    """
+
+    # required attributes
+    bo_typ: BoTyp = attr.ib(default=BoTyp.ENERGIEMENGE)
+    #: Eindeutige Nummer der Marktlokation bzw. der Messlokation, zu der die Energiemenge gehört
+    lokations_id: str = attr.ib(validator=attr.validators.instance_of(str))
+    # todo: add validator such that only mess- or marktlokations IDs are accepted + cross check with lokationstyp
+    #: Gibt an, ob es sich um eine Markt- oder Messlokation handelt
+    lokationstyp: Lokationstyp = attr.ib(validator=attr.validators.instance_of(Lokationstyp))
+
+    #: Gibt den Verbrauch in einer Zeiteinheit an
+    energieverbrauch: List[Verbrauch] = attr.ib(
+        validator=attr.validators.deep_iterable(
+            member_validator=attr.validators.instance_of(Verbrauch),
+            iterable_validator=check_list_length_at_least_one,
+        )
+    )
+    # there are no optional attributes
+
+
+class EnergiemengeSchema(GeschaeftsobjektSchema):
+    """
+    Schema for de-/serialization of Energiemenge
+    """
+
+    class_name = Energiemenge
+    # required attributes
+    lokations_id = fields.Str()
+    lokationstyp = EnumField(Lokationstyp)
+    energieverbrauch = fields.List(fields.Nested(VerbrauchSchema))


Test patch:
diff --git a/tests/test_energiemenge.py b/tests/test_energiemenge.py
new file mode 100644
index 000000000..69393f0a6
--- /dev/null
+++ b/tests/test_energiemenge.py
@@ -0,0 +1,29 @@
+import pytest  # type:ignore[import]
+
+from bo4e.bo.energiemenge import Energiemenge, EnergiemengeSchema
+from bo4e.enum.lokationstyp import Lokationstyp
+from tests.serialization_helper import assert_serialization_roundtrip  # type:ignore[import]
+from tests.test_verbrauch import example_verbrauch  # type:ignore[import]
+
+
+class TestEnergiemenge:
+    @pytest.mark.parametrize(
+        "energiemenge",
+        [
+            pytest.param(
+                Energiemenge(
+                    lokations_id="DE0123456789012345678901234567890",
+                    lokationstyp=Lokationstyp.MELO,
+                    energieverbrauch=[example_verbrauch],
+                )
+            ),
+        ],
+    )
+    def test_serialization_roundtrip(self, energiemenge: Energiemenge):
+        assert_serialization_roundtrip(energiemenge, EnergiemengeSchema())
+
+    def test_missing_required_attribute(self):
+        with pytest.raises(TypeError) as excinfo:
+            _ = Energiemenge()
+
+        assert "missing 3 required" in str(excinfo.value)
diff --git a/tests/test_verbrauch.py b/tests/test_verbrauch.py
index bfabf7fb5..b5274ec1f 100644
--- a/tests/test_verbrauch.py
+++ b/tests/test_verbrauch.py
@@ -8,6 +8,13 @@
 from bo4e.enum.wertermittlungsverfahren import Wertermittlungsverfahren
 from tests.serialization_helper import assert_serialization_roundtrip  # type:ignore[import]
 
+example_verbrauch = Verbrauch(
+    wert=Decimal(40),
+    obis_kennzahl="1-0:1.8.1",
+    mengeneinheit=Mengeneinheit.KWH,
+    wertermittlungsverfahren=Wertermittlungsverfahren.MESSUNG,
+)
+
 
 class TestVerbrauch:
     @pytest.mark.parametrize(
@@ -32,12 +39,7 @@ class TestVerbrauch:
                 },
             ),
             pytest.param(
-                Verbrauch(
-                    wert=Decimal(40),
-                    obis_kennzahl="1-0:1.8.1",
-                    mengeneinheit=Mengeneinheit.KWH,
-                    wertermittlungsverfahren=Wertermittlungsverfahren.MESSUNG,
-                ),
+                example_verbrauch,
                 {
                     "wert": "40",
                     "mengeneinheit": "KWH",