Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/restrictions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ such that it can be implemented in most programming languages.

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

* As a result, we cannot directly support `n x m`-relationships as they would require a back-reference.
If you're designing a database structure you have to manually add these relationship tables where needed.
Expand Down
10 changes: 8 additions & 2 deletions src/bo4e/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,10 @@
"Vertragskonditionen",
"Vertragsteil",
"VerwendungszweckProMarktrolle",
"Vorauszahlung",
"Zaehlwerk",
"Zaehlzeitregister",
"Zahlungsinformation",
"Zeitraum",
"Zeitreihenwert",
"Zustaendigkeit",
Expand Down Expand Up @@ -172,8 +174,8 @@
"Rollencodetyp",
"Sparte",
"Speicherart",
"Steuerart",
"SteuerkanalLeistungsbeschreibung",
"Steuerkennzeichen",
"StrEnum",
"Tarifkalkulationsmethode",
"Tarifmerkmal",
Expand All @@ -200,6 +202,7 @@
"Zaehlergroesse",
"Zaehlertyp",
"ZaehlertypSpezifikation",
"Zahlungsart",
"Zeitreihentyp",
"ZusatzAttribut",
"__version__",
Expand Down Expand Up @@ -303,8 +306,10 @@
from .com.vertragskonditionen import Vertragskonditionen
from .com.vertragsteil import Vertragsteil
from .com.verwendungszweckpromarktrolle import VerwendungszweckProMarktrolle
from .com.vorauszahlung import Vorauszahlung
from .com.zaehlwerk import Zaehlwerk
from .com.zaehlzeitregister import Zaehlzeitregister
from .com.zahlungsinformation import Zahlungsinformation
from .com.zeitraum import Zeitraum
from .com.zeitreihenwert import Zeitreihenwert
from .com.zustaendigkeit import Zustaendigkeit
Expand Down Expand Up @@ -378,8 +383,8 @@
from .enum.rollencodetyp import Rollencodetyp
from .enum.sparte import Sparte
from .enum.speicherart import Speicherart
from .enum.steuerart import Steuerart
from .enum.steuerkanalleistungsbeschreibung import SteuerkanalLeistungsbeschreibung
from .enum.steuerkennzeichen import Steuerkennzeichen
from .enum.strenum import StrEnum
from .enum.tarifkalkulationsmethode import Tarifkalkulationsmethode
from .enum.tarifmerkmal import Tarifmerkmal
Expand All @@ -404,6 +409,7 @@
from .enum.zaehlergroesse import Zaehlergroesse
from .enum.zaehlertyp import Zaehlertyp
from .enum.zaehlertypspezifikation import ZaehlertypSpezifikation
from .enum.zahlungsart import Zahlungsart
from .enum.zeitreihentyp import Zeitreihentyp
from .version import __gh_version__, __version__
from .zusatzattribut import ZusatzAttribut
Expand Down
60 changes: 50 additions & 10 deletions src/bo4e/bo/rechnung.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,22 @@
from ..com.betrag import Betrag
from ..com.rechnungsposition import Rechnungsposition
from ..com.steuerbetrag import Steuerbetrag
from ..com.vorauszahlung import Vorauszahlung
from ..com.zahlungsinformation import Zahlungsinformation
from ..com.zeitraum import Zeitraum
from ..enum.netznutzungrechnungsart import NetznutzungRechnungsart
from ..enum.netznutzungrechnungstyp import NetznutzungRechnungstyp
from ..enum.rechnungsstatus import Rechnungsstatus
from ..enum.rechnungstyp import Rechnungstyp
from ..enum.sparte import Sparte
from .energiemenge import Energiemenge
from .fremdkosten import Fremdkosten
from .geschaeftspartner import Geschaeftspartner
from .marktlokation import Marktlokation
from .marktteilnehmer import Marktteilnehmer
from .messlokation import Messlokation
from .vertrag import Vertrag
from .zaehler import Zaehler

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

