Skip to content

Commit 2e3c450

Browse files
authored
✨ Add COM AufAbschlagregional (#281)
* ✨ Add COM AufAbschlagregional * ✅ Add tests * 📦📝Generated docs * Remove duplicate-code warnings Got a duplicate code warnings due to similar validator lines
1 parent a0bb7b4 commit 2e3c450

File tree

4 files changed

+386
-1
lines changed

4 files changed

+386
-1
lines changed

docs/api/bo4e.com.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ bo4e.com.aufabschlagproort module
3636
:undoc-members:
3737
:show-inheritance:
3838

39+
bo4e.com.aufabschlagregional module
40+
-----------------------------------
41+
42+
.. automodule:: bo4e.com.aufabschlagregional
43+
:members:
44+
:undoc-members:
45+
:show-inheritance:
46+
3947
bo4e.com.aufabschlagstaffelproort module
4048
----------------------------------------
4149

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
"""
2+
Contains AufAbschlagRegional and corresponding marshmallow schema for de-/serialization
3+
"""
4+
5+
from typing import List, Optional
6+
7+
import attr
8+
from marshmallow import fields, post_load
9+
from marshmallow_enum import EnumField # type:ignore[import]
10+
11+
from bo4e.com.aufabschlagproort import AufAbschlagProOrt, AufAbschlagProOrtSchema
12+
from bo4e.com.com import COM, COMSchema
13+
from bo4e.com.energiemix import Energiemix, EnergiemixSchema
14+
from bo4e.com.preisgarantie import Preisgarantie, PreisgarantieSchema
15+
from bo4e.com.tarifeinschraenkung import Tarifeinschraenkung, TarifeinschraenkungSchema
16+
from bo4e.com.vertragskonditionen import Vertragskonditionen, VertragskonditionenSchema
17+
from bo4e.com.zeitraum import Zeitraum, ZeitraumSchema
18+
from bo4e.enum.aufabschlagstyp import AufAbschlagstyp
19+
from bo4e.enum.aufabschlagsziel import AufAbschlagsziel
20+
from bo4e.enum.waehrungseinheit import Waehrungseinheit
21+
from bo4e.validators import check_list_length_at_least_one
22+
23+
24+
# pylint: disable=too-few-public-methods, too-many-instance-attributes
25+
@attr.s(auto_attribs=True, kw_only=True)
26+
class AufAbschlagRegional(COM):
27+
"""
28+
Mit dieser Komponente können Auf- und Abschläge verschiedener Typen
29+
im Zusammenhang mit regionalen Gültigkeiten abgebildet werden.
30+
Hier sind auch die Auswirkungen auf verschiedene Tarifparameter modelliert,
31+
die sich durch die Auswahl eines Auf- oder Abschlags ergeben.
32+
"""
33+
34+
# required attributess
35+
#: Bezeichnung des Auf-/Abschlags
36+
bezeichnung: str = attr.ib(validator=attr.validators.instance_of(str))
37+
#: Werte für die gestaffelten Auf/Abschläge mit regionaler Eingrenzung
38+
betraege: List[AufAbschlagProOrt] = attr.ib(
39+
validator=[
40+
attr.validators.deep_iterable(
41+
member_validator=attr.validators.instance_of(AufAbschlagProOrt),
42+
iterable_validator=attr.validators.instance_of(list),
43+
),
44+
check_list_length_at_least_one,
45+
]
46+
)
47+
48+
# optional attributes
49+
#: Beschreibung zum Auf-/Abschlag
50+
beschreibung: Optional[str] = attr.ib(
51+
default=None, validator=attr.validators.optional(attr.validators.instance_of(str))
52+
)
53+
#:Typ des Aufabschlages (z.B. absolut oder prozentual)
54+
auf_abschlagstyp: Optional[AufAbschlagstyp] = attr.ib(
55+
default=None, validator=attr.validators.optional(attr.validators.instance_of(AufAbschlagstyp))
56+
)
57+
#: Diesem Preis oder den Kosten ist der Auf/Abschlag zugeordnet. Z.B. Arbeitspreis, Gesamtpreis etc.
58+
auf_abschlagsziel: Optional[AufAbschlagsziel] = attr.ib(
59+
default=None, validator=attr.validators.optional(attr.validators.instance_of(AufAbschlagsziel))
60+
)
61+
#: Gibt an in welcher Währungseinheit der Auf/Abschlag berechnet wird. Euro oder Ct.
62+
einheit: Optional[Waehrungseinheit] = attr.ib(
63+
default=None, validator=attr.validators.optional(attr.validators.instance_of(Waehrungseinheit))
64+
)
65+
#: Internetseite, auf der die Informationen zum Auf-/Abschlag veröffentlicht sind
66+
website: Optional[str] = attr.ib(default=None, validator=attr.validators.optional(attr.validators.instance_of(str)))
67+
#: Zusatzprodukte, die nur in Kombination mit diesem AufAbschlag erhältlich sind
68+
zusatzprodukte: Optional[List[str]] = attr.ib(
69+
default=None,
70+
validator=attr.validators.optional(
71+
attr.validators.deep_iterable(
72+
member_validator=attr.validators.instance_of(str),
73+
iterable_validator=attr.validators.instance_of(list),
74+
)
75+
),
76+
)
77+
#: Voraussetzungen, die erfüllt sein müssen, damit dieser AufAbschlag zur Anwendung kommen kann
78+
voraussetzungen: Optional[List[str]] = attr.ib(
79+
default=None,
80+
validator=attr.validators.optional(
81+
attr.validators.deep_iterable(
82+
member_validator=attr.validators.instance_of(str),
83+
iterable_validator=attr.validators.instance_of(list),
84+
)
85+
),
86+
)
87+
#: Durch die Anwendung des Auf/Abschlags kann eine Änderung des Tarifnamens auftreten.
88+
tarifnamensaenderungen: Optional[str] = attr.ib(
89+
default=None, validator=attr.validators.optional(attr.validators.instance_of(str))
90+
)
91+
#: Zeitraum, in dem der Abschlag zur Anwendung kommen kann
92+
gueltigkeitszeitraum: Optional[Zeitraum] = attr.ib(
93+
default=None, validator=attr.validators.optional(attr.validators.instance_of(Zeitraum))
94+
)
95+
energiemixaenderung: Optional[Energiemix] = attr.ib(
96+
default=None, validator=attr.validators.optional(attr.validators.instance_of(Energiemix))
97+
)
98+
""" Der Energiemix kann sich durch einen AufAbschlag ändern (z.B. zwei Cent Aufschlag für Ökostrom:
99+
Sollte dies der Fall sein, wird hier die neue Zusammensetzung des Energiemix angegeben."""
100+
vertagskonditionsaenderung: Optional[Vertragskonditionen] = attr.ib(
101+
default=None, validator=attr.validators.optional(attr.validators.instance_of(Vertragskonditionen))
102+
)
103+
""" Änderungen in den Vertragskonditionen. Falls in dieser Komponenten angegeben,
104+
werden die Tarifparameter hiermit überschrieben."""
105+
garantieaenderung: Optional[Preisgarantie] = attr.ib(
106+
default=None, validator=attr.validators.optional(attr.validators.instance_of(Preisgarantie))
107+
)
108+
""" Änderungen in den Garantievereinbarungen. Falls in dieser Komponenten angegeben,
109+
werden die Tarifparameter hiermit überschrieben."""
110+
einschraenkungsaenderung: Optional[Tarifeinschraenkung] = attr.ib(
111+
default=None, validator=attr.validators.optional(attr.validators.instance_of(Tarifeinschraenkung))
112+
)
113+
""" Änderungen in den Einschränkungen zum Tarif. Falls in dieser Komponenten angegeben,
114+
werden die Tarifparameter hiermit überschrieben."""
115+
116+
117+
class AufAbschlagRegionalSchema(COMSchema):
118+
"""
119+
Schema for de-/serialization of AufAbschlagRegional.
120+
"""
121+
122+
# required attributes
123+
bezeichnung = fields.Str()
124+
betraege = fields.List(fields.Nested(AufAbschlagProOrtSchema))
125+
126+
# optional attributes
127+
beschreibung = fields.Str(load_default=None)
128+
auf_abschlagstyp = EnumField(AufAbschlagstyp, load_default=None)
129+
auf_abschlagsziel = EnumField(AufAbschlagsziel, load_default=None)
130+
einheit = EnumField(Waehrungseinheit, load_default=None)
131+
website = fields.Str(load_default=None)
132+
zusatzprodukte = fields.List(fields.Str, load_default=None)
133+
voraussetzungen = fields.List(fields.Str, load_default=None)
134+
tarifnamensaenderungen = fields.Str(load_default=None)
135+
gueltigkeitszeitraum = fields.Nested(ZeitraumSchema, load_default=None)
136+
energiemixaenderung = fields.Nested(EnergiemixSchema, load_default=None)
137+
vertagskonditionsaenderung = fields.Nested(VertragskonditionenSchema, load_default=None)
138+
garantieaenderung = fields.Nested(PreisgarantieSchema, load_default=None)
139+
einschraenkungsaenderung = fields.Nested(TarifeinschraenkungSchema, load_default=None)
140+
141+
# pylint: disable=no-self-use, unused-argument
142+
@post_load
143+
def deserialize(self, data, **kwargs) -> AufAbschlagRegional:
144+
"""Deserialize JSON to AufAbschlagRegional object"""
145+
return AufAbschlagRegional(**data)

tests/test_aufabschlagregional.py

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
from datetime import datetime, timezone
2+
from decimal import Decimal
3+
4+
import pytest # type:ignore[import]
5+
6+
from bo4e.com.aufabschlagproort import AufAbschlagProOrt
7+
from bo4e.com.aufabschlagregional import AufAbschlagRegional, AufAbschlagRegionalSchema
8+
from bo4e.com.aufabschlagstaffelproort import AufAbschlagstaffelProOrt
9+
from bo4e.com.energieherkunft import Energieherkunft
10+
from bo4e.com.energiemix import Energiemix
11+
from bo4e.com.preisgarantie import Preisgarantie
12+
from bo4e.com.tarifeinschraenkung import Tarifeinschraenkung
13+
from bo4e.com.vertragskonditionen import Vertragskonditionen
14+
from bo4e.com.zeitraum import Zeitraum
15+
from bo4e.enum.aufabschlagstyp import AufAbschlagstyp
16+
from bo4e.enum.aufabschlagsziel import AufAbschlagsziel
17+
from bo4e.enum.erzeugungsart import Erzeugungsart
18+
from bo4e.enum.preisgarantietyp import Preisgarantietyp
19+
from bo4e.enum.sparte import Sparte
20+
from bo4e.enum.waehrungseinheit import Waehrungseinheit
21+
from tests.serialization_helper import assert_serialization_roundtrip # type:ignore[import]
22+
23+
24+
class TestAufAbschlagRegional:
25+
@pytest.mark.parametrize(
26+
"aufabschlagregional, expected_json_dict",
27+
[
28+
pytest.param(
29+
AufAbschlagRegional(
30+
bezeichnung="foo",
31+
betraege=[
32+
AufAbschlagProOrt(
33+
postleitzahl="01187",
34+
ort="Dresden",
35+
netznr="2",
36+
staffeln=[
37+
AufAbschlagstaffelProOrt(
38+
wert=Decimal(2.5),
39+
staffelgrenze_von=Decimal(1),
40+
staffelgrenze_bis=Decimal(5),
41+
)
42+
],
43+
),
44+
],
45+
),
46+
{
47+
"bezeichnung": "foo",
48+
"betraege": [
49+
{
50+
"postleitzahl": "01187",
51+
"ort": "Dresden",
52+
"netznr": "2",
53+
"staffeln": [
54+
{
55+
"wert": "2.5",
56+
"staffelgrenzeVon": "1",
57+
"staffelgrenzeBis": "5",
58+
}
59+
],
60+
},
61+
],
62+
"beschreibung": None,
63+
"aufAbschlagstyp": None,
64+
"aufAbschlagsziel": None,
65+
"einheit": None,
66+
"website": None,
67+
"zusatzprodukte": None,
68+
"voraussetzungen": None,
69+
"tarifnamensaenderungen": None,
70+
"gueltigkeitszeitraum": None,
71+
"energiemixaenderung": None,
72+
"vertagskonditionsaenderung": None,
73+
"garantieaenderung": None,
74+
"einschraenkungsaenderung": None,
75+
},
76+
id="only required attributes",
77+
),
78+
pytest.param(
79+
AufAbschlagRegional(
80+
bezeichnung="foo",
81+
betraege=[
82+
AufAbschlagProOrt(
83+
postleitzahl="01187",
84+
ort="Dresden",
85+
netznr="2",
86+
staffeln=[
87+
AufAbschlagstaffelProOrt(
88+
wert=Decimal(2.5),
89+
staffelgrenze_von=Decimal(1),
90+
staffelgrenze_bis=Decimal(5),
91+
)
92+
],
93+
),
94+
],
95+
beschreibung="bar",
96+
auf_abschlagstyp=AufAbschlagstyp.RELATIV,
97+
auf_abschlagsziel=AufAbschlagsziel.ARBEITSPREIS_HT,
98+
einheit=Waehrungseinheit.EUR,
99+
website="foo.bar",
100+
zusatzprodukte=["Asterix", "Obelix"],
101+
voraussetzungen=["Petterson", "Findus"],
102+
tarifnamensaenderungen="foobar",
103+
gueltigkeitszeitraum=Zeitraum(
104+
startdatum=datetime(2020, 1, 1, tzinfo=timezone.utc),
105+
enddatum=datetime(2020, 4, 1, tzinfo=timezone.utc),
106+
),
107+
energiemixaenderung=Energiemix(
108+
energiemixnummer=2,
109+
energieart=Sparte.STROM,
110+
bezeichnung="foo",
111+
gueltigkeitsjahr=2021,
112+
anteil=[
113+
Energieherkunft(
114+
erzeugungsart=Erzeugungsart.BIOGAS,
115+
anteil_prozent=Decimal(40),
116+
),
117+
],
118+
),
119+
vertagskonditionsaenderung=Vertragskonditionen(),
120+
garantieaenderung=Preisgarantie(
121+
preisgarantietyp=Preisgarantietyp.ALLE_PREISBESTANDTEILE_BRUTTO,
122+
zeitliche_gueltigkeit=Zeitraum(
123+
startdatum=datetime(2020, 1, 1, tzinfo=timezone.utc),
124+
enddatum=datetime(2020, 4, 1, tzinfo=timezone.utc),
125+
),
126+
),
127+
einschraenkungsaenderung=Tarifeinschraenkung(),
128+
),
129+
{
130+
"bezeichnung": "foo",
131+
"betraege": [
132+
{
133+
"postleitzahl": "01187",
134+
"ort": "Dresden",
135+
"netznr": "2",
136+
"staffeln": [
137+
{
138+
"wert": "2.5",
139+
"staffelgrenzeVon": "1",
140+
"staffelgrenzeBis": "5",
141+
}
142+
],
143+
},
144+
],
145+
"beschreibung": "bar",
146+
"aufAbschlagstyp": "RELATIV",
147+
"aufAbschlagsziel": "ARBEITSPREIS_HT",
148+
"einheit": "EUR",
149+
"website": "foo.bar",
150+
"zusatzprodukte": ["Asterix", "Obelix"],
151+
"voraussetzungen": ["Petterson", "Findus"],
152+
"tarifnamensaenderungen": "foobar",
153+
"gueltigkeitszeitraum": {
154+
"startdatum": "2020-01-01T00:00:00+00:00",
155+
"endzeitpunkt": None,
156+
"einheit": None,
157+
"enddatum": "2020-04-01T00:00:00+00:00",
158+
"startzeitpunkt": None,
159+
"dauer": None,
160+
},
161+
"energiemixaenderung": {
162+
"energiemixnummer": 2,
163+
"energieart": "STROM",
164+
"bezeichnung": "foo",
165+
"gueltigkeitsjahr": 2021,
166+
"anteil": [
167+
{
168+
"erzeugungsart": "BIOGAS",
169+
"anteilProzent": "40",
170+
}
171+
],
172+
"oekolabel": [],
173+
"bemerkung": None,
174+
"co2Emission": None,
175+
"atommuell": None,
176+
"website": None,
177+
"oekozertifikate": [],
178+
"oekoTopTen": None,
179+
},
180+
"vertagskonditionsaenderung": {
181+
"beschreibung": None,
182+
"anzahlAbschlaege": None,
183+
"vertragslaufzeit": None,
184+
"kuendigungsfrist": None,
185+
"vertragsverlaengerung": None,
186+
"abschlagszyklus": None,
187+
},
188+
"garantieaenderung": {
189+
"beschreibung": None,
190+
"preisgarantietyp": "ALLE_PREISBESTANDTEILE_BRUTTO",
191+
"zeitlicheGueltigkeit": {
192+
"startdatum": "2020-01-01T00:00:00+00:00",
193+
"endzeitpunkt": None,
194+
"einheit": None,
195+
"enddatum": "2020-04-01T00:00:00+00:00",
196+
"startzeitpunkt": None,
197+
"dauer": None,
198+
},
199+
},
200+
"einschraenkungsaenderung": {
201+
"zusatzprodukte": None,
202+
"voraussetzungen": None,
203+
"einschraenkungzaehler": None,
204+
"einschraenkungleistung": None,
205+
},
206+
},
207+
id="required and optional attributes",
208+
),
209+
],
210+
)
211+
def test_serialization_roundtrip(self, aufabschlagregional, expected_json_dict):
212+
"""
213+
Test de-/serialisation of AufAbschlagRegional with minimal attributes.
214+
"""
215+
assert_serialization_roundtrip(aufabschlagregional, AufAbschlagRegionalSchema(), expected_json_dict)
216+
217+
def test_missing_required_attribute(self):
218+
with pytest.raises(TypeError) as excinfo:
219+
_ = AufAbschlagRegional()
220+
221+
assert "missing 2 required" in str(excinfo.value)
222+
223+
def test_aufabschlagregional_betraege_required(self):
224+
with pytest.raises(ValueError) as excinfo:
225+
_ = (
226+
AufAbschlagRegional(
227+
bezeichnung="foo",
228+
betraege=[],
229+
),
230+
)
231+
232+
assert "List betraege must not be empty." in str(excinfo.value)

0 commit comments

Comments
 (0)