From 6c58bb6108f2f4522882aa742f202e378ae5b7c9 Mon Sep 17 00:00:00 2001 From: Andreas Luxbauer Date: Mon, 29 Dec 2025 14:55:05 +0100 Subject: [PATCH] CBAM --- .../cATEZ_Greenpulse_CBAM_CostCalculation.vb | 406 ++++++++++-------- 1 file changed, 222 insertions(+), 184 deletions(-) diff --git a/VERAG_PROG_ALLGEMEIN/Schnittstellen/ATEZ/GREENPULSE/cATEZ_Greenpulse_CBAM_CostCalculation.vb b/VERAG_PROG_ALLGEMEIN/Schnittstellen/ATEZ/GREENPULSE/cATEZ_Greenpulse_CBAM_CostCalculation.vb index db7fdca1..f1edf616 100644 --- a/VERAG_PROG_ALLGEMEIN/Schnittstellen/ATEZ/GREENPULSE/cATEZ_Greenpulse_CBAM_CostCalculation.vb +++ b/VERAG_PROG_ALLGEMEIN/Schnittstellen/ATEZ/GREENPULSE/cATEZ_Greenpulse_CBAM_CostCalculation.vb @@ -1,7 +1,7 @@ ' ============================================================================ ' GreenPulse – CBAM Carbon Cost API ' Single-file VB.NET implementation using Chilkat -' Adapted to latest Postman Collection (country_code, benchmark_value) +' Adapted to Postman Collection 29.12.2025 ' ============================================================================ Imports Chilkat @@ -26,41 +26,62 @@ Public Class cATEZ_Greenpulse_CBAM_CostCalculation ' ------------------------------------------------------------------------ Public Function GetCnCodes() As cCBAM_CnCode_Response - Dim url As String = _baseUrl & "/carbon-cost/cn-codes" - Dim resp As HttpResponse = _http.QuickGetObj(url) - - If resp Is Nothing Then - Throw New Exception(_http.LastErrorText) - End If + Dim resp = _http.QuickGetObj(_baseUrl & "/carbon-cost/cn-codes") + If resp Is Nothing Then Throw New Exception(_http.LastErrorText) Dim json As New JsonObject() json.Load(resp.BodyStr) - Dim result As New cCBAM_CnCode_Response With { - .success = json.BoolOf("success") - } + Dim r As New cCBAM_CnCode_Response With {.success = json.BoolOf("success")} - If result.success Then - result.data = New List(Of cCBAM_CnCode) - Dim arr = json.ArrayOf("data") - - For i As Integer = 0 To arr.Size - 1 - Dim o = arr.ObjectAt(i) - result.data.Add(New cCBAM_CnCode With { + If r.success Then + r.data = New List(Of cCBAM_CnCode) + Dim a = json.ArrayOf("data") + For i = 0 To a.Size - 1 + Dim o = a.ObjectAt(i) + r.data.Add(New cCBAM_CnCode With { .cn_code = o.StringOf("cn_code"), .cn_description = o.StringOf("cn_description") }) Next Else - Dim e = json.ObjectOf("error") - result.error = New cCBAM_Error With { - .code = e.StringOf("code"), - .message = e.StringOf("message"), - .statusCode = e.IntOf("statusCode") - } + r.error = ParseError(json) End If - Return result + Return r + End Function + + ' ------------------------------------------------------------------------ + ' GET /carbon-cost/cn-code-defaults + ' ------------------------------------------------------------------------ + Public Function GetCnCodeDefaults( + cn_code As String, + country_code As String, + Optional year As Integer? = Nothing + ) As cCBAM_CnCodeDefaults_Response + + Dim url = $"{_baseUrl}/carbon-cost/cn-code-defaults?cn_code={cn_code}&country_code={country_code}" + If year.HasValue Then url &= $"&year={year.Value}" + + Dim resp = _http.QuickGetObj(url) + If resp Is Nothing Then Throw New Exception(_http.LastErrorText) + + Dim json As New JsonObject() + json.Load(resp.BodyStr) + + Dim r As New cCBAM_CnCodeDefaults_Response With {.success = json.BoolOf("success")} + + If r.success Then + Dim d = json.ObjectOf("data") + r.data = New cCBAM_CnCodeDefaults With { + .default_emission = GetDec(d, "default_emission"), + .benchmark = GetNullableDec(d, "benchmark") + } + Else + r.error = ParseError(json) + End If + + Return r End Function ' ------------------------------------------------------------------------ @@ -71,76 +92,21 @@ Public Class cATEZ_Greenpulse_CBAM_CostCalculation VERAG_PROG_ALLGEMEIN.cChilkat_Helper.UnlockCilkat() - Dim url As String = _baseUrl & "/carbon-cost/calculate" - Dim resp As HttpResponse = _http.PostJson2(url, "application/json", req.ToJson()) + Dim resp = _http.PostJson2( + _baseUrl & "/carbon-cost/calculate", + "application/json", + req.ToJson() + ) - If resp Is Nothing Then - Throw New Exception(_http.LastErrorText) - End If - - Return ParseResponse(resp.BodyStr) + If resp Is Nothing Then Throw New Exception(_http.LastErrorText) + Return ParseCalcResponse(resp.BodyStr) End Function ' ------------------------------------------------------------------------ - ' JSON → Object mapping + ' calcCBAM – Convenience Wrapper ' ------------------------------------------------------------------------ - Private Function ParseResponse(jsonStr As String) _ - As cCBAM_CostCalculation_Response - - Dim json As New JsonObject() - json.Load(jsonStr) - - Dim result As New cCBAM_CostCalculation_Response With { - .success = json.BoolOf("success") - } - - If result.success Then - Dim d = json.ObjectOf("data") - - result.data = New cCBAM_CostCalculation_Data With { - .cost = GetDec(d, "cost"), - .cbam_emission = GetDec(d, "cbam_emission"), - .benchmark = GetDec(d, "benchmark"), - .phase_factor = GetDec(d, "phase_factor"), - .carbon_price = GetDec(d, "carbon_price"), - .currency = d.StringOf("currency") - } - - Dim det = d.ObjectOf("calculation_details") - result.data.calculation_details = New cCBAM_CostCalculation_Details With { - .cbam_emission = GetDec(det, "cbam_emission"), - .benchmark = GetDec(det, "benchmark"), - .adjusted_benchmark = GetDec(det, "adjusted_benchmark"), - .emission_difference = GetDec(det, "emission_difference"), - .weight = GetDec(det, "weight"), - .carbon_price = GetDec(det, "carbon_price") - } - Else - Dim e = json.ObjectOf("error") - result.error = New cCBAM_Error With { - .code = e.StringOf("code"), - .message = e.StringOf("message"), - .statusCode = e.IntOf("statusCode") - } - End If - - Return result - End Function - ' ------------------------------------------------------------------------ - ' Robust Decimal Parsing (Chilkat-safe) - ' ------------------------------------------------------------------------ - Private Shared Function GetDec(obj As JsonObject, name As String) As Decimal - Dim s As String = obj.StringOf(name) - If String.IsNullOrWhiteSpace(s) Then Return 0D - s = s.Replace(",", ".") - Dim v As Decimal - If Decimal.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, v) Then Return v - Return 0D - End Function - - ' ------------------------------------------------------------------------ - ' Convenience Wrapper für einfache CBAM-Kalkulation + ' calcCBAM – mit automatischem Fallback auf CN-Code Defaults ' ------------------------------------------------------------------------ Public Shared Function calcCBAM( cn_code As String, @@ -152,32 +118,23 @@ Public Class cATEZ_Greenpulse_CBAM_CostCalculation ) As String ' ------------------------------------------------------------ - ' Validierung (grundlegend, API macht Rest) + ' Basis-Validierung ' ------------------------------------------------------------ - If String.IsNullOrWhiteSpace(cn_code) OrElse cn_code.Length <> 8 OrElse Not IsNumeric(cn_code) Then - Return "Fehler: ungültiger CN-Code (muss 8-stellig numerisch sein)" + If cn_code Is Nothing OrElse Not IsNumeric(cn_code) _ + OrElse Not {4, 6, 8}.Contains(cn_code.Length) Then + Return "Fehler: CN-Code muss 4-, 6- oder 8-stellig numerisch sein" End If If weight Is Nothing OrElse Not IsNumeric(weight) OrElse CDbl(weight) <= 0 Then - Return "Fehler: ungültiges Gewicht (muss > 0 sein)" + Return "Fehler: Gewicht muss > 0 sein" End If - If String.IsNullOrWhiteSpace(country_code) OrElse country_code.Length <> 2 Then - Return "Fehler: country_code muss ein ISO-2 Code sein (z.B. TR)" + If country_code Is Nothing OrElse country_code.Length <> 2 Then + Return "Fehler: country_code muss ISO-2 sein" End If - If see_total <> "" And (see_total IsNot Nothing AndAlso Not IsNumeric(see_total)) Then - Return "Fehler: ungültige echte Emissionen (see_total)" - End If - - If year IsNot Nothing Then - If Not IsNumeric(year) OrElse CInt(year) < 2020 OrElse CInt(year) > 2100 Then - Return "Fehler: ungültiges Jahr" - End If - End If - - If benchmark_value <> "" And (benchmark_value IsNot Nothing AndAlso Not IsNumeric(benchmark_value)) Then - Return "Fehler: ungültiger Benchmark-Wert" + If year IsNot Nothing AndAlso Not IsNumeric(year) Then + Return "Fehler: ungültiges Jahr" End If ' ------------------------------------------------------------ @@ -185,6 +142,42 @@ Public Class cATEZ_Greenpulse_CBAM_CostCalculation ' ------------------------------------------------------------ Dim api As New cATEZ_Greenpulse_CBAM_CostCalculation() + ' ------------------------------------------------------------ + ' Prüfen, ob Defaults benötigt werden + ' ------------------------------------------------------------ + Dim needDefaults As Boolean = False + + If see_total Is Nothing OrElse Not IsNumeric(see_total) OrElse CDbl(see_total) < 0 Then + needDefaults = True + End If + + If benchmark_value Is Nothing OrElse Not IsNumeric(benchmark_value) OrElse CDbl(benchmark_value) < 0 Then + needDefaults = True + End If + + Dim defaultEmission As Decimal? = Nothing + Dim defaultBenchmark As Decimal? = Nothing + + If needDefaults Then + Try + Dim defResp = api.GetCnCodeDefaults( + cn_code, + country_code.ToUpperInvariant(), + If(IsNumeric(year), CInt(year), CType(Nothing, Integer?)) + ) + + If defResp.success Then + defaultEmission = defResp.data.default_emission + defaultBenchmark = defResp.data.benchmark + Else + Return $"FEHLER DEFAULTS {defResp.error.code}: {defResp.error.message}" + End If + + Catch ex As Exception + Return "Technischer Fehler beim Laden der CN-Code Defaults: " & ex.Message + End Try + End If + ' ------------------------------------------------------------ ' Request aufbauen ' ------------------------------------------------------------ @@ -194,115 +187,154 @@ Public Class cATEZ_Greenpulse_CBAM_CostCalculation .weight = CDec(weight) } - If If(see_total, "") <> "" Then + ' see_total + If IsNumeric(see_total) AndAlso CDbl(see_total) >= 0 Then req.see_total = CDec(see_total) - Else - req.see_total = Nothing + ElseIf defaultEmission.HasValue Then + req.see_total = defaultEmission.Value End If - If year IsNot Nothing Then + ' year + If IsNumeric(year) Then req.year = CInt(year) End If - If If(benchmark_value, "") <> "" Then + ' benchmark_value + If IsNumeric(benchmark_value) AndAlso CDbl(benchmark_value) >= 0 Then req.benchmark_value = CDec(benchmark_value) - Else - req.benchmark_value = Nothing + ElseIf defaultBenchmark.HasValue Then + req.benchmark_value = defaultBenchmark.Value End If ' ------------------------------------------------------------ - ' Request ausführen + ' Calculate ' ------------------------------------------------------------ Dim resp As cCBAM_CostCalculation_Response Try resp = api.CalculateCost(req) Catch ex As Exception - Return "Technischer Fehler bei API-Aufruf: " & ex.Message + Return "Technischer Fehler bei Kostenberechnung: " & ex.Message End Try - ' ------------------------------------------------------------ - ' Ergebnis aufbereiten - ' ------------------------------------------------------------ - Dim erg As String = "" - - If resp.success Then - - erg &= "CBAM Kostenberechnung erfolgreich" & vbCrLf - erg &= "--------------------------------" & vbCrLf - erg &= $"CN-Code: {cn_code}" & vbCrLf - erg &= $"Ursprungsland: {req.country_code}" & vbCrLf - erg &= $"Gewicht: {req.weight:N2} t" & vbCrLf - erg &= vbCrLf - - erg &= $"Kosten: {resp.data.cost:N2} {resp.data.currency}" & vbCrLf - erg &= $"CBAM Emission: {resp.data.cbam_emission:N5}" & vbCrLf - erg &= $"Benchmark: {resp.data.benchmark:N5}" & vbCrLf - erg &= $"Phase-Faktor: {resp.data.phase_factor:P2}" & vbCrLf - erg &= $"CO₂ Preis: {resp.data.carbon_price:N2} EUR/t" & vbCrLf - - erg &= vbCrLf - erg &= "Details:" & vbCrLf - erg &= $" Adjusted Benchmark: {resp.data.calculation_details.adjusted_benchmark:N5}" & vbCrLf - erg &= $" Emission Difference: {resp.data.calculation_details.emission_difference:N5}" & vbCrLf - erg &= $" Gewicht (berechnet): {resp.data.calculation_details.weight:N2} t" & vbCrLf - - Else - - erg &= "CBAM Kostenberechnung FEHLER" & vbCrLf - erg &= "--------------------------------" & vbCrLf - erg &= $"Code: {resp.error.code}" & vbCrLf - erg &= $"Message: {resp.error.message}" & vbCrLf - erg &= $"HTTP Status: {resp.error.statusCode}" & vbCrLf - + If Not resp.success Then + Return $"FEHLER {resp.error.code}: {resp.error.message}" End If - Return erg + ' ------------------------------------------------------------ + ' Ergebnis formatieren + ' ------------------------------------------------------------ + Dim d = resp.data + Dim s As String = "" + + s &= "CBAM Kostenberechnung" & vbCrLf + s &= "----------------------" & vbCrLf + s &= $"CN-Code: {cn_code}" & vbCrLf + s &= $"Ursprungsland: {req.country_code}" & vbCrLf + s &= $"Gewicht: {req.weight:N2} t" & vbCrLf + s &= vbCrLf + + s &= $"Kosten: {d.cost:N2} {d.currency}" & vbCrLf + s &= $"CBAM Emission: {d.cbam_emission:N5}" & vbCrLf + s &= $"Benchmark: {d.benchmark:N5}" & vbCrLf + s &= $"Phase-Faktor: {d.phase_factor:P2}" & vbCrLf + s &= $"CO₂ Preis: {d.carbon_price:N2} EUR/t" & vbCrLf + + If Not String.IsNullOrWhiteSpace(d.info_message) Then + s &= vbCrLf & "Info: " & d.info_message & vbCrLf + End If + + Return s End Function + ' ------------------------------------------------------------------------ + ' Helpers + ' ------------------------------------------------------------------------ + Private Function ParseCalcResponse(jsonStr As String) _ + As cCBAM_CostCalculation_Response + + Dim json As New JsonObject() + json.Load(jsonStr) + + Dim r As New cCBAM_CostCalculation_Response With {.success = json.BoolOf("success")} + + If r.success Then + Dim d = json.ObjectOf("data") + r.data = New cCBAM_CostCalculation_Data With { + .cost = GetDec(d, "cost"), + .cbam_emission = GetDec(d, "cbam_emission"), + .benchmark = GetDec(d, "benchmark"), + .phase_factor = GetDec(d, "phase_factor"), + .carbon_price = GetDec(d, "carbon_price"), + .currency = d.StringOf("currency"), + .info_message = d.StringOf("info_message") + } + + Dim cd = d.ObjectOf("calculation_details") + r.data.calculation_details = New cCBAM_CostCalculation_Details With { + .cbam_emission = GetDec(cd, "cbam_emission"), + .benchmark = GetDec(cd, "benchmark"), + .adjusted_benchmark = GetDec(cd, "adjusted_benchmark"), + .emission_difference = GetDec(cd, "emission_difference"), + .weight = GetDec(cd, "weight"), + .carbon_price = GetDec(cd, "carbon_price") + } + Else + r.error = ParseError(json) + End If + + Return r + End Function + + Private Shared Function ParseError(j As JsonObject) As cCBAM_Error + Dim e = j.ObjectOf("error") + Return New cCBAM_Error With { + .code = e.StringOf("code"), + .message = e.StringOf("message"), + .statusCode = e.IntOf("statusCode") + } + End Function + + Private Shared Function GetDec(o As JsonObject, n As String) As Decimal + Dim s = o.StringOf(n).Replace(",", ".") + Dim v As Decimal + Decimal.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, v) + Return v + End Function + + Private Shared Function GetNullableDec(o As JsonObject, n As String) As Decimal? + Dim s = o.StringOf(n) + If String.IsNullOrWhiteSpace(s) Then Return Nothing + Return GetDec(o, n) + End Function + End Class ' ============================================================================ -' REQUEST DTO +' DTOs ' ============================================================================ Public Class cCBAM_CostCalculation_Request - Public Property cn_code As String Public Property country_code As String Public Property weight As Decimal - Public Property see_total As Nullable(Of Decimal) - Public Property year As Nullable(Of Integer) - Public Property benchmark_value As Nullable(Of Decimal) + Public Property see_total As Decimal? + Public Property year As Integer? + Public Property benchmark_value As Decimal? Public Function ToJson() As String - Dim json As New JsonObject() - - json.UpdateString("cn_code", cn_code) - json.UpdateString("country_code", country_code) - json.UpdateNumber("weight", weight.ToString(CultureInfo.InvariantCulture)) - - If see_total IsNot Nothing AndAlso see_total.HasValue Then - json.UpdateNumber("see_total", see_total.Value.ToString(CultureInfo.InvariantCulture)) - End If - - If year.HasValue Then - json.UpdateInt("year", year.Value) - End If - - If benchmark_value IsNot Nothing AndAlso benchmark_value.HasValue Then - json.UpdateNumber("benchmark_value", benchmark_value.Value.ToString(CultureInfo.InvariantCulture)) - End If - - Return json.Emit() + Dim j As New JsonObject() + j.UpdateString("cn_code", cn_code) + j.UpdateString("country_code", country_code) + j.UpdateNumber("weight", weight.ToString(CultureInfo.InvariantCulture)) + If see_total.HasValue Then j.UpdateNumber("see_total", see_total.Value.ToString(CultureInfo.InvariantCulture)) + If year.HasValue Then j.UpdateInt("year", year.Value) + If benchmark_value.HasValue Then j.UpdateNumber("benchmark_value", benchmark_value.Value.ToString(CultureInfo.InvariantCulture)) + Return j.Emit() End Function - End Class -' ============================================================================ -' RESPONSE OBJECTS -' ============================================================================ Public Class cCBAM_CostCalculation_Response Public Property success As Boolean Public Property data As cCBAM_CostCalculation_Data @@ -316,6 +348,7 @@ Public Class cCBAM_CostCalculation_Data Public Property phase_factor As Decimal Public Property carbon_price As Decimal Public Property currency As String + Public Property info_message As String Public Property calculation_details As cCBAM_CostCalculation_Details End Class @@ -328,9 +361,6 @@ Public Class cCBAM_CostCalculation_Details Public Property carbon_price As Decimal End Class -' ============================================================================ -' CN CODE RESPONSE -' ============================================================================ Public Class cCBAM_CnCode_Response Public Property success As Boolean Public Property data As List(Of cCBAM_CnCode) @@ -342,9 +372,17 @@ Public Class cCBAM_CnCode Public Property cn_description As String End Class -' ============================================================================ -' ERROR OBJECT -' ============================================================================ +Public Class cCBAM_CnCodeDefaults_Response + Public Property success As Boolean + Public Property data As cCBAM_CnCodeDefaults + Public Property [error] As cCBAM_Error +End Class + +Public Class cCBAM_CnCodeDefaults + Public Property default_emission As Decimal + Public Property benchmark As Decimal? +End Class + Public Class cCBAM_Error Public Property code As String Public Property message As String