Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
18 changes: 18 additions & 0 deletions src/bo4e/bo/avis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""Contains class Avis"""
from bo4e.bo.geschaeftsobjekt import Geschaeftsobjekt
from bo4e.com.avisposition import Avisposition
from bo4e.com.betrag import Betrag
from bo4e.enum.avistyp import AvisTyp
from bo4e.enum.botyp import BoTyp


class Avis(Geschaeftsobjekt):
"""Avis BO"""

bo_typ: BoTyp = BoTyp.AVIS # added to botyp.py

#: required attributes
avis_nummer: str #: Eine im Verwendungskontext eindeutige Nummer für das Avis.
avis_typ: AvisTyp #: Gibt den Typ des Avis an.
positionen: list[Avisposition] #: Avispositionen
zu_zahlen: Betrag #: Summenbetrag
17 changes: 17 additions & 0 deletions src/bo4e/com/abweichung.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""Contains Class: Abweichung."""
from typing import Optional

from bo4e.com.com import COM
from bo4e.enum.abweichungsgrund import Abweichungsgrund


class Abweichung(COM):
"""Zur Angabe einer Abweichung bei Ablehnung einer COMDIS. (REMADV SG5 RFF und SG7 AJT/FTX)."""

# optional attributes
abweichungsgrund: Optional[Abweichungsgrund] = None #: Angabe Abweichungsgrund
abweichungsgrund_bemerkung: Optional[str] = None #: Nähere Erläuterung zum Abweichungsgrund
zugehoerige_rechnung: Optional[str] = None #: Zugehoerige Rechnung
abschlagsrechnung: Optional[str] = None #: Abschlagsrechnung
abweichungsgrund_code: Optional[str] = None #: Angabe Abweichungsgrund(Code)
abweichungsgrund_codeliste: Optional[str] = None #: Angabe Abweichungsgrund(Codeliste)
16 changes: 16 additions & 0 deletions src/bo4e/com/abweichungsposition.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""Contains class Abweichungsposition."""
from typing import Optional

from bo4e.com.com import COM


class Abweichungsposition(COM):
"""Zur Angabe einer Abweichung einer einzelnen Position."""

# optional attributes
abweichungsgrund_code: Optional[str] = None #: Angabe Abweichungsgrund(Code)
abweichungsgrund_codeliste: Optional[str] = None #:Angabe Abweichungsgrund(Codeliste)
abweichungsgrund_bemerkung: Optional[str] = None #: Nähere Erläuterung zum Abweichungsgrund
# todo: prüfen, ob zugehörige Rechnung angegeben ist, wenn Abw.Grund gegeben?
zugehoerige_rechnung: Optional[str] = None #: Zugehörige Rechnung
zugehoerige_bestellung: Optional[str] = None #: Zugehörige Bestellung
27 changes: 27 additions & 0 deletions src/bo4e/com/avisposition.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""Contains class Avisposition"""
from datetime import datetime
from typing import Optional

from bo4e.com.abweichung import Abweichung
from bo4e.com.betrag import Betrag
from bo4e.com.com import COM
from bo4e.com.rueckmeldungsposition import Rueckmeldungsposition


class Avisposition(COM):
"""Die Position eines Avis"""

# required attributes
rechnungs_nummer: str #: Die Rechnungsnummer der Rechnung, auf welche sich das Avis bezieht.
rechnungs_datum: datetime #: Das Rechnungsdatum der Rechnung, auf die sich das Avis bezieht.
ist_storno: bool
#: Kennzeichnung, ob es sich bei der Rechnung auf die sich das Avis bezieht, um eine Stornorechnung handelt.
gesamtbrutto: Betrag #: Überweisungsbetrag
zu_zahlen: Betrag #: Geforderter Rechnungsbetrag