Expand All @@ -43,11 +50,7 @@ class Rechnung(Geschaeftsobjekt):
"""

typ: Annotated[Literal[BoTyp.RECHNUNG], Field(alias="_typ")] = BoTyp.RECHNUNG
ist_storno: Optional[bool] = None
"""
Kennzeichnung, ob es sich um eine Stornorechnung handelt;
im Falle "true" findet sich im Attribut "originalrechnungsnummer" die Nummer der Originalrechnung.
"""

rechnungsnummer: Optional[str] = None
"""Eine im Verwendungskontext eindeutige Nummer für die Rechnung"""
rechnungsdatum: Optional[pydantic.AwareDatetime] = None
Expand All @@ -59,9 +62,13 @@ class Rechnung(Geschaeftsobjekt):
rechnungsperiode: Optional["Zeitraum"] = None
"""Der Zeitraum der zugrunde liegenden Lieferung zur Rechnung"""
rechnungsersteller: Optional["Geschaeftspartner"] = None
"""Der Aussteller der Rechnung, die Rollencodenummer kennt man über den im Geschäftspartner verlinkten Marktteilnehmer"""
"""
Der Aussteller der Rechnung. Die Rollencodenummer kennt man über den im Geschäftspartner verlinkten Marktteilnehmer.
"""
rechnungsempfaenger: Optional["Geschaeftspartner"] = None
"""Der Aussteller der Rechnung, die Rollencodenummer kennt man über den im Geschäftspartner verlinkten Marktteilnehmer"""
"""
Der Aussteller der Rechnung. Die Rollencodenummer kennt man über den im Geschäftspartner verlinkten Marktteilnehmer.
"""
gesamtnetto: Optional["Betrag"] = None
"""Die Summe der Nettobeträge der Rechnungsteile"""
gesamtsteuer: Optional["Betrag"] = None
Expand All @@ -70,6 +77,10 @@ class Rechnung(Geschaeftsobjekt):
"""Die Summe aus Netto- und Steuerbetrag"""
zu_zahlen: Optional["Betrag"] = None
"""Der zu zahlende Betrag, der sich aus (gesamtbrutto - vorausbezahlt - rabattBrutto) ergibt"""
zaehler: Optional[list["Zaehler"]] = None
zukuenftiger_abschlag: Optional["Betrag"] = None
kaeuferreferenz: Optional[str] = None

rechnungspositionen: Optional[list["Rechnungsposition"]] = None
"""Die Rechnungspositionen"""
rechnungstitel: Optional[str] = None
Expand All @@ -78,10 +89,10 @@ class Rechnung(Geschaeftsobjekt):
"""Status der Rechnung zur Kennzeichnung des Bearbeitungsstandes"""
original_rechnungsnummer: Optional[str] = None
"""Im Falle einer Stornorechnung (storno = true) steht hier die Rechnungsnummer der stornierten Rechnung"""
vorausgezahlt: Optional["Betrag"] = None
vorauszahlungen: Optional[list["Vorauszahlung"]] = None
"""Die Summe evtl. vorausgezahlter Beträge, z.B. Abschläge. Angabe als Bruttowert"""
rabatt_brutto: Optional["Betrag"] = None
"""Gesamtrabatt auf den Bruttobetrag"""
rabatt_netto: Optional["Betrag"] = None
"""Gesamtrabatt auf den Nettobetrag"""
steuerbetraege: Optional[list["Steuerbetrag"]] = None
"""
Eine Liste mit Steuerbeträgen pro Steuerkennzeichen/Steuersatz;
Expand All @@ -97,7 +108,36 @@ class Rechnung(Geschaeftsobjekt):
"""Kennzeichen, ob es sich um ein Original (true) oder eine Kopie handelt (false)"""
ist_simuliert: Optional[bool] = None
"""Kennzeichen, ob es sich um eine simulierte Rechnung, z.B. zur Rechnungsprüfung handelt"""
ist_storno: Optional[bool] = None
"""
Kennzeichnung, ob es sich um eine Stornorechnung handelt;
im Falle "true" findet sich im Attribut "originalrechnungsnummer" die Nummer der Originalrechnung.
"""
marktlokation: Optional["Marktlokation"] = None
"""Marktlokation, auf die sich die Rechnung bezieht"""
messlokation: Optional["Messlokation"] = None
"""Messlokation, auf die sich die Rechnung bezieht"""
teilrechnungen: Optional[list["Rechnung"]] = None
"""Rechnungen, die durch diese Rechnung zusammengefasst werden"""
zahlungsinformationen: Optional[list["Zahlungsinformation"]] = None
"""Informationen wie eine Rechnung bezahlt werden soll"""
vertrag: Optional["Vertrag"] = None
"""enthält Informationen über den der Rechnung zugrundeliegenden Vertrag für Rechnungen nach EnWG § 40"""
messstellenbetreiber: Optional["Marktteilnehmer"] = None
"""der Messtellenbetreiber an der Lieferstelle, relevant für Rechnungen gemäß EnWG § 40"""
netzbetreiber: Optional["Marktteilnehmer"] = None
"""der Netzbetreiber an der Lieferstelle, relevant für Rechnungen gemäß EnWG § 40"""
anfangszaehlerstand: Optional["Energiemenge"] = None
"""Für Verbrauchsbasierte Rechnungen der Zählerstand zur Beginn des abgerechneten Zeitraums, Pflicht für Rechnungen gemäß EnWG § 40"""
endzaehlerstand: Optional["Energiemenge"] = None
"""Für Verbrauchsbasierte Rechnungen der Zählerstand zum Ende des abgerechneten Zeitraums, Pflicht für Rechnungen gemäß EnWG § 40"""
aktueller_verbrauch: Optional["Energiemenge"] = None
"""Verbrauch des abgerechneten Zeitraums, Pflicht für Rechnungen gemäß EnWG § 40"""
jahresverbrauch: Optional["Energiemenge"] = None
"""ggf. auf einen Vergleichszeitraum hochgerechneter Verbrauch des abgerechneten Zeitraums zu Vergleichszwecken mit dem Vorjahr, gemäß EnWG § 40"""
vorjahresverbrauch: Optional["Energiemenge"] = None
"""ggf. auf einen Vergleichszeitraum hochgerechneter Verbrauch des vorherigen Jahres zu Vergleichszwecken mit dem aktuellen Jahr, gemäß EnWG § 40"""
fremdkosten: Optional["Fremdkosten"] = None
"""Zur Ausweisung der in die Kalkulation eingeflossenen Preise gemäß EnWG § 40"""
referenzverbraeuche: Optional[list["Energiemenge"]] = None
"""Verbräuche von Referenzkundengruppen gemäß EnWG § 40"""
32 changes: 11 additions & 21 deletions src/bo4e/com/rechnungsposition.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@
# pylint: disable=too-few-public-methods, too-many-instance-attributes
from typing import TYPE_CHECKING, Annotated, Literal, Optional

