Skip to content

Commit

Permalink
Add Zu- and Abschläge for Rechnungspositionen (#331)
Browse files Browse the repository at this point in the history
* Add Zu- and Abschläge for Rechnungspositionen

corresponding to
Hochfrequenz/BO4E-dotnet#572
Hochfrequenz/BO4E-dotnet#580
  • Loading branch information
hf-mrdachner authored Nov 15, 2024
1 parent aa2abc8 commit f3776b9
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 17 deletions.
39 changes: 23 additions & 16 deletions com/rechnungsposition.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,38 @@ package com
import (
"time"

"github.com/hochfrequenz/go-bo4e/enum/rechnungspositionsabschlag"
"github.com/hochfrequenz/go-bo4e/enum/rechnungspositionszuschlag"

"github.com/go-playground/validator/v10"
"github.com/hochfrequenz/go-bo4e/enum/bdewartikelnummer"
"github.com/hochfrequenz/go-bo4e/enum/waehrungscode"
"github.com/hochfrequenz/go-bo4e/enum/waehrungseinheit"
"github.com/hochfrequenz/go-bo4e/enum/zeiteinheit"
"github.com/shopspring/decimal"
)

// Rechnungsposition en sind Teil von Rechnung en. In einem Rechnungsteil wird jeweils eine in sich geschlossene LEISTUNG abgerechnet.
type Rechnungsposition struct {
Positionsnummer int `json:"positionsnummer,omitempty" validate:"required"` // Positionsnummer ist eine fortlaufende Nummer für die Rechnungsposition
LieferungVon time.Time `json:"lieferungVon,omitempty"` // LieferungVon ist ein _inklusiver_ Start der Lieferung für die abgerechnete LEISTUNG
LieferungBis time.Time `json:"lieferungBis,omitempty" validate:"omitempty,gtfield=LieferungVon"` // LieferungBis ist ein _exklusives_ Ende der Lieferung für die abgerechnete LEISTUNG
Positionstext string `json:"positionstext,omitempty" validate:"required"` // Positionstext ist eine Bezeichung für die abgerechnete Position.
Zeiteinheit zeiteinheit.Zeiteinheit `json:"zeiteinheit,omitempty"` // Zeiteinheit wird angegeben, falls sich der Preis auf eine Zeit bezieht, steht hier die Einheit, z.B. JAHR
Artikelnummer *bdewartikelnummer.BDEWArtikelnummer `json:"artikelnummer,omitempty"` // Artikelnummer ist eine Kennzeichnung der Rechnungsposition mit der Standard-Artikelnummer des BDEW
LokationsId string `json:"lokationsId,omitempty" validate:"omitempty,min=11,max=11,numeric"` // LokationsId ist die MarktlokationsId zu der diese Position gehört
PositionsMenge Menge `json:"positionsMenge,omitempty" validate:"required"` // PositionsMenge ist die abgerechnete Menge mit Einheit. Z.B. 4372 kWh
ZeitbezogeneMenge *Menge `json:"zeitbezogeneMenge,omitempty"` // ZeitbezogeneMenge ist eine optionale, auf die Zeiteinheit bezogene Untermenge. Z.B. bei einem Jahrespreis, 3 Monate oder 146 Tage. Basierend darauf wird der Preis aufgeteilt
Korrekturfaktor *float32 `json:"korrekturfaktor,omitempty" validate:"omitempty,min=-1,max=-1,numeric"` // Korrekturfaktor ist ein Faktor -1 der ggf. in Mehrmengen-Rechnungen vorkommt
Einzelpreis Preis `json:"einzelpreis,omitempty" validate:"required"` // Einzelpreis ist der Preis für eine Einheit der energetischen Menge
TeilsummeNetto Betrag `json:"teilsummeNetto,omitempty" validate:"required"` // TeilsummeNetto ist das Ergebnis der Multiplikation aus einzelpreis * positionsMenge * (Faktor aus zeitbezogeneMenge). Z.B. 12,60€ * 120 kW * 3/12 (für 3 Monate).
TeilsummeSteuer Steuerbetrag `json:"teilsummeSteuer,omitempty" validate:"required"` // TeilsummeSteuer ist der auf die Position entfallende Steuer, bestehend aus Steuersatz und Betrag
TeilrabattNetto *Betrag `json:"teilrabattNetto,omitempty"` // TeilrabattNetto ist der Rabatt für diese Position
ArtikelId *string `json:"artikelId,omitempty"` // Die ArtikelId ist Artikel-ID (zu verwenden seit 2022-10-01)
Ausfuehrungsdatum time.Time `json:"ausfuehrungsdatum,omitempty"` // Das Ausfuehrungsdatum leitet sich aus den Qualifier 203 Ausführungsdatum/-zeit ab
Positionsnummer int `json:"positionsnummer,omitempty" validate:"required"` // Positionsnummer ist eine fortlaufende Nummer für die Rechnungsposition
LieferungVon time.Time `json:"lieferungVon,omitempty"` // LieferungVon ist ein _inklusiver_ Start der Lieferung für die abgerechnete LEISTUNG
LieferungBis time.Time `json:"lieferungBis,omitempty" validate:"omitempty,gtfield=LieferungVon"` // LieferungBis ist ein _exklusives_ Ende der Lieferung für die abgerechnete LEISTUNG
Positionstext string `json:"positionstext,omitempty" validate:"required"` // Positionstext ist eine Bezeichung für die abgerechnete Position.
Zeiteinheit zeiteinheit.Zeiteinheit `json:"zeiteinheit,omitempty"` // Zeiteinheit wird angegeben, falls sich der Preis auf eine Zeit bezieht, steht hier die Einheit, z.B. JAHR
Artikelnummer *bdewartikelnummer.BDEWArtikelnummer `json:"artikelnummer,omitempty"` // Artikelnummer ist eine Kennzeichnung der Rechnungsposition mit der Standard-Artikelnummer des BDEW
LokationsId string `json:"lokationsId,omitempty" validate:"omitempty,min=11,max=11,numeric"` // LokationsId ist die MarktlokationsId zu der diese Position gehört
PositionsMenge Menge `json:"positionsMenge,omitempty" validate:"required"` // PositionsMenge ist die abgerechnete Menge mit Einheit. Z.B. 4372 kWh
ZeitbezogeneMenge *Menge `json:"zeitbezogeneMenge,omitempty"` // ZeitbezogeneMenge ist eine optionale, auf die Zeiteinheit bezogene Untermenge. Z.B. bei einem Jahrespreis, 3 Monate oder 146 Tage. Basierend darauf wird der Preis aufgeteilt
Korrekturfaktor *float32 `json:"korrekturfaktor,omitempty" validate:"omitempty,min=-1,max=-1,numeric"` // Korrekturfaktor ist ein Faktor -1 der ggf. in Mehrmengen-Rechnungen vorkommt
Einzelpreis Preis `json:"einzelpreis,omitempty" validate:"required"` // Einzelpreis ist der Preis für eine Einheit der energetischen Menge
TeilsummeNetto Betrag `json:"teilsummeNetto,omitempty" validate:"required"` // TeilsummeNetto ist das Ergebnis der Multiplikation aus einzelpreis * positionsMenge * (Faktor aus zeitbezogeneMenge). Z.B. 12,60€ * 120 kW * 3/12 (für 3 Monate).
TeilsummeSteuer Steuerbetrag `json:"teilsummeSteuer,omitempty" validate:"required"` // TeilsummeSteuer ist der auf die Position entfallende Steuer, bestehend aus Steuersatz und Betrag
TeilrabattNetto *Betrag `json:"teilrabattNetto,omitempty"` // TeilrabattNetto ist der Rabatt für diese Position
ArtikelId *string `json:"artikelId,omitempty"` // Die ArtikelId ist Artikel-ID (zu verwenden seit 2022-10-01)
Ausfuehrungsdatum time.Time `json:"ausfuehrungsdatum,omitempty"` // Das Ausfuehrungsdatum leitet sich aus den Qualifier 203 Ausführungsdatum/-zeit ab
Zuschlag *rechnungspositionszuschlag.RechnungspositionsZuschlag `json:"zuschlag,omitempty"` // Art des Zuschlag auf die Position
Abschlag *rechnungspositionsabschlag.RechnungspositionsAbschlag `json:"abschlag,omitempty"` // Art des Abschlag auf die Position
Gesamtzuabschlagsbetrag *decimal.Decimal `json:"gesamtzuabschlagsbetrag,omitempty"` // Der Betrag des Zu/Abschlages
}

// RechnungspositionStructLevelValidation does a cross check on a Rechnungsposition object
Expand Down
7 changes: 6 additions & 1 deletion com/rechnungsposition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"testing"
"time"

"github.com/hochfrequenz/go-bo4e/enum/rechnungspositionszuschlag"

"github.com/hochfrequenz/go-bo4e/internal"

"github.com/corbym/gocrest/is"
Expand Down Expand Up @@ -59,11 +61,14 @@ func Test_Rechnungsposition_Deserialization(t *testing.T) {
Steuerwert: newDecimalFromString("7"),
Waehrung: waehrungscode.EUR,
},
TeilrabattNetto: nil,
TeilrabattNetto: nil,
Zuschlag: internal.Ptr(rechnungspositionszuschlag.ANPASSUNG_PAUSCHALE_NETZENTGELTREDUZIERUNG),
Gesamtzuabschlagsbetrag: internal.Ptr(newDecimalFromString("13.5")),
}
serializedRechnungsposition, err := json.Marshal(rechnungsposition)
jsonString := string(serializedRechnungsposition)
then.AssertThat(t, strings.Contains(jsonString, "ABGABE_KWKG"), is.True()) // stringified enum
then.AssertThat(t, strings.Contains(jsonString, "zuschlag"), is.True())
then.AssertThat(t, err, is.Nil())
then.AssertThat(t, serializedRechnungsposition, is.Not(is.NilArray[byte]()))
var deserializedRechnungsposition com.Rechnungsposition
Expand Down
13 changes: 13 additions & 0 deletions enum/rechnungspositionsabschlag/rechnungspositionsabschlag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package rechnungspositionsabschlag

