Merge branch 'newMaster2024' of https://git.it.verag.ag/edv/SDL into newMaster2024

This commit is contained in:
2025-10-23 11:41:22 +02:00
22 changed files with 1973 additions and 23 deletions

View File

@@ -319,4 +319,184 @@ Public Class MyDatagridview
End Sub
' ========================================================
' ================== COPY/PASTE SUPPORT ==================
' ========================================================
' Standard: Nur erste Spalte (MRN) befüllen. Auf False setzen, um Blöcke (mehrere Spalten) zuzulassen.
<Browsable(True), DefaultValue(True)>
Public Property _PasteSingleColumn As Boolean = True
Protected Overrides Sub OnKeyDown(e As KeyEventArgs)
' Strg+V oder Shift+Insert => Einfügen
If (e.Control AndAlso e.KeyCode = Keys.V) OrElse (e.Shift AndAlso e.KeyCode = Keys.Insert) Then
PasteFromClipboard()
e.Handled = True
Return
End If
MyBase.OnKeyDown(e)
End Sub
''' <summary>
''' Fügt den Clipboard-Text (Excel: TAB/CRLF) in die DGV ein.
''' </summary>
Public Sub PasteFromClipboard()
Dim text As String = Clipboard.GetText()
If String.IsNullOrWhiteSpace(text) Then Exit Sub
Dim lines = text.Split({vbCrLf, vbLf, vbCr}, StringSplitOptions.None)
If lines.Length > 0 AndAlso lines(lines.Length - 1).Trim() = "" Then
ReDim Preserve lines(lines.Length - 2)
End If
If lines.Length = 0 Then Exit Sub
Dim startRow As Integer = If(Me.CurrentCell IsNot Nothing, Me.CurrentCell.RowIndex, 0)
Dim startCol As Integer = If(Me.CurrentCell IsNot Nothing, Me.CurrentCell.ColumnIndex, 0)
If startRow < 0 Then startRow = 0
If startCol < 0 Then startCol = 0
' Temporär das „Neue-Zeile“-Verhalten und Binding einfrieren
Dim oldAddRows = Me.AllowUserToAddRows
Me.AllowUserToAddRows = False
Me.SuspendLayout()
Dim cm As CurrencyManager = Nothing
Try
If TypeOf Me.DataSource Is DataTable Then
Dim bs As CurrencyManager = TryCast(Me.BindingContext(Me.DataSource), CurrencyManager)
cm = bs
cm?.SuspendBinding()
End If
For i As Integer = 0 To lines.Length - 1
Dim r As Integer = startRow + i
Dim rowText As String = lines(i)
If String.IsNullOrWhiteSpace(rowText) Then Continue For
Dim cells = rowText.Split(vbTab)
EnsureRowExists(r)
If _PasteSingleColumn Then
Dim targetCol As Integer = 0 ' MRN-Spalte
If targetCol >= 0 AndAlso targetCol < Me.Columns.Count Then
SetCellValueSafe(r, targetCol, cells(0))
End If
Else
For j As Integer = 0 To cells.Length - 1
Dim c As Integer = startCol + j
If c >= 0 AndAlso c < Me.Columns.Count Then
SetCellValueSafe(r, c, cells(j))
End If
Next
End If
Next
Me.EndEdit() ' Commits pending edits im Grid
If TypeOf Me.DataSource Is DataTable Then
DirectCast(Me.DataSource, DataTable).AcceptChanges()
End If
Finally
cm?.ResumeBinding()
Me.ResumeLayout()
Me.AllowUserToAddRows = oldAddRows
Me.Refresh()
End Try
End Sub
Private Sub SetCellValueSafe(rowIndex As Integer, colIndex As Integer, raw As String)
If rowIndex < 0 OrElse rowIndex >= Me.Rows.Count Then Exit Sub
Dim col = Me.Columns(colIndex)
Dim v As String = If(raw, String.Empty).Trim()
' Fall A: Datengebunden -> direkt in DataTable schreiben (über DataPropertyName)
If TypeOf Me.DataSource Is DataTable Then
Dim dt = DirectCast(Me.DataSource, DataTable)
Dim prop = If(String.IsNullOrWhiteSpace(col.DataPropertyName), col.Name, col.DataPropertyName)
If Not dt.Columns.Contains(prop) Then
' Kein gebundenes Feld vorhanden -> fallback auf Zellenwert
GoTo FallbackCell
End If
Dim targetType As Type = dt.Columns(prop).DataType
Dim obj As Object = DBNull.Value
Try
If targetType Is GetType(String) Then
obj = If(v = "", DBNull.Value, CType(v, String))
ElseIf targetType Is GetType(Date) OrElse targetType Is GetType(DateTime) Then
If v = "" Then
obj = DBNull.Value
Else
Dim d As DateTime
obj = If(DateTime.TryParse(v, d), d.Date, CType(v, Object))
End If
ElseIf targetType Is GetType(Integer) Then
Dim n As Integer
obj = If(Integer.TryParse(v, n), n, If(v = "", DBNull.Value, CType(v, Object)))
ElseIf targetType Is GetType(Decimal) OrElse targetType Is GetType(Double) OrElse targetType Is GetType(Single) Then
Dim decv As Decimal
obj = If(Decimal.TryParse(v, Globalization.NumberStyles.Any, Globalization.CultureInfo.CurrentCulture, decv),
Convert.ChangeType(decv, targetType),
If(v = "", DBNull.Value, CType(v, Object)))
Else
' generischer Versuch
obj = If(v = "", DBNull.Value, Convert.ChangeType(v, targetType))
End If
Catch
obj = If(v = "", DBNull.Value, v)
End Try
' DataRow sichern (bei neu erzeugten Zeilen existiert sie sicher)
Dim drv As DataRowView = TryCast(Me.Rows(rowIndex).DataBoundItem, DataRowView)
If drv IsNot Nothing Then
drv(prop) = obj
Else
' Falls kein DataRowView (unwahrscheinlich): direkt über Index
dt.Rows(rowIndex)(prop) = obj
End If
Return
End If
FallbackCell:
' Fall B: Ungebunden -> direkt in die Zelle schreiben
Dim cell = Me.Rows(rowIndex).Cells(colIndex)
If cell Is Nothing OrElse cell.ReadOnly Then Exit Sub
Try
If cell.ValueType Is GetType(Date) OrElse cell.ValueType Is GetType(DateTime) Then
If v = "" Then
cell.Value = Nothing
Else
Dim d As DateTime
cell.Value = If(DateTime.TryParse(v, d), d.Date, CType(v, Object))
End If
ElseIf cell.ValueType Is GetType(Integer) Then
Dim n As Integer
cell.Value = If(Integer.TryParse(v, n), n, If(v = "", Nothing, v))
ElseIf cell.ValueType Is GetType(Decimal) OrElse cell.ValueType Is GetType(Double) Then
Dim decv As Decimal
cell.Value = If(Decimal.TryParse(v, Globalization.NumberStyles.Any, Globalization.CultureInfo.CurrentCulture, decv), decv, If(v = "", Nothing, v))
Else
cell.Value = If(v = "", Nothing, v)
End If
Catch
cell.Value = If(v = "", Nothing, v)
End Try
End Sub
Private Sub EnsureRowExists(targetRow As Integer)
If targetRow < Me.Rows.Count Then Exit Sub
If TypeOf Me.DataSource Is DataTable Then
Dim dt = DirectCast(Me.DataSource, DataTable)
Do While targetRow >= (If(Me.AllowUserToAddRows, Me.Rows.Count - 1, Me.Rows.Count))
dt.Rows.Add(dt.NewRow())
Loop
Else
Do While targetRow >= (If(Me.AllowUserToAddRows, Me.Rows.Count - 1, Me.Rows.Count))
Me.Rows.Add()
Loop
End If
End Sub
' ========================================================
' ========================================================
End Class

View File

@@ -200,4 +200,29 @@ Public Class cZollArtikel
Return result
End Function
Public Shared Function FindZollArtikelByNummer(artikelListe As List(Of cZollArtikel), artikelnummer As String) As cZollArtikel
If artikelListe Is Nothing OrElse artikelnummer Is Nothing Then Return Nothing
Dim suchNr As String = artikelnummer.Trim().ToUpperInvariant()
' Finde alle Artikel mit gleicher Artikelnummer
Dim treffer = artikelListe.
Where(Function(a) a IsNot Nothing AndAlso
a.zollArt_Artikelnummer IsNot Nothing AndAlso
a.zollArt_Artikelnummer.ToString().Trim().ToUpperInvariant() = suchNr).
ToList()
If treffer.Count = 0 Then Return Nothing
If treffer.Count = 1 Then Return treffer(0)
' Wenn mehrere gefunden: wähle den mit längster Warencodenummer (zollArt_Warencodenummer)
Dim bester = treffer.OrderByDescending(Function(a)
Dim code = If(a.zollArt_Warencodenummer, "").ToString().Trim()
Return code.Length
End Function).
FirstOrDefault()
Return bester
End Function
End Class

View File

@@ -23,8 +23,8 @@ Public Class cATEZ_Greenpulse_KafkaDecs
'== Kafka: Konfiguration (Klassenebene)
'========================
Public Shared BootstrapServers As String = "192.168.85.250:9092" 'http://192.168.85.250:8888
Public Shared TopicName As String = "greenpulse.declarationdata.v1"
' Public Shared TopicName As String = "dev.greenpulse.declarationdata.v1"
' Public Shared TopicName As String = "greenpulse.declarationdata.v1"
Public Shared TopicName As String = "dev.greenpulse.declarationdata.v1"
' Falls SASL/TLS benötigt:
Public Shared UseSasl As Boolean = False
Public Shared SaslUsername As String = ""
@@ -66,6 +66,11 @@ Public Class cATEZ_Greenpulse_KafkaDecs
<JsonProperty("importerDetails")>
Public Property ImporterDetails As ImporterDetailsNode
'--- documents ---
<JsonProperty("documents")>
Public Property Documents As List(Of DocumentNode)
'--- declaration ---
Public Class DeclarationNode
<JsonProperty("declarationsourceId")>
@@ -201,6 +206,19 @@ Public Class cATEZ_Greenpulse_KafkaDecs
<JsonProperty("importerCoordinateLatitudeY")>
Public Property ImporterCoordinateLatitudeY As String
End Class
Public Class DocumentNode
<JsonProperty("reference")>
Public Property Reference As String
<JsonProperty("doc-type")>
Public Property DocType As String
<JsonProperty("mime-type")>
Public Property MimeType As String
<JsonProperty("blob")>
Public Property Blob As String
End Class
'========================
'== Serialisierung
@@ -267,7 +285,8 @@ Public Class cATEZ_Greenpulse_KafkaDecs
.ImporterPoBox = "PO DCL-123",
.ImporterCoordinateLongitudeX = "41.0091982",
.ImporterCoordinateLatitudeY = "28.9662187"
}
},
.Documents = New List(Of cATEZ_Greenpulse_KafkaDecs.DocumentNode)()
}
End Function
@@ -298,6 +317,8 @@ Public Class cATEZ_Greenpulse_KafkaDecs
.MaxInFlight = 5,
.MessageTimeoutMs = Math.Max(waitMs, 60000),
.RequestTimeoutMs = 30000,
.CompressionType = Confluent.Kafka.CompressionType.Zstd, ' gute Kompression
.MessageMaxBytes = 20971520, ' ≈ 20 MB darf Topic/Broker nicht übersteigen
.EnableDeliveryReports = True,
.AllowAutoCreateTopics = True
}
@@ -379,7 +400,7 @@ Public Class cATEZ_Greenpulse_KafkaDecsBuilder_DAKOSY
Dim obj As New cATEZ_Greenpulse_KafkaDecs() With {
.Declaration = New cATEZ_Greenpulse_KafkaDecs.DeclarationNode() With {
.DeclarationSourceId = SafeStr(head("Bezugsnummer_LRN")),
.DeclarationSourceId = SafeStr(head("Registriernummer_MRN")),
.DeclarationNo = SafeStr(head("Registriernummer_MRN")),
.DeclarationDate = FirstNonEmptyDateStr(head, {"Annahmedatum", "Überlassungsdatum"}),
.RequestedProcedure = SafeStr(head("Verfahren")),
@@ -412,7 +433,8 @@ Public Class cATEZ_Greenpulse_KafkaDecsBuilder_DAKOSY
.ImporterPoBox = "",
.ImporterCoordinateLongitudeX = "",
.ImporterCoordinateLatitudeY = ""
}
},
.Documents = New List(Of cATEZ_Greenpulse_KafkaDecs.DocumentNode)()
}
' 2) Commercial (Rechnung) aus Unterlagen N380, falls vorhanden
@@ -424,6 +446,38 @@ Public Class cATEZ_Greenpulse_KafkaDecsBuilder_DAKOSY
.DefaultIfEmpty(Nothing) _
.FirstOrDefault()
' --- Dokumente aus Unterlagen übernehmen ---
Dim SQLS As New VERAG_PROG_ALLGEMEIN.SQL
Dim SenungsId = SQLS.getValueTxtBySql("SELECT dy_SendungsId from [tblDakosy_Zollanmeldungen] where dy_BezugsNr=''", "FMZOLL",,, Nothing)
If SenungsId IsNot Nothing Then
If IsNumeric(SenungsId) AndAlso SenungsId > 0 Then
Dim ANH_LIST As New List(Of cAvisoAnhaenge)
cAvisoAnhaenge.LOAD_LIST_BySendung(ANH_LIST, SenungsId)
For Each doc In ANH_LIST
Select Case doc.anh_Art
Case "Rechnung", "eFatura"
Dim dateiBytes As Byte() = System.IO.File.ReadAllBytes(VERAG_PROG_ALLGEMEIN.cDATENSERVER.GET_PDFPath_BY_DocID(doc.anh_docId))
Dim d As New cATEZ_Greenpulse_KafkaDecs.DocumentNode With {
.Reference = doc.anh_Name,
.DocType = "invoice",
.MimeType = cATEZ_Greenpulse_KafkaDecsBuilder_DAKOSY.GuessMimeTypeFromNumber(doc.anh_Typ),
.Blob = Convert.ToBase64String(dateiBytes)
}
obj.Documents.Add(d)
End Select
Next
End If
End If
If invRow IsNot Nothing Then
obj.Commercial.InvoiceNumbers = SafeStr(invRow("Unterlagennummer"))
obj.Commercial.InvoiceDate = SafeDateStr(invRow("Unterlagendatum"))
@@ -519,4 +573,15 @@ Public Class cATEZ_Greenpulse_KafkaDecsBuilder_DAKOSY
Return String.IsNullOrWhiteSpace(Convert.ToString(value))
End Function
Public Shared Function GuessMimeTypeFromNumber(num As Object) As String
' Wenn du Dateiendungen erkennst (z. B. .pdf oder .jpg im Namen)
Dim s As String = SafeStr(num).ToLowerInvariant()
If s.EndsWith(".pdf") Or s.ToLower = "PDF" Then Return "application/pdf"
If s.EndsWith(".jpg") Or s.EndsWith(".jpeg") Or s.ToLower = "JPG" Or s.ToLower = "JPEG" Then Return "image/jpeg"
If s.EndsWith(".png") Or s.ToLower = "PNG" Then Return "image/png"
Return "application/octet-stream"
End Function
End Class

View File

@@ -62,7 +62,7 @@ Public Class cRelayHub
Public Class cRelayHubAddress
Public Property addressType As String
Public Property participantEORI As String
Public Property participantSubsidiaryNumber As String
Public Property participantSubsidiaryNumber As Integer
Public Property companyName As String
Public Property streetAndNumber As String
Public Property countryCode As String
@@ -131,7 +131,21 @@ Public Class cRelayHub
Try
VERAG_PROG_ALLGEMEIN.cChilkat_Helper.UnlockCilkat()
Dim jsonPayload As String = JsonConvert.SerializeObject(request)
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)
'Dim jsonPayload As String = JsonConvert.SerializeObject(request)
' 1. Versuch
Dim response As Chilkat.HttpResponse = SendJobOrder(jsonPayload)
@@ -208,6 +222,26 @@ Public Class cRelayHub
End Try
End Function
Private Shared Function Sanitize(req As cRelayHub.cRelayHubJobOrderRequest) As cRelayHub.cRelayHubJobOrderRequest
req.outputApplication = If(req.outputApplication, "").Trim()
req.dispatchCountry = If(req.dispatchCountry, "").Trim().ToUpperInvariant()
req.destinationCountry = If(req.destinationCountry, "").Trim().ToUpperInvariant()
' lokale Fail-fast Checks
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.")
Dim allowed = New HashSet(Of String)(StringComparer.Ordinal) From {
"test", "dakosy/sftp/vera", "evrim/excel", "sec/import/integration"
}
If Not allowed.Contains(req.outputApplication) Then
Throw New ApplicationException("outputApplication muss 'test' | 'dakosy/sftp/vera' | 'evrim/excel' | 'sec/import/integration' sein.")
End If
If req.regimeType <> "IMPORT" AndAlso req.regimeType <> "EXPORT" Then
Throw New ApplicationException("regimeType muss 'IMPORT' oder 'EXPORT' sein.")
End If
Return req
End Function
' Beispielfall
Function CreateSampleJobOrderRequest() As cRelayHubJobOrderRequest
Dim req As New cRelayHubJobOrderRequest With {