From 7e3fcdca472d93c0d7971108f45bd8ddcd1a89ff Mon Sep 17 00:00:00 2001 From: Freddy <100769900+FreddyFox892@users.noreply.github.com> Date: Fri, 15 Sep 2023 12:58:39 +0200 Subject: [PATCH] Add COMs (#5) * Ordner struktur * Add enums * 1 * 1 * Add enums * add COM * add docstring * Update requirements.in Co-authored-by: konstantin * Update src/ibims/com/zaehlpunkt.py Co-authored-by: konstantin * Update src/ibims/com/vertragskonto.py Co-authored-by: konstantin * Update src/ibims/com/sepa_info.py Co-authored-by: konstantin * Update src/ibims/com/vertragskonto.py Co-authored-by: konstantin * Update src/ibims/com/concessionfee.py Co-authored-by: konstantin --------- Co-authored-by: konstantin --- src/ibims/com/__init__.py | 14 ++++ src/ibims/com/adresse.py | 15 ++++ src/ibims/com/bankverbindung.py | 87 +++++++++++++++++++++++ src/ibims/com/concessionfee.py | 28 ++++++++ src/ibims/com/preisgarantie.py | 18 +++++ src/ibims/com/preisposition.py | 17 +++++ src/ibims/com/rechnungsposition.py | 18 +++++ src/ibims/com/sepa_info.py | 38 ++++++++++ src/ibims/com/steuerbetrag.py | 17 +++++ src/ibims/com/verbrauch.py | 107 +++++++++++++++++++++++++++++ src/ibims/com/vertragskonto.py | 97 ++++++++++++++++++++++++++ src/ibims/com/zaehlpunkt.py | 23 +++++++ src/ibims/com/zaehlwerk.py | 39 +++++++++++ src/ibims/meta/__init__.py | 15 ++++ 14 files changed, 533 insertions(+) create mode 100644 src/ibims/com/__init__.py create mode 100644 src/ibims/com/adresse.py create mode 100644 src/ibims/com/bankverbindung.py create mode 100644 src/ibims/com/concessionfee.py create mode 100644 src/ibims/com/preisgarantie.py create mode 100644 src/ibims/com/preisposition.py create mode 100644 src/ibims/com/rechnungsposition.py create mode 100644 src/ibims/com/sepa_info.py create mode 100644 src/ibims/com/steuerbetrag.py create mode 100644 src/ibims/com/verbrauch.py create mode 100644 src/ibims/com/vertragskonto.py create mode 100644 src/ibims/com/zaehlpunkt.py create mode 100644 src/ibims/com/zaehlwerk.py create mode 100644 src/ibims/meta/__init__.py diff --git a/src/ibims/com/__init__.py b/src/ibims/com/__init__.py new file mode 100644 index 00000000..84101f2b --- /dev/null +++ b/src/ibims/com/__init__.py @@ -0,0 +1,14 @@ +""" +Bundles extended and new COMs +""" +from ibims.com.adresse import AdresseErweitert +from ibims.com.bankverbindung import Bankverbindung +from ibims.com.concessionfee import ConcessionFee +from ibims.com.preisgarantie import PreisgarantieErweitert +from ibims.com.preisposition import PreispositionErweitert +from ibims.com.rechnungsposition import RechnungspositionErweitert +from ibims.com.sepa_info import SepaInfo +from ibims.com.steuerbetrag import SteuerbetragErweitert +from ibims.com.verbrauch import VerbrauchErweitert +from ibims.com.zaehlpunkt import Zaehlpunkt +from ibims.com.zaehlwerk import ZaehlwerkErweitert diff --git a/src/ibims/com/adresse.py b/src/ibims/com/adresse.py new file mode 100644 index 00000000..e0d3e745 --- /dev/null +++ b/src/ibims/com/adresse.py @@ -0,0 +1,15 @@ +""" +extension for the official bo4e adresse +""" + +from typing import Optional + +from bo4e.com.adresse import Adresse + + +class AdresseErweitert(Adresse): + """ + Extend Adresse with attribute ortsteil + """ + + ortsteil: Optional[str] = None diff --git a/src/ibims/com/bankverbindung.py b/src/ibims/com/bankverbindung.py new file mode 100644 index 00000000..25d939bf --- /dev/null +++ b/src/ibims/com/bankverbindung.py @@ -0,0 +1,87 @@ +""" +New COM to hold information about banking data +""" +from datetime import datetime +from typing import Optional + +from bo4e.com.com import COM +from pydantic import constr, field_validator + +from ibims.com import SepaInfo + + +class Bankverbindung(COM): + """ + This component contains bank connection information. + """ + + # required attributes + + iban: Optional[constr(min_length=15, max_length=34)] = None # type:ignore[valid-type] + """ + The IBAN (International Bank Account Number) is structured as follows: + # 2-digit: country code + # 2-digit: check digits + # up to 30-digit: account identification + + """ + # international variiert die Länge, länge(DE) = 22 + + bic: Optional[str] = None + """ + The BIC (Business Identifier Code) consists 8 or 11 alphanumeric characters, structured as follows: + # 4-digit: bank code + # 2-digit: country code + # 2-digit: location code + # 3-digit: branch office (optional) + """ + + # optional attributes + gueltig_seit: Optional[datetime] = None + """ + Inclusive date from which on the account information is valid + """ + gueltig_bis: Optional[datetime] = None + """ + Inclusive date till which on the account information is valid + """ + bankname: Optional[str] = None #: Name der Bank, z.b. 'Sparkasse Bremen' + """ + Consists the name of the Bank. + """ + sepa_info: Optional[SepaInfo] = None #: Informationen über das SEPA-Mandant + """ + contains an object of the SepaInfo class that contains details about the sepa mandates. + """ + kontoinhaber: Optional[str] = None + """ + contains the name of the account holder in the format 'firstname lastname' + """ + ouid: int + """ + contains the ouid that is need for the paymentMeans in the Customer Loader + """ + + # pylint:disable=unused-argument, no-self-argument + @field_validator("iban") + @classmethod + def validate_iban_laenge(cls, iban: str) -> str: + """ + validate the length of the iban. + """ + return iban + # validierung auf ländercod und sonst nur zahlen noch bauen. + # vlaidierung der iban nach https://ibanvalidieren.de/verifikation.html noch einbauen + # bzw. https://de.wikipedia.org/wiki/Internationale_Bankkontonummer#Zusammensetzung international. + + @field_validator("bic") + @classmethod + def validate_bic_laenge(cls, bic: str) -> str: + """ + validate the length of the bic. + """ + if len(bic) == 8 or len(bic) == 11: + return bic + raise ValueError("bic has not 8 or 11 characters.") + + # validierung auf alphanumerisch noch bauen diff --git a/src/ibims/com/concessionfee.py b/src/ibims/com/concessionfee.py new file mode 100644 index 00000000..fc3d65bf --- /dev/null +++ b/src/ibims/com/concessionfee.py @@ -0,0 +1,28 @@ +""" +Custom bo4e object, that was created for a migration project +""" + +from datetime import datetime + +from bo4e.com.com import COM + + +class ConcessionFee(COM): + # https://github.com/Hochfrequenz/integrated-bo4e-migration-models/issues/7 + """ + The Concession Fee object was created during a migration project. + It contains attributes needed for metering mapping. + """ + + market_location_id: str + """market location id of powercloud db""" + group: str | None = None + """group of the concession fee (e.g. "TA")""" + obis: str + """obis code""" + active_from: datetime + """inclusive active_from""" + active_until: datetime | None = None + """exclusive active_until, None means it is still active""" + ka: str | None = None + """amount of concession fee""" diff --git a/src/ibims/com/preisgarantie.py b/src/ibims/com/preisgarantie.py new file mode 100644 index 00000000..482a9c65 --- /dev/null +++ b/src/ibims/com/preisgarantie.py @@ -0,0 +1,18 @@ +""" +A Preisgarantie with MetaData added +""" +from typing import Optional + +from bo4e.com.preisgarantie import Preisgarantie +from bo4e.enum.preisgarantietyp import Preisgarantietyp + +from ibims.meta import MetaDataMixin + + +class PreisgarantieErweitert(Preisgarantie, MetaDataMixin): + """ + eine Preisgarantie mit Metadaten + """ + + preisgarantietyp: Optional[Preisgarantietyp] = None # type:ignore[assignment] + """overrides the original preisgarantietyp with a nullable""" diff --git a/src/ibims/com/preisposition.py b/src/ibims/com/preisposition.py new file mode 100644 index 00000000..87264dd2 --- /dev/null +++ b/src/ibims/com/preisposition.py @@ -0,0 +1,17 @@ +""" +New COM to hold information about different variations and reasons for account locks +""" + +from bo4e.com.preisposition import Preisposition +from bo4e.enum.steuerkennzeichen import Steuerkennzeichen + + +class PreispositionErweitert(Preisposition): + """ + Extesion of the bo4e Preisposition with taxrate + """ + + steuersatz: Steuerkennzeichen + """ + The taxrate is given as a enum + """ diff --git a/src/ibims/com/rechnungsposition.py b/src/ibims/com/rechnungsposition.py new file mode 100644 index 00000000..0eee8592 --- /dev/null +++ b/src/ibims/com/rechnungsposition.py @@ -0,0 +1,18 @@ +""" +extension of the official BO4E Rechnungsposition +""" + +from typing import Optional + +from bo4e.com.rechnungsposition import Rechnungsposition + +from ibims.enum import BDEWArtikelnummerErweitert + + +class RechnungspositionErweitert(Rechnungsposition): + """ + This class extend the com Rechnungsposition by replacing the enum BDEWArtikelnummer + with BDEWArtikelNummerErweitert + """ + + artikelnummer: Optional[BDEWArtikelnummerErweitert] = None # type: ignore[assignment] diff --git a/src/ibims/com/sepa_info.py b/src/ibims/com/sepa_info.py new file mode 100644 index 00000000..6f6ff5b9 --- /dev/null +++ b/src/ibims/com/sepa_info.py @@ -0,0 +1,38 @@ +""" +New COM for holding information about sepa mandates +""" +from datetime import datetime +from typing import Optional + +from bo4e.com.com import COM + + +class SepaInfo(COM): + """ + This class includes details about the sepa mandates. + """ + + # required attributes + + sepa_id: str + """ + System internal id of the SEPA mandate. + """ + # muss noch überprüft werden wie es allgemeingültig umzusetzen ist. + + sepa_zahler: bool + """ + there may be sepa information regardless of whether it is used or not. this field + confirms the use case e.g. it is false if the customer pays for himself + """ + + # optional attributes + creditor_identifier: Optional[str] = None + """ + Creditor Identifier is a number that identify the creditor of the sepa transaction + """ + + gueltig_seit: Optional[datetime] = None + """ + Inklusiver Zeitpunkt ab dem das SEPA Mandat gültig ist. + """ diff --git a/src/ibims/com/steuerbetrag.py b/src/ibims/com/steuerbetrag.py new file mode 100644 index 00000000..1b66dc1c --- /dev/null +++ b/src/ibims/com/steuerbetrag.py @@ -0,0 +1,17 @@ +""" +Extension of the official steuerbetrag +""" +from decimal import Decimal +from typing import Optional + +from bo4e.com.steuerbetrag import Steuerbetrag + + +class SteuerbetragErweitert(Steuerbetrag): + """ + This class extends the com Steuerbetrag with the attribute steuerwert_vorausgezahlt, which can also be found in the + go implementation: + https://github.com/Hochfrequenz/go-bo4e/blob/8dda93f8eda51557bb355a93e94c379111f0242b/com/steuerbetrag.go + """ + + steuerwert_vorausgezahlt: Optional[Decimal] = None #: tax amount of the prepaid amounts diff --git a/src/ibims/com/verbrauch.py b/src/ibims/com/verbrauch.py new file mode 100644 index 00000000..9ee98c17 --- /dev/null +++ b/src/ibims/com/verbrauch.py @@ -0,0 +1,107 @@ +""" +Extend COM Verbrauch to hold various more information +""" + +from datetime import datetime +from decimal import Decimal +from typing import Optional + +from bo4e.com.verbrauch import Verbrauch +from bo4e.enum.strenum import StrEnum + +from ibims.enum import Messwertstatus + + +class AblesendeRolle(StrEnum): + """ + Eine (Markt)Rolle, die Verbräuche abliest. + """ + + VNB = "VNB" #: der verteilnetzbetreiber + ENDKUNDE = "ENDKUNDE" #: der Endkunde selbst + VORIGER_LIEFERANT = "VORIGER_LIEFERANT" #: der vorherige Lieferant hat einen Verbrauch mitgeteilt) + MSB = "MSB" #: der messstellenbetreiber + SYSTEM = "SYSTEM" #: wert ist eine vom system erstellte schätzung + + +class Ablesungsstatus(StrEnum): + """ + State of the reading + """ + + GUELTIG = "GUELTIG" + UNGUELTIG = "UNGUELTIG" + ABGERECHNET = "ABGERECHNET" + + +class VerbrauchErweitert(Verbrauch): + """ + enhance the consumption object + """ + + # see https://github.com/bo4e/BO4E-python/issues/443 + + # optional attributes + ablesegrund: Optional[str] = None + """ + Reason why the counter is read + """ + ablesebeschreibung: Optional[str] = None + """ + Details to the reading + """ + periodenverbrauch: Optional[Decimal] = None + + periodenverbrauch_ursprung: Optional[str] = None + + ableser: Optional[AblesendeRolle] = None + """ + from whom is thew reading coming from + """ + status: Optional[Ablesungsstatus] = None + """ + reading status + """ + energiegehalt_gas: Optional[Decimal] = None + + energiegehalt_gas_gueltig_von: Optional[datetime] = None + """ + valid from date for the calorific amount (inclusive start) + """ + energiegehalt_gas_gueltig_bis: Optional[datetime] = None + """ + valid to date for the calorific amount (exclusive end) + """ + + umwandlungsfaktor_gas: Optional[Decimal] = None + + umwandlungsfaktor_gas_gueltig_von: Optional[datetime] = None + """ + valid from date for the conversion factor (inclusive start) + """ + umwandlungsfaktor_gas_gueltig_bis: Optional[datetime] = None + """ + valid to date for the conversion factor (exclusive end) + """ + messwertstatus: Optional[Messwertstatus] = None + """ + the messwertstatus is the status of meter reading + """ + + def is_single_tariff(self) -> bool: + """ + Returns true if the consumption is single tariff ("Eintarif") + """ + return "1.8.0" in self.obis_kennzahl + + def is_high_tariff(self) -> bool: + """ + Returns true if the consumption is high tariff (HT, "Hochtarif") + """ + return "1.8.1" in self.obis_kennzahl + + def is_low_tariff(self) -> bool: + """ + Returns true if the consumption is low tariff (NT, "Niedertarif") + """ + return "1.8.2" in self.obis_kennzahl diff --git a/src/ibims/com/vertragskonto.py b/src/ibims/com/vertragskonto.py new file mode 100644 index 00000000..66aa3333 --- /dev/null +++ b/src/ibims/com/vertragskonto.py @@ -0,0 +1,97 @@ +""" +New COMs to model the MBA and CBA structures in the customer loader. +""" +from datetime import datetime + +from bo4e.bo.vertrag import Vertrag +from bo4e.com.adresse import Adresse +from bo4e.com.com import COM +from bo4e.enum.kontaktart import Kontaktart +from pydantic import field_validator +from pydantic_core.core_schema import FieldValidationInfo + + +class Vertragskonto(COM): + """ + Bundle common attributes of MBA and CBA. + """ + + # required attributes + + ouid: int + """ + This ID is unique in a defined scope. + In this case, it is easier to construct the ouid before the CustomerLoader mapping because our solution to + construct these ouids involve iteration over all MBAs and CBAs. This is easier in the mapping Powercloud -> BO4E. + """ + vertrags_adresse: Adresse + """ + Address related to the `Vertragskonto` which is only used to create a common mba for `Vertragskonto`s with the same + address. + """ + vertragskontonummer: str + """ + This `vertragskontonummer` is intended to be used for the field `name` in the billing account data + (Customer Loader). + """ + rechnungsstellung: Kontaktart + """ + preferred way of delivering the bill to the customer + """ + + +class VertragskontoCBA(Vertragskonto): + """ + Models a CBA (child billing account) which directly relates to a single contract. It contains information about + locks and billing dates. But in the first place, CBAs will be grouped together by the address in their contracts. + For each group of CBAs with a common address there will be created an MBA (master billing + account) to support that the invoices for the CBAs can be bundled into a single invoice for the MBA. + """ + + vertrag: Vertrag + """ + Contracts in the CBA account + """ + + erstellungsdatum: datetime + """ + Creation date of the CBA account + """ + + rechnungsdatum_start: datetime + """ + first billing date + """ + rechnungsdatum_naechstes: datetime + """ + next billing date + """ + + +class VertragskontoMBA(Vertragskonto): + """ + Models an MBA (master billing account). Its main purpose is to bundle CBAs together having the same address in + their related contracts. This feature supports a single invoice for all CBAs instead of several + invoices for each. + """ + + cbas: list[VertragskontoCBA] + + @field_validator("cbas") + @classmethod + def validate_cbas_addresses( + cls, value: list[VertragskontoCBA], info: FieldValidationInfo + ) -> list[VertragskontoCBA]: + """ + This validator ensures that the MBA and the related CBAs all have the same address. It also ensures that + the MBA has at least one related CBA. + """ + if len(value) == 0: + raise ValueError("The MBA must have at least one CBA") + for cba in value: + if cba.vertrags_adresse != info.data["vertrags_adresse"]: + raise ValueError( + f"The address of the cba with `vertragsnummer `{cba.vertrag.vertragsnummer} " + "mismatches the MBA's address." + ) + return value diff --git a/src/ibims/com/zaehlpunkt.py b/src/ibims/com/zaehlpunkt.py new file mode 100644 index 00000000..5b5b3d79 --- /dev/null +++ b/src/ibims/com/zaehlpunkt.py @@ -0,0 +1,23 @@ +""" +Custom bo4e object, that was created for a migration project +""" + +from decimal import Decimal +from typing import Optional + +from bo4e.com.com import COM +from bo4e.enum.mengeneinheit import Mengeneinheit + + +class Zaehlpunkt(COM): + """ + The zaehlpunkt object was created during a migration project. + It contains attributes needed for metering mapping. + """ + + periodenverbrauch_vorhersage: Decimal + einheit_vorhersage: Mengeneinheit = Mengeneinheit.KWH + zeitreihentyp: str = "Z21" + kunden_wert: Decimal | None + einheit_kunde: Optional[Mengeneinheit] = None + grundzustaendiger: bool = True diff --git a/src/ibims/com/zaehlwerk.py b/src/ibims/com/zaehlwerk.py new file mode 100644 index 00000000..96bbf8a0 --- /dev/null +++ b/src/ibims/com/zaehlwerk.py @@ -0,0 +1,39 @@ +""" +Extend COM Zählwerk to hold various more information +""" + +from datetime import datetime +from typing import Optional + +from bo4e.com.zaehlwerk import Zaehlwerk + +from ibims.enum import Abgabeart + + +class ZaehlwerkErweitert(Zaehlwerk): + """ + enhance the register object + """ + + # see https://github.com/bo4e/BO4E-python/issues/443 + + # optional attributes + vorkommastellen: int + """ + Integer places of the register + """ + nachkommastellen: int + """ + Decimal places of the register + """ + schwachlastfaehig: bool + + konzessionsabgaben_typ: Optional[Abgabeart] = None + + active_from: datetime + + active_until: Optional[datetime] = None + + description: Optional[str] = None + + verbrauchsart: Optional[str] = None diff --git a/src/ibims/meta/__init__.py b/src/ibims/meta/__init__.py new file mode 100644 index 00000000..b5636d83 --- /dev/null +++ b/src/ibims/meta/__init__.py @@ -0,0 +1,15 @@ +""" +additional meta information for all BOs or COMponents +""" +from datetime import datetime +from typing import Optional + +from pydantic import BaseModel + + +class MetaDataMixin(BaseModel): + """ + a mixin that adds metadata to business objects and/or components + """ + + creation_date: Optional[datetime] = None