# optional attributes
ist_selbstausgestellt: Optional[bool] = None
#: Kennzeichnung, ob es sich bei der Rechnung auf die sich das Avis bezieht, um eine Stornorechnung handelt.
referenz: Optional[str] = None #: Referenzierung auf eine vorherige COMDIS-Nachricht
abweichungen: Optional[list[Abweichung]] = None #: Abweichungen bei Ablehnung einer COMDIS
positionen: Optional[list[Rueckmeldungsposition]] = None #: Rückmeldungspositionen
24 changes: 24 additions & 0 deletions src/bo4e/com/rueckmeldungsposition.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""Contains class Rueckmeldeposition"""
from typing import Optional

from pydantic import model_validator

from bo4e.com.abweichungsposition import Abweichungsposition
from bo4e.com.com import COM


class Rueckmeldungsposition(COM):
"""Zur Angabe einer Rückmeldung einer einzelnen Position."""

# optional attributes
positionsnummer: Optional[str] = None #: Positionsnummer der Referenzierung
abweichungspositionen: Optional[list[Abweichungsposition]] = None #: Abweichungspositionen

#: model validator for !XOR pos.nr. and abweichungspos.
@model_validator(mode="after")
def _field_combination_xor(self) -> "Rueckmeldungsposition":
if (self.positionsnummer and self.abweichungspositionen is None) or (
self.abweichungspositionen and self.positionsnummer is None
):
raise ValueError("Attributes missing")
return self
65 changes: 65 additions & 0 deletions src/bo4e/enum/abweichungsgrund.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""contains enum class Abweichungsgrund."""
from bo4e.enum.strenum import StrEnum


class Abweichungsgrund(StrEnum):
"""Gibt einen Abweichungsgrund bei Ablehung einer COMDIS an. (REMADV SG7 AJT 4465)"""

