Modules.Interfaces & Modules.Jobs: Verarbeitung von XML-Belegen im ZUGFeRD Service implementiert

This commit is contained in:
PitzM 2025-01-28 14:21:01 +01:00
parent 954df832ed
commit 3e2606a582
2 changed files with 165 additions and 46 deletions

View File

@ -1,7 +1,9 @@
Imports System.IO Imports System.IO
Imports System.Reflection
Imports System.Xml Imports System.Xml
Imports System.Xml.Serialization Imports System.Xml.Serialization
Imports DigitalData.Modules.Interfaces.Exceptions Imports DigitalData.Modules.Interfaces.Exceptions
Imports DigitalData.Modules.Interfaces.PDFEmbeds
Imports DigitalData.Modules.Interfaces.Peppol Imports DigitalData.Modules.Interfaces.Peppol
Imports DigitalData.Modules.Interfaces.ZUGFeRD Imports DigitalData.Modules.Interfaces.ZUGFeRD
Imports DigitalData.Modules.Logging Imports DigitalData.Modules.Logging
@ -138,6 +140,44 @@ Public Class ZUGFeRDInterface
End If End If
End Function End Function
Public Function GetSerializedXMLContentFromFile(oFileInfo As FileInfo) As ZugferdResult
Dim oResult = New ZugferdResult()
Try
Dim oFileSize As Integer = oFileInfo.Length
Dim oFileData As Byte() = File.ReadAllBytes(oFileInfo.FullName)
Dim oXmlFile As EmbeddedFile = New EmbeddedFile() With {
.FileName = oFileInfo.Name,
.FileContents = oFileData
}
Using oStream As New MemoryStream(oXmlFile.FileContents)
oResult = New ZugferdResult With {
.DataFileName = oXmlFile.FileName,
.XElementObject = XElement.Load(oStream)
}
End Using
Catch ex As ZUGFeRDExecption
' Don't log ZUGFeRD Exceptions here, they should be handled by the calling code.
' It also produces misleading error messages when checking if an attachment is a zugferd file.
Throw ex
Catch ex As Exception
_logger.Error(ex)
Throw New ZUGFeRDExecption(ErrorType.NoValidZugferd, "Datei ist eine ungültige XML Datei.")
End Try
If oResult.ValidationErrors.Any() Then
Throw New ValidationException() With {
.ValidationErrors = oResult.ValidationErrors
}
End If
Return SerializeZUGFeRDDocument(oResult)
End Function
''' <summary> ''' <summary>
''' Validates a ZUGFeRD File and extracts the XML Document from it ''' Validates a ZUGFeRD File and extracts the XML Document from it
''' </summary> ''' </summary>

View File

