-
Notifications
You must be signed in to change notification settings - Fork 5
Implement COM Rechnungsposition #305
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
258621d
Implement COM Rechnungsposition
hf-kklein ffd33bc
typoe check ignore
hf-kklein f0579ef
Merge branch 'master' into implement_com_rechnungsposition
hf-kklein e300233
remove dead code
hf-kklein d42c519
Merge branch 'master' into implement_com_rechnungsposition
hf-kklein e8a49e3
Merge branch 'master' into implement_com_rechnungsposition
hf-kklein ee6067c
black .
hf-kklein bd41a86
🔥remove duplicate code
hf-kklein 61fb0f3
add artikel id to rechnungsposition
hf-kklein 98f13a7
🔥 remove more code
hf-kklein File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| """ | ||
| Contains Rechnungsposition class and corresponding marshmallow schema for de-/serialization | ||
| """ | ||
| from datetime import datetime | ||
| from typing import Optional | ||
|
|
||
| import attr | ||
| from marshmallow import fields | ||
| from marshmallow_enum import EnumField # type:ignore[import] | ||
|
|
||
| from bo4e.com.betrag import Betrag, BetragSchema | ||
| from bo4e.com.com import COM, COMSchema | ||
| from bo4e.com.menge import Menge, MengeSchema | ||
| from bo4e.com.preis import Preis, PreisSchema | ||
| from bo4e.com.steuerbetrag import Steuerbetrag, SteuerbetragSchema | ||
| from bo4e.enum.artikelid import ArtikelId | ||
| from bo4e.enum.bdewartikelnummer import BDEWArtikelnummer | ||
| from bo4e.enum.zeiteinheit import Zeiteinheit | ||
| from bo4e.validators import check_bis_is_later_than_von, validate_marktlokations_id | ||
|
|
||
|
|
||
| # pylint: disable=too-few-public-methods, too-many-instance-attributes | ||
| @attr.s(auto_attribs=True, kw_only=True) | ||
| class Rechnungsposition(COM): | ||
| """ | ||
| Über Rechnungspositionen werden Rechnungen strukturiert. | ||
| In einem Rechnungsteil wird jeweils eine in sich geschlossene Leistung abgerechnet. | ||
| """ | ||
|
|
||
| # required attributes | ||
| #: Fortlaufende Nummer für die Rechnungsposition | ||
| positionsnummer: int = attr.ib(validator=attr.validators.instance_of(int)) | ||
|
|
||
| lieferung_von: datetime = attr.ib( | ||
| validator=[attr.validators.instance_of(datetime), check_bis_is_later_than_von] | ||
| ) #: Start der Lieferung für die abgerechnete Leistung (inklusiv) | ||
| lieferung_bis: datetime = attr.ib( | ||
| validator=[attr.validators.instance_of(datetime), check_bis_is_later_than_von] | ||
| ) #: Ende der Lieferung für die abgerechnete Leistung (exklusiv) | ||
|
|
||
| #: Bezeichung für die abgerechnete Position | ||
| positionstext: str = attr.ib(validator=attr.validators.instance_of(str)) | ||
|
|
||
| #: Die abgerechnete Menge mit Einheit | ||
| positions_menge: Menge = attr.ib(validator=attr.validators.instance_of(Menge)) | ||
| #: Der Preis für eine Einheit der energetischen Menge | ||
| einzelpreis: Preis = attr.ib(validator=attr.validators.instance_of(Preis)) | ||
|
|
||
| teilsumme_netto: Betrag = attr.ib(validator=attr.validators.instance_of(Betrag)) | ||
| """ | ||
| 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 | ||
|
|
||
| #: Auf die Position entfallende Steuer, bestehend aus Steuersatz und Betrag | ||
| teilsumme_steuer: Steuerbetrag = attr.ib(validator=attr.validators.instance_of(Steuerbetrag)) | ||
|
|
||
| # optional attributes | ||
| #: Falls sich der Preis auf eine Zeit bezieht, steht hier die Einheit | ||
| zeiteinheit: Optional[Zeiteinheit] = attr.ib( | ||
| default=None, validator=attr.validators.optional(attr.validators.instance_of(Zeiteinheit)) | ||
| ) | ||
|
|
||
| #: Kennzeichnung der Rechnungsposition mit der Standard-Artikelnummer des BDEW | ||
| artikelnummer: Optional[BDEWArtikelnummer] = attr.ib( | ||
| default=None, validator=attr.validators.optional(attr.validators.instance_of(BDEWArtikelnummer)) | ||
| ) | ||
| #: Marktlokation, die zu dieser Position gehört | ||
| lokations_id: Optional[str] = attr.ib(default=None, validator=attr.validators.optional(validate_marktlokations_id)) | ||
|
|
||
hf-aschloegl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| zeitbezogene_menge: Optional[Menge] = attr.ib( | ||
| default=None, validator=attr.validators.optional(attr.validators.instance_of(Menge)) | ||
| ) | ||
| """ | ||
| Eine auf die Zeiteinheit bezogene Untermenge. | ||
| Z.B. bei einem Jahrespreis, 3 Monate oder 146 Tage. | ||
| Basierend darauf wird der Preis aufgeteilt. | ||
| """ | ||
| #: Nettobetrag für den Rabatt dieser Position | ||
| teilrabatt_netto: Optional[Betrag] = attr.ib( | ||
| default=None, validator=attr.validators.optional(attr.validators.instance_of(Betrag)) | ||
| ) | ||
|
|
||
| artikel_id: Optional[ArtikelId] = attr.ib( | ||
| validator=attr.validators.optional(attr.validators.instance_of(ArtikelId)), default=None | ||
| ) #: Standardisierte vom BDEW herausgegebene Liste, welche im Strommarkt die BDEW-Artikelnummer ablöst | ||
|
|
||
| def _get_inclusive_start(self) -> datetime: | ||
| """return the inclusive start (used in the validator)""" | ||
| return self.lieferung_von | ||
|
|
||
| def _get_exclusive_end(self) -> datetime: | ||
| """return the exclusive end (used in the validator)""" | ||
| return self.lieferung_bis | ||
|
|
||
|
|
||
| class RechnungspositionSchema(COMSchema): | ||
| """ | ||
| Schema for de-/serialization of RechnungspositionSchema | ||
| """ | ||
|
|
||
| class_name = Rechnungsposition | ||
|
|
||
| # required attributes | ||
| positionsnummer = fields.Integer() | ||
| lieferung_von = fields.DateTime() | ||
| lieferung_bis = fields.DateTime() | ||
| positionstext = fields.String() | ||
| positions_menge = fields.Nested(MengeSchema) | ||
| einzelpreis = fields.Nested(PreisSchema) | ||
| teilsumme_netto = fields.Nested(BetragSchema) | ||
| teilsumme_steuer = fields.Nested(SteuerbetragSchema) | ||
|
|
||
| # optional attributes | ||
| zeiteinheit = EnumField(Zeiteinheit, load_default=None) | ||
| artikelnummer = EnumField(BDEWArtikelnummer, load_default=None) | ||
| lokations_id = fields.String(load_default=None) | ||
| zeitbezogene_menge = fields.Nested(MengeSchema, load_default=None) | ||
| teilrabatt_netto = fields.Nested(BetragSchema, load_default=None) | ||
| artikel_id = EnumField(ArtikelId, load_default=None) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| from datetime import datetime, timezone | ||
|
|
||
| import pytest # type:ignore[import] | ||
|
|
||
| from bo4e.com.rechnungsposition import Rechnungsposition, RechnungspositionSchema | ||
| from bo4e.enum.artikelid import ArtikelId | ||
| from bo4e.enum.bdewartikelnummer import BDEWArtikelnummer | ||
| from bo4e.enum.zeiteinheit import Zeiteinheit | ||
| from tests.serialization_helper import assert_serialization_roundtrip # type:ignore[import] | ||
| from tests.test_betrag import example_betrag # type:ignore[import] | ||
| from tests.test_menge import example_menge # type:ignore[import] | ||
| from tests.test_preis import example_preis # type:ignore[import] | ||
| from tests.test_steuerbetrag import example_steuerbetrag # type:ignore[import] | ||
|
|
||
|
|
||
| class TestRechnungsposition: | ||
| @pytest.mark.parametrize( | ||
| "rechnungsposition", | ||
| [ | ||
| pytest.param( | ||
| Rechnungsposition( | ||
| positionsnummer=1, | ||
| lieferung_von=datetime(2021, 3, 15, tzinfo=timezone.utc), | ||
| lieferung_bis=datetime(2022, 3, 15, tzinfo=timezone.utc), | ||
| positionstext="Besonders wertvolle Rechnungsposition", | ||
| zeiteinheit=Zeiteinheit.JAHR, | ||
| artikelnummer=BDEWArtikelnummer.AUSGLEICHSENERGIE_UNTERDECKUNG, | ||
| lokations_id="51238696781", | ||
| positions_menge=example_menge, | ||
| zeitbezogene_menge=example_menge, | ||
| einzelpreis=example_preis, | ||
| teilsumme_netto=example_betrag, | ||
| teilrabatt_netto=example_betrag, | ||
| teilsumme_steuer=example_steuerbetrag, | ||
| artikel_id=ArtikelId.ARTIKEL_2017004, | ||
| ), | ||
| id="maximal attributes", | ||
| ) | ||
| ], | ||
| ) | ||
| def test_serialization_roundtrip(self, rechnungsposition): | ||
| """ | ||
| Test de-/serialisation | ||
| """ | ||
| assert_serialization_roundtrip(rechnungsposition, RechnungspositionSchema()) | ||
|
|
||
| def test_missing_required_attribute(self): | ||
| with pytest.raises(TypeError) as excinfo: | ||
| _ = Rechnungsposition() | ||
| assert "missing 8 required" in str(excinfo.value) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.