#: PREIS_RECHENREGEL_FALSCH, 5
PREIS_RECHENREGEL_FALSCH = "PREIS_RECHENREGEL_FALSCH"
#: FALSCHER_ABRECHNUNGSZEITRAUM, 9
FALSCHER_ABRECHNUNGSZEITRAUM = "FALSCHER_ABRECHNUNGSZEITRAUM"
#: UNBEKANNTE_MARKTLOKATION_MESSLOKATION, 14
UNBEKANNTE_MARKTLOKATION_MESSLOKATION = "UNBEKANNTE_MARKTLOKATION_MESSLOKATION"
#: SONSTIGER_ABWEICHUNGSGRUND, 28
SONSTIGER_ABWEICHUNGSGRUND = "SONSTIGER_ABWEICHUNGSGRUND"
#: DOPPELTE_RECHNUNG, 53
DOPPELTE_RECHNUNG = "DOPPELTE_RECHNUNG"
#: ABRECHNUNGSBEGINN_UNGLEICH_VERTRAGSBEGINN, Z01
ABRECHNUNGSBEGINN_UNGLEICH_VERTRAGSBEGINN = "ABRECHNUNGSBEGINN_UNGLEICH_VERTRAGSBEGINN"
#: ABRECHNUNGSENDE_UNGLEICH_VERTRAGSENDE, Z02
ABRECHNUNGSENDE_UNGLEICH_VERTRAGSENDE = "ABRECHNUNGSENDE_UNGLEICH_VERTRAGSENDE"
#: BETRAG_DER_ABSCHLAGSRECHNUNG_FALSCH, Z03
BETRAG_DER_ABSCHLAGSRECHNUNG_FALSCH = "BETRAG_DER_ABSCHLAGSRECHNUNG_FALSCH"
#: VORAUSBEZAHLTER_BETRAG_FALSCH, Z04
VORAUSBEZAHLTER_BETRAG_FALSCH = "VORAUSBEZAHLTER_BETRAG_FALSCH"
#: ARTIKEL_NICHT_VEREINBART, Z06
ARTIKEL_NICHT_VEREINBART = "ARTIKEL_NICHT_VEREINBART"
#: NETZNUTZUNGSMESSWERTE_ENERGIEMENGEN_FEHLEN, Z07
NETZNUTZUNGSMESSWERTE_ENERGIEMENGEN_FEHLEN = "NETZNUTZUNGSMESSWERTE_ENERGIEMENGEN_FEHLEN"
#: RECHNUNGSNUMMER_BEREITS_ERHALTEN, Z08
RECHNUNGSNUMMER_BEREITS_ERHALTEN = "RECHNUNGSNUMMER_BEREITS_ERHALTEN"
#: NETZNUTZUNGSMESSWERTE_ENERGIEMENGEN_FALSCH, Z10
NETZNUTZUNGSMESSWERTE_ENERGIEMENGEN_FALSCH = "NETZNUTZUNGSMESSWERTE_ENERGIEMENGEN_FALSCH"
#: ZEITLICHE_MENGENANGABE_FEHLERHAFT, Z33
ZEITLICHE_MENGENANGABE_FEHLERHAFT = "ZEITLICHE_MENGENANGABE_FEHLERHAFT"
#: FALSCHER_BILANZIERUNGSBEGINN, Z35
FALSCHER_BILANZIERUNGSBEGINN = "FALSCHER_BILANZIERUNGSBEGINN"
#: FALSCHES_NETZNUTZUNGSENDE, Z36
FALSCHES_NETZNUTZUNGSENDE = "FALSCHES_NETZNUTZUNGSENDE"
#: BILANZIERTE_MENGE_FEHLT, Z37
BILANZIERTE_MENGE_FEHLT = "BILANZIERTE_MENGE_FEHLT"
#: BILANZIERTE_MENGE_FALSCH, Z38
BILANZIERTE_MENGE_FALSCH = "BILANZIERTE_MENGE_FALSCH"
#: NETZNUTZUNGSABRECHNUNG_FEHLT, Z39
NETZNUTZUNGSABRECHNUNG_FEHLT = "NETZNUTZUNGSABRECHNUNG_FEHLT"
#: REVERSE_CHARGE_ANWENDUNG_FEHLT_ODER_FEHLERHAFT, Z40
REVERSE_CHARGE_ANWENDUNG_FEHLT_ODER_FEHLERHAFT = "REVERSE_CHARGE_ANWENDUNG_FEHLT_ODER_FEHLERHAFT"
#: ALLOKATIONSLISTE_FEHLT, Z41
ALLOKATIONSLISTE_FEHLT = "ALLOKATIONSLISTE_FEHLT"
#: MEHR_MINDERMENGE_FALSCH, Z42
MEHR_MINDERMENGE_FALSCH = "MEHR_MINDERMENGE_FALSCH"
#: UNGUELTIGES_RECHNUNGSDATUM, Z43
UNGUELTIGES_RECHNUNGSDATUM = "UNGUELTIGES_RECHNUNGSDATUM"
#: ZEITINTERVALL_DER_BILANZIERTEN_MENGE_INKONSISTENT, Z44
ZEITINTERVALL_DER_BILANZIERTEN_MENGE_INKONSISTENT = "ZEITINTERVALL_DER_BILANZIERTEN_MENGE_INKONSISTENT"
#: RECHNUNGSEMPFAENGER_WIDERSPRICHT_DER_STEUERRECHTLICHEN_EINSCHAETZUNG_DES_RECHNUNGSSTELLERS, Z45
RECHNUNGSEMPFAENGER_WIDERSPRICHT_DER_STEUERRECHTLICHEN_EINSCHAETZUNG_DES_RECHNUNGSSTELLERS = (
"RECHNUNGSEMPFAENGER_WIDERSPRICHT_DER_STEUERRECHTLICHEN_EINSCHAETZUNG_DES_RECHNUNGSSTELLERS"
)
#: ANGEGEBENE_QUOTES_AN_MARKTLOKATION_NICHT_VORHANDEN, Z52
ANGEGEBENE_QUOTES_AN_MARKTLOKATION_NICHT_VORHANDEN = "ANGEGEBENE_QUOTES_AN_MARKTLOKATION_NICHT_VORHANDEN"
#: RECHNUNGSABWICKLUNG_NICHT_VEREINBART, Z53
RECHNUNGSABWICKLUNG_NICHT_VEREINBART = "RECHNUNGSABWICKLUNG_NICHT_VEREINBART"
#: COMDIS_WIRD_ABGELEHNT, Z63
COMDIS_WIRD_ABGELEHNT = "COMDIS_WIRD_ABGELEHNT"
9 changes: 9 additions & 0 deletions src/bo4e/enum/avistyp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""Contains enum class Avisposition"""
from bo4e.enum.strenum import StrEnum


class AvisTyp(StrEnum):
"""Gibt den Typ des Avis an. (REMADV BGM 1001)."""

ABGELEHNTE_FORDERUNG = "ABGELEHNTE_FORDERUNG" #: ABGELEHNTE_FORDERUNG
ZAHLUNGSAVIS = "ZAHLUNGSAVIS" #: ZAHLUNGSAVIS
1 change: 1 addition & 0 deletions src/bo4e/enum/botyp.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class BoTyp(StrEnum):
ANGEBOT = "ANGEBOT"
ANSPRECHPARTNER = "ANSPRECHPARTNER"
AUSSCHREIBUNG = "AUSSCHREIUNG"
AVIS = "AVIS"
BUENDELVERTRAG = "BUENDELVERTRAG"
ENERGIEMENGE = "ENERGIEMENGE"
FREMDKOSTEN = "FREMDKOSTEN"
Expand Down
55 changes: 55 additions & 0 deletions tests/test_abweichung.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# todo: cf. alias_generator=camelize in geschaeftsobjekte.py
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warum ist hier ein todo

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from typing import Any, Dict

import pytest

from bo4e.com.abweichung import Abweichung
from bo4e.enum.abweichungsgrund import Abweichungsgrund
from tests.serialization_helper import assert_serialization_roundtrip

#: create full example
example_abweichung = Abweichung(
abweichungsgrund=Abweichungsgrund.UNBEKANNTE_MARKTLOKATION_MESSLOKATION,
abweichungsgrund_bemerkung="sonst",
zugehoerige_rechnung="458011",
abschlagsrechnung="4580112",
abweichungsgrund_code="14",
abweichungsgrund_codeliste="G_0081",
)


class Test_Abweichung:
@pytest.mark.parametrize(
"abweichung, expected_json_dict",
[
pytest.param(
example_abweichung,
{
"abweichungsgrund": Abweichungsgrund.UNBEKANNTE_MARKTLOKATION_MESSLOKATION,
"abweichungsgrundBemerkung": "sonst",
"zugehoerigeRechnung": "458011",
"abschlagsrechnung": "4580112",
"abweichungsgrundCode": "14",
"abweichungsgrundCodeliste": "G_0081",
},
id="max param test",
),
pytest.param(
Abweichung(),
{
"abweichungsgrund": None,
"abweichungsgrundBemerkung": None,
"zugehoerigeRechnung": None,
"abschlagsrechnung": None,
"abweichungsgrundCode": None,
"abweichungsgrundCodeliste": None,
},
id="min param test",
),
],
)
def test_serialization_roundtrip(self, abweichung: Abweichung, expected_json_dict: Dict[str, Any]) -> None:
"""
Test de-/serialisation of Abweichung with minimal attributes.
"""
assert_serialization_roundtrip(abweichung, expected_json_dict)
53 changes: 53 additions & 0 deletions tests/test_abweichungsposition.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# todo: cf. alias_generator=camelize in geschaeftsobjekte.py
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same, wahy a todo?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from typing import Any, Dict

import pytest

from bo4e.com.abweichungsposition import Abweichungsposition
from tests.serialization_helper import assert_serialization_roundtrip

#: full example
example_abweichungsposition = Abweichungsposition(
abweichungsgrund_code="14",
abweichungsgrund_codeliste="G_0081",
abweichungsgrund_bemerkung="Umsatzsteuersatz",
zugehoerige_rechnung="458011",
zugehoerige_bestellung="foo",
)


class TestAbweichungsposition:
@pytest.mark.parametrize(
"abweichungsposition, expected_json_dict",
[
pytest.param(
example_abweichungsposition,
{
"abweichungsgrundCode": "14",
"abweichungsgrundCodeliste": "G_0081",
"abweichungsgrundBemerkung": "Umsatzsteuersatz",
"zugehoerigeRechnung": "458011",
"zugehoerigeBestellung": "foo",
},
id="max param test",
),
pytest.param(
Abweichungsposition(),
{
"abweichungsgrundCode": None,
"abweichungsgrundCodeliste": None,
"abweichungsgrundBemerkung": None,
"zugehoerigeRechnung": None,
"zugehoerigeBestellung": None,
},
id="min param test",
),
],
)
def test_serialization_roundtrip(
self, abweichungsposition: Abweichungsposition, expected_json_dict: Dict[str, Any]
) -> None:
"""
Test de-/serialisation of Abweichungsposition with minimal attributes.
"""
assert_serialization_roundtrip(abweichungsposition, expected_json_dict)
Loading