@ -166,10 +166,18 @@ Public Class ImportZUGFeRDFiles
End If End If
Dim oEmailDataBase = _email.GetEmailDataForMessageId(oMessageId) Dim oEmailDataBase = _email.GetEmailDataForMessageId(oMessageId)
Dim oFileCounter As Integer = 0
Try Try
For Each oFile In oFileGroupFiles For Each oFile In oFileGroupFiles
Dim oResult As ProcessFileResult = ProcessFile(oMessageId, oEmailDataBase, oZUGFeRDCount, oFile, oConnections, oArgs) oFileCounter += 1
Dim oResult As ProcessFileResult
If oFileCounter = 1 AndAlso oFile.Name.ToUpper.EndsWith(".XML") Then
oResult = ProcessXMLFile(oMessageId, oZUGFeRDCount, oFile, oConnections, oArgs)
Else
oResult = ProcessFile(oMessageId, oZUGFeRDCount, oFile, oConnections, oArgs)
End If
If oResult.ZugferdFileFound = True Then If oResult.ZugferdFileFound = True Then
_logger.Debug("Zugferd File found") _logger.Debug("Zugferd File found")
@ -455,34 +463,25 @@ Public Class ImportZUGFeRDFiles
Return oRejectionCodeString Return oRejectionCodeString
End Function End Function
Private Function ProcessFile(pMessageId As String, pEmailData As EmailData, pZugferdFiles As Integer, oFile As FileInfo, oConnections As DatabaseConnections, pArgs As WorkerArgs) As ProcessFileResult Private Function ProcessXMLFile(pMessageId As String, pZugferdFileCounter As Integer, pFile As FileInfo, pConnections As DatabaseConnections, pArgs As WorkerArgs) As ProcessFileResult
Dim oDocument As ZUGFeRDInterface.ZugferdResult Dim oDocument As ZUGFeRDInterface.ZugferdResult
Dim oResult As New ProcessFileResult() Dim oResult As New ProcessFileResult()
' Only pdf files are allowed from here on If pFile.Extension.Equals(".xml", StringComparison.OrdinalIgnoreCase) = False Then
If Not oFile.Name.ToUpper.EndsWith(".PDF") Then ' Diese Methode ist nur für den xml-Beleg gedacht
_logger.Debug("Skipping non-pdf file {0}", oFile.Name)
oResult.EmailAttachmentFiles.Add(oFile)
' Checking filesize for attachment files
If _filesystem.TestFileSizeIsLessThanMaxFileSize(oFile.FullName, pArgs.MaxAttachmentSizeInMegaBytes) = False Then
_logger.Warn("Filesize for File [{0}] exceeded limit of {1} MB", oFile.Name, pArgs.MaxAttachmentSizeInMegaBytes)
Throw New FileSizeLimitReachedException(oFile.Name, pArgs.MaxAttachmentSizeInMegaBytes)
End If
Return oResult Return oResult
End If End If
_logger.Info("Start processing file {0}", oFile.Name) _logger.Info("Start xml processing file {0}", pFile.Name)
' Checking filesize for pdf files ' Checking filesize
If _filesystem.TestFileSizeIsLessThanMaxFileSize(oFile.FullName, pArgs.MaxAttachmentSizeInMegaBytes) = False Then If _filesystem.TestFileSizeIsLessThanMaxFileSize(pFile.FullName, pArgs.MaxAttachmentSizeInMegaBytes) = False Then
_logger.Warn("Filesize for File [{0}] exceeded limit of {1} MB", oFile.Name, pArgs.MaxAttachmentSizeInMegaBytes) _logger.Warn("Filesize for File [{0}] exceeded limit of {1} MB", pFile.Name, pArgs.MaxAttachmentSizeInMegaBytes)
Throw New FileSizeLimitReachedException(oFile.Name, pArgs.MaxAttachmentSizeInMegaBytes) Throw New FileSizeLimitReachedException(pFile.Name, pArgs.MaxAttachmentSizeInMegaBytes)
End If End If
Try Try
oDocument = _zugferd.ExtractZUGFeRDFileWithGDPicture(oFile.FullName) oDocument = _zugferd.GetSerializedXMLContentFromFile(pFile)
Catch ex As ValidationException Catch ex As ValidationException
Throw ex Throw ex
@ -490,70 +489,156 @@ Public Class ImportZUGFeRDFiles
Catch ex As ZUGFeRDExecption Catch ex As ZUGFeRDExecption
Select Case ex.ErrorType Select Case ex.ErrorType
Case ZUGFeRDInterface.ErrorType.NoZugferd Case ZUGFeRDInterface.ErrorType.NoZugferd
_logger.Info("File [{0}] is not a valid ZUGFeRD document. Skipping.", oFile.Name) _logger.Info("File [{0}] is not a valid ZUGFeRD document. Skipping.", pFile.Name)
oResult.EmailAttachmentFiles.Add(oFile) oResult.EmailAttachmentFiles.Add(pFile)
Return oResult Return oResult
Case ZUGFeRDInterface.ErrorType.UnsupportedFormat Case ZUGFeRDInterface.ErrorType.UnsupportedFormat
_logger.Info("File [{0}/{1}] is an unsupported ZUFeRD document format!", oFile.Name, ex.XmlFile) _logger.Info("File [{0}/{1}] is an unsupported ZUFeRD document format!", pFile.Name, ex.XmlFile)
Throw New UnsupportedFerdException(ex.XmlFile) Throw New UnsupportedFerdException(ex.XmlFile)
Case ZUGFeRDInterface.ErrorType.NoValidZugferd Case ZUGFeRDInterface.ErrorType.NoValidZugferd
_logger.Info("File [{0}] is an Incorrectly formatted ZUGFeRD document!", oFile.Name) _logger.Info("File [{0}] is an Incorrectly formatted ZUGFeRD document!", pFile.Name)
Throw New InvalidFerdException() Throw New InvalidFerdException()
Case Else Case Else
_logger.Warn("Unexpected Error occurred while extracting ZUGFeRD Information from file {0}", oFile.Name) _logger.Warn("Unexpected Error occurred while extracting ZUGFeRD Information from file {0}", pFile.Name)
Throw ex
End Select
End Try
Try
Dim sqlResult As Boolean = StoreXMLItemsInDatabase(pMessageId, oDocument, pFile, pConnections, pArgs)
Catch ex As Exception
Throw ex
End Try
_logger.Debug("File processed.")
Dim oMD5Checksum = _hash.GenerateAndCheck_MD5Sum(pFile, pMessageId, pArgs.IgnoreRejectionStatus)
oResult.ZugferdFileFound = True
oResult.MD5Checksum = oMD5Checksum
oResult.ZugferdFileCount = 1 ' Es kann hier nur genau einen Treffer geben!
Return oResult
End Function
Private Function ProcessFile(pMessageId As String, pZugferdFileCounter As Integer, pFile As FileInfo, pConnections As DatabaseConnections, pArgs As WorkerArgs) As ProcessFileResult
Dim oDocument As ZUGFeRDInterface.ZugferdResult
Dim oResult As New ProcessFileResult()
' Only pdf files are allowed from here on
If Not pFile.Name.ToUpper.EndsWith(".PDF") Then
_logger.Debug("Skipping non-pdf file {0}", pFile.Name)
oResult.EmailAttachmentFiles.Add(pFile)
' Checking filesize for attachment files
If _filesystem.TestFileSizeIsLessThanMaxFileSize(pFile.FullName, pArgs.MaxAttachmentSizeInMegaBytes) = False Then
_logger.Warn("Filesize for File [{0}] exceeded limit of {1} MB", pFile.Name, pArgs.MaxAttachmentSizeInMegaBytes)
Throw New FileSizeLimitReachedException(pFile.Name, pArgs.MaxAttachmentSizeInMegaBytes)
End If
Return oResult
End If
_logger.Info("Start processing file {0}", pFile.Name)
' Checking filesize for pdf files
If _filesystem.TestFileSizeIsLessThanMaxFileSize(pFile.FullName, pArgs.MaxAttachmentSizeInMegaBytes) = False Then
_logger.Warn("Filesize for File [{0}] exceeded limit of {1} MB", pFile.Name, pArgs.MaxAttachmentSizeInMegaBytes)
Throw New FileSizeLimitReachedException(pFile.Name, pArgs.MaxAttachmentSizeInMegaBytes)
End If
Try
oDocument = _zugferd.ExtractZUGFeRDFileWithGDPicture(pFile.FullName)
Catch ex As ValidationException
Throw ex
Catch ex As ZUGFeRDExecption
Select Case ex.ErrorType
Case ZUGFeRDInterface.ErrorType.NoZugferd
_logger.Info("File [{0}] is not a valid ZUGFeRD document. Skipping.", pFile.Name)
oResult.EmailAttachmentFiles.Add(pFile)
Return oResult
Case ZUGFeRDInterface.ErrorType.UnsupportedFormat
_logger.Info("File [{0}/{1}] is an unsupported ZUFeRD document format!", pFile.Name, ex.XmlFile)
Throw New UnsupportedFerdException(ex.XmlFile)
Case ZUGFeRDInterface.ErrorType.NoValidZugferd
_logger.Info("File [{0}] is an Incorrectly formatted ZUGFeRD document!", pFile.Name)
Throw New InvalidFerdException()
Case Else
_logger.Warn("Unexpected Error occurred while extracting ZUGFeRD Information from file {0}", pFile.Name)
Throw ex Throw ex
End Select End Select
End Try End Try
' Check if there are more than one ZUGFeRD files ' Check if there are more than one ZUGFeRD files
If pZugferdFiles = 1 Then If pZugferdFileCounter = 1 Then
Throw New TooMuchFerdsException() Throw New TooMuchFerdsException()
End If End If
' Since extraction went well, increase the amount of ZUGFeRD files ' Since extraction went well, increase the amount of ZUGFeRD files
pZugferdFiles += 1 pZugferdFileCounter += 1
_logger.Info("Zugferd file found. Increasing counter.") _logger.Info("Zugferd file found. Increasing counter.")
' Extract all attachments with the extensions specified in `AllowedExtensions`. ' Extract all attachments with the extensions specified in `AllowedExtensions`.
' If you need to extract and use embedded xml files, you need to filter out the zugferd-invoice.xml yourself. ' If you need to extract and use embedded xml files, you need to filter out the zugferd-invoice.xml yourself.
' Right now the zugferd-invoice.xml is filtered out because `AllowedExtensions` does not contain `xml`. ' Right now the zugferd-invoice.xml is filtered out because `AllowedExtensions` does not contain `xml`.
Dim oAttachments = _embeds.Extract(oFile.FullName, AllowedExtensions) Dim oAttachments = _embeds.Extract(pFile.FullName, AllowedExtensions)
If oAttachments Is Nothing Then If oAttachments Is Nothing Then
_logger.Warn("Attachments for file [{0}] could not be extracted", oFile.FullName) _logger.Warn("Attachments for file [{0}] could not be extracted", pFile.FullName)
Else Else
oResult.EmbeddedAttachmentFiles.AddRange(oAttachments) oResult.EmbeddedAttachmentFiles.AddRange(oAttachments)
End If End If
' Check the Checksum and rejection status Try
Dim oMD5Checksum = _hash.GenerateAndCheck_MD5Sum(oFile, pMessageId, pArgs.IgnoreRejectionStatus) Dim sqlResult As Boolean = StoreXMLItemsInDatabase(pMessageId, oDocument, pFile, pConnections, pArgs)
Catch ex As Exception
Throw ex
End Try
_logger.Debug("File processed.")
' Check the Checksum and rejection status
Dim oMD5Checksum = _hash.GenerateAndCheck_MD5Sum(pFile, pMessageId, pArgs.IgnoreRejectionStatus)
oResult.ZugferdFileFound = True
oResult.MD5Checksum = oMD5Checksum
oResult.ZugferdFileCount = pZugferdFileCounter
Return oResult
End Function
Private Function StoreXMLItemsInDatabase(pMessageId As String, pDocument As ZUGFeRDInterface.ZugferdResult, pFile As FileInfo, pConnections As DatabaseConnections, pArgs As WorkerArgs) As Boolean
' Check the document against the configured property map and return: ' Check the document against the configured property map and return:
' - a List of valid properties ' - a List of valid properties
' - a List of missing properties ' - a List of missing properties
Dim oPropertyMap = _zugferd.FilterPropertyMap(pArgs.PropertyMap, oDocument.Specification) Dim oPropertyMap = _zugferd.FilterPropertyMap(pArgs.PropertyMap, pDocument.Specification)
Dim oCheckResult = _zugferd.PropertyValues.CheckPropertyValues(oDocument.SchemaObject, oPropertyMap, pMessageId) Dim oCheckResult = _zugferd.PropertyValues.CheckPropertyValues(pDocument.SchemaObject, oPropertyMap, pMessageId)
_logger.Info("Properties checked: [{0}] missing properties / [{1}] valid properties found.", oCheckResult.MissingProperties.Count, oCheckResult.ValidProperties.Count) _logger.Info("Properties checked: [{0}] missing properties / [{1}] valid properties found.", oCheckResult.MissingProperties.Count, oCheckResult.ValidProperties.Count)
If oCheckResult.MissingProperties.Count > 0 Then If oCheckResult.MissingProperties.Count > 0 Then
_logger.Warn("[{0}] missing properties found. Exiting.", oCheckResult.MissingProperties.Count) _logger.Warn("[{0}] missing properties found. Exiting.", oCheckResult.MissingProperties.Count)
Throw New MissingValueException(oFile, oCheckResult.MissingProperties) Throw New MissingValueException(pFile, oCheckResult.MissingProperties)
Else Else
_logger.Debug("No missing properties found. Continuing.") _logger.Debug("No missing properties found. Continuing.")
End If End If
If DeleteExistingPropertyValues(pMessageId, oConnections) = False Then If DeleteExistingPropertyValues(pMessageId, pConnections) = False Then
Throw New Exception("Could not cleanup data. Exiting.") Throw New Exception("Could not cleanup data. Exiting.")
End If End If
' DataTable vorbereiten ' DataTable vorbereiten
Dim oDataTable As DataTable = FillDataTable(pMessageId, oCheckResult, oDocument.Specification, oDocument.UsedXMLSchema) Dim oDataTable As DataTable = FillDataTable(pMessageId, oCheckResult, pDocument.Specification, pDocument.UsedXMLSchema)
' ColumnList initialisieren ' ColumnList initialisieren
Dim oColumnNames As List(Of String) = New List(Of String) From { Dim oColumnNames As List(Of String) = New List(Of String) From {
@ -565,7 +650,7 @@ Public Class ImportZUGFeRDFiles
"IS_REQUIRED" "IS_REQUIRED"
} }
Dim oBulkResult = BulkInsert(oConnections, oDataTable, "TBEDMI_ITEM_VALUE", oColumnNames) Dim oBulkResult = BulkInsert(pConnections, oDataTable, "TBEDMI_ITEM_VALUE", oColumnNames)
If oBulkResult = False Then If oBulkResult = False Then
_logger.Error("Bulk Insert for MessageId [{0}] failed!", pMessageId) _logger.Error("Bulk Insert for MessageId [{0}] failed!", pMessageId)
@ -573,15 +658,7 @@ Public Class ImportZUGFeRDFiles
End If End If
_logger.Info("Bulk Insert finished. [{0}] rows inserted for MessageId [{1}].", oDataTable.Rows.Count, pMessageId) _logger.Info("Bulk Insert finished. [{0}] rows inserted for MessageId [{1}].", oDataTable.Rows.Count, pMessageId)
Return True
_logger.Debug("File processed.")
oResult.ZugferdFileFound = True
oResult.MD5Checksum = oMD5Checksum
oResult.ZugferdFileCount = pZugferdFiles
Return oResult
End Function End Function
Private Function FillDataTable(pMessageId As String, pCheckResult As PropertyValues.CheckPropertyValuesResult, pSpecification As String, pUsedXMLSchema As String) As DataTable Private Function FillDataTable(pMessageId As String, pCheckResult As PropertyValues.CheckPropertyValuesResult, pSpecification As String, pUsedXMLSchema As String) As DataTable
@ -656,6 +733,8 @@ Public Class ImportZUGFeRDFiles
Catch ex As Exception Catch ex As Exception
_logger.Warn("Step [{0}] with SQL [{1}] was not successful.", oStep, oDelSQL) _logger.Warn("Step [{0}] with SQL [{1}] was not successful.", oStep, oDelSQL)
End Try End Try
Return False
End Function End Function
Private Function BulkInsert(pConnections As DatabaseConnections, pTable As DataTable, pDestinationTable As String, pColumns As List(Of String)) As Boolean Private Function BulkInsert(pConnections As DatabaseConnections, pTable As DataTable, pDestinationTable As String, pColumns As List(Of String)) As Boolean