Die Methode `Create_PDFfromXML` wurde vollständig überarbeitet, um die Struktur und Lesbarkeit zu verbessern. Die Logik wurde modularisiert, indem neue Methoden wie `InitializeFilePaths`, `InitializePDF`, `ProcessInvoiceData` und `FinalizePDF` eingeführt wurden. Neue Hilfsklassen (`FilePaths`, `PdfRenderContext`, `InvoiceItemData`) wurden hinzugefügt, um die Datenstrukturierung und den Kontext zu verbessern. Die Verarbeitung von Bereichen und Folgeelementen wurde in spezifische Methoden ausgelagert (`HandleAreaSwitch`, `HandleFollowUpItem`). Die Rendering-Logik wurde durch Methoden wie `RenderDisplayItem` und `RenderMultiLineText` vereinfacht. Neue Konstanten für Layout und Textformate wurden eingeführt, um die Standardisierung zu fördern. Die Debug-Logs wurden erweitert, um detaillierte Einblicke in die Verarbeitungsschritte zu bieten. Die Änderungen verbessern die Wartbarkeit, Modularität und Robustheit der PDF-Erstellung erheblich.
983 lines
42 KiB
VB.net
983 lines
42 KiB
VB.net
Imports System.Collections.Generic
|
|
Imports System.Data
|
|
Imports System.IO
|
|
Imports DigitalData.Modules.Base
|
|
Imports DigitalData.Modules.Database
|
|
Imports DigitalData.Modules.Logging
|
|
Imports GdPicture14
|
|
Imports System.Drawing
|
|
Imports System.Linq
|
|
Imports System.Text.RegularExpressions
|
|
'11.11.2025
|
|
|
|
Public Class XRechnungViewDocument
|
|
Private ReadOnly _logger As Logger
|
|
Private ReadOnly _logConfig As LogConfig
|
|
Private ReadOnly _filesystem As FilesystemEx
|
|
Private ReadOnly _file As ZUGFeRD.FileFunctions
|
|
Private ReadOnly _gdpictureLicenseKey As String
|
|
Private fontResName As String
|
|
Private fontResNameBold As String
|
|
Private fontResNameItalic As String
|
|
|
|
' Layout-Konstanten
|
|
Private Const MARGIN_LEFT As Integer = 10
|
|
Private Const MARGIN_TOP As Integer = 15
|
|
Private Const LINE_WIDTH As Integer = 200
|
|
Private Const PAGE_HEIGHT_LIMIT As Integer = 270
|
|
Private Const FOOTER_Y As Integer = 280
|
|
Private Const FOOTER_TEXT_Y As Integer = 285
|
|
Private Const LINE_HEIGHT As Integer = 5
|
|
|
|
' Spalten-Positionen für Tabellen
|
|
Private Const COL_POS_NUMBER As Integer = 10
|
|
Private Const COL_POS_AMOUNT As Integer = 19
|
|
Private Const COL_POS_UNIT As Integer = 35
|
|
Private Const COL_POS_TEXT As Integer = 50
|
|
Private Const COL_POS_REASON As Integer = 20
|
|
Private Const COL_POS_TAX As Integer = 163
|
|
Private Const COL_POS_SUM As Integer = 181
|
|
Private Const COL_VALUE_X As Integer = 70
|
|
|
|
' Text-Größen
|
|
Private Const TEXT_SIZE_TITLE As Integer = 18
|
|
Private Const TEXT_SIZE_NORMAL As Integer = 10
|
|
|
|
' Text-Längen für Umbruch
|
|
Private Const MAX_TEXT_LENGTH_FULL As Integer = 112
|
|
Private Const MAX_TEXT_LENGTH_POSITION As Integer = 64
|
|
Private Const MAX_TEXT_LENGTH_NOTE As Integer = 70
|
|
|
|
Public Sub New(LogConfig As LogConfig, MSSQL As MSSQLServer, GDPictureLicenseKey As String)
|
|
_logConfig = LogConfig
|
|
_logger = LogConfig.GetLogger()
|
|
_filesystem = New FilesystemEx(_logConfig)
|
|
_file = New ZUGFeRD.FileFunctions(LogConfig, MSSQL)
|
|
_gdpictureLicenseKey = GDPictureLicenseKey
|
|
End Sub
|
|
|
|
Public Function Create_PDFfromXML(pXmlFile As FileInfo, pDTItemValues As DataTable) As FileInfo
|
|
_logger.Debug("Create_PDFfromXML() Start")
|
|
|
|
Try
|
|
' 1. Initialisierung der Dateipfade
|
|
Dim paths As FilePaths = InitializeFilePaths(pXmlFile)
|
|
If paths Is Nothing Then Return Nothing
|
|
|
|
' 2. PDF erstellen und konfigurieren
|
|
Dim pdfDoc As GdPicturePDF = InitializePDF()
|
|
If pdfDoc Is Nothing Then Return Nothing
|
|
|
|
' 3. Rendering-Context erstellen
|
|
Dim context As New PdfRenderContext(pdfDoc, paths.CreatedString)
|
|
|
|
' 4. Erste Seite mit Header/Footer
|
|
CreateNewPage(context)
|
|
|
|
' 5. Daten verarbeiten und rendern
|
|
ProcessInvoiceData(context, pDTItemValues)
|
|
|
|
' 6. XML einbetten und speichern
|
|
Return FinalizePDF(pdfDoc, paths)
|
|
|
|
Catch ex As Exception
|
|
_logger.Error(ex)
|
|
Return Nothing
|
|
End Try
|
|
End Function
|
|
|
|
#Region "Initialisierung"
|
|
|
|
Private Function InitializeFilePaths(xmlFile As FileInfo) As FilePaths
|
|
Try
|
|
Dim xmlPath As String = xmlFile.FullName
|
|
Dim tempPath As String = Path.Combine(Path.GetDirectoryName(xmlPath), "temp")
|
|
|
|
' Temp-Verzeichnis erstellen
|
|
If Not Directory.Exists(tempPath) Then
|
|
Directory.CreateDirectory(tempPath)
|
|
End If
|
|
|
|
Dim tempXmlPath As String = Path.Combine(tempPath, "xrechnung.xml")
|
|
If File.Exists(tempXmlPath) Then
|
|
File.Delete(tempXmlPath)
|
|
End If
|
|
|
|
Dim pdfFilename As String = Regex.Replace(xmlFile.Name, ".xml", ".pdf", RegexOptions.IgnoreCase)
|
|
Dim outputPath As String = Path.Combine(Path.GetDirectoryName(xmlPath), pdfFilename)
|
|
|
|
If File.Exists(outputPath) Then
|
|
File.Delete(outputPath)
|
|
End If
|
|
|
|
Dim paths As New FilePaths With {
|
|
.XmlPath = xmlPath,
|
|
.TempPath = tempPath,
|
|
.TempXmlPath = tempXmlPath,
|
|
.PdfFilename = pdfFilename,
|
|
.OutputPath = outputPath,
|
|
.CreatedString = $"Maschinell erstellt durch / Automatically created by Digital Data E-Rechnung Parser: {Now.ToString}"
|
|
}
|
|
|
|
_logger.Debug("Create_PDFfromXML() Resulting PDF Filepath: [{0}]", paths.OutputPath)
|
|
Return paths
|
|
|
|
Catch ex As Exception
|
|
_logger.Error("Error initializing file paths", ex)
|
|
Return Nothing
|
|
End Try
|
|
End Function
|
|
|
|
Private Function InitializePDF() As GdPicturePDF
|
|
Try
|
|
Dim licManager As New LicenseManager()
|
|
licManager.RegisterKEY(_gdpictureLicenseKey)
|
|
|
|
Dim pdf As New GdPicturePDF()
|
|
Dim status As GdPictureStatus = pdf.NewPDF(PdfConformance.PDF_A_3a)
|
|
|
|
If status <> GdPictureStatus.OK Then
|
|
_logger.Warn($"Error initializing PDF: {status}")
|
|
Return Nothing
|
|
End If
|
|
|
|
' PDF-Einstellungen
|
|
pdf.SetOrigin(PdfOrigin.PdfOriginTopLeft)
|
|
pdf.SetMeasurementUnit(PdfMeasurementUnit.PdfMeasurementUnitMillimeter)
|
|
pdf.SetLineWidth(1)
|
|
pdf.SetTitle("xInvoice VisualReceipt")
|
|
pdf.SetAuthor("Digital Data GmbH, Ludwig Rinn Str. 16, 35452 Heuchelheim")
|
|
|
|
' Fonts initialisieren
|
|
fontResName = pdf.AddStandardFont(PdfStandardFont.PdfStandardFontHelvetica)
|
|
fontResNameBold = pdf.AddStandardFont(PdfStandardFont.PdfStandardFontHelveticaBold)
|
|
fontResNameItalic = pdf.AddStandardFont(PdfStandardFont.PdfStandardFontHelveticaBoldOblique)
|
|
|
|
Return pdf
|
|
|
|
Catch ex As Exception
|
|
_logger.Error("Error initializing PDF", ex)
|
|
Return Nothing
|
|
End Try
|
|
End Function
|
|
|
|
#End Region
|
|
|
|
#Region "Seiten-Management"
|
|
|
|
Private Sub CreateNewPage(context As PdfRenderContext)
|
|
Dim status As GdPictureStatus = context.PDF.NewPage(PdfPageSizes.PdfPageSizeA4)
|
|
If status <> GdPictureStatus.OK Then
|
|
Throw New Exception($"Could not create page: {status}")
|
|
End If
|
|
|
|
DrawHeader(context.PDF)
|
|
DrawFooter(context.PDF, context.CreatedString)
|
|
context.YPosition = MARGIN_TOP + 30 ' Nach Header
|
|
End Sub
|
|
|
|
Private Sub DrawHeader(pdf As GdPicturePDF)
|
|
Dim y As Integer = MARGIN_TOP
|
|
|
|
pdf.SetTextSize(TEXT_SIZE_TITLE)
|
|
pdf.DrawText(fontResName, MARGIN_LEFT, y, "xRechnung Sichtbeleg - xInvoice Visual Receipt")
|
|
y += 10
|
|
|
|
pdf.SetTextSize(TEXT_SIZE_NORMAL)
|
|
pdf.DrawText(fontResNameItalic, MARGIN_LEFT, y, XRechnungStrings.CommentSichtbeleg_DE_Row1)
|
|
y += LINE_HEIGHT
|
|
pdf.DrawText(fontResNameItalic, MARGIN_LEFT, y, XRechnungStrings.CommentSichtbeleg_DE_Row2)
|
|
y += LINE_HEIGHT
|
|
pdf.DrawText(fontResNameItalic, MARGIN_LEFT, y, XRechnungStrings.CommentSichtbeleg_EN_Row1)
|
|
y += LINE_HEIGHT
|
|
pdf.DrawText(fontResNameItalic, MARGIN_LEFT, y, XRechnungStrings.CommentSichtbeleg_EN_Row2)
|
|
End Sub
|
|
|
|
Private Sub DrawFooter(pdf As GdPicturePDF, createdString As String)
|
|
pdf.DrawLine(MARGIN_LEFT, FOOTER_Y, LINE_WIDTH, FOOTER_Y)
|
|
pdf.DrawText(fontResName, MARGIN_LEFT, FOOTER_TEXT_Y, createdString)
|
|
End Sub
|
|
|
|
Private Sub CheckAndCreateNewPageIfNeeded(context As PdfRenderContext)
|
|
If context.YPosition >= PAGE_HEIGHT_LIMIT Then
|
|
Dim status As GdPictureStatus = context.PDF.NewPage(PdfPageSizes.PdfPageSizeA4)
|
|
If status <> GdPictureStatus.OK Then
|
|
_logger.Warn($"Could not create a second page. The error was: {status}")
|
|
Throw New Exception($"Could not create page: {status}")
|
|
End If
|
|
|
|
DrawHeader(context.PDF)
|
|
DrawFooter(context.PDF, context.CreatedString)
|
|
context.YPosition = MARGIN_TOP + 30
|
|
End If
|
|
End Sub
|
|
|
|
#End Region
|
|
|
|
#Region "Datenverarbeitung"
|
|
|
|
Private Sub ProcessInvoiceData(context As PdfRenderContext, dataTable As DataTable)
|
|
Dim formerItemSpecName As String = ""
|
|
|
|
For Each oRow As DataRow In dataTable.Rows
|
|
' Prüfen ob neue Seite benötigt wird
|
|
CheckAndCreateNewPageIfNeeded(context)
|
|
|
|
Dim itemData As New InvoiceItemData(oRow)
|
|
|
|
' Interne Zeilen behandeln
|
|
If itemData.NormalizedArea = "INTERNAL" Then
|
|
HandleInternalRow(context, itemData)
|
|
Continue For
|
|
End If
|
|
|
|
_logger.Debug($"WorkingItem: Area=[{itemData.NormalizedArea}] SpecName=[{itemData.SpecName}] Value=[{itemData.Value}] Caption=[{itemData.Caption}] Display=[{itemData.Display}] LastRow=[{itemData.IsLastRowSameArea}]")
|
|
|
|
' WICHTIG: Area-Switch-Flag VORHER setzen!
|
|
Dim isAreaSwitch As Boolean = (context.CurrentArea <> itemData.NormalizedArea)
|
|
|
|
' Area-Wechsel behandeln
|
|
If isAreaSwitch Then
|
|
formerItemSpecName = "" ' Reset bei Area-Wechsel
|
|
HandleAreaSwitch(context, itemData)
|
|
Else
|
|
HandleFollowUpItem(context, itemData, formerItemSpecName)
|
|
End If
|
|
|
|
' Item anzeigen - MIT Area-Switch-Info übergeben!
|
|
If itemData.Display AndAlso Not String.IsNullOrEmpty(itemData.Value) Then
|
|
RenderDisplayItem(context, itemData, isAreaSwitch) ' ← Parameter hinzugefügt!
|
|
End If
|
|
Next
|
|
End Sub
|
|
|
|
Private Sub HandleInternalRow(context As PdfRenderContext, itemData As InvoiceItemData)
|
|
_logger.Debug("Next Item as Area is internal")
|
|
If itemData.SpecName = "STATIC_Y_SWITCH" Then
|
|
context.YPosition = CInt(itemData.Value)
|
|
End If
|
|
End Sub
|
|
|
|
#End Region
|
|
|
|
#Region "Area-Switch Handling"
|
|
|
|
Private Sub HandleAreaSwitch(context As PdfRenderContext, itemData As InvoiceItemData)
|
|
' WICHTIG: CurrentArea speichert die NORMALISIERTE Area
|
|
context.CurrentArea = itemData.NormalizedArea
|
|
context.CreateTextBox = False
|
|
_logger.Debug($"Area-Switch to: {context.CurrentArea}")
|
|
|
|
' Area-Header zeichnen - ÜBERGIBT itemData komplett
|
|
Dim areaCaption As String = GetAreaCaption(context, itemData)
|
|
If Not String.IsNullOrEmpty(areaCaption) Then
|
|
DrawAreaHeader(context, areaCaption)
|
|
DrawAreaSpecificHeaders(context)
|
|
End If
|
|
|
|
' Area-spezifische Initialisierung - VERWENDET context.CurrentArea (normalisiert)
|
|
Select Case context.CurrentArea
|
|
Case "TYPE"
|
|
HandleTypeAreaSwitch(context, itemData)
|
|
Case "POSITION"
|
|
HandlePositionAreaSwitch(context, itemData)
|
|
Case "ALLOWANCE"
|
|
HandleAllowanceAreaSwitch(context, itemData)
|
|
Case "INCL_NOTE"
|
|
HandleIncludedNoteAreaSwitch(context, itemData)
|
|
Case "TAXPOS"
|
|
HandleTaxPosAreaSwitch(context, itemData)
|
|
Case "AMOUNT"
|
|
context.CreateTextBox = True
|
|
End Select
|
|
End Sub
|
|
|
|
Private Function GetAreaCaption(context As PdfRenderContext, itemData As InvoiceItemData) As String
|
|
' VERWENDET NormalizedArea für Switch, aber AreaCaptionFromPattern für INCL_NOTE
|
|
Select Case itemData.NormalizedArea
|
|
Case "TYPE"
|
|
Return $"{Return_InvType(itemData.Value)} [{itemData.Value}]"
|
|
Case "SELLER"
|
|
Return "Verkäufer / Seller:"
|
|
Case "BUYER"
|
|
Return "Käufer / Buyer:"
|
|
Case "HEAD"
|
|
Return String.Empty
|
|
Case "POSITION"
|
|
Return "Positionen / Positions:"
|
|
Case "INCL_NOTE"
|
|
' Verwendet den Caption aus dem Pattern INCL_NOTE#...
|
|
If Not String.IsNullOrEmpty(itemData.AreaCaptionFromPattern) Then
|
|
Return itemData.AreaCaptionFromPattern
|
|
End If
|
|
' Fallback wenn kein Pattern vorhanden
|
|
Return "Notizen und Hinweise / Notes:"
|
|
Case "ALLOWANCE"
|
|
Return GetAllowanceCaption(context, itemData)
|
|
Case "AMOUNT"
|
|
Return "Beträge / Amounts:"
|
|
Case "TAXPOS"
|
|
Return "Steuerbeträge / Tax amounts:"
|
|
Case "PAYMENT"
|
|
Return "Zahlungsinformationen / Payment details:"
|
|
Case "EXEMPTION"
|
|
Return "UST.-Befreiungsgrund / Exemption reason:"
|
|
Case Else
|
|
Return String.Empty
|
|
End Select
|
|
End Function
|
|
|
|
Private Function GetAllowanceCaption(context As PdfRenderContext, itemData As InvoiceItemData) As String
|
|
If itemData.SpecName = "RECEIPT_ALLOWANCE_CHARGE_INDICATOR" Then
|
|
If itemData.Value = "False" Then
|
|
context.HasDiscount = True
|
|
Return "Rabatt/Discount:"
|
|
Else
|
|
Return "Zuschlag/Surcharge:"
|
|
End If
|
|
End If
|
|
Return "Zu- oder Abschlag/Surcharge or Discount:"
|
|
End Function
|
|
|
|
Private Sub DrawAreaHeader(context As PdfRenderContext, caption As String)
|
|
context.YPosition += LINE_HEIGHT
|
|
context.PDF.DrawLine(MARGIN_LEFT, context.YPosition, LINE_WIDTH, context.YPosition)
|
|
context.YPosition += LINE_HEIGHT
|
|
context.PDF.DrawText(fontResNameBold, MARGIN_LEFT, context.YPosition, caption)
|
|
context.YPosition += LINE_HEIGHT
|
|
|
|
If context.CurrentArea = "TYPE" Then
|
|
context.PDF.DrawLine(MARGIN_LEFT, context.YPosition, LINE_WIDTH, context.YPosition)
|
|
context.YPosition += LINE_HEIGHT
|
|
End If
|
|
End Sub
|
|
|
|
Private Sub DrawAreaSpecificHeaders(context As PdfRenderContext)
|
|
'VERWENDET context.CurrentArea (bereits normalisiert)
|
|
Select Case context.CurrentArea
|
|
Case "POSITION"
|
|
DrawPositionTableHeader(context)
|
|
Case "INCL_NOTE"
|
|
' Kein spezifischer Header für INCL_NOTE mehr nötig
|
|
' Der Caption kommt bereits aus GetAreaCaption
|
|
Case "ALLOWANCE" ' ← KORRIGIERT!
|
|
DrawAllowanceTableHeader(context)
|
|
End Select
|
|
End Sub
|
|
|
|
Private Sub DrawPositionTableHeader(context As PdfRenderContext)
|
|
context.PDF.DrawText(fontResName, COL_POS_NUMBER, context.YPosition, "Pos#")
|
|
context.PDF.DrawText(fontResName, COL_POS_AMOUNT, context.YPosition, "Anz./am.")
|
|
context.PDF.DrawText(fontResName, COL_POS_UNIT, context.YPosition, "Einh/Unit")
|
|
context.PDF.DrawText(fontResName, COL_POS_TEXT, context.YPosition, "Pos.Text")
|
|
context.PDF.DrawText(fontResName, COL_POS_TAX, context.YPosition, "Steuer/Tax")
|
|
context.PDF.DrawText(fontResName, COL_POS_SUM, context.YPosition, "Betrag/Sum")
|
|
End Sub
|
|
|
|
Private Sub DrawAllowanceTableHeader(context As PdfRenderContext)
|
|
context.PDF.DrawText(fontResName, COL_POS_NUMBER, context.YPosition, "Pos#")
|
|
context.PDF.DrawText(fontResName, COL_POS_REASON, context.YPosition, "Grund/Reason")
|
|
context.PDF.DrawText(fontResName, COL_POS_TAX, context.YPosition, "Steuer/Tax")
|
|
context.PDF.DrawText(fontResName, COL_POS_SUM, context.YPosition, "Betrag/Sum")
|
|
context.PositionCount = 0
|
|
End Sub
|
|
|
|
#End Region
|
|
|
|
#Region "Area-Specific Switch Handlers"
|
|
|
|
Private Sub HandleTypeAreaSwitch(context As PdfRenderContext, itemData As InvoiceItemData)
|
|
If itemData.SpecName = "INVOICE_CURRENCY" Then
|
|
If itemData.Value <> "EUR" Then
|
|
context.CurrencySymbol = itemData.Value
|
|
End If
|
|
End If
|
|
End Sub
|
|
|
|
Private Sub HandlePositionAreaSwitch(context As PdfRenderContext, itemData As InvoiceItemData)
|
|
If itemData.SpecName = "INVOICE_POSITION_AMOUNT" Then
|
|
' KEIN Increment hier - wird in Follow-Up gemacht
|
|
context.YDynamic = 0
|
|
context.YPosition += LINE_HEIGHT ' Neue Zeile für erste Position
|
|
context.PDF.DrawText(fontResName, COL_POS_NUMBER, context.YPosition, "1") ' Erste Position ist immer 1
|
|
context.PDF.DrawText(fontResName, COL_POS_AMOUNT, context.YPosition, itemData.Value)
|
|
context.PositionCount = 1 ' Zähler auf 1 setzen statt increment
|
|
itemData.Display = False
|
|
End If
|
|
End Sub
|
|
|
|
Private Sub HandleAllowanceAreaSwitch(context As PdfRenderContext, itemData As InvoiceItemData)
|
|
' Erste Allowance: Prüfe ob es CHARGE_INDICATOR (Metadata) oder ACTUAL_AMOUNT (erste Zeile) ist
|
|
If itemData.SpecName = "RECEIPT_ALLOWANCE_CHARGE_INDICATOR" Then
|
|
' Nur Metadata - nichts rendern, nur Flag setzen
|
|
_logger.Debug($"Found RECEIPT_ALLOWANCE_CHARGE_INDICATOR with value [{itemData.Value}]. Setting HasDiscount={itemData.Value = "False"} in context.")
|
|
itemData.Display = False
|
|
ElseIf {"RECEIPT_ALLOWANCE_ACTUAL_AMOUNT", "POSITION_ALLOWANCE_ACTUAL_AMOUNT"}.Contains(itemData.SpecName) Then
|
|
' Erste Allowance-Zeile
|
|
context.YPosition += LINE_HEIGHT
|
|
context.PDF.DrawText(fontResName, COL_POS_NUMBER, context.YPosition, "1")
|
|
Dim currTerm As String = FormatCurrency(itemData.Value, context.CurrencySymbol)
|
|
If context.HasDiscount AndAlso itemData.SpecName = "RECEIPT_ALLOWANCE_ACTUAL_AMOUNT" AndAlso Not currTerm.StartsWith("-") Then
|
|
currTerm = "-" + currTerm
|
|
End If
|
|
context.PDF.DrawText(fontResName, COL_POS_SUM, context.YPosition, currTerm) ' ← In COL_POS_SUM!
|
|
context.PositionCount = 1
|
|
itemData.Display = False
|
|
_logger.Debug($"First Allowance/Charge rendered with amount [{currTerm}]. PositionCount set to 1. HasDiscount={context.HasDiscount}. Display set to False.")
|
|
End If
|
|
End Sub
|
|
|
|
Private Sub HandleIncludedNoteAreaSwitch(context As PdfRenderContext, itemData As InvoiceItemData)
|
|
' Erste Note direkt ausgeben
|
|
context.PDF.DrawText(fontResName, MARGIN_LEFT, context.YPosition, itemData.Value)
|
|
itemData.Display = False
|
|
End Sub
|
|
|
|
Private Sub HandleTaxPosAreaSwitch(context As PdfRenderContext, itemData As InvoiceItemData)
|
|
If itemData.SpecName = "INVOICE_TAXPOS_RATE" Then
|
|
context.PositionCount = 1
|
|
context.TaxPosText = $"{itemData.Value} %: " ' ← In Context speichern!
|
|
context.IsFirstTaxPosDisplay = True ' ← Flag setzen!
|
|
itemData.Display = False
|
|
_logger.Debug($"TAXPOS RATE in AreaSwitch accumulated: [{context.TaxPosText}]")
|
|
End If
|
|
End Sub
|
|
|
|
#End Region
|
|
|
|
|
|
#Region "Follow-Up Item Handling"
|
|
Private Sub HandleFollowUpItem(context As PdfRenderContext, itemData As InvoiceItemData, ByRef formerItemSpecName As String)
|
|
_logger.Debug($"FollowItem START - CurrentArea: [{context.CurrentArea}] - ItemArea: [{itemData.NormalizedArea}] - SpecName: [{itemData.SpecName}] - Value: [{itemData.Value}] - YPos: [{context.YPosition}]")
|
|
|
|
Dim descriptionFollowup As Boolean = False
|
|
|
|
Select Case context.CurrentArea
|
|
Case "POSITION", "ALLOWANCE"
|
|
_logger.Debug($"FollowItem: Entering POSITION/ALLOWANCE handler")
|
|
HandlePositionOrAllowanceFollowUp(context, itemData, formerItemSpecName, descriptionFollowup)
|
|
Case "HEAD"
|
|
_logger.Debug($"FollowItem: Entering HEAD handler")
|
|
HandleHeadFollowUp(itemData)
|
|
Case "TAXPOS"
|
|
_logger.Debug($"FollowItem: Entering TAXPOS handler")
|
|
HandleTaxPosFollowUp(context, itemData)
|
|
Case "INCL_NOTE"
|
|
_logger.Debug($"FollowItem: Entering INCL_NOTE handler - YPosition before={context.YPosition}")
|
|
context.YPosition += LINE_HEIGHT
|
|
context.PDF.DrawText(fontResName, MARGIN_LEFT, context.YPosition, itemData.Value)
|
|
itemData.Display = False
|
|
_logger.Debug($"FollowItem: INCL_NOTE handler - YPosition after={context.YPosition}, Display set to False")
|
|
Case Else
|
|
_logger.Warn($"FollowItem: UNHANDLED CurrentArea=[{context.CurrentArea}] for SpecName=[{itemData.SpecName}]")
|
|
End Select
|
|
|
|
_logger.Debug($"FollowItem END - CurrentArea: [{context.CurrentArea}] - YPos: [{context.YPosition}] - Display: [{itemData.Display}]")
|
|
End Sub
|
|
|
|
Private Sub HandlePositionOrAllowanceFollowUp(context As PdfRenderContext, itemData As InvoiceItemData, ByRef formerItemSpecName As String, ByRef descriptionFollowup As Boolean)
|
|
' Former Item Tracking
|
|
If itemData.SpecName <> formerItemSpecName AndAlso formerItemSpecName <> "" Then
|
|
If itemData.SpecName = "INVOICE_POSITION_ARTICLE_DESCRIPTION" AndAlso formerItemSpecName = "INVOICE_POSITION_ARTICLE" Then
|
|
descriptionFollowup = True
|
|
Else
|
|
formerItemSpecName = itemData.SpecName
|
|
End If
|
|
Else
|
|
formerItemSpecName = itemData.SpecName
|
|
End If
|
|
|
|
' Spezifische Item-Behandlung
|
|
If {"INVOICE_POSITION_AMOUNT", "POSITION_ALLOWANCE_ACTUAL_AMOUNT", "RECEIPT_ALLOWANCE_ACTUAL_AMOUNT"}.Contains(itemData.SpecName) Then
|
|
HandlePositionAmountFollowUp(context, itemData, descriptionFollowup)
|
|
ElseIf itemData.SpecName = "INVOICE_POSITION_UNIT_TYPE" Then
|
|
HandleUnitTypeFollowUp(context, itemData)
|
|
ElseIf {"POSITION_ALLOWANCE_REASON", "RECEIPT_ALLOWANCE_REASON"}.Contains(itemData.SpecName) Then
|
|
' ALLOWANCE_REASON direkt in Spalte schreiben
|
|
context.PDF.DrawText(fontResName, COL_POS_REASON, context.YPosition, itemData.Value)
|
|
itemData.Display = False
|
|
ElseIf {"INVOICE_POSITION_ARTICLE", "INVOICE_POSITION_ARTICLE_DESCRIPTION"}.Contains(itemData.SpecName) Then
|
|
HandleArticleTextFollowUp(context, itemData, descriptionFollowup)
|
|
ElseIf itemData.SpecName = "INVOICE_POSITION_NOTE" Then
|
|
HandlePositionNoteFollowUp(context, itemData)
|
|
ElseIf {"INVOICE_TAXPOS_TAX_RATE", "INVOICE_TAXPOS_RATE"}.Contains(itemData.SpecName) Then
|
|
' ← NUR für POSITION: Tax Rate
|
|
HandleTaxRateFollowUp(context, itemData)
|
|
ElseIf {"RECEIPT_ALLOWANCE_VAT_RATE", "POSITION_ALLOWANCE_VAT_RATE"}.Contains(itemData.SpecName) Then
|
|
' ← NEU: Für ALLOWANCE: VAT Rate (nicht CALCULATION_PERCENT!)
|
|
HandleTaxRateFollowUp(context, itemData)
|
|
ElseIf itemData.SpecName = "INVOICE_POSITION_TAX_AMOUNT" Then
|
|
HandlePositionTaxAmountFollowUp(context, itemData)
|
|
ElseIf {"RECEIPT_ALLOWANCE_VAT_CODE", "POSITION_ALLOWANCE_VAT_CODE"}.Contains(itemData.SpecName) Then
|
|
' VAT_CODE wird nicht angezeigt (nur Metadata)
|
|
itemData.Display = False
|
|
ElseIf {"RECEIPT_ALLOWANCE_CALCULATION_PERCENT", "POSITION_ALLOWANCE_CALCULATION_PERCENT"}.Contains(itemData.SpecName) Then
|
|
' ← NEU: CALCULATION_PERCENT wird nicht angezeigt (nur Metadata)
|
|
itemData.Display = False
|
|
ElseIf itemData.SpecName = "RECEIPT_ALLOWANCE_CHARGE_INDICATOR" Then
|
|
' CHARGE_INDICATOR im Follow-Up (zweite Allowance) wird nicht angezeigt
|
|
itemData.Display = False
|
|
End If
|
|
End Sub
|
|
|
|
Private Sub HandlePositionAmountFollowUp(context As PdfRenderContext, itemData As InvoiceItemData, descriptionFollowup As Boolean)
|
|
context.PositionCount += 1
|
|
If Not descriptionFollowup Then
|
|
context.YPlus = 0
|
|
context.YDynamic = 0
|
|
End If
|
|
|
|
' WICHTIG: Neue Zeile für jede neue Position!
|
|
context.YPosition += LINE_HEIGHT
|
|
|
|
context.PDF.DrawText(fontResName, COL_POS_NUMBER, context.YPosition, context.PositionCount.ToString())
|
|
|
|
If itemData.SpecName = "INVOICE_POSITION_AMOUNT" Then
|
|
context.PDF.DrawText(fontResName, COL_POS_AMOUNT, context.YPosition, itemData.Value)
|
|
ElseIf {"POSITION_ALLOWANCE_ACTUAL_AMOUNT", "RECEIPT_ALLOWANCE_ACTUAL_AMOUNT"}.Contains(itemData.SpecName) Then
|
|
Dim term As String = FormatCurrency(itemData.Value, context.CurrencySymbol)
|
|
If context.HasDiscount AndAlso itemData.SpecName = "RECEIPT_ALLOWANCE_ACTUAL_AMOUNT" AndAlso Not term.StartsWith("-") Then
|
|
term = "-" + term
|
|
End If
|
|
context.PDF.DrawText(fontResName, COL_POS_SUM, context.YPosition, term)
|
|
Else
|
|
If context.YDynamic = 0 Then
|
|
context.YDynamic = context.YPosition
|
|
End If
|
|
RenderMultiLineText(context, itemData.Value, COL_POS_AMOUNT, MAX_TEXT_LENGTH_POSITION)
|
|
End If
|
|
|
|
itemData.Display = False
|
|
End Sub
|
|
|
|
Private Sub HandleUnitTypeFollowUp(context As PdfRenderContext, itemData As InvoiceItemData)
|
|
context.YPlus = 0
|
|
Dim unit As String = Return_UnitType(itemData.Value)
|
|
context.PDF.DrawText(fontResName, COL_POS_UNIT, context.YPosition, unit)
|
|
itemData.Display = False
|
|
End Sub
|
|
|
|
Private Sub HandleArticleTextFollowUp(context As PdfRenderContext, itemData As InvoiceItemData, descriptionFollowup As Boolean)
|
|
If Not descriptionFollowup Then
|
|
context.YPlus = 0
|
|
End If
|
|
If context.YDynamic = 0 Then
|
|
context.YDynamic = context.YPosition
|
|
End If
|
|
|
|
Dim xPos As Integer = COL_POS_TEXT
|
|
If itemData.SpecName.Contains("ALLOWANCE") Then
|
|
xPos = COL_POS_REASON
|
|
End If
|
|
|
|
RenderMultiLineText(context, itemData.Value, xPos, MAX_TEXT_LENGTH_POSITION)
|
|
itemData.Display = False
|
|
End Sub
|
|
|
|
Private Sub HandlePositionNoteFollowUp(context As PdfRenderContext, itemData As InvoiceItemData)
|
|
If context.YDynamic = 0 Then
|
|
context.YDynamic = context.YPosition
|
|
End If
|
|
RenderMultiLineText(context, itemData.Value, COL_POS_TEXT, MAX_TEXT_LENGTH_NOTE)
|
|
itemData.Display = False
|
|
End Sub
|
|
|
|
Private Sub HandleTaxRateFollowUp(context As PdfRenderContext, itemData As InvoiceItemData)
|
|
context.PDF.DrawText(fontResName, COL_POS_TAX, context.YPosition, $"{itemData.Value} %")
|
|
itemData.Display = False
|
|
End Sub
|
|
|
|
Private Sub HandlePositionTaxAmountFollowUp(context As PdfRenderContext, itemData As InvoiceItemData)
|
|
Dim yPos As Double = context.YPosition - 3.5
|
|
Dim taxTerm As String = FormatCurrency(itemData.Value, context.CurrencySymbol)
|
|
context.PDF.DrawTextBox(fontResName, 177, yPos, 198, YCoo_TextBoxPlus5(yPos),
|
|
TextAlignment.TextAlignmentFar, TextAlignment.TextAlignmentNear,
|
|
taxTerm)
|
|
itemData.Display = False ' WICHTIG: Display auf False setzen!
|
|
End Sub
|
|
|
|
Private Sub HandleHeadFollowUp(itemData As InvoiceItemData)
|
|
If {"INVOICE_DATE", "INVOICE_SERVICE_DATE"}.Contains(itemData.SpecName) Then
|
|
itemData.Value = StringFunctions.DatetimeStringToGermanStringConverter(itemData.Value, _logger)
|
|
End If
|
|
End Sub
|
|
|
|
Private Sub HandleTaxPosFollowUp(context As PdfRenderContext, itemData As InvoiceItemData)
|
|
' TAXPOS Items werden zu einem String kombiniert
|
|
If itemData.SpecName = "INVOICE_TAXPOS_RATE" Then
|
|
context.PositionCount += 1
|
|
context.TaxPosText = $"{itemData.Value} %: " ' Speichern statt direkt setzen
|
|
itemData.Display = False
|
|
_logger.Debug($"TAXPOS RATE accumulated: [{context.TaxPosText}]")
|
|
ElseIf itemData.SpecName = "INVOICE_TAXPOS_AMOUNT" Then
|
|
Dim amount As String = FormatCurrency(itemData.Value, context.CurrencySymbol)
|
|
context.TaxPosText &= amount ' Anhängen
|
|
itemData.Display = False
|
|
_logger.Debug($"TAXPOS AMOUNT accumulated: [{context.TaxPosText}]")
|
|
ElseIf itemData.SpecName = "INVOICE_TAXPOS_TYPE" Then
|
|
context.TaxPosText &= $" {itemData.Value}" ' Anhängen
|
|
itemData.Value = context.TaxPosText ' JETZT den kombinierten String setzen
|
|
itemData.Display = True ' Und anzeigen!
|
|
context.TaxPosText = "" ' Reset für nächste TAXPOS
|
|
_logger.Debug($"TAXPOS TYPE final: [{itemData.Value}], Display=True")
|
|
ElseIf itemData.Value.Contains("EXEMPTION") Then
|
|
_logger.Debug($"We got an Exemption: {itemData.Value}")
|
|
End If
|
|
End Sub
|
|
|
|
#End Region
|
|
|
|
#Region "Rendering"
|
|
Private Sub RenderDisplayItem(context As PdfRenderContext, itemData As InvoiceItemData, isAreaSwitch As Boolean)
|
|
' Spezielle Behandlung für TAXPOS: Erstes Display-Item nach Area-Switch
|
|
Dim skipLineHeight As Boolean = isAreaSwitch OrElse context.IsFirstTaxPosDisplay
|
|
|
|
If context.IsFirstTaxPosDisplay Then
|
|
context.IsFirstTaxPosDisplay = False ' Reset nach Verwendung
|
|
_logger.Debug($"RenderDisplayItem: TAXPOS first display item - skipping line height")
|
|
End If
|
|
|
|
' Y-Position anpassen - ABER NICHT nach Area-Switch oder erstem TAXPOS Display!
|
|
If Not itemData.IsLastRowSameArea AndAlso Not skipLineHeight Then
|
|
context.YPosition += LINE_HEIGHT
|
|
_logger.Debug($"RenderDisplayItem: Adding line height. New YPosition: {context.YPosition}")
|
|
End If
|
|
|
|
' Formatierung für Währungsfelder
|
|
Dim displayValue As String = itemData.Value
|
|
If ShouldFormatAsCurrency(context.CurrentArea, itemData.SpecName) Then
|
|
displayValue = FormatCurrency(itemData.Value, context.CurrencySymbol)
|
|
End If
|
|
|
|
' Rendern basierend auf Layout
|
|
If Not String.IsNullOrEmpty(itemData.Caption) Then
|
|
RenderLabelValuePair(context, itemData.Caption, displayValue)
|
|
Else
|
|
If itemData.IsLastRowSameArea Then
|
|
context.PDF.DrawText(fontResName, itemData.XPosition, context.YPosition, displayValue)
|
|
Else
|
|
RenderTextWithWrapping(context, displayValue)
|
|
End If
|
|
End If
|
|
End Sub
|
|
|
|
Private Function ShouldFormatAsCurrency(area As String, specName As String) As Boolean
|
|
If area <> "AMOUNT" AndAlso area <> "ALLOWANCE" Then Return False
|
|
|
|
Dim currencyFields As String() = {
|
|
"INVOICE_TOTAL_TAX", "INVOICE_TOTAL_NET", "INVOICE_TOTAL_GROSS",
|
|
"INVOICE_TOTAL_CHARGE_AMOUNT", "POSITION_ALLOWANCE_ACTUAL_AMOUNT",
|
|
"RECEIPT_ALLOWANCE_ACTUAL_AMOUNT", "POSITION_ALLOWANCE_CALCULATION_PERCENT",
|
|
"RECEIPT_ALLOWANCE_CALCULATION_PERCENT"
|
|
}
|
|
|
|
Return currencyFields.Contains(specName)
|
|
End Function
|
|
|
|
Private Sub RenderLabelValuePair(context As PdfRenderContext, label As String, value As String)
|
|
context.PDF.DrawText(fontResName, MARGIN_LEFT, context.YPosition, label)
|
|
|
|
If context.CreateTextBox Then
|
|
Dim textBoxYPos As Integer = context.YPosition - 3
|
|
context.PDF.DrawTextBox(fontResName, COL_VALUE_X, textBoxYPos, 90, YCoo_TextBoxPlus5(textBoxYPos),
|
|
TextAlignment.TextAlignmentFar, TextAlignment.TextAlignmentCenter,
|
|
value)
|
|
Else
|
|
context.PDF.DrawText(fontResName, COL_VALUE_X, context.YPosition, value)
|
|
End If
|
|
End Sub
|
|
|
|
Private Sub RenderTextWithWrapping(context As PdfRenderContext, text As String)
|
|
If text.Length > MAX_TEXT_LENGTH_FULL Then
|
|
For i As Integer = 0 To text.Length - 1 Step MAX_TEXT_LENGTH_FULL
|
|
Dim length As Integer = Math.Min(MAX_TEXT_LENGTH_FULL, text.Length - i)
|
|
Dim part As String = text.Substring(i, length)
|
|
context.PDF.DrawText(fontResName, MARGIN_LEFT, context.YPosition, part)
|
|
context.YPosition += LINE_HEIGHT
|
|
Next
|
|
Else
|
|
context.PDF.DrawText(fontResName, MARGIN_LEFT, context.YPosition, text)
|
|
End If
|
|
End Sub
|
|
|
|
Private Sub RenderMultiLineText(context As PdfRenderContext, text As String, xPos As Integer, maxLength As Integer)
|
|
Dim partsNL As List(Of String) = StringFunctions.SplitTextByNewLine(text)
|
|
|
|
For Each linePart As String In partsNL
|
|
Dim parts As List(Of String) = StringFunctions.SplitText_Length(linePart, maxLength)
|
|
For Each part As String In parts
|
|
context.PDF.DrawText(fontResName, xPos, context.YDynamic, part)
|
|
context.YDynamic += LINE_HEIGHT
|
|
context.YPlus += LINE_HEIGHT
|
|
Next
|
|
Next
|
|
End Sub
|
|
|
|
#End Region
|
|
|
|
#Region "Finalisierung"
|
|
|
|
Private Function FinalizePDF(pdf As GdPicturePDF, paths As FilePaths) As FileInfo
|
|
Try
|
|
' XML einbetten
|
|
If File.Exists(paths.XmlPath) Then
|
|
pdf.EmbedFile(paths.XmlPath, "E-invoice XML attachment")
|
|
Else
|
|
_logger.Info("XML File is not existing and could not be embedded!")
|
|
Return Nothing
|
|
End If
|
|
|
|
pdf.EnableCompression(True)
|
|
|
|
' Speichern
|
|
Dim status As GdPictureStatus = pdf.SaveToFile(paths.OutputPath)
|
|
If status = GdPictureStatus.OK Then
|
|
_logger.Info("PDF VisualReceipt generated successfully!")
|
|
_logger.Debug("Vor MOVE... oxmlFilePath: [{0}] / oTempFilePath: [{1}]", paths.XmlPath, paths.TempXmlPath)
|
|
File.Move(paths.XmlPath, paths.TempXmlPath)
|
|
|
|
pdf.CloseDocument()
|
|
Dim result As New FileInfo(paths.OutputPath)
|
|
_logger.Info("Create_PDFfromXML() End successfully. File [{0}] written.", result.FullName)
|
|
Return result
|
|
Else
|
|
_logger.Warn($"Error generating PDF VisualReceipt: {status}")
|
|
pdf.CloseDocument()
|
|
Return Nothing
|
|
End If
|
|
|
|
Catch ex As Exception
|
|
pdf.CloseDocument()
|
|
_logger.Error("Error finalizing PDF", ex)
|
|
Return Nothing
|
|
End Try
|
|
End Function
|
|
|
|
#End Region
|
|
|
|
#Region "Helper Classes"
|
|
|
|
Private Class FilePaths
|
|
Public Property XmlPath As String
|
|
Public Property TempPath As String
|
|
Public Property TempXmlPath As String
|
|
Public Property PdfFilename As String
|
|
Public Property OutputPath As String
|
|
Public Property CreatedString As String
|
|
End Class
|
|
|
|
Private Class PdfRenderContext
|
|
Public Property PDF As GdPicturePDF
|
|
Public Property YPosition As Integer
|
|
Public Property CurrentArea As String
|
|
Public Property CurrencySymbol As String
|
|
Public Property PositionCount As Integer
|
|
Public Property HasDiscount As Boolean
|
|
Public Property YDynamic As Integer
|
|
Public Property YPlus As Integer
|
|
Public Property CreateTextBox As Boolean
|
|
Public Property CreatedString As String
|
|
Public Property TaxPosText As String
|
|
Public Property IsFirstTaxPosDisplay As Boolean ' ← NEU
|
|
|
|
Public Sub New(pdf As GdPicturePDF, createdString As String)
|
|
Me.PDF = pdf
|
|
Me.CreatedString = createdString
|
|
YPosition = MARGIN_TOP
|
|
CurrentArea = String.Empty
|
|
CurrencySymbol = "€"
|
|
PositionCount = 0
|
|
HasDiscount = False
|
|
YDynamic = 0
|
|
YPlus = 0
|
|
CreateTextBox = False
|
|
TaxPosText = ""
|
|
IsFirstTaxPosDisplay = False ' ← NEU
|
|
End Sub
|
|
End Class
|
|
|
|
Private Class InvoiceItemData
|
|
Public Property Area As String
|
|
Public Property SpecName As String
|
|
Public Property Value As String
|
|
Public Property Caption As String
|
|
Public Property Display As Boolean
|
|
Public Property IsLastRowSameArea As Boolean
|
|
Public Property XPosition As Integer
|
|
Public Property AreaSwitch As Boolean
|
|
|
|
' Neue Property für normalisierte Area
|
|
Public ReadOnly Property NormalizedArea As String
|
|
Get
|
|
Return NormalizeAreaName(Area)
|
|
End Get
|
|
End Property
|
|
|
|
' Neue Property für Area-Caption (nur bei INCL_NOTE#)
|
|
Public ReadOnly Property AreaCaptionFromPattern As String
|
|
Get
|
|
If Area.StartsWith("INCL_NOTE#") Then
|
|
Return Area.Substring(10) ' Entfernt "INCL_NOTE#"
|
|
End If
|
|
Return String.Empty
|
|
End Get
|
|
End Property
|
|
|
|
Public Sub New(row As DataRow)
|
|
Area = GetSafeString(row, "Area")
|
|
SpecName = GetSafeString(row, "SPEC_NAME")
|
|
Value = GetSafeString(row, "ITEM_VALUE")
|
|
Caption = GetSafeString(row, "Row_Caption")
|
|
Display = GetSafeBoolean(row, "Display", False)
|
|
IsLastRowSameArea = GetSafeBoolean(row, "Y_eq_lastrow", False)
|
|
XPosition = GetSafeInteger(row, "xPosition", MARGIN_LEFT)
|
|
AreaSwitch = False
|
|
End Sub
|
|
|
|
Private Shared Function GetSafeString(row As DataRow, columnName As String) As String
|
|
If row.Table.Columns.Contains(columnName) AndAlso Not IsDBNull(row.Item(columnName)) Then
|
|
Return row.Item(columnName).ToString()
|
|
End If
|
|
Return String.Empty
|
|
End Function
|
|
|
|
Private Shared Function GetSafeBoolean(row As DataRow, columnName As String, defaultValue As Boolean) As Boolean
|
|
If row.Table.Columns.Contains(columnName) AndAlso Not IsDBNull(row.Item(columnName)) Then
|
|
Return CBool(row.Item(columnName))
|
|
End If
|
|
Return defaultValue
|
|
End Function
|
|
|
|
Private Shared Function GetSafeInteger(row As DataRow, columnName As String, defaultValue As Integer) As Integer
|
|
If row.Table.Columns.Contains(columnName) AndAlso Not IsDBNull(row.Item(columnName)) Then
|
|
Return CInt(row.Item(columnName))
|
|
End If
|
|
Return defaultValue
|
|
End Function
|
|
|
|
' Statische Hilfsmethode für Area-Normalisierung
|
|
Private Shared Function NormalizeAreaName(area As String) As String
|
|
If area.StartsWith("INCL_NOTE#") Then
|
|
Return "INCL_NOTE"
|
|
End If
|
|
Return area
|
|
End Function
|
|
End Class
|
|
|
|
#End Region
|
|
|
|
#Region "Hilfsfunktionen"
|
|
|
|
Private Function FormatCurrency(ByVal pValue As String, pCurrencySymbol As String) As String
|
|
pValue = pValue.Trim()
|
|
|
|
Dim oBetrag As Decimal
|
|
Dim culture As Globalization.CultureInfo
|
|
|
|
' Erkennung des Dezimaltrennzeichens
|
|
If pValue.Contains(",") AndAlso Not pValue.Contains(".") Then
|
|
culture = New Globalization.CultureInfo("de-DE") ' Komma → deutsches Format
|
|
ElseIf pValue.Contains(".") AndAlso Not pValue.Contains(",") Then
|
|
culture = New Globalization.CultureInfo("en-US") ' Punkt → englisches Format
|
|
Else
|
|
' Mischformat oder Tausendertrennzeichen → Fallback auf aktuelle Culture
|
|
culture = Globalization.CultureInfo.CurrentCulture
|
|
End If
|
|
|
|
' Parsen mit gewählter Culture
|
|
If Not Decimal.TryParse(pValue, Globalization.NumberStyles.Any, culture, oBetrag) Then
|
|
Throw New FormatException($"Ungültiger Zahlenwert: {pValue}")
|
|
End If
|
|
|
|
' Formatieren mit deutscher Culture
|
|
Dim oFormatiert As String = oBetrag.ToString("N2", New Globalization.CultureInfo("de-DE"))
|
|
Return $"{oFormatiert} {pCurrencySymbol}"
|
|
End Function
|
|
|
|
Private Function FormatStringT(ByVal text As String) As String
|
|
Return text.Replace(vbCr, " - ").Replace(vbLf, "").Replace(vbTab, " ")
|
|
End Function
|
|
|
|
Private Function RemoveNewlinesAndTabs(ByVal text As String) As String
|
|
Return text.Replace(vbCr, " - ").Replace(vbLf, "").Replace(vbTab, " ")
|
|
End Function
|
|
|
|
Private Function YCoo_TextBoxMinus5(yPosition As Integer) As Integer
|
|
Return yPosition - 5
|
|
End Function
|
|
|
|
Private Function YCoo_TextBoxPlus5(yPosition As Integer) As Integer
|
|
Return yPosition + 5
|
|
End Function
|
|
|
|
Function SplitTextByNewLine(text As String) As List(Of String)
|
|
If String.IsNullOrEmpty(text) Then
|
|
Return New List(Of String)()
|
|
End If
|
|
|
|
' Zerlege den Text anhand von Zeilenumbrüchen
|
|
Dim lines As List(Of String) = text.Split({vbCrLf, vbLf, vbCr}, StringSplitOptions.None).ToList()
|
|
Return lines
|
|
End Function
|
|
|
|
Private Function Return_InvType(pType As String) As String
|
|
Dim oReturn As String = "Rechnung/invoice"
|
|
If pType = "380" Then
|
|
oReturn = "Handelsrechnung/Commercial invoice"
|
|
ElseIf pType = "381" Then
|
|
oReturn = "Gutschriftanzeige/Credit advice"
|
|
ElseIf pType = "384" Then
|
|
oReturn = "Rechnungskorrektur/Invoice correction"
|
|
ElseIf pType = "386" Then
|
|
oReturn = "Vorauszahlungsrechnung/Prepayment invoice"
|
|
ElseIf pType = "326" Then
|
|
oReturn = "Teilrechnung/Partial invoice"
|
|
ElseIf pType = "84" Then
|
|
oReturn = "Gutschrift/Credit note"
|
|
ElseIf pType = "389" Then
|
|
oReturn = "Gutschriftsverfahren/Credit note procedure"
|
|
End If
|
|
Return oReturn
|
|
End Function
|
|
|
|
Private Function Return_UnitType(pType As String) As String
|
|
Dim oReturn As String = "Stück/pc"
|
|
If pType = "C62" Then
|
|
oReturn = "Stück/pc"
|
|
ElseIf pType = "DAY" Then
|
|
oReturn = "Tag/day"
|
|
ElseIf pType = "HAR" Then
|
|
oReturn = "Hek/hec"
|
|
ElseIf pType = "HUR" Then
|
|
oReturn = "h"
|
|
ElseIf pType = "KGM" Then
|
|
oReturn = "kg"
|
|
ElseIf pType = "KTM" Then
|
|
oReturn = "km"
|
|
ElseIf pType = "KWH" Then
|
|
oReturn = pType
|
|
ElseIf pType = "LS" Then
|
|
oReturn = "pausch/flat"
|
|
ElseIf pType = "MIN" Then
|
|
oReturn = "minute"
|
|
ElseIf pType = "MTK" Then
|
|
oReturn = "QM/SM"
|
|
ElseIf pType = "Kubik/CM" Then
|
|
oReturn = "MTR"
|
|
ElseIf pType = "Meter" Then
|
|
oReturn = "minute"
|
|
ElseIf pType = "P1" Then
|
|
oReturn = "%"
|
|
ElseIf pType = "SET" Then
|
|
oReturn = "Set"
|
|
ElseIf pType = "TNE" Then
|
|
oReturn = "Tonne/ton"
|
|
ElseIf pType = "WEE" Then
|
|
oReturn = "Woche/week"
|
|
End If
|
|
Return oReturn
|
|
End Function
|
|
|
|
#End Region
|
|
|
|
End Class |