// Zeitreihentyp sind die Codes der Summenzeitreihentypen
// Note that this enum is not official BO4E standard (yet)!
//
//go:generate stringer --type RechnungspositionsAbschlag
//go:generate jsonenums --type RechnungspositionsAbschlag
type RechnungspositionsAbschlag int

const (
GEMEINDERABATT RechnungspositionsAbschlag = iota + 1 // Gemeinderabatt nach Konzessionsabgabenverordnung
ABSCHLAG_ANPASSUNG // Anpassung nach § 19, Absatz 2 Stromnetzentgeltverordnung
)

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Code auto-generated; DO NOT EDIT.
package rechnungspositionsabschlag

import (
"database/sql/driver"
"fmt"

"github.com/hochfrequenz/go-bo4e/internal/typemapper"
)

// Value returns the string representation of r or an error, if no string representation exists.
// It implements the sql.Valuer interface to be useable by sql drivers when storing enums.
func (r RechnungspositionsAbschlag) Value() (driver.Value, error) {
s, ok := _RechnungspositionsAbschlagValueToName[r]
if ok {
return s, nil
}
return nil, fmt.Errorf("could not stringify %s", r)
}

// Scan sets r to the enum value represented by src.
// It implements the sql.Scanner interface to be useable by sql drivers when reading from database.
func (r *RechnungspositionsAbschlag) Scan(src interface{}) error {
f := typemapper.TypeFromValue[RechnungspositionsAbschlag]
v, err := f(src, _RechnungspositionsAbschlagNameToValue)
if err != nil {
return err
}

*r = v
return nil
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions enum/rechnungspositionszuschlag/rechnungspositionszuschlag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package rechnungspositionszuschlag

// Zeitreihentyp sind die Codes der Summenzeitreihentypen
// Note that this enum is not official BO4E standard (yet)!
//
//go:generate stringer --type RechnungspositionsZuschlag
//go:generate jsonenums --type RechnungspositionsZuschlag
type RechnungspositionsZuschlag int

const (
UMSPANNUNGSZUSCHLAG RechnungspositionsZuschlag = iota + 1 //
ALLEIN_GENUTZTE_BETRIEBSMITTEL
ZUSCHLAG_ANPASSUNG
ANPASSUNG_PAUSCHALE_NETZENTGELTREDUZIERUNG
)

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Code auto-generated; DO NOT EDIT.
package rechnungspositionszuschlag

import (
"database/sql/driver"
"fmt"

"github.com/hochfrequenz/go-bo4e/internal/typemapper"
)

// Value returns the string representation of r or an error, if no string representation exists.
// It implements the sql.Valuer interface to be useable by sql drivers when storing enums.
func (r RechnungspositionsZuschlag) Value() (driver.Value, error) {
s, ok := _RechnungspositionsZuschlagValueToName[r]
if ok {
return s, nil
}
return nil, fmt.Errorf("could not stringify %s", r)
}

// Scan sets r to the enum value represented by src.
// It implements the sql.Scanner interface to be useable by sql drivers when reading from database.
func (r *RechnungspositionsZuschlag) Scan(src interface{}) error {
f := typemapper.TypeFromValue[RechnungspositionsZuschlag]
v, err := f(src, _RechnungspositionsZuschlagNameToValue)
if err != nil {
return err
}

*r = v
return nil
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit f3776b9

Please sign in to comment.