CBAM, SUMA, RelayHub

This commit is contained in:
2026-10-06 09:24:20 +02:00
parent 7be9708ede
commit 9eb8a1d800
13 changed files with 1786 additions and 382 deletions

View File

@@ -36,6 +36,7 @@ Public Class cRelayHub
Public Property contactPersonPhoneNumber As String
Public Property contactPersonPosition As String
Public Property contactPersonEmail As String ' <--- NEU
End Class
Public Class cRelayHubCost
@@ -45,11 +46,12 @@ Public Class cRelayHub
Public Class cRelayHubHeaderData
Public Property agentContact As cRelayHubAgentContact
Public Property declarantIsConsignee As Boolean
Public Property representationRelationshipCode As String ' <- war String
Public Property inputTaxDeduction As Boolean ' <- war String
' DAKOSY
Public Property declarantIsConsignee As Boolean?
Public Property representationRelationshipCode As String
Public Property inputTaxDeduction As Boolean?
Public Property procedureCodeRequested As String
' Entfernt: goodsStatus, costs (nicht im Schema an dieser Stelle)
Public Property transportMeansArrivalIdentity As String
Public Property transportMeansNationalityCode As String
Public Property previousAdministrativeReferenceType As String
@@ -59,6 +61,13 @@ Public Class cRelayHub
Public Property departureCountry As String
Public Property addressedCustomsOffice As String
Public Property dv1CostAllocation As cRelayHubDv1CostAllocation
' MODALTRANS
Public Property serviceCode As String
Public Property docType As String
Public Property declarationType As String
Public Property declarantEori As String
Public Property additionalProcedureCode As String
End Class
Public Class cRelayHubAddress
@@ -70,6 +79,7 @@ Public Class cRelayHub
Public Property countryCode As String
Public Property postalCode As String
Public Property city As String
Public Property district As String
End Class
Public Class cRelayHubDeclaration
@@ -80,15 +90,42 @@ Public Class cRelayHub
Public Class cRelayHubAdditionalData
Public Property transaction As cRelayHubTransaction
Public Property micImportShipment As cRelayHubMicImportShipment
Public Property declaration As List(Of cRelayHubDeclaration)
End Class
Public Class cRelayHubMicImportShipment
Public Property company As String
Public Property plant As String
Public Property connectionId As String
Public Property partnerId As String
Public Property partyId As String
Public Property messageId As String
Public Property transactionId As String
Public Property dateTime As String
Public Property sequenceNum As Integer?
Public Property actionCode As String
Public Property shipmentId As String
Public Property shipmentPartial As String
Public Property shipmentVersion As Integer?
Public Property documentNumber As Integer?
Public Property shipmentType As String
Public Property customsDate As String
End Class
Public Class cRelayHubAdditionalSettings
Public Property combineOptions As List(Of String)
Public Property combineItemsPriorityDocumentType As String
Public Property disableOutbound As Boolean?
Public Property disableDocumentConverter As Boolean?
Public Property disableAiToCommon As Boolean?
Public Property disableValidation As Boolean?
Public Property targetLang As String
End Class
Public Class cRelayHubJobOrderRequest
<JsonIgnore>
Public Property environment As cRelayHubEnvironment = cRelayHubEnvironment.PROD
Public Property referenceNo As String
Public Property dispatchCountry As String
Public Property destinationCountry As String
@@ -100,13 +137,27 @@ Public Class cRelayHub
' Neu: Pflichtfeld lt. Fehlermeldung
Public Property outputApplication As String ' "test" | "dakosy/sftp/vera" | "evrim/excel" | "sec/import/integration"
' "modaltrans/customs/declaration"
End Class
Public Class cRelayHubOutputApplication
Public Const Dakosy As String = "dakosy/sftp/verg"
Public Const Modaltrans As String = "modaltrans/customs/declaration"
Public Const MIC_NL_IMPORT As String = "MIC_NL_IMPORT"
End Class
Public Enum cRelayHubEnvironment
TEST
PROD
End Enum
Public Class cRelayHubJobOrderResponse
Public Property id As String
Public Property status As String
Public Property createdAt As String
Public Property referenceNo As String
Public Property jobOrderProcessingId As String
Public Property jobOrderNo As String
End Class
Public Class cRelayHubApiResult
@@ -117,42 +168,61 @@ Public Class cRelayHub
Public Property Data As cRelayHubJobOrderResponse ' Nur bei 201
End Class
Private Shared ReadOnly ENVIRONMENT As cRelayHubEnvironment = cRelayHubEnvironment.PROD
Shared API_URL As String = "https://declaranthub.singlewindow.io/api/v1-0"
Private Shared Function GetApiUrl(environment As cRelayHubEnvironment) As String
Select Case environment
Case cRelayHubEnvironment.TEST
Return "https://test-relayhub.singlewindow.io/api/v1-0"
Case cRelayHubEnvironment.PROD
Return "https://declaranthub.singlewindow.io/api/v1-0"
Case Else
Throw New ApplicationException("Unbekannte RelayHub-Umgebung.")
End Select
End Function
Public Class cRelayHub_sendToRelayHub_JobOrderRequest
Shared API_URL As String = "https://declaranthub.singlewindow.io/api/v1-0"
' Low-level Sender: holt Access-Token aus cRelayHubToken und sendet JSON
Private Shared Function SendJobOrder(jsonPayload As String) As Chilkat.HttpResponse
Private Shared Function SendJobOrder(jsonPayload As String, environment As cRelayHubEnvironment) As Chilkat.HttpResponse
Dim http As New Chilkat.Http
http.SetRequestHeader("Accept", "application/json")
' *** Token aus der separaten Token-Klasse beziehen ***
Dim token As String = cRelayHubToken.GetAccessToken()
Dim token As String = cRelayHubToken.GetAccessToken(environment)
http.AuthToken = token
'http.AuthToken = "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJqdkkzQ3BfbTVTb3JpdUFmSEZkZzJWUGgxcDFNWHA3ZnpjNFhyVHpGWVc4In0.eyJleHAiOjE3NjEzMDE4NTQsImlhdCI6MTc2MTIxNTQ1NCwianRpIjoiNWY3MjdkZTUtMmQ2MS00MThkLWJhYWQtNmY5ODM0NDk2NjY2IiwiaXNzIjoiaHR0cHM6Ly9sb2dpbi5zaW5nbGV3aW5kb3cuaW8vYXV0aC9yZWFsbXMvYWdzdyIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiI5OWMwOTFmMy01Mjg1LTRhNzUtODdjNC0zODJlNThlNTBmNGMiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhZ3N3LWFkbWluIiwic2lkIjoiMzRjMDY4ZGMtNjY3My00NDk5LTgzODAtMjVkOGYyNjI1NGY4IiwiYWxsb3dlZC1vcmlnaW5zIjpbImh0dHBzOi8vc2luZ2xld2luZG93LmlvIiwiaHR0cHM6Ly9hZ3N3LnNpbmdsZXdpbmRvdy5pbyIsImh0dHA6Ly9sb2NhbGhvc3Q6MzAwMSJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1hZ3N3Iiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZHVjdC1jYXRhbG9nLXNjcCBwcm9maWxlIGVtYWlsIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsIm5hbWUiOiJEaHViLVByb2QgVXNlciIsInByZWZlcnJlZF91c2VybmFtZSI6ImRodWJAdmVyYWcuYWciLCJnaXZlbl9uYW1lIjoiRGh1Yi1Qcm9kIiwiZmFtaWx5X25hbWUiOiJVc2VyIiwiZW1haWwiOiJkaHViQHZlcmFnLmFnIn0.TZ8GkXrL7SSz1CEOnLB2eAAuVcYu3wlNtnAF6Y3wt0lnMGyse5tsI0YYSfGZG_Ae8zVcPqHqVT4rhDOgIPooBkq71z35ppd6b-FUaS6hSy4cMWQaidsHZr3hkID8amVQcB2vz9Yg985h3MOCSIn86d0x9i0euew3wEpF8eEHSfk_Wqf8jeJbiXAPHfy92K4Xh8IIuh-GXUTyN-NmGM94_NhcQwb4e7YMXjPPuslUsjABEfOvT7v4-AnO5B3ZczX0h0UgafmfiIyXSaL4IzYZojoMJ0CRsHdAK-j4gzXqz4lW0A9KMmHGHn01Oftt9mXfrEmCj0XGWF9xvBuYhxMUAQ"
Console.WriteLine("Using Token =: " & http.AuthToken)
Console.WriteLine("API_URL =: " & API_URL)
Return http.PostJson2(API_URL & "/job-orders/init", "application/json", jsonPayload)
Dim apiUrl As String = GetApiUrl(environment)
Console.WriteLine("API_URL =: " & apiUrl)
Return http.PostJson2(apiUrl & "/job-orders/init", "application/json", jsonPayload)
End Function
' Public API: erstellt Job-Order mit 401-Retry
Public Shared Function getJSON(request As cRelayHubJobOrderRequest) As String
Dim result As New cRelayHubApiResult()
Try
Dim sanitized = Sanitize(request)
Dim settings As New JsonSerializerSettings With {
.NullValueHandling = NullValueHandling.Ignore,
.ContractResolver = New Newtonsoft.Json.Serialization.DefaultContractResolver With {
.NamingStrategy = New Newtonsoft.Json.Serialization.CamelCaseNamingStrategy()
}
}
Dim jsonPayload As String = JsonConvert.SerializeObject(sanitized, settings)
Console.WriteLine("JSON → " & jsonPayload)
Return JsonConvert.SerializeObject(request)
Dim settings As New JsonSerializerSettings With {
.NullValueHandling = NullValueHandling.Ignore,
.ContractResolver = New Newtonsoft.Json.Serialization.DefaultContractResolver With {
.NamingStrategy = New Newtonsoft.Json.Serialization.CamelCaseNamingStrategy()
}
}
Dim jsonPayload As String =
JsonConvert.SerializeObject(sanitized, settings)
Console.WriteLine("JSON → " & jsonPayload)
Return jsonPayload
Catch ex As Exception
VERAG_PROG_ALLGEMEIN.cErrorHandler.ERR(ex.Message, ex.StackTrace, System.Reflection.MethodInfo.GetCurrentMethod.Name)
VERAG_PROG_ALLGEMEIN.cErrorHandler.ERR(
ex.Message,
ex.StackTrace,
System.Reflection.MethodInfo.GetCurrentMethod.Name)
Return "Error"
End Try
End Function
@@ -178,7 +248,7 @@ Public Class cRelayHub
' 1. Versuch
Dim response As Chilkat.HttpResponse = SendJobOrder(jsonPayload)
Dim response As Chilkat.HttpResponse = SendJobOrder(jsonPayload, request.environment)
If response Is Nothing Then
Return New cRelayHubApiResult With {
.Success = False, .StatusCode = 0, .Message = "Verbindungsfehler",
@@ -273,11 +343,11 @@ Public Class cRelayHub
Case "Rechnung", "Konsi-Rechnung", "INVOICE" : fileType = "commercial_invoice"
Case "CMR" : fileType = "cmr"
Case "Ausfuhr", "EXPORT" : fileType = "tr_export_declaration"
Case "ATR", "A.TR", "ATR-EUR1", "EUR1" : fileType = "atr"
Case "ATR", "A.TR", "ATR-EUR1" : fileType = "atr"
Case "Vorpapier", "T1" : fileType = "transit_declaration"
Case "Ursprungszeugnis", "CoO" : fileType = "origin"
Case "Lieferschein" : fileType = "delivery_note"
Case "Packliste" : fileType = "packaging_list"
Case "Packliste" : fileType = "packing_list"
Case "Freihandelszone", "FTZ" : fileType = "ftz"
Case "EUR1", "EUR", "EUR.1" : fileType = "eur"
Case "GGED_D", "GGED-D" : fileType = "gged_p"
@@ -287,7 +357,47 @@ Public Class cRelayHub
End If
Return fileType
End Function
Private Shared Function Sanitize(req As cRelayHub.cRelayHubJobOrderRequest) As cRelayHub.cRelayHubJobOrderRequest
Private Shared Function IsModaltrans(req As cRelayHubJobOrderRequest) As Boolean
Return String.Equals(req.outputApplication, "modaltrans/customs/declaration", StringComparison.OrdinalIgnoreCase)
End Function
Private Shared Function IsMIC_NL_IMPORT(req As cRelayHubJobOrderRequest) As Boolean
Return String.Equals(req.outputApplication, cRelayHubOutputApplication.MIC_NL_IMPORT, StringComparison.OrdinalIgnoreCase)
End Function
Private Shared Sub SetMIC_NL_IMPORT(req As cRelayHubJobOrderRequest)
If req.environment = cRelayHubEnvironment.PROD Then Throw New ApplicationException("MIC NL Import ist derzeit nur im RelayHub-Testsystem verfügbar.")
req.outputApplication = "mic/import"
req.regimeType = "IM"
If req.additionalData Is Nothing Then req.additionalData = New cRelayHubAdditionalData
If req.additionalData.micImportShipment Is Nothing Then req.additionalData.micImportShipment = New cRelayHubMicImportShipment
Dim mic = req.additionalData.micImportShipment
mic.company = "NL"
mic.plant = "IN"
mic.connectionId = "MICWebService"
mic.actionCode = "DIF"
If String.IsNullOrWhiteSpace(mic.partnerId) Then mic.partnerId = req.referenceNo
If String.IsNullOrWhiteSpace(mic.shipmentId) Then mic.shipmentId = req.referenceNo
If String.IsNullOrWhiteSpace(mic.messageId) Then mic.messageId = req.referenceNo
If String.IsNullOrWhiteSpace(mic.transactionId) Then mic.transactionId = req.referenceNo
If String.IsNullOrWhiteSpace(mic.dateTime) Then mic.dateTime = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")
If String.IsNullOrWhiteSpace(mic.shipmentType) Then mic.shipmentType = "E"
If req.additionalSettings Is Nothing Then req.additionalSettings = New cRelayHubAdditionalSettings
req.additionalSettings.disableOutbound = False
End Sub
Private Shared Function Sanitize(req As cRelayHubJobOrderRequest) As cRelayHubJobOrderRequest
If req Is Nothing Then Throw New ApplicationException("RelayHub Request fehlt.")
Dim isMicNlImport As Boolean = IsMIC_NL_IMPORT(req)
If isMicNlImport Then SetMIC_NL_IMPORT(req)
req.outputApplication = If(req.outputApplication, "").Trim()
req.dispatchCountry = If(req.dispatchCountry, "").Trim().ToUpperInvariant()
req.destinationCountry = If(req.destinationCountry, "").Trim().ToUpperInvariant()
@@ -295,31 +405,41 @@ Public Class cRelayHub
If req.dispatchCountry.Length <> 2 Then Throw New ApplicationException("dispatchCountry ISO-2 erforderlich.")
If req.destinationCountry.Length <> 2 Then Throw New ApplicationException("destinationCountry ISO-2 erforderlich.")
' AdditionalSettings -> combineOptions: nur erlaubte Keys, distinct, lower-case
Dim allowed As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase) From {
"description", "commodity_code", "country_of_origin", "unit_type"
}
If isMicNlImport Then
If req.additionalData Is Nothing OrElse req.additionalData.micImportShipment Is Nothing Then Throw New ApplicationException("micImportShipment für MIC NL Import erforderlich.")
If req.additionalSettings IsNot Nothing Then
Dim mic = req.additionalData.micImportShipment
mic.partnerId = If(mic.partnerId, "").Trim()
mic.partyId = If(mic.partyId, "").Trim()
mic.shipmentId = If(mic.shipmentId, "").Trim()
If req.additionalSettings.combineOptions IsNot Nothing Then
Dim cleaned = req.additionalSettings.combineOptions.
Where(Function(o) Not String.IsNullOrWhiteSpace(o)).
Select(Function(o) o.Trim().ToLowerInvariant()).
Where(Function(o) allowed.Contains(o)).
Distinct().
ToList()
If mic.partnerId = "" Then Throw New ApplicationException("MIC partnerId erforderlich.")
If mic.partyId = "" Then Throw New ApplicationException("MIC partyId erforderlich.")
If mic.shipmentId = "" Then Throw New ApplicationException("MIC shipmentId erforderlich.")
req.additionalSettings.combineOptions = cleaned
ElseIf IsModaltrans(req) Then
If req.additionalData IsNot Nothing Then
req.additionalData.transaction = Nothing
If req.additionalData.declaration IsNot Nothing Then
For Each declaration In req.additionalData.declaration
declaration.objectIdentification = Nothing
Next
End If
End If
If req.additionalSettings IsNot Nothing AndAlso req.additionalSettings.combineOptions Is Nothing Then req.additionalSettings.combineOptions = New List(Of String)
Else
Dim allowed As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase) From {
"description",
"commodity_code",
"country_of_origin",
"unit_type"
}
req.additionalSettings.combineItemsPriorityDocumentType =
If(req.additionalSettings.combineItemsPriorityDocumentType, "").Trim()
If req.additionalSettings.combineItemsPriorityDocumentType = "" Then
req.additionalSettings.combineItemsPriorityDocumentType = Nothing
If req.additionalSettings IsNot Nothing Then
If req.additionalSettings.combineOptions IsNot Nothing Then req.additionalSettings.combineOptions = req.additionalSettings.combineOptions.Where(Function(o) Not String.IsNullOrWhiteSpace(o)).Select(Function(o) o.Trim().ToLowerInvariant()).Where(Function(o) allowed.Contains(o)).Distinct().ToList()
req.additionalSettings.combineItemsPriorityDocumentType = If(req.additionalSettings.combineItemsPriorityDocumentType, "").Trim()
If req.additionalSettings.combineItemsPriorityDocumentType = "" Then req.additionalSettings.combineItemsPriorityDocumentType = Nothing
End If
End If
Return req