218 lines
9.4 KiB
VB.net
218 lines
9.4 KiB
VB.net
Imports System.Globalization
|
||
Imports DevExpress.CodeParser
|
||
Imports DevExpress.Data.Controls
|
||
Imports DevExpress.XtraPrinting
|
||
|
||
Public Class ClassFormat
|
||
Public Const CURRENCY_FORMAT = "C2"
|
||
Public Const DECIMAL_FORMAT = "F"
|
||
Public Const DATE_FORMAT = "d"
|
||
Public Const DATETIME_FORMAT = "G"
|
||
|
||
Public Shared Function GetFormatString(pFormatString As String) As String
|
||
Select Case pFormatString
|
||
Case ClassControlCreator.CONTROL_TYPE_CURRENCY
|
||
Return CURRENCY_FORMAT
|
||
|
||
Case ClassControlCreator.CONTROL_TYPE_DOUBLE
|
||
Return DECIMAL_FORMAT
|
||
|
||
Case ClassControlCreator.CONTROL_TYPE_DATE
|
||
Return DATE_FORMAT
|
||
|
||
Case ClassControlCreator.CONTROL_TYPE_DATETIME
|
||
Return DATETIME_FORMAT
|
||
|
||
Case Else
|
||
Return String.Empty
|
||
|
||
End Select
|
||
End Function
|
||
''' <summary>
|
||
''' Normalisiert einen numerischen String für die invariante Kultur-Konvertierung.
|
||
''' Entfernt Tausendertrennzeichen und ersetzt Dezimaltrennzeichen durch Punkt.
|
||
''' </summary>
|
||
Private Shared Function NormalizeNumericString(pValue As String) As String
|
||
If String.IsNullOrWhiteSpace(pValue) Then
|
||
Return pValue
|
||
End If
|
||
|
||
Dim normalized As String = pValue.Trim()
|
||
|
||
LOGGER.Debug($"[NormalizeNumericString] Input: [{pValue}]")
|
||
|
||
' Entferne Währungssymbole und Leerzeichen
|
||
normalized = System.Text.RegularExpressions.Regex.Replace(normalized, "[€$£¥\s]", "")
|
||
|
||
Dim hasDot As Boolean = normalized.Contains(".")
|
||
Dim hasComma As Boolean = normalized.Contains(",")
|
||
|
||
LOGGER.Debug($"[NormalizeNumericString] After cleanup: [{normalized}], HasDot={hasDot}, HasComma={hasComma}")
|
||
|
||
If hasDot AndAlso hasComma Then
|
||
' Beide vorhanden: Das letzte ist der Dezimaltrenner
|
||
Dim lastDotPos As Integer = normalized.LastIndexOf(".")
|
||
Dim lastCommaPos As Integer = normalized.LastIndexOf(",")
|
||
|
||
LOGGER.Debug($"[NormalizeNumericString] Both separators found: LastDotPos={lastDotPos}, LastCommaPos={lastCommaPos}")
|
||
|
||
If lastDotPos > lastCommaPos Then
|
||
normalized = normalized.Replace(",", "")
|
||
Else
|
||
normalized = normalized.Replace(".", "").Replace(",", ".")
|
||
End If
|
||
|
||
ElseIf hasComma Then
|
||
Dim commaCount As Integer = normalized.Count(Function(c) c = ","c)
|
||
LOGGER.Debug($"[NormalizeNumericString] Only comma found: CommaCount={commaCount}")
|
||
|
||
If commaCount = 1 Then
|
||
Dim lastCommaPos As Integer = normalized.LastIndexOf(",")
|
||
Dim digitsAfterComma As Integer = normalized.Length - lastCommaPos - 1
|
||
LOGGER.Debug($"[NormalizeNumericString] Single comma: DigitsAfterComma={digitsAfterComma}")
|
||
|
||
If digitsAfterComma <= 3 Then
|
||
normalized = normalized.Replace(",", ".")
|
||
Else
|
||
normalized = normalized.Replace(",", "")
|
||
End If
|
||
Else
|
||
normalized = normalized.Replace(",", "")
|
||
End If
|
||
|
||
ElseIf hasDot Then
|
||
Dim dotCount As Integer = normalized.Count(Function(c) c = "."c)
|
||
LOGGER.Debug($"[NormalizeNumericString] Only dot found: DotCount={dotCount}")
|
||
|
||
If dotCount = 1 Then
|
||
Dim lastDotPos As Integer = normalized.LastIndexOf(".")
|
||
Dim digitsAfterDot As Integer = normalized.Length - lastDotPos - 1
|
||
|
||
LOGGER.Debug($"[NormalizeNumericString] Single dot: DigitsAfterDot={digitsAfterDot}")
|
||
|
||
' ✅ KRITISCHE ÄNDERUNG: Prüfe auch Stellen VOR dem Punkt
|
||
Dim digitsBeforeDot As Integer = lastDotPos
|
||
|
||
' Heuristik: Wenn <= 3 Stellen nach Punkt UND >= 1 Stelle davor → Dezimaltrenner
|
||
' Wenn > 3 Stellen davor UND <= 3 Stellen nach Punkt → unklar, vermutlich Dezimal
|
||
' Wenn > 3 Stellen nach Punkt → definitiv KEIN Dezimaltrenner
|
||
|
||
If digitsAfterDot > 3 Then
|
||
LOGGER.Warn($"⚠️ [NormalizeNumericString] Dot with {digitsAfterDot} digits after → treating as THOUSAND separator!")
|
||
normalized = normalized.Replace(".", "")
|
||
ElseIf digitsAfterDot >= 1 AndAlso digitsAfterDot <= 3 Then
|
||
' Wahrscheinlich Dezimaltrenner (z.B. 5464.17 oder 120.5)
|
||
LOGGER.Debug($"[NormalizeNumericString] Dot treated as decimal separator ({digitsBeforeDot} digits before, {digitsAfterDot} after)")
|
||
Else
|
||
' digitsAfterDot = 0 → Punkt am Ende, vermutlich Fehler
|
||
LOGGER.Warn($"⚠️ [NormalizeNumericString] Dot at end of string → removing")
|
||
normalized = normalized.Replace(".", "")
|
||
End If
|
||
Else
|
||
' Mehrere Punkte → Tausendertrenner
|
||
LOGGER.Debug($"[NormalizeNumericString] Multiple dots → removing all")
|
||
normalized = normalized.Replace(".", "")
|
||
End If
|
||
Else
|
||
LOGGER.Debug($"[NormalizeNumericString] No separators found → integer or already normalized")
|
||
End If
|
||
|
||
LOGGER.Debug($"[NormalizeNumericString] Output: [{normalized}]")
|
||
|
||
Return normalized
|
||
End Function
|
||
|
||
''' <summary>
|
||
''' Converts a string according to the type information, using the invariant culture
|
||
''' </summary>
|
||
''' <param name="pValue"></param>
|
||
''' <param name="pType"></param>
|
||
''' <returns></returns>
|
||
Public Shared Function GetConvertedValue(pValue As Object, pType As String) As Object
|
||
Dim oConvertedValue
|
||
LOGGER.Debug($"GetConvertedValue: {pType}")
|
||
|
||
Select Case pType
|
||
Case ClassControlCreator.CONTROL_TYPE_DOUBLE
|
||
Try
|
||
' ✅ IMMER normalisieren – auch für DB-Werte!
|
||
Dim normalizedValue As String = NormalizeNumericString(pValue?.ToString())
|
||
If Double.TryParse(normalizedValue, NumberStyles.Float, CultureInfo.InvariantCulture, oConvertedValue) Then
|
||
Return oConvertedValue
|
||
End If
|
||
Catch ex As Exception
|
||
LOGGER.Error(ex)
|
||
End Try
|
||
|
||
Case ClassControlCreator.CONTROL_TYPE_CURRENCY
|
||
Try
|
||
' ✅ KRITISCH: Normalisierung VOR Konvertierung
|
||
Dim normalizedValue As String = NormalizeNumericString(pValue?.ToString())
|
||
LOGGER.Debug($"GetConvertedValue CURRENCY: Original=[{pValue}], Normalized=[{normalizedValue}]")
|
||
|
||
If Double.TryParse(normalizedValue, NumberStyles.Float, CultureInfo.InvariantCulture, oConvertedValue) Then
|
||
Return oConvertedValue
|
||
End If
|
||
Catch ex As Exception
|
||
LOGGER.Error($"Currency conversion failed for [{pValue}]: {ex.Message}")
|
||
LOGGER.Error(ex)
|
||
End Try
|
||
|
||
Case ClassControlCreator.CONTROL_TYPE_INTEGER
|
||
Try
|
||
Dim normalizedValue As String = NormalizeNumericString(pValue?.ToString())
|
||
If Integer.TryParse(normalizedValue, NumberStyles.Integer, CultureInfo.InvariantCulture, oConvertedValue) Then
|
||
Return oConvertedValue
|
||
End If
|
||
Catch ex As Exception
|
||
LOGGER.Error(ex)
|
||
End Try
|
||
|
||
Case Else
|
||
LOGGER.Debug($"GetConvertedValue - Case ELSE - pType is {pType}")
|
||
Try
|
||
oConvertedValue = pValue.ToString
|
||
Catch ex As Exception
|
||
LOGGER.Warn($"Error in GetConvertedValue: pType is {pType} - converting value to String")
|
||
oConvertedValue = ""
|
||
End Try
|
||
End Select
|
||
|
||
Return oConvertedValue
|
||
End Function
|
||
|
||
''' <summary>
|
||
''' Converts values to their respective data type and then back to string
|
||
''' using INVARIANT culture for consistency across systems.
|
||
''' </summary>
|
||
''' <param name="pValue"></param>
|
||
''' <returns></returns>
|
||
Public Shared Function GetStringValue(pValue As Object) As String
|
||
' ✅ FIX: Immer InvariantCulture verwenden für DB-Speicherung
|
||
Select Case pValue.GetType
|
||
Case GetType(Single)
|
||
' ✅ NEU: InvariantCulture statt CurrentCulture
|
||
Return DirectCast(pValue, Single).ToString(CultureInfo.InvariantCulture)
|
||
|
||
Case GetType(Double)
|
||
' ✅ NEU: InvariantCulture statt CurrentCulture
|
||
Return DirectCast(pValue, Double).ToString(CultureInfo.InvariantCulture)
|
||
|
||
Case GetType(Decimal)
|
||
' ✅ NEU: InvariantCulture statt CurrentCulture
|
||
Return DirectCast(pValue, Decimal).ToString(CultureInfo.InvariantCulture)
|
||
|
||
Case GetType(Date)
|
||
' Datum: ISO 8601 Format für Culture-Unabhängigkeit
|
||
Return DirectCast(pValue, Date).ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)
|
||
|
||
Case GetType(DateTime)
|
||
' DateTime: ISO 8601 Format
|
||
Return DirectCast(pValue, DateTime).ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)
|
||
|
||
Case Else
|
||
Return pValue.ToString
|
||
End Select
|
||
End Function
|
||
End Class
|