Skip to content

Commit c3c7875

Browse files
authored
feat: ✨Rework BO Rechnung (#976)
1 parent 5a31853 commit c3c7875

15 files changed

Lines changed: 236 additions & 95 deletions

docs/restrictions.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ such that it can be implemented in most programming languages.
1717

1818
* In general, BOs won't be referenced by COMs but it's not prohibited. In fact, there are some cases in which a COM
1919
references a BO. Keep that in mind when designing a package structure e.g. to prevent circular import errors.
20-
* There are no circular references. This means, when drawing a reference graph of BO4E there will be no loops.
20+
* There are no circular references **except for** self references. A model can reference itself in the same class.
21+
This means, when drawing a reference graph of BO4E there will be no loops except for "tiny" loops corresponding
22+
to self references. E.g. `BO Rechnung` can have a field `teilrechnungen` of type `list[BO Rechnung]`.
23+
But it cannot reference another model which references back to `BO Rechnung`.
2124

2225
* As a result, we cannot directly support `n x m`-relationships as they would require a back-reference.
2326
If you're designing a database structure you have to manually add these relationship tables where needed.

src/bo4e/__init__.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,10 @@
101101
"Vertragskonditionen",
102102
"Vertragsteil",
103103
"VerwendungszweckProMarktrolle",
104+
"Vorauszahlung",
104105
"Zaehlwerk",
105106
"Zaehlzeitregister",
107+
"Zahlungsinformation",
106108
"Zeitraum",
107109
"Zeitreihenwert",
108110
"Zustaendigkeit",
@@ -172,8 +174,8 @@
172174
"Rollencodetyp",
173175
"Sparte",
174176
"Speicherart",
177+
"Steuerart",
175178
"SteuerkanalLeistungsbeschreibung",
176-
"Steuerkennzeichen",
177179
"StrEnum",
178180
"Tarifkalkulationsmethode",
179181
"Tarifmerkmal",
@@ -200,6 +202,7 @@
200202
"Zaehlergroesse",
201203
"Zaehlertyp",
202204
"ZaehlertypSpezifikation",
205+
"Zahlungsart",
203206
"Zeitreihentyp",
204207
"ZusatzAttribut",
205208
"__version__",
@@ -303,8 +306,10 @@
303306
from .com.vertragskonditionen import Vertragskonditionen
304307
from .com.vertragsteil import Vertragsteil
305308
from .com.verwendungszweckpromarktrolle import VerwendungszweckProMarktrolle
309+
from .com.vorauszahlung import Vorauszahlung
306310
from .com.zaehlwerk import Zaehlwerk
307311
from .com.zaehlzeitregister import Zaehlzeitregister
312+
from .com.zahlungsinformation import Zahlungsinformation
308313
from .com.zeitraum import Zeitraum
309314
from .com.zeitreihenwert import Zeitreihenwert
310315
from .com.zustaendigkeit import Zustaendigkeit
@@ -378,8 +383,8 @@
378383
from .enum.rollencodetyp import Rollencodetyp
379384
from .enum.sparte import Sparte
380385
from .enum.speicherart import Speicherart
386+
from .enum.steuerart import Steuerart
381387
from .enum.steuerkanalleistungsbeschreibung import SteuerkanalLeistungsbeschreibung
382-
from .enum.steuerkennzeichen import Steuerkennzeichen
383388
from .enum.strenum import StrEnum
384389
from .enum.tarifkalkulationsmethode import Tarifkalkulationsmethode
385390
from .enum.tarifmerkmal import Tarifmerkmal
@@ -404,6 +409,7 @@
404409
from .enum.zaehlergroesse import Zaehlergroesse
405410
from .enum.zaehlertyp import Zaehlertyp
406411
from .enum.zaehlertypspezifikation import ZaehlertypSpezifikation
412+
from .enum.zahlungsart import Zahlungsart
407413
from .enum.zeitreihentyp import Zeitreihentyp
408414
from .version import __gh_version__, __version__
409415
from .zusatzattribut import ZusatzAttribut

src/bo4e/bo/rechnung.py

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,22 @@
1515
from ..com.betrag import Betrag
1616
from ..com.rechnungsposition import Rechnungsposition
1717
from ..com.steuerbetrag import Steuerbetrag
18+
from ..com.vorauszahlung import Vorauszahlung
19+
from ..com.zahlungsinformation import Zahlungsinformation
1820
from ..com.zeitraum import Zeitraum
1921
from ..enum.netznutzungrechnungsart import NetznutzungRechnungsart
2022
from ..enum.netznutzungrechnungstyp import NetznutzungRechnungstyp
2123
from ..enum.rechnungsstatus import Rechnungsstatus
2224
from ..enum.rechnungstyp import Rechnungstyp
2325
from ..enum.sparte import Sparte
26+
from .energiemenge import Energiemenge
27+
from .fremdkosten import Fremdkosten
2428
from .geschaeftspartner import Geschaeftspartner
2529
from .marktlokation import Marktlokation
30+
from .marktteilnehmer import Marktteilnehmer
2631
from .messlokation import Messlokation
32+
from .vertrag import Vertrag
33+
from .zaehler import Zaehler
2734

2835
# pylint: disable=too-few-public-methods, too-many-instance-attributes
2936

@@ -43,11 +50,7 @@ class Rechnung(Geschaeftsobjekt):
4350
"""
4451

4552
typ: Annotated[Literal[BoTyp.RECHNUNG], Field(alias="_typ")] = BoTyp.RECHNUNG
46-
ist_storno: Optional[bool] = None
47-
"""
48-
Kennzeichnung, ob es sich um eine Stornorechnung handelt;
49-
im Falle "true" findet sich im Attribut "originalrechnungsnummer" die Nummer der Originalrechnung.
50-
"""
53+
5154
rechnungsnummer: Optional[str] = None
5255
"""Eine im Verwendungskontext eindeutige Nummer für die Rechnung"""
5356
rechnungsdatum: Optional[pydantic.AwareDatetime] = None
@@ -59,9 +62,13 @@ class Rechnung(Geschaeftsobjekt):
5962
rechnungsperiode: Optional["Zeitraum"] = None
6063
"""Der Zeitraum der zugrunde liegenden Lieferung zur Rechnung"""
6164
rechnungsersteller: Optional["Geschaeftspartner"] = None
62-
"""Der Aussteller der Rechnung, die Rollencodenummer kennt man über den im Geschäftspartner verlinkten Marktteilnehmer"""
65+
"""
66+
Der Aussteller der Rechnung. Die Rollencodenummer kennt man über den im Geschäftspartner verlinkten Marktteilnehmer.
67+
"""
6368
rechnungsempfaenger: Optional["Geschaeftspartner"] = None
64-
"""Der Aussteller der Rechnung, die Rollencodenummer kennt man über den im Geschäftspartner verlinkten Marktteilnehmer"""
69+
"""
70+
Der Aussteller der Rechnung. Die Rollencodenummer kennt man über den im Geschäftspartner verlinkten Marktteilnehmer.
71+
"""
6572
gesamtnetto: Optional["Betrag"] = None
6673
"""Die Summe der Nettobeträge der Rechnungsteile"""
6774
gesamtsteuer: Optional["Betrag"] = None
@@ -70,6 +77,10 @@ class Rechnung(Geschaeftsobjekt):
7077
"""Die Summe aus Netto- und Steuerbetrag"""
7178
zu_zahlen: Optional["Betrag"] = None
7279
"""Der zu zahlende Betrag, der sich aus (gesamtbrutto - vorausbezahlt - rabattBrutto) ergibt"""
80+
zaehler: Optional[list["Zaehler"]] = None
81+
zukuenftiger_abschlag: Optional["Betrag"] = None
82+
kaeuferreferenz: Optional[str] = None
83+
7384
rechnungspositionen: Optional[list["Rechnungsposition"]] = None
7485
"""Die Rechnungspositionen"""
7586
rechnungstitel: Optional[str] = None
@@ -78,10 +89,10 @@ class Rechnung(Geschaeftsobjekt):
7889
"""Status der Rechnung zur Kennzeichnung des Bearbeitungsstandes"""
7990
original_rechnungsnummer: Optional[str] = None
8091
"""Im Falle einer Stornorechnung (storno = true) steht hier die Rechnungsnummer der stornierten Rechnung"""
81-
vorausgezahlt: Optional["Betrag"] = None
92+
vorauszahlungen: Optional[list["Vorauszahlung"]] = None
8293
"""Die Summe evtl. vorausgezahlter Beträge, z.B. Abschläge. Angabe als Bruttowert"""
83-
rabatt_brutto: Optional["Betrag"] = None
84-
"""Gesamtrabatt auf den Bruttobetrag"""
94+
rabatt_netto: Optional["Betrag"] = None
95+
"""Gesamtrabatt auf den Nettobetrag"""
8596
steuerbetraege: Optional[list["Steuerbetrag"]] = None
8697
"""
8798
Eine Liste mit Steuerbeträgen pro Steuerkennzeichen/Steuersatz;
@@ -97,7 +108,36 @@ class Rechnung(Geschaeftsobjekt):
97108
"""Kennzeichen, ob es sich um ein Original (true) oder eine Kopie handelt (false)"""
98109
ist_simuliert: Optional[bool] = None
99110
"""Kennzeichen, ob es sich um eine simulierte Rechnung, z.B. zur Rechnungsprüfung handelt"""
111+
ist_storno: Optional[bool] = None
112+
"""
113+
Kennzeichnung, ob es sich um eine Stornorechnung handelt;
114+
im Falle "true" findet sich im Attribut "originalrechnungsnummer" die Nummer der Originalrechnung.
115+
"""
100116
marktlokation: Optional["Marktlokation"] = None
101117
"""Marktlokation, auf die sich die Rechnung bezieht"""
102118
messlokation: Optional["Messlokation"] = None
103119
"""Messlokation, auf die sich die Rechnung bezieht"""
120+
teilrechnungen: Optional[list["Rechnung"]] = None
121+
"""Rechnungen, die durch diese Rechnung zusammengefasst werden"""
122+
zahlungsinformationen: Optional[list["Zahlungsinformation"]] = None
123+
"""Informationen wie eine Rechnung bezahlt werden soll"""
124+
vertrag: Optional["Vertrag"] = None
125+
"""enthält Informationen über den der Rechnung zugrundeliegenden Vertrag für Rechnungen nach EnWG § 40"""
126+
messstellenbetreiber: Optional["Marktteilnehmer"] = None
127+
"""der Messtellenbetreiber an der Lieferstelle, relevant für Rechnungen gemäß EnWG § 40"""
128+
netzbetreiber: Optional["Marktteilnehmer"] = None
129+
"""der Netzbetreiber an der Lieferstelle, relevant für Rechnungen gemäß EnWG § 40"""
130+
anfangszaehlerstand: Optional["Energiemenge"] = None
131+
"""Für Verbrauchsbasierte Rechnungen der Zählerstand zur Beginn des abgerechneten Zeitraums, Pflicht für Rechnungen gemäß EnWG § 40"""
132+
endzaehlerstand: Optional["Energiemenge"] = None
133+
"""Für Verbrauchsbasierte Rechnungen der Zählerstand zum Ende des abgerechneten Zeitraums, Pflicht für Rechnungen gemäß EnWG § 40"""
134+
aktueller_verbrauch: Optional["Energiemenge"] = None
135+
"""Verbrauch des abgerechneten Zeitraums, Pflicht für Rechnungen gemäß EnWG § 40"""
136+
jahresverbrauch: Optional["Energiemenge"] = None
137+
"""ggf. auf einen Vergleichszeitraum hochgerechneter Verbrauch des abgerechneten Zeitraums zu Vergleichszwecken mit dem Vorjahr, gemäß EnWG § 40"""
138+
vorjahresverbrauch: Optional["Energiemenge"] = None
139+
"""ggf. auf einen Vergleichszeitraum hochgerechneter Verbrauch des vorherigen Jahres zu Vergleichszwecken mit dem aktuellen Jahr, gemäß EnWG § 40"""
140+
fremdkosten: Optional["Fremdkosten"] = None
141+
"""Zur Ausweisung der in die Kalkulation eingeflossenen Preise gemäß EnWG § 40"""
142+
referenzverbraeuche: Optional[list["Energiemenge"]] = None
143+
"""Verbräuche von Referenzkundengruppen gemäß EnWG § 40"""

src/bo4e/com/rechnungsposition.py

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@
55
# pylint: disable=too-few-public-methods, too-many-instance-attributes
66
from typing import TYPE_CHECKING, Annotated, Literal, Optional
77

8-
import pydantic
98
from pydantic import Field
109

1110
from ..enum.comtyp import ComTyp
1211
from ..utils import postprocess_docstring
1312
from .com import COM
1413

1514
if TYPE_CHECKING:
15+
from ..com.steuerbetrag import Steuerbetrag
16+
from ..com.zeitraum import Zeitraum
1617
from ..enum.bdewartikelnummer import BDEWArtikelnummer
1718
from ..enum.mengeneinheit import Mengeneinheit
1819
from .betrag import Betrag
1920
from .menge import Menge
2021
from .preis import Preis
21-
from .steuerbetrag import Steuerbetrag
2222

2323

2424
@postprocess_docstring
@@ -41,10 +41,8 @@ class Rechnungsposition(COM):
4141
positionsnummer: Optional[int] = None
4242
"""Fortlaufende Nummer für die Rechnungsposition"""
4343

44-
lieferung_von: Optional[pydantic.AwareDatetime] = None
45-
"""Start der Lieferung für die abgerechnete Leistung (inklusiv)"""
46-
lieferung_bis: Optional[pydantic.AwareDatetime] = None
47-
"""Ende der Lieferung für die abgerechnete Leistung (exklusiv)"""
44+
lieferungszeitraum: Optional["Zeitraum"] = None
45+
"""Zeitraum der Lieferung für die abgerechnete Leistung"""
4846

4947
positionstext: Optional[str] = None
5048
"""Bezeichung für die abgerechnete Position"""
@@ -53,34 +51,26 @@ class Rechnungsposition(COM):
5351
"""Die abgerechnete Menge mit Einheit"""
5452
einzelpreis: Optional["Preis"] = None
5553
"""Der Preis für eine Einheit der energetischen Menge"""
56-
57-
teilsumme_netto: Optional["Betrag"] = None
54+
gesamtpreis: Optional["Betrag"] = None
5855
"""
5956
Das Ergebnis der Multiplikation aus einzelpreis * positionsMenge * (Faktor aus zeitbezogeneMenge).
6057
Z.B. 12,60€ * 120 kW * 3/12 (für 3 Monate).
6158
"""
59+
6260
# the cross check in general doesn't work because Betrag and Preis use different enums to describe the currency
6361
# see https://github.com/Hochfrequenz/BO4E-python/issues/126
6462

65-
teilsumme_steuer: Optional["Steuerbetrag"] = None
63+
artikelnummer: Optional["BDEWArtikelnummer"] = None
64+
"""Kennzeichnung der Rechnungsposition mit der Standard-Artikelnummer des BDEW"""
65+
artikel_id: Optional[str] = None
66+
"""Standardisierte vom BDEW herausgegebene Liste, welche im Strommarkt die BDEW-Artikelnummer ablöst"""
67+
steuerbetrag: Optional["Steuerbetrag"] = None
6668
"""Auf die Position entfallende Steuer, bestehend aus Steuersatz und Betrag"""
67-
6869
zeiteinheit: Optional["Mengeneinheit"] = None
6970
"""Falls sich der Preis auf eine Zeit bezieht, steht hier die Einheit"""
70-
71-
artikelnummer: Optional["BDEWArtikelnummer"] = None
72-
"""Kennzeichnung der Rechnungsposition mit der Standard-Artikelnummer des BDEW"""
73-
lokations_id: Optional[str] = None
74-
"""Marktlokation, die zu dieser Position gehört"""
75-
7671
zeitbezogene_menge: Optional["Menge"] = None
7772
"""
7873
Eine auf die Zeiteinheit bezogene Untermenge.
7974
Z.B. bei einem Jahrespreis, 3 Monate oder 146 Tage.
8075
Basierend darauf wird der Preis aufgeteilt.
8176
"""
82-
teilrabatt_netto: Optional["Betrag"] = None
83-
"""Nettobetrag für den Rabatt dieser Position"""
84-
85-
artikel_id: Optional[str] = None
86-
"""Standardisierte vom BDEW herausgegebene Liste, welche im Strommarkt die BDEW-Artikelnummer ablöst"""

src/bo4e/com/steuerbetrag.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from .com import COM
1313

1414
if TYPE_CHECKING:
15-
from ..enum.steuerkennzeichen import Steuerkennzeichen
15+
from ..enum.steuerart import Steuerart
1616
from ..enum.waehrungscode import Waehrungscode
1717

1818

@@ -35,13 +35,13 @@ class Steuerbetrag(COM):
3535

3636
typ: Annotated[Literal[ComTyp.STEUERBETRAG], Field(alias="_typ")] = ComTyp.STEUERBETRAG
3737

38-
steuerkennzeichen: Optional["Steuerkennzeichen"] = None
39-
"""Kennzeichnung des Steuersatzes, bzw. Verfahrens."""
40-
sondersteuersatz: Optional[Decimal] = None
41-
"""Angabe des Steuersatzes in %, sofern steuerkennzeichen = UST_SONDER"""
38+
steuerart: Optional["Steuerart"] = None
39+
"""Kennzeichnung der Steuerart, bzw. Verfahrens."""
40+
steuersatz: Optional[Decimal] = None
41+
"""Angabe des Steuersatzes in %"""
4242
basiswert: Optional[Decimal] = None
4343
"""Nettobetrag für den die Steuer berechnet wurde. Z.B. 100"""
4444
steuerwert: Optional[Decimal] = None
4545
"""Aus dem Basiswert berechnete Steuer. Z.B. 19 (bei UST_19)"""
46-
waehrung: Optional["Waehrungscode"] = None
46+
waehrungscode: Optional["Waehrungscode"] = None
4747
"""Währung. Z.B. Euro."""

src/bo4e/com/vorauszahlung.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""
2+
Contains Vorauszahlung class
3+
"""
4+
5+
from typing import TYPE_CHECKING, Annotated, Literal, Optional
6+
7+
from pydantic import AwareDatetime, Field
8+
9+
from ..enum.comtyp import ComTyp
10+
from ..utils import postprocess_docstring
11+
from .com import COM
12+
13+
if TYPE_CHECKING:
14+
from .betrag import Betrag
15+
16+
17+
# pylint: disable=too-few-public-methods
18+
19+
20+
@postprocess_docstring
21+
class Vorauszahlung(COM):
22+
"""
23+
Dient zur Identifizierung eines vorausgezahlten Betrags.
24+
25+
.. raw:: html
26+
27+
<object data="../_static/images/bo4e/com/Vorauszahlung.svg" type="image/svg+xml"></object>
28+
29+
.. HINT::
30+
`Vorauszahlung JSON Schema <https://json-schema.app/view/%23?url=https://raw.githubusercontent.com/BO4E/BO4E-Schemas/{__gh_version__}/src/bo4e_schemas/com/Vorauszahlung.json>`_
31+
32+
"""
33+
34+
typ: Annotated[Literal[ComTyp.VORAUSZAHLUNG], Field(alias="_typ")] = ComTyp.VORAUSZAHLUNG
35+
36+
betrag: Optional["Betrag"] = None
37+
datum: Optional[AwareDatetime] = None
38+
referenz: Optional[str] = None
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""
2+
Contains Zahlungsinformation class
3+
"""
4+
5+
from typing import TYPE_CHECKING, Annotated, Literal, Optional
6+
7+
from pydantic import Field
8+
9+
from ..enum.comtyp import ComTyp
10+
from ..utils import postprocess_docstring
11+
from .com import COM
12+
13+
if TYPE_CHECKING:
14+
from ..enum.zahlungsart import Zahlungsart
15+
16+
17+
@postprocess_docstring
18+
class Zahlungsinformation(COM):
19+
"""
20+
Mit dieser Komponente kann eine einzelne Zahlungsinformation dargestellt werden.
21+
22+
.. raw:: html
23+
24+
<object data="../_static/images/bo4e/com/Zahlungsinformation.svg" type="image/svg+xml"></object>
25+
26+
.. HINT::
27+
`Zahlungsinformation JSON Schema <https://json-schema.app/view/%23?url=https://raw.githubusercontent.com/BO4E/BO4E-Schemas/{__gh_version__}/src/bo4e_schemas/com/Zahlungsinformation.json>`_
28+
29+
"""
30+
31+
typ: Annotated[Literal[ComTyp.ZAHLUNGSINFORMATION], Field(alias="_typ")] = ComTyp.ZAHLUNGSINFORMATION
32+
33+
zahlungsart: Optional["Zahlungsart"] = None
34+
"""Die Zahlungsart dieser Zahlungsinformation"""
35+
iban: Optional[str] = None
36+
"""Eine IBAN-Nummer"""
37+
bic: Optional[str] = None
38+
"""Eine BIC-Nummer"""
39+
kontoinhaber: Optional[str] = None
40+
"""Der Name des Kontoinhabers"""
41+
betreff: Optional[str] = None
42+
"""Eine konstante Betreffzeile für Überweisungen"""
43+
sepa_referenz: Optional[str] = None
44+
"""Eine SEPA-Referenz"""

src/bo4e/enum/comtyp.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,10 @@ class ComTyp(StrEnum):
6666
VERTRAGSKONDITIONEN = "VERTRAGSKONDITIONEN"
6767
VERTRAGSTEIL = "VERTRAGSTEIL"
6868
VERWENDUNGSZWECKPROMARKTROLLE = "VERWENDUNGSZWECKPROMARKTROLLE"
69+
VORAUSZAHLUNG = "VORAUSZAHLUNG"
6970
ZAEHLWERK = "ZAEHLWERK"
7071
ZAEHLZEITREGISTER = "ZAEHLZEITREGISTER"
72+
ZAHLUNGSINFORMATION = "ZAHLUNGSINFORMATION"
7173
ZEITRAUM = "ZEITRAUM"
7274
ZEITREIHENWERT = "ZEITREIHENWERT"
7375
ZUSTAENDIGKEIT = "ZUSTAENDIGKEIT"

0 commit comments

Comments
 (0)