Imports System.Collections.Generic Imports System.Data Imports System.Data.SqlClient Imports System.IO Imports System.Linq Imports DigitalData.Modules.Base Imports DigitalData.Modules.Config Imports DigitalData.Modules.Database Imports DigitalData.Modules.Interfaces Imports DigitalData.Modules.Interfaces.Exceptions Imports DigitalData.Modules.Interfaces.PropertyValues Imports DigitalData.Modules.Jobs.Exceptions Imports DigitalData.Modules.Logging Imports GdPicture14 Public Class ImportZUGFeRDFiles Implements IJob Public Const ZUGFERD_IN = "ZUGFeRD in" Public Const ZUGFERD_ERROR = "ZUGFeRD Error" Public Const ZUGFERD_SUCCESS = "ZUGFeRD Success" Public Const ZUGFERD_EML = "ZUGFeRD Eml" Public Const ZUGFERD_REJECTED_EML = "ZUGFeRD Eml Rejected" Public Const ZUGFERD_ATTACHMENTS = "ZUGFeRD Attachments" Public Const ZUGFERD_NO_ZUGFERD = "Non-ZUGFeRD Files" Public HISTORY_ID As Integer Private Const DIRECTORY_DONT_MOVE = "DIRECTORY_DONT_MOVE" Private Const MIME_TYPE_PDF = "application/pdf" Private Const MIME_TYPE_XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" Private Const MIME_TYPE_ODT = "application/vnd.oasis.opendocument.spreadsheet" ' List of allowed extensions for PDF/A Attachments ' This list should not contain xml so the zugferd xml file will be filtered out Private ReadOnly AllowedExtensions As New List(Of String) From {"docx", "doc", "pdf", "xls", "xlsx", "ppt", "pptx", "txt"} ' List of the Columns we need to store embedded files on disk and database Private ReadOnly EmbeddedFilesColumnNames As List(Of String) = New List(Of String) From { "ATTACHMENT_FILE_FILENAME", "ATTACHMENT_FILE_VALUE", "ATTACHMENT_FILE_MIMECODE" } ' List of the allowed MIME-Codes ' Allowed Values are: '- application/pdf '- application/vnd.openxmlformats-officedocument.spreadsheetml.sheet (xlsx) '- application/vnd.oasis.opendocument.spreadsheet (odt) '- image/jpeg '- image/png '- image/tiff (UBL) '- text/csv '- text/xml (UBL) Private ReadOnly AllowedMimeTypesInEmbeddedFiles As List(Of String) = New List(Of String) From { MIME_TYPE_PDF, MIME_TYPE_XLSX, MIME_TYPE_ODT } Private ReadOnly _logger As Logger Private ReadOnly _logConfig As LogConfig Private ReadOnly _filesystem As FilesystemEx Private ReadOnly _mssql As MSSQLServer Private ReadOnly _email As ZUGFeRD.EmailFunctions Private ReadOnly _file As ZUGFeRD.FileFunctions Private ReadOnly _history As ZUGFeRD.HistoryFunctions Private ReadOnly _hash As HashFunctions Private ReadOnly _embeds As PDFEmbeds Private SQL_xRechnung_ItemTemplate As String = "" Private ReadOnly _gdpictureLicenseKey As String Private ReadOnly _xRechnungCreator As XRechnungViewDocument Private _zugferd As ZUGFeRDInterface Private _EmailOutAccountId As Integer Private MyTemplateValues_xInvDT As DataTable Private Class ProcessFileResult Public ZugferdFileFound As Boolean = False Public ZugferdFileCount As Integer = 0 Public MD5Checksum As String = Nothing Public EmailAttachmentFiles As New List(Of FileInfo) Public EmbeddedAttachmentFiles As New List(Of PDFEmbeds.EmbeddedFile) End Class Private Class DatabaseConnections Public Property SQLServerConnection As SqlConnection Public Property SQLServerTransaction As SqlTransaction End Class Public Sub New(LogConfig As LogConfig, Optional MSSQL As MSSQLServer = Nothing) _logConfig = LogConfig _logger = LogConfig.GetLogger() _filesystem = New FilesystemEx(_logConfig) _mssql = MSSQL _email = New ZUGFeRD.EmailFunctions(LogConfig, _mssql) _file = New ZUGFeRD.FileFunctions(LogConfig, _mssql) _history = New ZUGFeRD.HistoryFunctions(LogConfig, _mssql) _embeds = New PDFEmbeds(LogConfig) _hash = New HashFunctions(_logConfig, _mssql) _xRechnungCreator = New XRechnungViewDocument(_logConfig, _mssql, _gdpictureLicenseKey) _logger.Debug("Registering GDPicture License") If _mssql IsNot Nothing Then _gdpictureLicenseKey = ConfigDbFunct.GetProductLicense("GDPICTURE", "11.2024", _logConfig, _mssql.CurrentConnectionString) Else _logger.Warn("GDPicture License could not be registered! MSSQL is not enabled!") Throw New ArgumentNullException("MSSQL") End If End Sub Public Sub Start(Arguments As Object) Implements IJob.Start Dim oArgs As WorkerArgs = Arguments Dim oAttachmentExtractor = New PDFEmbeds(_logConfig) _EmailOutAccountId = oArgs.EmailOutProfileId _zugferd = New ZUGFeRDInterface(_logConfig, _gdpictureLicenseKey, New ZUGFeRDInterface.ZugferdOptions() With { .AllowFacturX_Filename = oArgs.AllowFacturX, .AllowXRechnung_Filename = oArgs.AllowXRechnung, .AllowPeppol_3_x_Schema = oArgs.AllowPeppolBISBill3x }) _logger.Debug("Starting Job {0}", [GetType].Name) If oArgs.AllowXRechnung Then ' TODO - Config-Schalter hat NIX mit XRechnung-Dateien u. Sichtbelegen zu tun, sondern nur mit Dateinamen zu tun. Dim oSQL = "SELECT SQL_COMMAND FROM TBDD_SQL_COMMANDS WHERE TITLE = 'VWDD_ZUGFERD_VIEW_RECEIPT_TEMPLATE_ITEMS'" SQL_xRechnung_ItemTemplate = _mssql.GetScalarValue(oSQL) End If Try 'For Each oPath As String In oArgs.WatchDirectory Dim oPath As String = oArgs.WatchDirectory Dim oDirInfo As New DirectoryInfo(oPath) _logger.Debug($"Start processing directory {oDirInfo.FullName}") If oDirInfo.Exists = False Then _logger.Warn("Watch directory exists. Exiting.") Exit Sub End If ' Filter out *.lock files Dim oFiles As List(Of FileInfo) = oDirInfo. GetFiles(). Where(Function(f) Not f.Name.EndsWith(".lock")). ToList() If oFiles.Count = 0 Then _logger.Debug("No files to process. Exiting.") Exit Sub End If _logger.Info("Found {0} files", oFiles.Count) ' Group files by messageId Dim oGrouped As Dictionary(Of String, List(Of FileInfo)) = _zugferd.FileGroup.GroupFiles(oFiles) _logger.Info("Found [{0}] file groups", oGrouped.Count) 'Process each file group together 'oGrouped equals one e-invoice For Each oFileGroup In oGrouped ' Start a new transaction for each file group. ' This way we can rollback database changes for the whole filegroup in case something goes wrong. Dim oSQLConnection As SqlConnection = _mssql.GetConnection() Dim oSQLTransaction As SqlTransaction = oSQLConnection?.BeginTransaction() Dim oConnections As New DatabaseConnections() With { .SQLServerConnection = oSQLConnection, .SQLServerTransaction = oSQLTransaction } ' Count the amount of ZUGFeRD files Dim oZUGFeRDCount As Integer = 0 ' Set the default Move Directory Dim oMoveDirectory As String = oArgs.ErrorDirectory ' Flag to save if the whole process was a success. ' Will be set only at the end of the function if no error occurred. Dim oIsSuccess As Boolean = False ' Flag to save if the occurred error (if any) was expected ' Used to determine if transactions should be committed or not Dim oExpectedError As Boolean = True ' Create file lists Dim oEInvoiceFileGroup As List(Of FileInfo) = oFileGroup.Value Dim oEmailAttachmentFiles As New List(Of FileInfo) Dim oEmbeddedAttachmentFiles As New List(Of PDFEmbeds.EmbeddedFile) Dim oMessageId As String = oFileGroup.Key Dim oMD5CheckSum As String = String.Empty _logger.Info("START processing file group {0}", oMessageId) If _file.CheckFileAge(oEInvoiceFileGroup, oArgs.MinFileAgeInMinutes) Then _logger.Info("At least one file was created less than [{0}] minutes ago. Skipping file group.", oArgs.MinFileAgeInMinutes) Continue For End If Dim oEmailDataBase = _email.GetEmailDataForMessageId(oMessageId) Dim oFileCounter As Integer = 0 Try For Each oFile In oEInvoiceFileGroup 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 _logger.Debug("eInvoice File found") oMD5CheckSum = oResult.MD5Checksum oZUGFeRDCount = oResult.ZugferdFileCount oEmailAttachmentFiles.AddRange(oResult.EmailAttachmentFiles) oEmbeddedAttachmentFiles.AddRange(oResult.EmbeddedAttachmentFiles) Else ' No zugferd found! oEmailAttachmentFiles.AddRange(oResult.EmailAttachmentFiles) oEmbeddedAttachmentFiles.AddRange(oResult.EmbeddedAttachmentFiles) End If Next 'Check if there are no ZUGFeRD files If oZUGFeRDCount = 0 Then ' If NonZugferdDirectory is not set, a NoFerdsException will be thrown and a rejection will be generated ' This is the default/initial behaviour. If String.IsNullOrEmpty(oArgs.NonZugferdDirectory) Then Throw New NoFerdsException() End If ' Also, if the directory is set but does not exist, still a rejection will be generated. If Not Directory.Exists(oArgs.NonZugferdDirectory) Then Throw New NoFerdsException() End If ' Only if the directory is set and does exist, it will be used and any file groups which ' do NOT CONTAIN ANY ZUGFERD DOCUMENTS, are moved to that directory. Throw New NoFerdsAlternateException() End If 'If no errors occurred, update the history table for this MessageId If String.IsNullOrEmpty(oMD5CheckSum) Then _history.Update_HistoryEntry(oMessageId, String.Empty, "SUCCESS (with empty MD5Hash)") Else _history.Update_HistoryEntry(oMessageId, oMD5CheckSum, "SUCCESS") End If oIsSuccess = True oMoveDirectory = oArgs.SuccessDirectory Catch ex As ValidationException _logger.Error(ex) Dim oRejectionCodeString = GetRejectionCodeString(oMessageId, ErrorCode.ValidationException) 'Dim oMessage = "REJECTED - ZUGFeRD yes but formal validation failed!" _history.Update_HistoryEntry(oMessageId, oMD5CheckSum, oRejectionCodeString) Dim oErrors = ex.ValidationErrors Dim oErrorList As String = "" Dim oErrorListDE As String = "" For Each oError In oErrors oErrorList += $"
  • Element '{oError.ElementName}' with Value '{oError.ElementValue}': {oError.ErrorMessage}
  • " oErrorListDE += $"
  • Element '{oError.ElementName}' mit Wert '{oError.ElementValue}': {oError.ErrorMessageDE}
  • " Next Dim oBody = String.Format(EmailStrings.EMAIL_VALIDATION_ERROR, oErrorList) Dim oEmailData = _file.MoveAndRenameEmailToRejected(oArgs, oMessageId) _email.AddToEmailQueueMSSQL(oMessageId, oSQLTransaction, oBody, oEmailData, "ValidationException", _EmailOutAccountId, oArgs.NamePortal, oArgs.RejectionTemplateId, ErrorCode.ValidationException, oErrorListDE, oErrorList) AddRejectedState(oMessageId, oRejectionCodeString, "Die Rechnungsvalidierung ist fehlgeschlagen!", "", oSQLTransaction) Catch ex As MD5HashException _logger.Error(ex) Dim oRejectionCodeString = GetRejectionCodeString(oMessageId, ErrorCode.MD5HashException) ' When MD5HashException is thrown, we don't have a MD5Hash yet. ' Thats why we set it to String.Empty here. 'Dim oMessage = "REJECTED - Already processed (MD5Hash)" _history.Update_HistoryEntry(oMessageId, String.Empty, oRejectionCodeString) Dim oBody = String.Format(EmailStrings.EMAIL_MD5_ERROR, ex.FileName) Dim oEmailData = _file.MoveAndRenameEmailToRejected(oArgs, oMessageId) _email.AddToEmailQueueMSSQL(oMessageId, oSQLTransaction, oBody, oEmailData, "MD5HashException", _EmailOutAccountId, oArgs.NamePortal, oArgs.RejectionTemplateId, ErrorCode.MD5HashException, ex.FileName, "") AddRejectedState(oMessageId, oRejectionCodeString, "Die gesendete Rechnung wurde bereits verarbeitet!", "", oSQLTransaction) Catch ex As UnsupportedFerdException _logger.Error(ex) Dim oRejectionCodeString = GetRejectionCodeString(oMessageId, ErrorCode.UnsupportedFerdException) ' When UnsupportedFerdException is thrown, we don't have a MD5Hash yet. ' Thats why we set it to String.Empty here. _history.Update_HistoryEntry(oMessageId, String.Empty, oRejectionCodeString) Dim oEmailData = _file.MoveAndRenameEmailToRejected(oArgs, oMessageId) Dim oBody As String = String.Format(EmailStrings.EMAIL_UNSUPPORTED_DOCUMENT, oEmailData.Subject, ex.XmlFile) _email.AddToEmailQueueMSSQL(oMessageId, oSQLTransaction, oBody, oEmailData, "UnsupportedFerdException", _EmailOutAccountId, oArgs.NamePortal, oArgs.RejectionTemplateId, ErrorCode.UnsupportedFerdException, ex.XmlFile, "") AddRejectedState(oMessageId, oRejectionCodeString, "Nicht unterstütztes Datenformat", "", oSQLTransaction) Catch ex As InvalidFerdException _logger.Error(ex) Dim oRejectionCodeString = GetRejectionCodeString(oMessageId, ErrorCode.InvalidFerdException) ' When InvalidFerdException is thrown, we don't have a MD5Hash yet. ' Thats why we set it to String.Empty here. _history.Update_HistoryEntry(oMessageId, String.Empty, oRejectionCodeString) Dim oEmailData = _file.MoveAndRenameEmailToRejected(oArgs, oMessageId) Dim oBody = String.Format(EmailStrings.EMAIL_INVALID_DOCUMENT, oEmailData.Subject) _email.AddToEmailQueueMSSQL(oMessageId, oSQLTransaction, oBody, oEmailData, "InvalidFerdException", _EmailOutAccountId, oArgs.NamePortal, oArgs.RejectionTemplateId, ErrorCode.InvalidFerdException, "", "") AddRejectedState(oMessageId, oRejectionCodeString, "Inkorrektes Format", "", oSQLTransaction) Catch ex As TooMuchFerdsException _logger.Error(ex) Dim oRejectionCodeString = GetRejectionCodeString(oMessageId, ErrorCode.TooMuchFerdsException) _history.Update_HistoryEntry(oMessageId, oMD5CheckSum, oRejectionCodeString) Dim oEmailData = _file.MoveAndRenameEmailToRejected(oArgs, oMessageId) Dim oBody = String.Format(EmailStrings.EMAIL_TOO_MUCH_FERDS, oEmailData.Subject) _email.AddToEmailQueueMSSQL(oMessageId, oSQLTransaction, oBody, oEmailData, "TooMuchFerdsException", _EmailOutAccountId, oArgs.NamePortal, oArgs.RejectionTemplateId, ErrorCode.TooMuchFerdsException, "", "") AddRejectedState(oMessageId, oRejectionCodeString, "Email enthielt mehr als ein eInvoice-Dokument", "", oSQLTransaction) Catch ex As NoFerdsException _logger.Error(ex) Dim oRejectionCodeString = GetRejectionCodeString(oMessageId, ErrorCode.NoFerdsException) _history.Update_HistoryEntry(oMessageId, oMD5CheckSum, oRejectionCodeString) Dim oEmailData = _file.MoveAndRenameEmailToRejected(oArgs, oMessageId) Dim oBody = String.Format(EmailStrings.EMAIL_NO_FERDS, oEmailData.Subject) _email.AddToEmailQueueMSSQL(oMessageId, oSQLTransaction, oBody, oEmailData, "NoFerdsException", _EmailOutAccountId, oArgs.NamePortal, oArgs.RejectionTemplateId, ErrorCode.NoFerdsException, "", "") AddRejectedState(oMessageId, oRejectionCodeString, "Email enthielt keine eInvoice-Dokumente", "", oSQLTransaction) Catch ex As MissingValueException _logger.Error(ex) Dim oRejectionCodeString = GetRejectionCodeString(oMessageId, ErrorCode.MissingValueException) _history.Update_HistoryEntry(oMessageId, oMD5CheckSum, oRejectionCodeString) Dim oMissingFieldList As String = "" For Each oMissingFieldDescription In ex.MissingProperties oMissingFieldList += $"
  • {oMissingFieldDescription.Description}
    {oMissingFieldDescription.XMLPath}
  • " Next Dim oOrgFilename = _hash.GetOriginalFilename(ex.File.Name) Dim oBody = _email.CreateBodyForMissingProperties(ex.File.Name, ex.MissingProperties) Dim oEmailData = _file.MoveAndRenameEmailToRejected(oArgs, oMessageId) _email.AddToEmailQueueMSSQL(oMessageId, oSQLTransaction, oBody, oEmailData, "MissingValueException", _EmailOutAccountId, oArgs.NamePortal, oArgs.RejectionTemplateId, ErrorCode.MissingValueException, oOrgFilename, oMissingFieldList) AddRejectedState(oMessageId, oRejectionCodeString, "Es fehlten eInvoice-Spezifikationen", "", oSQLTransaction) Catch ex As FileSizeLimitReachedException _logger.Error(ex) Dim oRejectionCodeString = GetRejectionCodeString(oMessageId, ErrorCode.FileSizeLimitReachedException) _history.Update_HistoryEntry(oMessageId, oMD5CheckSum, oRejectionCodeString) Dim oEmailData = _file.MoveAndRenameEmailToRejected(oArgs, oMessageId) Dim oKey = FileSizeLimitReachedException.KEY_FILENAME Dim oFileExceedingThreshold As String = IIf(ex.Data.Contains(oKey), ex.Data.Item(oKey), "") Dim oOrgFilename = _hash.GetOriginalFilename(oFileExceedingThreshold) Dim oBody = String.Format(EmailStrings.EMAIL_FILE_SIZE_REACHED, oArgs.MaxAttachmentSizeInMegaBytes, oOrgFilename) _email.AddToEmailQueueMSSQL(oMessageId, oSQLTransaction, oBody, oEmailData, "FileSizeLimitReachedException", _EmailOutAccountId, oArgs.NamePortal, oArgs.RejectionTemplateId, ErrorCode.FileSizeLimitReachedException, oArgs.MaxAttachmentSizeInMegaBytes, oOrgFilename) AddRejectedState(oMessageId, oRejectionCodeString, "Erlaubte Dateigröße überschritten", "", oSQLTransaction) Catch ex As NoFerdsAlternateException ' TODO: Maybe dont even log this 'error', since it's not really an error and it might happen *A LOT* _logger.Error(ex) oMoveDirectory = oArgs.NonZugferdDirectory Catch ex As OutOfMemoryException _logger.Warn("OutOfMemory Error occurred: {0}", ex.Message) _logger.Error(ex) ' Send Email to Digital Data Dim oBody = _email.CreateBodyForUnhandledException(oMessageId, ex) Dim oEmailData As New EmailData With { .From = oArgs.ExceptionEmailAddress, .Subject = $"OutOfMemoryException im eInvoice-Parser @ {oMessageId}" } _email.AddToEmailQueueMSSQL(oMessageId, oSQLTransaction, oBody, oEmailData, "OutOfMemoryException", _EmailOutAccountId, oArgs.NamePortal, oArgs.RejectionTemplateId, ErrorCode.UnhandledException, ex.Message, ex.StackTrace) ' Rollback Transaction oSQLTransaction.Rollback() oMoveDirectory = DIRECTORY_DONT_MOVE oExpectedError = False Catch ex As Exception _logger.Warn("Unknown Error occurred: {0}", ex.Message) _logger.Error(ex) oMoveDirectory = DIRECTORY_DONT_MOVE oExpectedError = False If oSQLConnection IsNot Nothing And oSQLTransaction IsNot Nothing Then ' Send Email to Digital Data Dim oBody = _email.CreateBodyForUnhandledException(oMessageId, ex) Dim oEmailData As New EmailData With { .From = oArgs.ExceptionEmailAddress, .Subject = $"UnhandledException im eInvoice-Parser @ {oMessageId}" } _email.AddToEmailQueueMSSQL(oMessageId, oSQLTransaction, oBody, oEmailData, "UnhandledException", _EmailOutAccountId, oArgs.NamePortal, oArgs.RejectionTemplateId, ErrorCode.UnhandledException, ex.Message, ex.StackTrace) ' Rollback Transaction oSQLTransaction.Rollback() End If Finally Dim oxRechnungHandle As Boolean = False Try Dim oRegularMove As Boolean = False ' If an application error occurred, dont move files so they will be processed again later If oMoveDirectory = DIRECTORY_DONT_MOVE Then _logger.Info("Application Error occurred. Files for message Id {0} will not be moved.", oMessageId) ElseIf oArgs.AllowXRechnung And oIsSuccess And oEInvoiceFileGroup.Item(0).Extension.Equals(".xml", StringComparison.OrdinalIgnoreCase) = True Then _logger.Debug("Before Creating the PDF-File from XML data / Before Commit") oxRechnungHandle = True ' Hier das neue PDF erzeugen 'but before we need to get all Data we need MyTemplateValues_xInvDT = Nothing Dim oSQL_MsgIDReplace = SQL_xRechnung_ItemTemplate oSQL_MsgIDReplace = oSQL_MsgIDReplace.Replace("@MSG_ID", oFileGroup.Key) If oSQLTransaction IsNot Nothing Then ' Commit Transaction oSQLTransaction.Commit() _logger.Debug("XML commit triggered") End If MyTemplateValues_xInvDT = _mssql.GetDatatable(oSQL_MsgIDReplace) If Not IsNothing(MyTemplateValues_xInvDT) Then If MyTemplateValues_xInvDT.Rows.Count > 0 Then Dim oViewReceiptFileInfo As FileInfo = _xRechnungCreator.Create_PDFfromXML(oEInvoiceFileGroup.Item(0), MyTemplateValues_xInvDT) If Not IsNothing(oViewReceiptFileInfo) Then oEInvoiceFileGroup.Item(0) = oViewReceiptFileInfo oRegularMove = True End If End If End If Else oRegularMove = True End If If oRegularMove Then ' Move all files of the current group _file.MoveFiles(oArgs, oMessageId, oEInvoiceFileGroup, oEmailAttachmentFiles, oEmbeddedAttachmentFiles, oMoveDirectory, oIsSuccess) End If _logger.Info("END processing file group {0}", oMessageId) Catch ex As Exception ' Send Email to Digital Data Dim oBody = _email.CreateBodyForUnhandledException(oMessageId, ex) Dim oEmailData As New EmailData With { .From = oArgs.ExceptionEmailAddress, .Subject = $"FileMoveException im eInvoice-Parser @ {oMessageId}" } _email.AddToEmailQueueMSSQL(oMessageId, oSQLTransaction, oBody, oEmailData, "FileMoveException", _EmailOutAccountId, oArgs.NamePortal, oArgs.RejectionTemplateId, ErrorCode.UnhandledException, ex.Message, ex.StackTrace) _logger.Warn("Could not move files!") _logger.Error(ex) Throw ex End Try Try ' If everything went OK or an expected error occurred, ' finally commit all changes To the Database ' ================================================================== If oIsSuccess Or oExpectedError Then _logger.Debug("Before default sql commit: oxRechnungHandle [{0}]", oxRechnungHandle) If oxRechnungHandle = False AndAlso oSQLTransaction IsNot Nothing Then ' Commit Transaction oSQLTransaction.Commit() _logger.Debug("default commit triggered") End If End If Catch ex As Exception _logger.Error(ex) _logger.Warn("Database Transactions were not committed successfully.") End Try Try If oSQLConnection IsNot Nothing Then _logger.Debug("Before default sql close") oSQLConnection.Close() End If Catch ex As Exception _logger.Error(ex) _logger.Warn("Database Connections were not closed successfully.") End Try End Try Next _logger.Debug("Finishing Job {0}", Me.GetType.Name) Catch ex As Exception _logger.Error(ex) _logger.Info("Job Failed! See error log for details") End Try End Sub Private Function GetRejectionCodeString(pMessageId As String, pRejectionCode As ErrorCode) As String Dim intCode As Integer = DirectCast(pRejectionCode, Integer) Dim oRejectionCodeString = $"{EmailStrings.ErrorCodePraefix}{intCode}" ' Wir wollen im error-Log den Code und die MessageID haben, um die es geht Dim oInfoMessage = $"Rejection {oRejectionCodeString} triggered for '{pMessageId}'" _logger.Error(oInfoMessage) Return oRejectionCodeString End Function 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 oResult As New ProcessFileResult() If pFile.Extension.Equals(".xml", StringComparison.OrdinalIgnoreCase) = False Then ' Diese Methode ist nur für den xml-Beleg gedacht Return oResult End If _logger.Info("Start xml processing file {0}", pFile.Name) ' Checking filesize 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.GetSerializedXMLContentFromFile(pFile) oDocument.ReceiptFileType = ZUGFeRDInterface.RECEIPT_TYPE_XML Catch ex As ValidationException Throw ex Catch ex As ZUGFeRDExecption If ex.ErrorCode > 0 Then _logger.Info("New Rejection Logik") Select Case ex.ErrorCode Case ErrorCode.NoFerdsException _logger.Info("File [{0}] is not a valid eInvoice document. Skipping.", pFile.Name) oResult.EmailAttachmentFiles.Add(pFile) Return oResult Case ErrorCode.UnsupportedFerdException _logger.Info("File [{0}/{1}] is an unsupported eInvoice document format!", pFile.Name, ex.XmlFile) Throw New UnsupportedFerdException(ex.XmlFile) Case ErrorCode.InvalidFerdException _logger.Info("File [{0}] is an incorrectly formatted eInvoice document!", pFile.Name) Throw New InvalidFerdException() Case Else _logger.Warn("Unexpected Error occurred while extracting eInvoice Information from file {0}", pFile.Name) Throw ex End Select End If End Try Try Dim sqlResult As Boolean = False If oDocument IsNot Nothing Then sqlResult = StoreXMLItemsInDatabase(pMessageId, oDocument, pFile, pConnections, pArgs, oResult) End If 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) oDocument.ReceiptFileType = ZUGFeRDInterface.RECEIPT_TYPE_PDF Catch ex As ValidationException Throw ex Catch ex As ZUGFeRDExecption If ex.ErrorCode > 0 Then _logger.Info("New Rejection Logik") Select Case ex.ErrorCode Case ErrorCode.NoFerdsException _logger.Info("File [{0}] is not a valid eInvoice document. Skipping.", pFile.Name) oResult.EmailAttachmentFiles.Add(pFile) Return oResult Case ErrorCode.UnsupportedFerdException _logger.Info("File [{0}/{1}] is an unsupported eInvoice document format!", pFile.Name, ex.XmlFile) Throw New UnsupportedFerdException(ex.XmlFile) Case ErrorCode.InvalidFerdException _logger.Info("File [{0}] is an incorrectly formatted eInvoice document!", pFile.Name) Throw New InvalidFerdException() Case Else _logger.Warn("Unexpected Error occurred while extracting eInvoice Information from file {0}", pFile.Name) Throw ex End Select End If End Try ' Check if there are more than one ZUGFeRD files If pZugferdFileCounter = 1 Then Throw New TooMuchFerdsException() End If ' Since extraction went well, increase the amount of ZUGFeRD files pZugferdFileCounter += 1 _logger.Info("Zugferd file found. Increasing counter.") ' 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. ' Right now the zugferd-invoice.xml is filtered out because `AllowedExtensions` does not contain `xml`. Dim oAttachments = _embeds.Extract(pFile.FullName, AllowedExtensions) If oAttachments Is Nothing Then _logger.Warn("Attachments for file [{0}] could not be extracted", pFile.FullName) Else oResult.EmbeddedAttachmentFiles.AddRange(oAttachments) End If Try Dim sqlResult As Boolean = StoreXMLItemsInDatabase(pMessageId, oDocument, pFile, pConnections, pArgs, oResult) 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, pProcessFileResult As ProcessFileResult) As Boolean ' Check the document against the configured property map and return: ' - a List of valid properties ' - a List of missing properties Dim oPropertyMap As Dictionary(Of String, XmlItemProperty) = _zugferd.FilterPropertyMap(pArgs.PropertyMapList, pDocument.Specification) 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) If oCheckResult.MissingProperties.Count > 0 Then _logger.Warn("[{0}] missing properties found. Exiting.", oCheckResult.MissingProperties.Count) Throw New MissingValueException(pFile, oCheckResult.MissingProperties) Else _logger.Debug("No missing properties found. Continuing.") End If ' Daten in die Datenbank speichern If BulkInsertDataToDatabase(pMessageId, pDocument, pConnections, oCheckResult) = False Then _logger.Error("Bulk Insert for MessageId [{0}] failed!", pMessageId) Throw New Exception("Bulk Insert failed! Exiting.") End If ' Eingebettete Dateien speichern If HandleEmbeddedAttachments(pMessageId, pDocument, pConnections, oCheckResult, pArgs, pProcessFileResult) = False Then _logger.Debug("Files saving for MessageId [{0}] failed!", pMessageId) End If Return True End Function ''' ''' Hier werden die Dateianhänge behandelt, die im XML als base64 eingetragen wurden. ''' Die zusammengehörigen Knoten müssen über "FILES" gruppiert werden! ''' ''' Die erwarteten Knoten-Namen sind in der List EmbeddedFilesColumnNames enthalten! ''' Private Function HandleEmbeddedAttachments(pMessageId As String, pDocument As ZUGFeRDInterface.ZugferdResult, pConnections As DatabaseConnections, pCheckResult As CheckPropertyValuesResult, pArgs As WorkerArgs, pProcessFileResult As ProcessFileResult) As Boolean If (pCheckResult Is Nothing) Then _logger.Debug("pCheckResult is empty!") Return True End If If (CheckEmbeddedAttachmentEntries(pCheckResult) = False) Then _logger.Debug("No embedded Files in XML found!") Return True End If Dim embAttachmentList As List(Of ValidProperty) = pCheckResult.ValidProperties.Where( Function(z) Return EmbeddedFilesColumnNames.Contains(z.TableColumn) End Function ).ToList() If embAttachmentList Is Nothing OrElse embAttachmentList.Count <= 0 Then _logger.Debug("No Fields for Embedded Files configured!") Return True End If ' GroupCounter Werte in Hashset eintragen, um distinct Werte zu erhalten Dim oIndexList As HashSet(Of Integer) = New HashSet(Of Integer) For Each resultItem In embAttachmentList oIndexList.Add(resultItem.GroupCounter) Next Dim oOutputPath As String = GetOutputPathForEmbeddedAttachments(pArgs) Dim nextAttachmentIndex As Integer = 0 nextAttachmentIndex = GetNextAttachmentIndex(pMessageId) If nextAttachmentIndex <= 0 Then nextAttachmentIndex = 1 End If For Each groupIndex In oIndexList Dim oMimeCodeString As String = String.Empty Dim oOrgFilename As String = String.Empty Dim oBase64String As String = String.Empty Dim oMimeTypeProperty As ValidProperty = GetIndexProperty(embAttachmentList, groupIndex, "ATTACHMENT_FILE_MIMECODE") If oMimeTypeProperty IsNot Nothing AndAlso oMimeTypeProperty.Value.IsNotNullOrEmpty() Then oMimeCodeString = oMimeTypeProperty.Value.ToLower() Else _logger.Debug("Empty MIME-Code! File can not be stored!") Continue For End If Dim oFilenameProperty As ValidProperty = GetIndexProperty(embAttachmentList, groupIndex, "ATTACHMENT_FILE_FILENAME") If oFilenameProperty IsNot Nothing AndAlso oFilenameProperty.Value.IsNotNullOrEmpty() Then oOrgFilename = oFilenameProperty.Value Else _logger.Debug("Empty Filename! File can not be stored!") Continue For End If Dim oBase64ValueProperty As ValidProperty = GetIndexProperty(embAttachmentList, groupIndex, "ATTACHMENT_FILE_VALUE") If oBase64ValueProperty IsNot Nothing AndAlso oBase64ValueProperty.Value.IsNotNullOrEmpty() Then oBase64String = oBase64ValueProperty.Value Else _logger.Error("Empty base64 String! File can not be stored!") Continue For End If Dim newAttachmentFilename = pMessageId + "~attm" + nextAttachmentIndex.ToString Dim oFileExtension = GetEmbeddedFileExtension(oMimeTypeProperty.Value) If oFileExtension.IsNotNullOrEmpty() Then newAttachmentFilename += "." + oFileExtension Else _logger.Warn("No extension found! File can not be stored!") Continue For End If Dim embeddedFilePath = Path.Combine(oOutputPath, newAttachmentFilename) _logger.Debug("Next Attachment File is [{0}]", embeddedFilePath) If SaveBase64ToDisk(embeddedFilePath, oBase64String) = True Then _logger.Debug("Saved file [{0}] to disk", embeddedFilePath) pProcessFileResult.EmailAttachmentFiles.Add(New FileInfo(embeddedFilePath)) Else _logger.Error("Could not save File to Disk!") Dim oReasonString = "Could not save file " + newAttachmentFilename + " to disk." AddRejectedState(pMessageId, oReasonString, "EMBEDDED FILE CONSISTENCY NOT OK", "Info GUI", pConnections.SQLServerTransaction) Continue For End If If TestFileOnDisk(embeddedFilePath, oMimeCodeString) = False Then _logger.Error("Could not save File to Disk!") Dim oReasonString = "Validation of file " + newAttachmentFilename + " on disk was NOT succesfully." AddRejectedState(pMessageId, oReasonString, "EMBEDDED FILE CONSISTENCY NOT OK", "Info GUI", pConnections.SQLServerTransaction) Continue For End If If InsertAttachmentHistoryEntry(pMessageId, oOrgFilename, embeddedFilePath) = False Then _logger.Error("Could not save attachment Data to DB!") Return False End If If InsertEmbeddedFileDataToDB(pMessageId, oBase64String, oOrgFilename, oMimeCodeString, groupIndex) = False Then _logger.Error("Could not save attachment Data to DB!") Return False End If nextAttachmentIndex += 1 Next Return True End Function Private Function TestFileOnDisk(pEmbeddedFilePath As String, pMimeCodeString As String) As Boolean Try If pMimeCodeString = MIME_TYPE_PDF Then Dim oGdPicturePDF As New GdPicturePDF Dim oStatus As GdPictureStatus = oGdPicturePDF.LoadFromFile(pEmbeddedFilePath, True) If oStatus <> GdPictureStatus.OK Then _logger.Error("File [{0}] has no proper state!", pEmbeddedFilePath) Return False End If Else ' Test other files Dim fileInfo As FileInfo = New FileInfo(pEmbeddedFilePath) If fileInfo.Exists = False Then _logger.Error("Could not find File [{0}] on Disk!", pEmbeddedFilePath) Return False End If End If Return True Catch ex As Exception _logger.Error(ex) Return False End Try End Function Private Function GetEmbeddedFileExtension(pMimeTypeValue As String) As String If pMimeTypeValue.IsNullOrEmpty() Then _logger.Warn("Empty MimeCode is not allowed!") Return String.Empty End If Select Case pMimeTypeValue.ToLower() Case MIME_TYPE_PDF Return "pdf" Case MIME_TYPE_XLSX Return "xlsx" Case MIME_TYPE_ODT Return "odt" Case "image/jpeg" Return "jpg" Case "image/png" Return "png" Case "image/tiff" Return "tif" Case "text/csv" Return "csv" Case "text/xml" Return "xml" Case Else Return String.Empty End Select End Function Private Shared Function GetIndexProperty(pListResult As List(Of ValidProperty), pGroupIndex As Integer, pTableColumn As String) As ValidProperty Return pListResult.Where( Function(z) Return z.GroupCounter = pGroupIndex AndAlso z.TableColumn = pTableColumn End Function ).FirstOrDefault End Function ''' ''' Speichert die Daten inkl. base64-String in die Datenbank ''' Private Function InsertEmbeddedFileDataToDB(pMessageId As String, pItemValue As String, pOrgFilename As String, pMimeType As String, pGroupIndex As Integer) As Boolean Try Dim oCommand = New SqlCommand( "INSERT INTO TBEDMI_ITEM_FILES ( REFERENCE_GUID, ITEM_VALUE, ORG_FILENAME, MIME_TYPE, GROUP_INDEX, CREATED_WHO ) VALUES ( @MESSAGE_ID, @ITEM_VALUE, @ORG_FILENAME, @MIME_TYPE, @GROUP_INDEX, @CREATED_WHO )") Dim fileBytes As Byte() = Convert.FromBase64String(pItemValue) oCommand.Parameters.Add("MESSAGE_ID", SqlDbType.VarChar, 250).Value = pMessageId oCommand.Parameters.Add("ITEM_VALUE", SqlDbType.VarBinary).Value = fileBytes oCommand.Parameters.Add("ORG_FILENAME", SqlDbType.VarChar, 256).Value = pOrgFilename oCommand.Parameters.Add("MIME_TYPE", SqlDbType.VarChar, 256).Value = pMimeType oCommand.Parameters.Add("GROUP_INDEX", SqlDbType.Int).Value = pGroupIndex oCommand.Parameters.Add("CREATED_WHO", SqlDbType.VarChar, 100).Value = "eInvoice Parser" _mssql.ExecuteNonQuery(oCommand) Return True Catch ex As Exception _logger.Error(ex) Return False End Try End Function ''' ''' Ermittelt den Ausgabepfad für die eingebetteten Anhänge ''' Private Function GetOutputPathForEmbeddedAttachments(pArgs As WorkerArgs) As String Return pArgs.WatchDirectory End Function ''' ''' Prüft, ob Embedded Attachments in den XML-Ergebnissen enthalten sind, ''' in dem geprüft wird, ob bestimmte MIME-Codes vorhanden sind. ''' ''' ''' Private Function CheckEmbeddedAttachmentEntries(pCheckResult As CheckPropertyValuesResult) As Boolean Try Dim resultList = pCheckResult.ValidProperties.Where( Function(z) Return (z.TableColumn = "ATTACHMENT_FILE_MIMECODE" AndAlso AllowedMimeTypesInEmbeddedFiles.Contains(z.Value.ToLower())) End Function ).ToList() If resultList.Count > 0 Then _logger.Info("Found [{0}] embedded XML-Attachments.", resultList.Count) Return True Else _logger.Info("No embedded XML-Attachments found.") Return False End If Catch ex As Exception _logger.Error("Error searching pCheckResult! {0}", ex.Message) Return False End Try End Function ''' ''' Speichere base64 als Datei auf der Platte ab. ''' Private Function SaveBase64ToDisk(pExportFilePath As String, pBase64String As String) As Boolean Try Dim oFilename As String = pExportFilePath Dim base64BinaryDataString As String = pBase64String Dim binaryDataString As Byte() = Convert.FromBase64String(base64BinaryDataString) ' Using verwenden, um blockieren der Datei zu verhindern Using fs = New FileStream(oFilename, FileMode.Create, FileAccess.ReadWrite) fs.Write(binaryDataString, 0, binaryDataString.Length) fs.Flush() fs.Close() End Using Return True Catch ex As Exception _logger.Error("Could NOT save File [{0}] to Disk! Exception: [{1}]", pExportFilePath, ex.Message) Return False End Try End Function ''' ''' Die Methode lädt die bisherigen Dateinamen zu einer MessageID ''' Die Datei mit dem höchsten Index gibt den folgenden Index vor. ''' ''' Nächster Attachment Index Private Function GetNextAttachmentIndex(pMessageId As String) As Integer Try Dim oSQL = $"SELECT count(*) FROM TBEMLP_HISTORY_ATTACHMENT WHERE EMAIL_MSGID = '{pMessageId}'" Dim sqlResult = _mssql.GetScalarValue(oSQL) If sqlResult = 0 Then sqlResult = 1 ' Kleinster Index = 1 End If Return sqlResult Catch ex As Exception _logger.Error(ex) Throw ex End Try End Function ''' ''' Speichert die Infos zu einem embedded Dateianhang in die DB. ''' Mangels EMail-Daten werden die EMail-Felder (FROM, BODY, usw.) nicht gefüllt ''' ''' true, wenn erfolgreich Private Function InsertAttachmentHistoryEntry(pMessageId As String, pFileName As String, pNewFileName As String) As Boolean Try Dim oCommand = New SqlCommand( "INSERT INTO TBEMLP_HISTORY_ATTACHMENT ( WORK_PROCESS, EMAIL_MSGID, EMAIL_ATTMT, EMAIL_ATTMT_INDEX, EMAIL_FROM, EMAIL_BODY ) VALUES ( @WORK_PROCESS, @MESSAGE_ID, @ATTACHMENT, @ATTACHMENT_INDEX, '-', '-' )") oCommand.Parameters.Add("WORK_PROCESS", SqlDbType.VarChar, 100).Value = "Attachment Sniffer (Embedded Files)" oCommand.Parameters.Add("MESSAGE_ID", SqlDbType.VarChar, 500).Value = pMessageId oCommand.Parameters.Add("ATTACHMENT", SqlDbType.VarChar, 500).Value = pFileName oCommand.Parameters.Add("ATTACHMENT_INDEX", SqlDbType.VarChar, 500).Value = pNewFileName _mssql.ExecuteNonQuery(oCommand) Return True Catch ex As Exception _logger.Error(ex) Return False End Try End Function Private Function BulkInsertDataToDatabase(pMessageId As String, pDocument As ZUGFeRDInterface.ZugferdResult, pConnections As DatabaseConnections, pCheckResults As CheckPropertyValuesResult) As Boolean If DeleteExistingPropertyValues(pMessageId, pConnections) = False Then Throw New Exception("Could not cleanup data. Exiting.") End If ' DataTable vorbereiten Dim oDataTable As DataTable = FillDataTable(pMessageId, pCheckResults, pDocument) ' ColumnList initialisieren Dim oColumnNames As List(Of String) = New List(Of String) From { "REFERENCE_GUID", "ITEM_DESCRIPTION", "ITEM_VALUE", "GROUP_COUNTER", "SPEC_NAME", "IS_REQUIRED" } Dim oBulkResult = BulkInsert(pConnections, oDataTable, "TBEDMI_ITEM_VALUE", oColumnNames) If oBulkResult = False Then _logger.Error("Bulk Insert for MessageId [{0}] failed!", pMessageId) Throw New Exception("Bulk Insert failed! Exiting.") End If _logger.Info("Bulk Insert finished. [{0}] rows inserted for MessageId [{1}].", oDataTable.Rows.Count, pMessageId) Return True End Function Private Function FillDataTable(pMessageId As String, pCheckResult As PropertyValues.CheckPropertyValuesResult, pDocument As ZUGFeRDInterface.ZugferdResult) As DataTable Dim oDataTable As DataTable = New DataTable() oDataTable.Columns.Add(New DataColumn("REFERENCE_GUID", GetType(String))) oDataTable.Columns.Add(New DataColumn("ITEM_DESCRIPTION", GetType(String))) oDataTable.Columns.Add(New DataColumn("ITEM_VALUE", GetType(String))) oDataTable.Columns.Add(New DataColumn("GROUP_COUNTER", GetType(Int32))) oDataTable.Columns.Add(New DataColumn("SPEC_NAME", GetType(String))) oDataTable.Columns.Add(New DataColumn("IS_REQUIRED", GetType(Boolean))) ' Erste Zeile enthält die Spezifikation Dim oFirstRow As DataRow = oDataTable.NewRow() oFirstRow("REFERENCE_GUID") = pMessageId oFirstRow("ITEM_DESCRIPTION") = "Verwendete Spezifikation der E-Rechnung" oFirstRow("ITEM_VALUE") = pDocument.Specification oFirstRow("GROUP_COUNTER") = 0 oFirstRow("SPEC_NAME") = "ZUGFERD_SPECIFICATION" oFirstRow("IS_REQUIRED") = False _logger.Debug("Mapping Property [ZUGFERD_SPECIFICATION] with value [{0}]", pDocument.Specification) oDataTable.Rows.Add(oFirstRow) ' Zweite Zeile enthält das verwendete XML Schema Dim oSecondRow As DataRow = oDataTable.NewRow() oSecondRow("REFERENCE_GUID") = pMessageId oSecondRow("ITEM_DESCRIPTION") = "Verwendetes XML-Schema (XSD) der E-Rechnung" oSecondRow("ITEM_VALUE") = pDocument.UsedXMLSchema oSecondRow("GROUP_COUNTER") = 0 oSecondRow("SPEC_NAME") = "ZUGFERD_XML_SCHEMA" oSecondRow("IS_REQUIRED") = False _logger.Debug("Mapping Property [ZUGFERD_XML_SCHEMA] with value [{0}]", pDocument.UsedXMLSchema) oDataTable.Rows.Add(oSecondRow) ' Dritte Zeile enthält das verwendete Datei-Format des Belegs (PDF/XML) Dim oThirdRow As DataRow = oDataTable.NewRow() oThirdRow("REFERENCE_GUID") = pMessageId oThirdRow("ITEM_DESCRIPTION") = "Dateityp der E-Rechnung" oThirdRow("ITEM_VALUE") = pDocument.ReceiptFileType oThirdRow("GROUP_COUNTER") = 0 oThirdRow("SPEC_NAME") = "RECEIPT_FILE_TYPE" oThirdRow("IS_REQUIRED") = False _logger.Debug("Mapping Property [RECEIPT_FILE_TYPE] with value [{0}]", pDocument.ReceiptFileType) oDataTable.Rows.Add(oThirdRow) For Each oProperty In pCheckResult.ValidProperties ' ItemType = 3 => eingebettete Datei, nicht den base64 speichern If oProperty.ItemType = 3 Then Continue For End If ' ItemType = 0 (normale texte) dürfen nicht leer sein If oProperty.ItemType = 0 And oProperty.Value.IsNullOrEmpty Then _logger.Debug("No Mapping for Property [{0}] with empty value, because of ItemType = 0.", oProperty.TableColumn) Continue For End If ' If GroupCounter is -1, it means this is a default property that can only occur once. ' Set the actual inserted value to 0 Dim oGroupCounterValue As Integer = oProperty.GroupCounter If oGroupCounterValue = -1 Then oGroupCounterValue = 0 End If 'If oProperty.Value.Length > 900 Then ' _logger.Warn("Value for field [{0}] is longer than 900 characters, will be truncated!", oProperty.TableColumn) 'End If Dim oDescription As String If oProperty.EN16931_ID IsNot Nothing AndAlso oProperty.EN16931_ID.Length > 1 Then oDescription = oProperty.EN16931_ID + " (" + oProperty.Description + ")" Else oDescription = oProperty.Description End If Dim oNewRow As DataRow = oDataTable.NewRow() oNewRow("REFERENCE_GUID") = pMessageId oNewRow("ITEM_DESCRIPTION") = oDescription 'oNewRow("ITEM_VALUE") = oProperty.Value.Truncate(900) oNewRow("ITEM_VALUE") = oProperty.Value oNewRow("GROUP_COUNTER") = oGroupCounterValue oNewRow("SPEC_NAME") = oProperty.TableColumn oNewRow("IS_REQUIRED") = oProperty.IsRequired _logger.Debug("Mapping Property [{0}] with value [{1}]", oProperty.TableColumn, oProperty.Value) oDataTable.Rows.Add(oNewRow) Next Return oDataTable End Function Private Function DeleteExistingPropertyValues(pMessageId As String, pConnections As DatabaseConnections) As Boolean Dim oDelSQL = $"DELETE FROM TBEDMI_ITEM_VALUE where REFERENCE_GUID = '{pMessageId}'" Dim oStep As String = "TBEDMI_ITEM_VALUE Delete MessageID Items" Try Dim retValue As Boolean = _mssql.ExecuteNonQueryWithConnectionObject(oDelSQL, pConnections.SQLServerConnection, MSSQLServer.TransactionMode.ExternalTransaction, pConnections.SQLServerTransaction) Return retValue Catch ex As Exception _logger.Warn("Step [{0}] with SQL [{1}] was not successful.", oStep, oDelSQL) End Try Try oDelSQL = $"DELETE FROM TBEDMI_ITEM_FILES where REFERENCE_GUID = '{pMessageId}'" oStep = "TBEDMI_ITEM_FILES Delete MessageID Items" Dim retValue As Boolean = _mssql.ExecuteNonQueryWithConnectionObject(oDelSQL, pConnections.SQLServerConnection, MSSQLServer.TransactionMode.ExternalTransaction, pConnections.SQLServerTransaction) Return retValue Catch ex As Exception _logger.Warn("Step [{0}] with SQL [{1}] was not successful.", oStep, oDelSQL) End Try Return False End Function Private Function BulkInsert(pConnections As DatabaseConnections, pTable As DataTable, pDestinationTable As String, pColumns As List(Of String)) As Boolean Using oBulkCopy = New SqlBulkCopy(pConnections.SQLServerConnection, SqlBulkCopyOptions.Default, pConnections.SQLServerTransaction) oBulkCopy.DestinationTableName = pDestinationTable For Each oColumn In pColumns oBulkCopy.ColumnMappings.Add(New SqlBulkCopyColumnMapping(oColumn, oColumn)) Next Try oBulkCopy.WriteToServer(pTable) Catch ex As Exception _logger.Error(ex) Return False End Try End Using Return True End Function ''' ''' Fügt neue Datensätze in Tabelle TBEMLP_HISTORY_STATE ein, ''' per Prozedur DD_ECM.[dbo].[PRCUST_ADD_HISTORY_STATE] ''' Private Sub AddRejectedState(pMessageID As String, pTitle As String, pTitle1 As String, pComment As String, pTransaction As SqlTransaction) Try 'PRCUST_ADD_HISTORY_STATE: @MessageID VARCHAR(250), @TITLE1 VARCHAR(250), @TITLE2 VARCHAR(250) Dim oSQL = $"EXEC PRCUST_ADD_HISTORY_STATE '{pMessageID}','{pTitle}','{pTitle1}','{pComment.Replace("'", "''")}'" _mssql.ExecuteNonQuery(oSQL, pTransaction) Catch ex As Exception _logger.Error(ex) End Try End Sub End Class