import pydantic
from pydantic import Field

from ..enum.comtyp import ComTyp
from ..utils import postprocess_docstring
from .com import COM

if TYPE_CHECKING:
from ..com.steuerbetrag import Steuerbetrag
from ..com.zeitraum import Zeitraum
from ..enum.bdewartikelnummer import BDEWArtikelnummer
from ..enum.mengeneinheit import Mengeneinheit
from .betrag import Betrag
from .menge import Menge
from .preis import Preis
from .steuerbetrag import Steuerbetrag


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

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

positionstext: Optional[str] = None
"""Bezeichung für die abgerechnete Position"""
Expand All @@ -53,34 +51,26 @@ class Rechnungsposition(COM):
"""Die abgerechnete Menge mit Einheit"""
einzelpreis: Optional["Preis"] = None
"""Der Preis für eine Einheit der energetischen Menge"""

teilsumme_netto: Optional["Betrag"] = None
gesamtpreis: Optional["Betrag"] = None
"""
Das Ergebnis der Multiplikation aus einzelpreis * positionsMenge * (Faktor aus zeitbezogeneMenge).
Z.B. 12,60€ * 120 kW * 3/12 (für 3 Monate).
"""

# the cross check in general doesn't work because Betrag and Preis use different enums to describe the currency
# see https://github.com/Hochfrequenz/BO4E-python/issues/126

teilsumme_steuer: Optional["Steuerbetrag"] = None
artikelnummer: Optional["BDEWArtikelnummer"] = None
"""Kennzeichnung der Rechnungsposition mit der Standard-Artikelnummer des BDEW"""
artikel_id: Optional[str] = None
"""Standardisierte vom BDEW herausgegebene Liste, welche im Strommarkt die BDEW-Artikelnummer ablöst"""
steuerbetrag: Optional["Steuerbetrag"] = None
"""Auf die Position entfallende Steuer, bestehend aus Steuersatz und Betrag"""

zeiteinheit: Optional["Mengeneinheit"] = None
"""Falls sich der Preis auf eine Zeit bezieht, steht hier die Einheit"""

artikelnummer: Optional["BDEWArtikelnummer"] = None
"""Kennzeichnung der Rechnungsposition mit der Standard-Artikelnummer des BDEW"""
lokations_id: Optional[str] = None
"""Marktlokation, die zu dieser Position gehört"""

zeitbezogene_menge: Optional["Menge"] = None
"""
Eine auf die Zeiteinheit bezogene Untermenge.
Z.B. bei einem Jahrespreis, 3 Monate oder 146 Tage.
Basierend darauf wird der Preis aufgeteilt.
"""
teilrabatt_netto: Optional["Betrag"] = None
"""Nettobetrag für den Rabatt dieser Position"""

artikel_id: Optional[str] = None
"""Standardisierte vom BDEW herausgegebene Liste, welche im Strommarkt die BDEW-Artikelnummer ablöst"""
12 changes: 6 additions & 6 deletions src/bo4e/com/steuerbetrag.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from .com import COM

if TYPE_CHECKING:
from ..enum.steuerkennzeichen import Steuerkennzeichen
from ..enum.steuerart import Steuerart
from ..enum.waehrungscode import Waehrungscode


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

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

steuerkennzeichen: Optional["Steuerkennzeichen"] = None
"""Kennzeichnung des Steuersatzes, bzw. Verfahrens."""
sondersteuersatz: Optional[Decimal] = None
"""Angabe des Steuersatzes in %, sofern steuerkennzeichen = UST_SONDER"""
steuerart: Optional["Steuerart"] = None
"""Kennzeichnung der Steuerart, bzw. Verfahrens."""
steuersatz: Optional[Decimal] = None
"""Angabe des Steuersatzes in %"""
basiswert: Optional[Decimal] = None
"""Nettobetrag für den die Steuer berechnet wurde. Z.B. 100"""
steuerwert: Optional[Decimal] = None
"""Aus dem Basiswert berechnete Steuer. Z.B. 19 (bei UST_19)"""
waehrung: Optional["Waehrungscode"] = None
waehrungscode: Optional["Waehrungscode"] = None
"""Währung. Z.B. Euro."""
38 changes: 38 additions & 0 deletions src/bo4e/com/vorauszahlung.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""
Contains Vorauszahlung class
"""

from typing import TYPE_CHECKING, Annotated, Literal, Optional

from pydantic import AwareDatetime, Field

from ..enum.comtyp import ComTyp
from ..utils import postprocess_docstring
from .com import COM

if TYPE_CHECKING:
from .betrag import Betrag


# pylint: disable=too-few-public-methods


@postprocess_docstring
class Vorauszahlung(COM):
"""
Dient zur Identifizierung eines vorausgezahlten Betrags.

.. raw:: html

<object data="../_static/images/bo4e/com/Vorauszahlung.svg" type="image/svg+xml"></object>

.. HINT::
`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>`_

"""

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

betrag: Optional["Betrag"] = None
datum: Optional[AwareDatetime] = None
referenz: Optional[str] = None
44 changes: 44 additions & 0 deletions src/bo4e/com/zahlungsinformation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""
Contains Zahlungsinformation class
"""

from typing import TYPE_CHECKING, Annotated, Literal, Optional

from pydantic import Field

from ..enum.comtyp import ComTyp
from ..utils import postprocess_docstring
from .com import COM

if TYPE_CHECKING:
from ..enum.zahlungsart import Zahlungsart


@postprocess_docstring
class Zahlungsinformation(COM):
"""
Mit dieser Komponente kann eine einzelne Zahlungsinformation dargestellt werden.

.. raw:: html

<object data="../_static/images/bo4e/com/Zahlungsinformation.svg" type="image/svg+xml"></object>

.. HINT::
`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>`_

"""

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

zahlungsart: Optional["Zahlungsart"] = None
"""Die Zahlungsart dieser Zahlungsinformation"""
iban: Optional[str] = None
"""Eine IBAN-Nummer"""
bic: Optional[str] = None
"""Eine BIC-Nummer"""
kontoinhaber: Optional[str] = None
"""Der Name des Kontoinhabers"""
betreff: Optional[str] = None
"""Eine konstante Betreffzeile für Überweisungen"""
sepa_referenz: Optional[str] = None
"""Eine SEPA-Referenz"""
2 changes: 2 additions & 0 deletions src/bo4e/enum/comtyp.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,10 @@ class ComTyp(StrEnum):
VERTRAGSKONDITIONEN = "VERTRAGSKONDITIONEN"
VERTRAGSTEIL = "VERTRAGSTEIL"
VERWENDUNGSZWECKPROMARKTROLLE = "VERWENDUNGSZWECKPROMARKTROLLE"
VORAUSZAHLUNG = "VORAUSZAHLUNG"
ZAEHLWERK = "ZAEHLWERK"
ZAEHLZEITREGISTER = "ZAEHLZEITREGISTER"
ZAHLUNGSINFORMATION = "ZAHLUNGSINFORMATION"
ZEITRAUM = "ZEITRAUM"
ZEITREIHENWERT = "ZEITREIHENWERT"
ZUSTAENDIGKEIT = "ZUSTAENDIGKEIT"
Loading
Loading