MONSTER: Rename Monorepo to Modules, only keep Projects under Modules.*

This commit is contained in:
Jonathan Jenne
2022-09-29 13:46:00 +02:00
parent e87b97bfec
commit 042bbce9f4
1557 changed files with 380 additions and 160017 deletions

14
Jobs/App.config Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="FirebirdSql.Data.FirebirdClient" publicKeyToken="3750abcc3150b00c" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.0.0" newVersion="7.5.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@@ -0,0 +1,14 @@
Public Class ADSyncArgs
Inherits JobArgs
''' <summary>
''' LDAP URI that acts as the root node of searches, ex: LDAP://DIGITALDATA
''' </summary>
Public RootPath As String
Public UserFilter As String
Public GroupFilter As String
Public DisableFirebird As String
Public DisableMSSQL As String
End Class

View File

@@ -0,0 +1,85 @@
Imports System.Collections.Generic
Imports System.Data
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Interfaces
Imports DigitalData.Modules.Logging
Public Class ADSyncJob
Inherits JobBase
Implements IJob(Of ADSyncArgs)
Public Sub New(LogConfig As LogConfig, Firebird As Firebird, MSSQL As MSSQLServer)
MyBase.New(LogConfig, Firebird, MSSQL)
End Sub
Public Sub Start(Arguments As ADSyncArgs) Implements IJob(Of ADSyncArgs).Start
Dim oJobName As String = [GetType]().Name
Try
Dim oSync = New ActiveDirectoryInterface(_LogConfig, Arguments.RootPath)
_Logger.Info("Running job {0}", oJobName)
If oSync.Authenticate() = False Then
_Logger.Warn("Job {0} could not be completed! Authentication failed!", oJobName)
Exit Sub
End If
Dim oGroups = GetGroups()
Dim oAttributeMappings = GetAttributeMappings()
_Logger.Debug("Found {0} Groups", oGroups.Count)
For Each oGroup In oGroups
_Logger.Debug("Syncing Group [{0}]", oGroup)
Dim oSyncedUsers = oSync.SyncUsersForGroup(oGroup, oAttributeMappings, _Firebird, _MSSQL, Arguments.UserFilter)
If oSyncedUsers Is Nothing Then
_Logger.Warn("Group [{0}] could not be synced!", oGroup)
ElseIf oSyncedUsers.Count > 0 Then
_Logger.Info("Processed [{0}] users for group [{1}]", oSyncedUsers.Count, oGroup)
End If
Next
_Logger.Info("Job {0} completed!", oJobName)
Catch ex As Exception
_Logger.Warn("Job {0} failed!", oJobName)
_Logger.Error(ex)
End Try
End Sub
Private Function GetGroups() As List(Of String)
Try
Dim oGroups As New List(Of String)
Dim oDatatable = _MSSQL.GetDatatable("SELECT NAME FROM TBDD_GROUPS WHERE AD_SYNC = 1 AND ACTIVE = 1")
For Each oRow As DataRow In oDatatable.Rows
oGroups.Add(oRow.Item("NAME"))
Next
Return oGroups
Catch ex As Exception
_Logger.Error(ex)
Return New List(Of String)
End Try
End Function
Private Function GetAttributeMappings() As List(Of AttributeMapping)
Dim oDatatable = _MSSQL.GetDatatable("SELECT * FROM TBDD_EXTATTRIBUTES_MATCHING")
Dim oAttributeMappings = New List(Of AttributeMapping)
For Each oRow As DataRow In oDatatable.Rows
oAttributeMappings.Add(New AttributeMapping() With {
.AttributeName = oRow.Item("EXT_ATTRIBUTE"),
.FirebirdSyskey = oRow.Item("FB_SYS_KEY"),
.MSSQLColumn = oRow.Item("TBDD_USER_COLUMN")
})
Next
Return oAttributeMappings
End Function
Public Function ShouldStart(Arguments As ADSyncArgs) As Boolean Implements IJob(Of ADSyncArgs).ShouldStart
Return Arguments.Enabled
End Function
End Class

View File

@@ -0,0 +1,5 @@
Public Class GraphQLArgs
Inherits JobArgs
Public QueryConfigPath As String
End Class

View File

@@ -0,0 +1,6 @@
Public Class GraphQLConfig
Public Property BaseUrl As String = ""
Public Property Email As String = ""
Public Property Password As String = ""
Public Property CertificateFingerprint As String = ""
End Class

View File

@@ -0,0 +1,223 @@
Option Explicit On
Imports System.IO
Imports DigitalData.Modules.Interfaces
Imports DigitalData.Modules.Jobs
Imports DigitalData.Modules.Config
Imports DigitalData.Modules.Logging
Imports Newtonsoft.Json.Linq
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text.RegularExpressions
Imports DigitalData.Modules.Database
Imports System.Data
Public Class GraphQLJob
Inherits JobBase
Implements IJob(Of GraphQLArgs)
Private _GraphQL As GraphQLInterface = Nothing
Private Const PLACEHOLDER_STATIC = "STATIC:"
Public Sub New(LogConfig As LogConfig, MSSQL As MSSQLServer)
MyBase.New(LogConfig, Nothing, MSSQL)
End Sub
Public Sub Start(Args As GraphQLArgs) Implements IJob(Of GraphQLArgs).Start
Try
Dim oConfigPath As String = Args.QueryConfigPath
Dim oConfigManager As New ConfigManager(Of GraphQLConfig)(_LogConfig, oConfigPath)
With oConfigManager.Config
_GraphQL = New GraphQLInterface(_LogConfig, .BaseUrl, .Email, .Password, .CertificateFingerprint)
End With
' Login to get cookie
_Logger.Debug("Logging in")
Dim oLoginResponse = _GraphQL.Login()
' save cookie for future requests
_GraphQL.SaveCookies(oLoginResponse.Cookies.Item(0))
_Logger.Debug("Loading Queries")
' Load query data from TBCUST_JOBRUNNER_QUERY
Dim oQueryTable As DataTable = _MSSQL.GetDatatable("SELECT * FROM TBCUST_JOBRUNNER_QUERY ORDER BY OPERATION_NAME, CLEAR_BEFORE_FILL ASC")
Dim oQueryList As New List(Of GraphQL.Query)
' Save query data to business objects
For Each oRow As DataRow In oQueryTable.Rows
Dim oQuery As New GraphQL.Query With {
.Id = oRow.Item("GUID"),
.Name = oRow.Item("TITLE"),
.ClearBeforeFill = oRow.Item("CLEAR_BEFORE_FILL"),
.ConnectionId = oRow.Item("CON_ID"), ' TODO: Connection String?
.DestinationTable = oRow.Item("DESTINATION_TABLE"),
.OperationName = oRow.Item("OPERATION_NAME"),
.MappingBasePath = oRow.Item("MAPPING_BASE_PATH"),
.QueryString = oRow.Item("QUERY_STRING"),
.QueryConstraint = oRow.Item("QUERY_CONSTRAINT")
}
oQueryList.Add(oQuery)
Next
_Logger.Debug("Getting the data from GraphQL")
For Each oQuery As GraphQL.Query In oQueryList
Try
_Logger.NewBlock($"Query [{oQuery.Name}]")
Dim oConnectionId As Integer = oQuery.ConnectionId
Dim oConnectionString = _MSSQL.Get_ConnectionStringforID(oConnectionId)
Dim oDatabase As New MSSQLServer(_LogConfig, oConnectionString)
' Reset all records to status = 0
_Logger.Info("Resetting data with constraint [{1}]", oQuery.Name, oQuery.QueryConstraint)
Dim oResetSQL = $"UPDATE {oQuery.DestinationTable} SET STATUS = 0"
If oQuery.QueryConstraint <> String.Empty Then
oResetSQL &= $" WHERE {oQuery.QueryConstraint}"
End If
_MSSQL.ExecuteNonQuery(oResetSQL)
_Logger.Info("Getting data..", oQuery.Name)
' get the data from GraphQL
Dim oDataResponse = _GraphQL.GetData(oQuery.QueryString, oQuery.OperationName)
Dim oResult As String
' write data to string
Using oStream = oDataResponse.GetResponseStream()
Using oReader As New StreamReader(oStream)
oResult = oReader.ReadToEnd()
End Using
End Using
' Fill the query object with field mapping data from TBCUST_JOBRUNNER_QUERY_MAPPING
Dim oSQL As String = "SELECT t2.* FROM TBCUST_JOBRUNNER_QUERY_MAPPING t
JOIN TBCUST_JOBRUNNER_MAPPING t2 ON t.MAPPING_ID = t2.GUID
WHERE t.QUERY_ID = {0}"
Dim oMappingTable As DataTable = _MSSQL.GetDatatable(String.Format(oSQL, oQuery.Id))
For Each oMapping As DataRow In oMappingTable.Rows
oQuery.MappingFields.Add(New GraphQL.FieldMapping With {
.DestinationColumn = oMapping.Item("DestinationColumn"),
.SourcePath = oMapping.Item("SourcePath")
})
Next
' Handle the response from GraphQL and insert Data
Dim oQueryHandleResult = HandleResponse(oResult, oQuery, oDatabase)
If IsNothing(oQueryHandleResult) Then
Continue For
End If
' Finally delete all old records
Dim oDeleteSQL = $"DELETE FROM {oQuery.DestinationTable} WHERE STATUS = 0"
If oQuery.QueryConstraint <> String.Empty Then
oDeleteSQL &= $" AND {oQuery.QueryConstraint}"
End If
_Logger.Info("Success, deleting old records..", oQuery.Name)
_MSSQL.ExecuteNonQuery(oDeleteSQL)
Catch ex As Exception
_Logger.Warn("Error while getting Data for Name/OperationName [{0}]/[{1}]", oQuery.Name, oQuery.OperationName)
_Logger.Error(ex)
_Logger.Info("Failure, deleting new records..", oQuery.Name)
' If a crash happens, delete all records which were inserted in this run,
' thus going back to the previous state
Dim oDeleteSQL = $"DELETE FROM {oQuery.DestinationTable} WHERE STATUS = 1"
If oQuery.QueryConstraint <> String.Empty Then
oDeleteSQL &= $" AND {oQuery.QueryConstraint}"
End If
_MSSQL.ExecuteNonQuery(oDeleteSQL)
Finally
_Logger.EndBlock()
End Try
Next
' logout
_Logger.Debug("Logging out")
Dim oLogoutResponse = _GraphQL.Logout()
Catch ex As Exception
_Logger.Error(ex)
Throw ex
End Try
End Sub
Private Function HandleResponse(JsonString As String, QueryData As GraphQL.Query, DB As Database.MSSQLServer) As GraphQL.Query
Dim oObj As JObject = JObject.Parse(JsonString)
Dim oResultList As JToken
If _GraphQL.ReadJSONPathFragmented(oObj, QueryData.MappingBasePath) = False Then
_Logger.Warn("There is an error in the MappingBasePath [{1}] configuration of query [{0}]", QueryData.Name, QueryData.MappingBasePath)
End If
Try
oResultList = oObj.SelectToken(QueryData.MappingBasePath, errorWhenNoMatch:=True)
Catch ex As Exception
_Logger.Warn("HandleResponse: Could not find BasePath: [{0}] for query [{1}]", QueryData.MappingBasePath, QueryData.Name)
_Logger.Error(ex)
Return Nothing
End Try
If oResultList Is Nothing Then
_Logger.Warn("HandleResponse: Could not find BasePath: [{0}] for query [{1}]", QueryData.MappingBasePath, QueryData.Name)
Return Nothing
End If
_Logger.Info("HandleResponse: Processing Queue [{0}] with [{1}] Items", QueryData.Name, oResultList.Count)
For Each oResultItem As JToken In oResultList
Try
Dim oValues As New List(Of String)
Dim oKeys As New List(Of String)
For Each oMapping In QueryData.MappingFields
Dim oValue As String = String.Empty
If oMapping.SourcePath.StartsWith(PLACEHOLDER_STATIC) Then
oValue = oMapping.SourcePath.Replace(PLACEHOLDER_STATIC, String.Empty)
Else
Dim oToken = oResultItem.SelectToken(oMapping.SourcePath)
If oToken Is Nothing Then
_Logger.Warn("HandleResponse: Could not find value at SourcePath: {0}", oMapping.SourcePath)
oValue = String.Empty
Else
oValue = oToken.ToString
End If
End If
oValues.Add(oValue)
oKeys.Add(oMapping.DestinationColumn)
Next
Dim oColumnValues = oValues.
Select(Function(Value) Regex.Replace(Value, "'", "''")).
Select(Function(Value) $"'{Value}'").
ToList()
Dim oValueString = String.Join(",", oColumnValues)
Dim oColumns = String.Join(",", oKeys.ToArray)
Dim oSQL As String = $"INSERT INTO {QueryData.DestinationTable} ({oColumns}) VALUES ({oValueString})"
DB.ExecuteNonQuery(oSQL)
Catch ex As Exception
_Logger.Error(ex)
End Try
Next
Return QueryData
End Function
Public Function ShouldStart(Arguments As GraphQLArgs) As Boolean Implements IJob(Of GraphQLArgs).ShouldStart
Return Arguments.Enabled
End Function
End Class

View File

@@ -0,0 +1,24 @@
Imports System.Collections.Generic
Namespace GraphQL
Public Class Query
Public Property Id As Integer
Public Property Name As String
Public Property ConnectionId As String = ""
Public Property ClearBeforeFill As Boolean = False
Public Property ClearCommand As String = ""
Public Property QueryString As String = ""
Public Property QueryConstraint As String = ""
Public Property OperationName As String = ""
Public Property DestinationTable As String = ""
Public Property MappingBasePath As String = ""
Public Property MappingFields As New List(Of FieldMapping)
End Class
Public Class FieldMapping
Public SourcePath As String = ""
Public DestinationColumn As String = ""
Public Value As String = ""
End Class
End Namespace

View File

@@ -0,0 +1,5 @@
Public Class EmailData
Public Attachment As String = ""
Public Subject As String
Public From As String
End Class

View File

@@ -0,0 +1,203 @@
Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.Database
Imports System.Data
Imports System.IO
Imports System.Data.SqlClient
Public Class EmailFunctions
Private ReadOnly _logConfig As LogConfig
Private ReadOnly _logger As Logger
Private ReadOnly _mssql As MSSQLServer
Private ReadOnly _firebird As Firebird
Public Sub New(LogConfig As LogConfig, MSSQL As MSSQLServer, Firebird As Firebird)
_logConfig = LogConfig
_logger = _logConfig.GetLogger()
_mssql = MSSQL
_firebird = Firebird
End Sub
Public Sub AddToEmailQueueFB(MessageId As String, BodyText As String, EmailData As EmailData, NamePortal As String)
If EmailData Is Nothing Then
_logger.Warn("EmailData is empty. Email will not be sent!")
Exit Sub
End If
Try
Dim oJobId = RandomValue(1, 10000)
Dim oReference = MessageId
Dim oEmailTo = ""
Dim oSubject = EmailStrings.EMAIL_SUBJECT_REJECTED.Replace(EmailStrings.constNAME_ZUGFERD_PORTAL, NamePortal)
Dim oAccountId = 1
Dim oCreatedWho = "ZUGFeRD Service"
Dim oFinalBodyText = String.Format(EmailStrings.EMAIL_WRAPPING_TEXT.Replace(EmailStrings.constNAME_ZUGFERD_PORTAL, NamePortal), BodyText)
Dim oEmailAddress = EmailData.From
Dim oAttachment = EmailData.Attachment
If IsNothing(oEmailAddress) OrElse String.IsNullOrWhiteSpace(oEmailAddress) Then
_logger.Warn("Could not find email-address for MessageId {0}", MessageId)
oEmailTo = String.Empty
Else
oEmailTo = oEmailAddress
End If
_logger.Debug("Generated Email:")
_logger.Debug("To: {0}", oEmailTo)
_logger.Debug("Subject: {0}", oSubject)
_logger.Debug("Body {0}", oFinalBodyText)
Dim osql = $"select * from TBEDM_EMAIL_QUEUE where REFERENCE1 = '{oReference} and EMAIL_TO = ''{oEmailTo}' and EMAIL_SUBJ = '{oSubject}'"
Dim oDTResult As DataTable = _firebird.GetDatatable(osql)
If oDTResult.Rows.Count = 0 Then
Dim oSQLInsert = $"INSERT INTO TBEDM_EMAIL_QUEUE "
oSQLInsert &= "(JOB_ID, REFERENCE1, EMAIL_ACCOUNT_ID, EMAIL_TO, EMAIL_SUBJ, EMAIL_BODY, CREATEDWHO, EMAIL_ATTMT1) VALUES "
oSQLInsert &= $"({oJobId}, '{oReference}', {oAccountId}, '{oEmailTo}', '{oSubject}', '{oFinalBodyText.Replace("'", "''")}', '{oCreatedWho}', '{oAttachment}')"
_firebird.ExecuteNonQuery(oSQLInsert)
_logger.Debug("Email Queue updated for MessageId {0}.", MessageId, oEmailTo)
Else
_logger.Debug("Email has already been sent!!")
End If
Catch ex As Exception
_logger.Error(ex)
End Try
End Sub
Public Sub AddToEmailQueueMSSQL(MessageId As String, BodyText As String, pEmailData As EmailData, SourceProcedure As String, pEmailAccountId As Integer, NamePortal As String)
If pEmailData Is Nothing Then
_logger.Warn("EmailData is empty. Email will not be sent!")
Exit Sub
End If
Try
Dim oJobId = RandomValue(1, 10000)
Dim oReference = MessageId
Dim oEmailTo = ""
Dim oSubject = EmailStrings.EMAIL_SUBJECT_REJECTED.Replace(EmailStrings.constNAME_ZUGFERD_PORTAL, NamePortal)
Dim oCreatedWho = "ZUGFeRD Service"
Dim oMaskedBodyText = BodyText.Replace("'", "''")
Dim oSubjectBodyText = String.Format(EmailStrings.EMAIL_SUBJECT_TEXT.Replace(EmailStrings.constNAME_ZUGFERD_PORTAL, NamePortal), pEmailData.Subject).Replace("'", "''")
Dim oCompleteBodyText = oMaskedBodyText & oSubjectBodyText
Dim oFinalBodyText = String.Format(EmailStrings.EMAIL_WRAPPING_TEXT.Replace(EmailStrings.constNAME_ZUGFERD_PORTAL, NamePortal), oCompleteBodyText)
Dim oEmailAddress = pEmailData.From
Dim oAttachment = pEmailData.Attachment
If oAttachment <> String.Empty Then
_logger.Debug($"Attachment_String [{oAttachment}]!")
If IO.File.Exists(oAttachment) = False Then
_logger.Info($"Attachment.File [{oAttachment}] is not existing!!!")
End If
End If
If IsNothing(oEmailAddress) OrElse String.IsNullOrWhiteSpace(oEmailAddress) Then
_logger.Warn("Could not find email-address for MessageId {0}", MessageId)
oEmailTo = String.Empty
Else
oEmailTo = oEmailAddress
End If
_logger.Debug("Generated Email:")
_logger.Debug("To: {0}", oEmailTo)
_logger.Debug("Subject: {0}", oSubject)
_logger.Debug("Body {0}", oFinalBodyText)
Dim osql = $"Select MAX(GUID) FROM TBEMLP_HISTORY WHERE EMAIL_MSGID = '{MessageId}'"
Dim oHistoryID = _mssql.GetScalarValue(osql)
'osql = $"select * from TBEMLP_EMAIL_OUT where REFERENCE_ID = {oHistoryID} and EMAIL_ADRESS = '{oEmailTo}' and EMAIL_SUBJ = '{oSubject}'"
'Dim oDTResult As DataTable = _mssql.GetDatatable(osql)
If IsNumeric(oHistoryID) Then
Dim oInsert = $"INSERT INTO [dbo].[TBEMLP_EMAIL_OUT] (
[REMINDER_TYPE_ID]
,[SENDING_PROFILE]
,[REFERENCE_ID]
,[REFERENCE_STRING]
,[WF_ID]
,[EMAIL_ADRESS]
,[EMAIL_SUBJ]
,[EMAIL_BODY]
,[COMMENT]
,[ADDED_WHO]
,EMAIL_ATTMT1)
VALUES
(77
,{pEmailAccountId}
,{oHistoryID}
,'{MessageId}'
,77
,'{oEmailTo}'
,'{oSubject}'
,'{oFinalBodyText}'
,'{SourceProcedure}'
,'{oCreatedWho}'
,'{oAttachment}')"
_mssql.ExecuteNonQuery(oInsert)
Else
'If oDTResult.Rows.Count = 0 Then
' _logger.Debug("Email has already been sent!!")
'Else
_logger.Warn("Could not get oHistoryID in AddToEmailQueueMSSQL!!")
' End If
End If
Catch ex As Exception
_logger.Error(ex)
End Try
End Sub
Public Function GetEmailDataForMessageId(MessageId As String) As EmailData
Dim oSQL = $"SELECT EMAIL_FROM, EMAIL_SUBJECT, EMAIL_ATTMT1 FROM TBEDM_EMAIL_PROFILER_HISTORY WHERE EMAIL_MSGID = '{MessageId}'"
Try
Dim oDatatable = _firebird.GetDatatable(oSQL)
Dim oRow As DataRow
If oDatatable.Rows.Count = 0 Then
_logger.Warn("Got no results for MessageId {0}", MessageId)
Return Nothing
ElseIf oDatatable.Rows.Count > 1 Then
_logger.Warn("Got too many results for MessageId {0}. Using last row.", MessageId)
End If
_logger.Debug("Got Email Data for FileId {0}", MessageId)
oRow = oDatatable.Rows.Item(oDatatable.Rows.Count - 1)
Return New EmailData() With {
.From = oRow.Item("EMAIL_FROM"),
.Attachment = oRow.Item("EMAIL_ATTMT1"),
.Subject = oRow.Item("EMAIL_SUBJECT")
}
Catch ex As Exception
_logger.Warn("Could not fetch Email Data for FileId {0}", MessageId)
Return Nothing
End Try
End Function
Public Function GetOriginalEmailPath(OriginalEmailDirectory As String, MessageId As String) As String
Dim oAttachmentDirectory = OriginalEmailDirectory
Dim oAttachmentFile = MessageId & ".eml"
Dim oAttachmentPath = Path.Combine(oAttachmentDirectory, oAttachmentFile)
If File.Exists(oAttachmentPath) Then
Return oAttachmentPath
Else
_logger.Warn("Email File {0} does not exist. Empty String will be returned.", oAttachmentPath)
Return String.Empty
End If
End Function
Public Function GetEmailPathWithSubjectAsName(RejectedEmailDirectory As String, UncleanedSubject As String) As String
Dim oCleanSubject = String.Join("", UncleanedSubject.Split(Path.GetInvalidPathChars()))
Dim oAttachmentDirectory = RejectedEmailDirectory
Dim oAttachmentFile = oCleanSubject & ".eml"
Dim oAttachmentPath = Path.Combine(oAttachmentDirectory, oAttachmentFile)
Return oAttachmentPath
End Function
Private Function RandomValue(lowerBound As Integer, upperBound As Integer) As Integer
Dim oRandomValue = CInt(Math.Floor((upperBound - lowerBound + 1) * Rnd())) + lowerBound
Return oRandomValue
End Function
End Class

View File

@@ -0,0 +1,40 @@
Public Class EmailStrings
Public Const constNAME_ZUGFERD_PORTAL = "@NAME_ZUGFERD_PORTAL"
Public Const EMAIL_WRAPPING_TEXT = "<html><body style=''font-family:""Arial"";font-size:10.0pt''>Sehr geehrte Damen und Herren,<br/><br/>
das @NAME_ZUGFERD_PORTAL zur Verarbeitung der Eingangsrechnungen im ZUGFeRD-Format konnte die von Ihnen gesandte Rechnung
leider nicht verarbeiten! <br><br> Grund: {0}<p>Bitte prüfen Sie die Datei und nehmen Sie bei Bedarf mit uns Kontakt auf.<p>
Vielen Dank für Ihr Verständnis.<br>Mit freundlichen Grüßen<br>Ihre IT-Abteilung</body></html>"
Public Const EMAIL_SUBJECT_TEXT = "<p>Der Betreff der Original-Email war: <em>{0}</em></p>"
Public Const EMAIL_SUBJECT_REJECTED = "@NAME_ZUGFERD_PORTAL: Beleg abgelehnt"
Public Const EMAIL_SUBJECT_EXCEPTION = "@NAME_ZUGFERD_PORTAL: Unbehandelte Ausnahme"
Public Const EMAIL_UNHANDLED_EXCEPTION = """
<p>Beim Verarbeiten der Datei mit der Message ID '{0}' ist ein schwerer Fehler aufgetreten.</p>
<p>Fehlerbeschreibung: {1}</p>
<pre>{2}</pre>
"""
Public Const EMAIL_MISSINGPROPERTIES_1 = "<p>Die angehängte Datei entspricht nicht dem WISAG ZUGFeRD-Format: {0}</p>"
Public Const EMAIL_MISSINGPROPERTIES_2 = "<p>Die folgenden Eigenschaften wurden als ERFORDERLICH eingestuft, wurden aber nicht gefunden:<p/>"
Public Const EMAIL_MD5_ERROR = "<p>Die von Ihnen gesendete Rechnung wurde bereits von unserem System verarbeitet.</p>"
Public Const EMAIL_TOO_MUCH_FERDS = "<p>Ihre Email enthielt mehr als ein ZUGFeRD-Dokument.</p>"
Public Const EMAIL_NO_FERDS = "<p>Ihre Email enthielt keine ZUGFeRD-Dokumente.</p>"
Public Const EMAIL_FILE_SIZE_REACHED = "
<p>Die von Ihnen gesendete Rechnung oder einer der Rechnungs-Anhänge überschreitet die erlaubte Größe von <strong>{0} MB</strong>.</p>
<p>Die folgende Datei hat die erlaubte Größe überschritten:<ul>
<li>{1}</li>
</ul></p>
"
Public Const EMAIL_INVALID_DOCUMENT = "
<p>Ihre Email enthielt ein ZUGFeRD Dokument, welches aber inkorrekt formatiert wurde.</p>
<p>Mögliche Gründe für ein inkorrektes Format:<ul>
<li>Betrags-Werte weisen ungültiges Format auf (25,01 anstatt 25.01)</li>
</ul></p>
"
End Class

View File

@@ -0,0 +1,825 @@
Imports System.Collections.Generic
Imports System.Data
Imports System.IO
Imports System.Linq
Imports System.Security.Cryptography
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Interfaces
Imports DigitalData.Modules.Interfaces.Exceptions
Imports DigitalData.Modules.Jobs.Exceptions
Imports DigitalData.Modules.Logging
Imports FirebirdSql.Data.FirebirdClient
Imports System.Data.SqlClient
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"
' 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 List(Of String) = New List(Of String) From {"docx", "doc", "pdf", "xls", "xlsx", "ppt", "pptx", "txt"}
Private ReadOnly _logger As Logger
Private ReadOnly _logConfig As LogConfig
Private ReadOnly _zugferd As ZUGFeRDInterface
Private ReadOnly _firebird As Firebird
Private ReadOnly _filesystem As Filesystem.File
Private ReadOnly _EmailOutAccountId As Integer
Private ReadOnly _mssql As MSSQLServer
Private ReadOnly _email As EmailFunctions
Public Sub New(LogConfig As LogConfig, Firebird As Firebird, pEmailOutAccount As Integer, pPortalName As String, Optional MSSQL As MSSQLServer = Nothing)
_logConfig = LogConfig
_logger = LogConfig.GetLogger()
_firebird = Firebird
_filesystem = New Filesystem.File(_logConfig)
_mssql = MSSQL
_EmailOutAccountId = pEmailOutAccount
_email = New EmailFunctions(LogConfig, _mssql, _firebird)
_logger.Debug("Registering GDPicture License")
If _mssql IsNot Nothing Then
Dim oSQL = "SELECT LICENSE FROM TBDD_3RD_PARTY_MODULES WHERE NAME = 'GDPICTURE'"
Dim oLicenseKey As String = _mssql.GetScalarValue(oSQL)
_zugferd = New ZUGFeRDInterface(_logConfig, oLicenseKey)
Else
_logger.Warn("GDPicture License could not be registered! MSSQL is not enabled!")
Throw New ArgumentNullException("MSSQL")
End If
End Sub
Private Function MoveAndRenameEmailToRejected(Args As WorkerArgs, MessageId As String) As EmailData
Dim oEmailData = _email.GetEmailDataForMessageId(MessageId)
Dim oSource = _email.GetOriginalEmailPath(Args.OriginalEmailDirectory, MessageId)
Dim oDateSubDirectoryName As String = Now.ToString("yyyy-MM-dd")
Dim oDestination As String
Dim oRejectedDirectory As String = Path.Combine(Args.RejectedEmailDirectory, oDateSubDirectoryName)
' Create the destination directory if it does not exist
If Not Directory.Exists(oRejectedDirectory) Then
Try
Directory.CreateDirectory(oRejectedDirectory)
Catch ex As Exception
_logger.Error(ex)
End Try
End If
' If oEmailData is Nothing, TBEDM_EMAIL_PROFILER_HISTORY for MessageId was not found.
' This only should happen when testing and db-tables are deleted frequently
If oEmailData Is Nothing Then
oDestination = _email.GetEmailPathWithSubjectAsName(oRejectedDirectory, MessageId)
Else
oDestination = _email.GetEmailPathWithSubjectAsName(oRejectedDirectory, oEmailData.Subject)
End If
_logger.Debug("Destination for eml file is {0}", oDestination)
Dim oFinalFileName = _filesystem.GetVersionedFilename(oDestination)
_logger.Debug("Versioned filename for eml file is {0}", oFinalFileName)
If oEmailData Is Nothing Then
_logger.Warn("Could not get Email Data from firebird-database. File {0} will not be moved!", oSource)
Return Nothing
End If
Try
_logger.Info("Moving email from {0} to {1}", oSource, oFinalFileName)
IO.File.Move(oSource, oFinalFileName)
oEmailData.Attachment = oFinalFileName
Catch ex As Exception
_logger.Warn("File {0} could not be moved! Original Filename will be used!", oSource)
_logger.Error(ex)
oEmailData.Attachment = oSource
End Try
Return oEmailData
End Function
Private Sub AddRejectedState(oMessageID As String, oTitle As String, oTitle1 As String, oComment As String, Transaction As SqlTransaction)
Try
'PRCUST_ADD_HISTORY_STATE: @MessageID VARCHAR(250), @TITLE1 VARCHAR(250), @TITLE2 VARCHAR(250)
Dim oSQL = $"EXEC PRCUST_ADD_HISTORY_STATE '{oMessageID}','{oTitle}','{oTitle1}','{oComment.Replace("'", "''")}'"
_mssql.ExecuteNonQuery(oSQL, Transaction)
Catch ex As Exception
_logger.Error(ex)
End Try
End Sub
Public Sub Start(Arguments As Object) Implements IJob.Start
Dim oArgs As WorkerArgs = Arguments
Dim oPropertyExtractor = New PropertyValues(_logConfig)
Dim oAttachmentExtractor = New PDFEmbeds(_logConfig)
_logger.Debug("Starting Job {0}", [GetType].Name)
Try
For Each oPath As String In oArgs.WatchDirectories
Dim oDirInfo As New DirectoryInfo(oPath)
_logger.Debug($"Start processing directory {oDirInfo.FullName}")
If oDirInfo.Exists Then
' Filter out *.lock files
Dim oFiles As List(Of FileInfo) = oDirInfo.
GetFiles().
Where(Function(f) Not f.Name.EndsWith(".lock")).
ToList()
Dim oFileCount = oFiles.Count
Dim oCurrentFileCount = 0
If oFileCount = 0 Then
_logger.Debug("No files to process.")
Continue For
Else
_logger.Info("Found {0} files", oFileCount)
End If
' 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
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 oFBConnection As FbConnection = _firebird.GetConnection()
Dim oFBTransaction As FbTransaction = oFBConnection.BeginTransaction()
Dim oSQLConnection As SqlConnection = _mssql.GetConnection()
Dim oSQLTransaction As SqlTransaction = oSQLConnection?.BeginTransaction()
If oSQLConnection Is Nothing Then
_logger.Warn("SQL Connection was not set. No INSERTs for MSSQL Server will be performed!")
oArgs.InsertIntoSQLServer = False
End If
' 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 oFileGroupFiles 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 oMissingProperties As New List(Of String)
Dim oMD5CheckSum As String = String.Empty
_logger.NewBlock($"Message Id {oMessageId}")
_logger.Info("Start processing file group {0}", oMessageId)
Try
For Each oFile In oFileGroupFiles
' 09.12.2021: oDocument is now an Object, because have different classes corresponding to the
' different versions of ZUGFeRD and the type is unknown at compile-time.
Dim oDocument As Object
' Start a global group counter for each file
Dim oGlobalGroupCounter = 0
' Clear missing properties for the new file
oMissingProperties = New List(Of String)
oCurrentFileCount += 1
' Only pdf files are allowed from here on
If Not oFile.Name.ToUpper.EndsWith(".PDF") Then
_logger.Debug("Skipping non-pdf file {0}", oFile.Name)
oEmailAttachmentFiles.Add(oFile)
' Checking filesize for attachment files
If Check_FileSize(oFile, oArgs.MaxAttachmentSizeInMegaBytes) = False Then
_logger.Warn("Filesize for File [{0}] exceeded limit of {1} MB", oFile.Name, oArgs.MaxAttachmentSizeInMegaBytes)
Throw New FileSizeLimitReachedException(oFile.Name, oArgs.MaxAttachmentSizeInMegaBytes)
End If
Continue For
End If
_logger.Info("Start processing file {0}", oFile.Name)
' Checking filesize for pdf files
If Check_FileSize(oFile, oArgs.MaxAttachmentSizeInMegaBytes) = False Then
_logger.Warn("Filesize for File [{0}] exceeded limit of {1} MB", oFile.Name, oArgs.MaxAttachmentSizeInMegaBytes)
Throw New FileSizeLimitReachedException(oFile.Name, oArgs.MaxAttachmentSizeInMegaBytes)
End If
Try
oDocument = _zugferd.ExtractZUGFeRDFileWithGDPicture(oFile.FullName)
Catch ex As ZUGFeRDExecption
Select Case ex.ErrorType
Case ZUGFeRDInterface.ErrorType.NoZugferd
_logger.Info("File [{0}] is not a valid ZUGFeRD document. Skipping.", oFile.Name)
oEmailAttachmentFiles.Add(oFile)
Continue For
Case ZUGFeRDInterface.ErrorType.NoValidZugferd
_logger.Warn("File [{0}] is an Incorrectly formatted ZUGFeRD document!", oFile.Name)
Throw New InvalidFerdException()
Case Else
_logger.Warn("Unexpected Error occurred while extracting ZUGFeRD Information from file {0}", oFile.Name)
Throw ex
End Select
End Try
' 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 = oAttachmentExtractor.Extract(oFile.FullName, AllowedExtensions)
If oAttachments Is Nothing Then
_logger.Warn("Attachments for file [{0}] could not be extracted", oFile.FullName)
Else
oEmbeddedAttachmentFiles.AddRange(oAttachments)
End If
' Check the Checksum and rejection status
oMD5CheckSum = GenerateAndCheck_MD5Sum(oFile.FullName, oArgs.IgnoreRejectionStatus)
' Check if there are more than one ZUGFeRD files
If oZUGFeRDCount = 1 Then
Throw New TooMuchFerdsException()
End If
' Since extraction went well, increase the amount of ZUGFeRD files
oZUGFeRDCount += 1
' Check the document against the configured property map and return:
' - a List of valid properties
' - a List of missing properties
Dim oCheckResult = _zugferd.PropertyValues.CheckPropertyValues(oDocument, oArgs.PropertyMap, oMessageId)
_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)
oMissingProperties = oCheckResult.MissingProperties
Throw New MissingValueException(oFile)
End If
_logger.Debug("No missing properties found. Continuing.")
Dim oDelSQL = $"DELETE FROM TBEDMI_ITEM_VALUE where REFERENCE_GUID = '{oMessageId}'"
Dim oStep As String
oStep = "Firebird TBEDMI_ITEM_VALUE Delete messageID Items"
Try
_firebird.ExecuteNonQueryWithConnection(oDelSQL, oFBConnection, Firebird.TransactionMode.ExternalTransaction, oFBTransaction)
Catch ex As Exception
_logger.Error(ex)
_logger.Warn("Step [{0}] with SQL [{1}] was not successful.", oStep, oDelSQL)
End Try
If oArgs.InsertIntoSQLServer = True Then
oStep = "MSSQL TBEDMI_ITEM_VALUE Delete messageID Items"
Try
_mssql.ExecuteNonQueryWithConnectionObject(oDelSQL, oSQLConnection, MSSQLServer.TransactionMode.ExternalTransaction, oSQLTransaction)
Catch ex As Exception
_logger.Warn("Step [{0}] with SQL [{1}] was not successful.", oStep, oDelSQL)
End Try
End If
For Each oProperty In oCheckResult.ValidProperties
Dim oGroupCounterValue = oProperty.GroupCounter
' If GroupCounter is -1, it means this is a default property that can only occur once.
' Set the actual inserted value to 0
If oGroupCounterValue = -1 Then
oGroupCounterValue = 0
End If
Dim oCommand = $"INSERT INTO {oProperty.TableName} (REFERENCE_GUID, ITEM_DESCRIPTION, ITEM_VALUE, GROUP_COUNTER,SPEC_NAME,IS_REQUIRED) VALUES
('{oMessageId}', '{oProperty.Description}', '{oProperty.Value.Replace("'", "''")}', {oGroupCounterValue},'{oProperty.TableColumn}','{oProperty.ISRequired}')"
_logger.Debug("Mapping Property [{0}] with value [{1}], Will be inserted into table [{2}]", oProperty.TableColumn, oProperty.Value.Replace("'", "''"), oProperty.TableName)
' Insert into SQL Server
If oArgs.InsertIntoSQLServer = True Then
Dim oResult = _mssql.ExecuteNonQueryWithConnectionObject(oCommand, oSQLConnection, MSSQLServer.TransactionMode.ExternalTransaction, oSQLTransaction)
If oResult = False Then
_logger.Warn($"SQL Command [{oCommand}] was not successful. Check the log.")
End If
End If
' Insert into Firebird
_firebird.ExecuteNonQueryWithConnection(oCommand, oFBConnection, Firebird.TransactionMode.ExternalTransaction, oFBTransaction)
Next
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 oArgs.NonZugferdDirectory Is Nothing OrElse oArgs.NonZugferdDirectory = String.Empty Then
Throw New NoFerdsException()
End If
' Also, if the directory is set but does not exist, still a rejection will be generated.
If Not IO.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...
'Log the History
If oMD5CheckSum <> String.Empty Then
Create_HistoryEntry(oMessageId, oMD5CheckSum, "SUCCESS", oFBTransaction)
'Dim oInsertCommand = $"INSERT INTO TBEDM_ZUGFERD_HISTORY_IN (MESSAGE_ID, MD5HASH) VALUES ('{oMessageId}', '{oMD5CheckSum}')"
'_firebird.ExecuteNonQueryWithConnection(oInsertCommand, oFBConnection, Firebird.TransactionMode.ExternalTransaction, oFBTransaction)
'' History ID is only need in case of an error
'oFBTransaction.Commit()
'Try
' Dim oSQL = $"SELECT MAX(GUID) FROM TBEDM_ZUGFERD_HISTORY_IN WHERE MESSAGE_ID = '{oMessageId}'"
' HISTORY_ID = _firebird.GetScalarValue(oSQL)
'Catch ex As Exception
' HISTORY_ID = 0
'End Try
Else
Create_HistoryEntry(oMessageId, String.Empty, "SUCCESS (with empty MD5Hash)", oFBTransaction)
End If
oIsSuccess = True
oMoveDirectory = oArgs.SuccessDirectory
Catch ex As MD5HashException
_logger.Error(ex)
Dim oMessage = "REJECTED - Already processed (MD5Hash)"
Update_HistoryEntry(oMessageId, oMD5CheckSum, oMessage, oFBTransaction)
Dim oBody = EmailStrings.EMAIL_MD5_ERROR
Dim oEmailData = MoveAndRenameEmailToRejected(oArgs, oMessageId)
_email.AddToEmailQueueMSSQL(oMessageId, oBody, oEmailData, "MD5HashException", _EmailOutAccountId, oArgs.NamePortal)
AddRejectedState(oMessageId, "MD5HashException", "Die gesendete Rechnung wurde bereits verarbeitet!", "", oSQLTransaction)
Catch ex As InvalidFerdException
_logger.Error(ex)
' When InvalidFerdException is thrown, we don't have a MD5Hash yet.
' That 's why we set it to String.Empty here.
Create_HistoryEntry(oMessageId, String.Empty, "REJECTED - ZUGFeRD yes but incorrect format", oFBTransaction)
Dim oBody = EmailStrings.EMAIL_INVALID_DOCUMENT
Dim oEmailData = MoveAndRenameEmailToRejected(oArgs, oMessageId)
_email.AddToEmailQueueMSSQL(oMessageId, oBody, oEmailData, "InvalidFerdException", _EmailOutAccountId, oArgs.NamePortal)
AddRejectedState(oMessageId, "InvalidFerdException", "Inkorrekte Formate", "", oSQLTransaction)
Catch ex As TooMuchFerdsException
_logger.Error(ex)
Create_HistoryEntry(oMessageId, oMD5CheckSum, "REJECTED - More than one ZUGFeRD-document in email", oFBTransaction)
Dim oBody = EmailStrings.EMAIL_TOO_MUCH_FERDS
Dim oEmailData = MoveAndRenameEmailToRejected(oArgs, oMessageId)
_email.AddToEmailQueueMSSQL(oMessageId, oBody, oEmailData, "TooMuchFerdsException", _EmailOutAccountId, oArgs.NamePortal)
AddRejectedState(oMessageId, "TooMuchFerdsException", "Email enthielt mehr als ein ZUGFeRD-Dokument", "", oSQLTransaction)
Catch ex As NoFerdsException
_logger.Error(ex)
Create_HistoryEntry(oMessageId, oMD5CheckSum, "REJECTED - no ZUGFeRD-Document in email", oFBTransaction)
Dim oBody = EmailStrings.EMAIL_NO_FERDS
Dim oEmailData = MoveAndRenameEmailToRejected(oArgs, oMessageId)
_email.AddToEmailQueueMSSQL(oMessageId, oBody, oEmailData, "NoFerdsException", _EmailOutAccountId, oArgs.NamePortal)
AddRejectedState(oMessageId, "NoFerdsException", " Email enthielt keine ZUGFeRD-Dokumente", "", 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 MissingValueException
_logger.Error(ex)
Dim oMessage As String = ""
For Each prop In oMissingProperties
oMessage &= $"- {prop}"
Next
Create_HistoryEntry(oMessageId, oMD5CheckSum, $"REJECTED - Missing Required Properties: [{oMessage}]", oFBTransaction)
Dim oBody = CreateBodyForMissingProperties(ex.File.Name, oMissingProperties)
Dim oEmailData = MoveAndRenameEmailToRejected(oArgs, oMessageId)
_email.AddToEmailQueueMSSQL(oMessageId, oBody, oEmailData, "MissingValueException", _EmailOutAccountId, oArgs.NamePortal)
AddRejectedState(oMessageId, "MissingValueException", "Es fehlten ZugferdSpezifikationen", oMessage, oSQLTransaction)
Catch ex As FileSizeLimitReachedException
_logger.Error(ex)
Create_HistoryEntry(oMessageId, oMD5CheckSum, "REJECTED - File size limit reached", oFBTransaction)
Dim oEmailData = MoveAndRenameEmailToRejected(oArgs, oMessageId)
Dim oKey = FileSizeLimitReachedException.KEY_FILENAME
Dim oFileExceedingThreshold As String = IIf(ex.Data.Contains(oKey), ex.Data.Item(oKey), "")
Dim oFileWithoutMessageId = oFileExceedingThreshold.
Replace(oMessageId, "").
Replace("~", "")
Dim oBody = String.Format(EmailStrings.EMAIL_FILE_SIZE_REACHED, oArgs.MaxAttachmentSizeInMegaBytes, oFileWithoutMessageId)
_email.AddToEmailQueueMSSQL(oMessageId, oBody, oEmailData, "FileSizeLimitReachedException", _EmailOutAccountId, oArgs.NamePortal)
AddRejectedState(oMessageId, "FileSizeLimitReachedException", "Erlaubte Dateigröße überschritten", "", oSQLTransaction)
Catch ex As OutOfMemoryException
_logger.Warn("OutOfMemory Error occurred: {0}", ex.Message)
_logger.Error(ex)
' Send Email to Digital Data
Dim oBody = CreateBodyForUnhandledException(oMessageId, ex)
Dim oEmailData As New EmailData With {
.From = oArgs.ExceptionEmailAddress,
.Subject = $"OutOfMemoryException im ZUGFeRD-Parser @ {oMessageId}"
}
_email.AddToEmailQueueMSSQL(oMessageId, oBody, oEmailData, "OutOfMemoryException", _EmailOutAccountId, oArgs.NamePortal)
' Rollback Firebird
oFBTransaction.Rollback()
' Rollback MSSQL
oSQLTransaction.Rollback()
oMoveDirectory = DIRECTORY_DONT_MOVE
oExpectedError = False
Catch ex As Exception
_logger.Warn("Unknown Error occurred: {0}", ex.Message)
_logger.Error(ex)
' Send Email to Digital Data
Dim oBody = CreateBodyForUnhandledException(oMessageId, ex)
Dim oEmailData As New EmailData With {
.From = oArgs.ExceptionEmailAddress,
.Subject = $"UnhandledException im ZUGFeRD-Parser @ {oMessageId}"
}
_email.AddToEmailQueueMSSQL(oMessageId, oBody, oEmailData, "UnhandledException", _EmailOutAccountId, oArgs.NamePortal)
' Rollback Firebird
oFBTransaction.Rollback()
' Rollback MSSQL
oSQLTransaction.Rollback()
oMoveDirectory = DIRECTORY_DONT_MOVE
oExpectedError = False
Finally
Try
' 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)
Else
' Move all files of the current group
MoveFiles(oArgs, oMessageId, oFileGroupFiles, oEmailAttachmentFiles, oEmbeddedAttachmentFiles, oMoveDirectory, oIsSuccess)
End If
_logger.Info("Finished processing file group {0}", oMessageId)
Catch ex As Exception
' Send Email to Digital Data
Dim oBody = CreateBodyForUnhandledException(oMessageId, ex)
Dim oEmailData As New EmailData With {
.From = oArgs.ExceptionEmailAddress,
.Subject = $"FileMoveException im ZUGFeRD-Parser @ {oMessageId}"
}
_email.AddToEmailQueueMSSQL(oMessageId, oBody, oEmailData, "FileMoveException", _EmailOutAccountId, oArgs.NamePortal)
_logger.Warn("Could not move files!")
_logger.Error(ex)
Throw ex
Finally
_logger.EndBlock()
End Try
Try
' If everything went OK or an expected error occurred,
' finally commit all changes To the Database
' ==================================================================
If oIsSuccess Or oExpectedError Then
' Commit SQL Transaction
oSQLTransaction.Commit()
' Commit Firebird Transaction
oFBTransaction.Commit()
End If
Catch ex As Exception
_logger.Error(ex)
_logger.Warn("Database Transactions were not committed successfully.")
End Try
Try
oFBConnection.Close()
oSQLConnection.Close()
Catch ex As Exception
_logger.Error(ex)
_logger.Warn("Database Connections were not closed successfully.")
End Try
End Try
Next
End If
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 Sub MoveFiles(
Args As WorkerArgs,
MessageId As String,
Files As List(Of FileInfo),
AttachmentFiles As List(Of FileInfo),
EmbeddedAttachments As List(Of PDFEmbeds.EmbeddedFile),
MoveDirectory As String,
IsSuccess As Boolean)
Dim oFinalMoveDirectory As String = MoveDirectory
Dim oDateSubDirectoryName As String = Now.ToString("yyyy\\MM\\dd")
Dim oAttachmentDirectory As String = Path.Combine(oFinalMoveDirectory, Args.AttachmentsSubDirectory, oDateSubDirectoryName)
' Files will be moved to a subfolder for the current day if they are rejected
If Not IsSuccess Then
oFinalMoveDirectory = Path.Combine(oFinalMoveDirectory, oDateSubDirectoryName)
End If
' Create directories if they don't exist
If Not Directory.Exists(oFinalMoveDirectory) Then
Try
Directory.CreateDirectory(oFinalMoveDirectory)
Catch ex As Exception
_logger.Error(ex)
End Try
End If
If Not Directory.Exists(oAttachmentDirectory) And AttachmentFiles.Count > 0 Then
Try
Directory.CreateDirectory(oAttachmentDirectory)
Catch ex As Exception
_logger.Error(ex)
End Try
End If
' Filter out Attachments from `Files`
Dim oInvoiceFiles As List(Of FileInfo) = Files.Except(AttachmentFiles).ToList()
' Move PDF/A Files
For Each oFile In oInvoiceFiles
Try
Dim oFilePath = _filesystem.GetVersionedFilename(Path.Combine(oFinalMoveDirectory, oFile.Name))
_filesystem.MoveTo(oFile.FullName, oFilePath, oFinalMoveDirectory)
_logger.Info("File moved to {0}", oFilePath)
Catch ex As Exception
_logger.Warn("Could not move file {0}", oFile.FullName)
_logger.Error(ex)
End Try
Next
' Move non-PDF/A Email Attachments/Files
For Each oFile In AttachmentFiles
Try
Dim oFilePath = _filesystem.GetVersionedFilename(Path.Combine(oAttachmentDirectory, oFile.Name))
_filesystem.MoveTo(oFile.FullName, oFilePath, oAttachmentDirectory)
_logger.Info("Attachment moved to {0}", oFilePath)
Catch ex As Exception
_logger.Warn("Could not move attachment {0}", oFile.FullName)
_logger.Error(ex)
End Try
Next
' Write Embedded Files to disk
For Each oResult In EmbeddedAttachments
Try
Dim oFileName As String = $"{MessageId}~{oResult.FileName}"
Dim oFilePath As String = Path.Combine(oAttachmentDirectory, oFileName)
If Not File.Exists(oAttachmentDirectory) Then
Directory.CreateDirectory(oAttachmentDirectory)
End If
Using oWriter As New FileStream(oFilePath, FileMode.Create)
oWriter.Write(oResult.FileContents, 0, oResult.FileContents.Length)
_logger.Info("Embedded Attachment moved to {0}", oFilePath)
End Using
Catch ex As Exception
_logger.Warn("Could not save embedded attachment {0}", oResult.FileName)
_logger.Error(ex)
End Try
Next
_logger.Info("Finished moving files")
End Sub
Private Function CreateBodyForMissingProperties(OriginalFilename As String, MissingProperties As List(Of String)) As String
Dim oBody = String.Format(EmailStrings.EMAIL_MISSINGPROPERTIES_1, OriginalFilename)
If MissingProperties.Count > 0 Then
oBody &= $"{vbNewLine}{vbNewLine}"
oBody &= EmailStrings.EMAIL_MISSINGPROPERTIES_2
oBody &= $"{vbNewLine}{vbNewLine}"
For Each prop In MissingProperties
oBody &= $"- {prop}"
Next
End If
Return oBody
End Function
Private Function CreateBodyForUnhandledException(MessageId As String, Exception As Exception) As String
Dim oBody = String.Format(EmailStrings.EMAIL_UNHANDLED_EXCEPTION, MessageId, Exception.Message, Exception.StackTrace)
Return oBody
End Function
Private Function CreateMD5(ByVal Filename As String) As String
Try
Dim oMD5 As New MD5CryptoServiceProvider
Dim oHash As Byte()
Dim oHashString As String
Dim oResult As String = ""
Using oFileStream As New FileStream(Filename, FileMode.Open, FileAccess.Read, FileShare.Read, 8192)
oHash = oMD5.ComputeHash(oFileStream)
oHashString = BitConverter.ToString(oHash)
End Using
oResult = oHashString.Replace("-", "")
Return oResult
Catch ex As Exception
_logger.Error(ex)
Return ""
End Try
End Function
Private Function Create_HistoryEntry(MessageId As String, MD5Checksum As String, Message As String, Transaction As FbTransaction) As Boolean
Try
Dim oConnection = _firebird.GetConnection()
Dim oSQL = $"INSERT INTO TBEDM_ZUGFERD_HISTORY_IN (COMMENT, MD5HASH, MESSAGE_ID) VALUES ('{Message}', '{MD5Checksum}', '{MessageId}')"
' 09.07.2021: This can't be in the transaction since the history
' Entry needs to be accessed by MoveAndRenameEmailToRejected shortly after
_firebird.ExecuteNonQueryWithConnection(oSQL, oConnection, Firebird.TransactionMode.WithTransaction)
' Close the connection
oConnection.Close()
If Message.Contains("REJECTED") Then
oSQL = $"UPDATE TBEMLP_HISTORY SET STATUS = 'REJECTED', COMMENT = '{Message}', CUST_REJECTED = 1,CUST_REJECTED_WHEN = GETDATE() WHERE EMAIL_MSGID = '{MessageId}'"
_mssql.ExecuteNonQuery(oSQL)
End If
Return True
Catch ex As Exception
_logger.Warn("History Entry count not be created for message id [{0}] and md5 [{1}]", MessageId, MD5Checksum)
_logger.Error(ex)
Return False
End Try
End Function
Private Function Update_HistoryEntry(MessageId As String, MD5Checksum As String, Message As String, Transaction As FbTransaction) As Boolean
Try
Dim oConnection = _firebird.GetConnection()
Dim oSQL = $"UPDATE TBEDM_ZUGFERD_HISTORY_IN SET COMMENT = '{Message}' WHERE MD5HASH = '{MD5Checksum}' AND MESSAGE_ID = '{MessageId}'"
_firebird.ExecuteNonQueryWithConnection(oSQL, oConnection, Firebird.TransactionMode.WithTransaction)
' Close the connection
oConnection.Close()
Return True
Catch ex As Exception
_logger.Warn("History Entry count not be updated for message id [{0}] and md5 [{1}]", MessageId, MD5Checksum)
_logger.Error(ex)
Return False
End Try
End Function
''' <summary>
''' Generates the MD5 Checksum of a file and checks it against the histroy table TBEDM_ZUGFERD_HISTORY_IN
''' </summary>
''' <param name="pFilePath">The path of the file to be checked</param>
''' <param name="pIgnoreRejectionStatus">Should the check take into account the rejection status of the file?</param>
''' <returns>The MD5 Checksum of the file, or an empty string, if the Checksum could not be created</returns>
''' <exception cref="MD5HashException">Throws, when the file should be rejected, ie. if it already exists in the table</exception>
Private Function GenerateAndCheck_MD5Sum(pFilePath As String, pIgnoreRejectionStatus As Boolean) As String
Dim oMD5CheckSum = CreateMD5(pFilePath)
' Exit if MD5 could not be created
If oMD5CheckSum = String.Empty Then
_logger.Warn("MD5 Checksum is nothing for file [{0}]!", pFilePath)
Return oMD5CheckSum
End If
' Check if Checksum exists in History Table
Dim oCheckCommand = $"SELECT * FROM TBEDM_ZUGFERD_HISTORY_IN WHERE GUID = (SELECT MAX(GUID) FROM TBEDM_ZUGFERD_HISTORY_IN WHERE UPPER(MD5HASH) = UPPER('{oMD5CheckSum}'))"
Dim oTable As DataTable = _firebird.GetDatatable(oCheckCommand, Firebird.TransactionMode.NoTransaction)
' If History entries could not be fetched, just return the MD5 Checksum
If IsNothing(oTable) Then
_logger.Warn("Be careful: oExistsDT is nothing for file [{0}]!", pFilePath)
Return oMD5CheckSum
End If
' If Checksum does not exist in History entries, just return the MD5 Checksum
If oTable.Rows.Count = 0 Then
_logger.Debug("File [{0}] was not found in History!", pFilePath)
Return oMD5CheckSum
End If
' ====================================================
' Checksum exists in History entries, reject!
' ====================================================
Dim oRejected As Boolean
Dim oHistoryId As Integer
' Try to read Rejected Status and History Id
Try
Dim oRow As DataRow = oTable.Rows.Item(0)
oRejected = DirectCast(oRow.Item("REJECTED"), Boolean)
oHistoryId = oRow.Item("GUID")
Catch ex As Exception
_logger.Warn("Error while converting REJECTED: " & ex.Message)
oRejected = False
End Try
_logger.Info("File has already been processed...")
' If the file was already rejected, it is allowed to be processed again,
' even if the Checksum exists in the history entries (default case)
' Which means, if it was not rejected before, it will be rejected in any case!
'
' This logic can be overwritten by the IgnoreRejectionStatus parameter.
' If it is set to true, the file will be rejected if the file exists in the history entries,
' regardless of the rejected parameter.
If oRejected = True And pIgnoreRejectionStatus = True Then
_logger.Info("ZuGFeRDFile already has been processed, but formerly obviously was rejected!")
Else
Throw New MD5HashException($"There is already an identical invoice! - HistoryID [{oHistoryId}]")
End If
Return oMD5CheckSum
End Function
''' <summary>
''' Checks the size of the supplied file.
''' </summary>
''' <param name="pFileInfo"></param>
''' <param name="pMaxFileSizeInMegaBytes"></param>
''' <returns></returns>
Private Function Check_FileSize(pFileInfo As FileInfo, pMaxFileSizeInMegaBytes As Integer) As Boolean
_logger.Info("Checking Filesize of {0}", pFileInfo.Name)
_logger.Debug("Filesize threshold is {0} MB.", pMaxFileSizeInMegaBytes)
If pMaxFileSizeInMegaBytes <= 0 Then
_logger.Debug("Filesize is not configured. Skipping check.")
Return True
End If
Dim oMaxSize = pMaxFileSizeInMegaBytes * 1024 * 1024
If oMaxSize > 0 And pFileInfo.Length > oMaxSize Then
_logger.Debug("Filesize is bigger than threshold. Rejecting.")
Return False
Else
_logger.Debug("Filesize is smaller than threshold. All fine.")
Return True
End If
End Function
End Class

View File

@@ -0,0 +1,23 @@
Imports System.Collections.Generic
Imports DigitalData.Modules.Interfaces
Public Class WorkerArgs
' Directory Parameters
Public WatchDirectories As New List(Of String)
Public SuccessDirectory As String = Nothing
Public ErrorDirectory As String = Nothing
Public OriginalEmailDirectory As String = Nothing
Public RejectedEmailDirectory As String = Nothing
Public AttachmentsSubDirectory As String = Nothing
Public NonZugferdDirectory As String = Nothing
' Property Parameter
Public PropertyMap As New Dictionary(Of String, XmlItemProperty)
' Misc Flag Parameters
Public InsertIntoSQLServer As Boolean = False
Public ExceptionEmailAddress As String = Nothing
Public IgnoreRejectionStatus As Boolean = False
Public MaxAttachmentSizeInMegaBytes As Integer = -1
Public NamePortal As String = "NO PORTAL_NAME IN CONFIG"
End Class

67
Jobs/Exceptions.vb Normal file
View File

@@ -0,0 +1,67 @@
Imports System.IO
Public Class Exceptions
Public Class MissingValueException
Inherits ApplicationException
Public ReadOnly File As FileInfo
Public Sub New(File As FileInfo)
MyBase.New()
Me.File = File
End Sub
End Class
Public Class TooMuchFerdsException
Inherits ApplicationException
Public Sub New()
MyBase.New("More than one ZUGFeRD document found")
End Sub
End Class
Public Class FileSizeLimitReachedException
Inherits ApplicationException
Public Const KEY_FILENAME = "FILENAME"
Public Sub New(pFilePath As String, pFileSizeLimitInMegaBytes As Integer)
MyBase.New($"At least one file exceeded the filesize limit of {pFileSizeLimitInMegaBytes}MB: {pFilePath}")
Data.Add(KEY_FILENAME, pFilePath)
End Sub
End Class
Public Class InvalidFerdException
Inherits ApplicationException
Public Sub New()
MyBase.New("ZUGFeRD document found but was not formatted correctly")
End Sub
End Class
Public Class NoFerdsException
Inherits ApplicationException
Public Sub New()
MyBase.New("No ZUGFeRD documents found")
End Sub
End Class
Public Class NoFerdsAlternateException
Inherits ApplicationException
Public Sub New()
MyBase.New("No ZUGFeRD documents found, no rejection will be generated")
End Sub
End Class
Public Class MD5HashException
Inherits ApplicationException
Public Sub New(pInfo As String)
MyBase.New(pInfo)
End Sub
End Class
End Class

4
Jobs/JobArgs.vb Normal file
View File

@@ -0,0 +1,4 @@
Public Class JobArgs
Public Enabled As Boolean
Public Interval As Long
End Class

16
Jobs/JobBase.vb Normal file
View File

@@ -0,0 +1,16 @@
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Logging
Public Class JobBase
Protected _LogConfig As LogConfig
Protected _Logger As Logger
Protected _Firebird As Firebird
Protected _MSSQL As MSSQLServer
Public Sub New(LogConfig As LogConfig, Firebird As Firebird, MSSQL As MSSQLServer)
_LogConfig = LogConfig
_Logger = LogConfig.GetLogger()
_Firebird = Firebird
_MSSQL = MSSQL
End Sub
End Class

8
Jobs/JobConfig.vb Normal file
View File

@@ -0,0 +1,8 @@
Imports System.Collections.Generic
Public Class JobConfig
Public Enabled As Boolean
Public StartImmediately As Boolean
Public CronExpression As String
Public Arguments As Dictionary(Of String, String)
End Class

78
Jobs/JobConfigParser.vb Normal file
View File

@@ -0,0 +1,78 @@
Imports System.Collections.Generic
Imports System.Text.RegularExpressions
Public Class JobConfigParser
Private Shared JobOptionsRegex As New Regex("(?<enabled>True|False|Debug)\|(?<cron>[\w\d\s,\?*-/]*)(?:\|(?<args>(?:\w*::[^,\n]+,?)*))?")
Private Shared JobArgumentsRegex As New Regex("(?:(?:\w+::[^,\n]+,?)?)+")
Private Const ARGS_ITEM_DELIMITER As String = ","
Private Const ARGS_KEYVALUE_DELIMITER As String = "::"
Private Const ARGS_LIST_DELIMITER As String = "|"
''' <summary>
''' Parse a job config string. ex: True|0 0/3 * * * ?|Arg1::Foo,Arg2::Bar
''' </summary>
''' <param name="ConfigString"></param>
''' <returns>A populated JobConfig object</returns>
Public Shared Function ParseConfig(ConfigString As String) As JobConfig
If JobOptionsRegex.IsMatch(ConfigString) Then
Dim oMatches = JobOptionsRegex.Matches(ConfigString)
Dim oOptions As New JobConfig
Dim oSplitOptions As String() = ConfigString.Split(ARGS_LIST_DELIMITER)
If oSplitOptions.Length = 3 Then
oOptions = ParseEnabled(oSplitOptions(0), oOptions)
oOptions.CronExpression = oSplitOptions(1)
oOptions.Arguments = ParseOptionalArguments(oSplitOptions(2))
ElseIf oSplitOptions.Length = 2 Then
oOptions = ParseEnabled(oSplitOptions(0), oOptions)
oOptions.CronExpression = oSplitOptions(1)
oOptions.Arguments = New Dictionary(Of String, String)
Else
Throw New ArgumentException("Config Malformed")
End If
Return oOptions
Else
Throw New ArgumentException("Config Malformed")
End If
End Function
Public Shared Function ParseEnabled(EnabledValue As String, Options As JobConfig) As JobConfig
Select Case EnabledValue
Case "True"
Options.Enabled = True
Options.StartImmediately = False
Case "Debug"
Options.Enabled = True
Options.StartImmediately = True
Case Else
Options.Enabled = False
Options.StartImmediately = False
End Select
Return Options
End Function
Private Shared Function ParseOptionalArguments(ArgsString As String) As Dictionary(Of String, String)
Dim oArgsDictionary As New Dictionary(Of String, String)
If JobArgumentsRegex.IsMatch(ArgsString) Then
Dim oArgs As String() = ArgsString.Split(ARGS_ITEM_DELIMITER)
For Each oArg In oArgs
Dim oDelimiter As String() = New String() {ARGS_KEYVALUE_DELIMITER}
Dim oArgSplit = oArg.Split(oDelimiter, StringSplitOptions.RemoveEmptyEntries)
Regex.Split(oArg, "::")
If oArgSplit.Length = 2 Then
oArgsDictionary.Add(oArgSplit(0), oArgSplit(1))
Else
Throw New ArgumentException("Config Malformed")
End If
Next
End If
Return oArgsDictionary
End Function
End Class

9
Jobs/JobInterface.vb Normal file
View File

@@ -0,0 +1,9 @@
Public Interface IJob
Sub Start(Arguments As Object)
End Interface
Public Interface IJob(Of T)
Sub Start(Arguments As T)
Function ShouldStart(Arguments As T) As Boolean
End Interface

139
Jobs/Jobs.vbproj Normal file
View File

@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{39EC839A-3C30-4922-A41E-6B09D1DDE5C3}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>DigitalData.Modules.Jobs</RootNamespace>
<AssemblyName>DigitalData.Modules.Jobs</AssemblyName>
<FileAlignment>512</FileAlignment>
<MyType>Empty</MyType>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<OutputPath>bin\Debug\</OutputPath>
<DocumentationFile>
</DocumentationFile>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<DefineDebug>false</DefineDebug>
<DefineTrace>true</DefineTrace>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DocumentationFile>
</DocumentationFile>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
</PropertyGroup>
<PropertyGroup>
<OptionExplicit>On</OptionExplicit>
</PropertyGroup>
<PropertyGroup>
<OptionCompare>Binary</OptionCompare>
</PropertyGroup>
<PropertyGroup>
<OptionStrict>Off</OptionStrict>
</PropertyGroup>
<PropertyGroup>
<OptionInfer>On</OptionInfer>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Import Include="Microsoft.VisualBasic" />
<Import Include="System" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="My Project\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.vb</LastGenOutput>
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Base\Base.vbproj">
<Project>{6ea0c51f-c2b1-4462-8198-3de0b32b74f8}</Project>
<Name>Base</Name>
</ProjectReference>
<ProjectReference Include="..\Config\Config.vbproj">
<Project>{44982f9b-6116-44e2-85d0-f39650b1ef99}</Project>
<Name>Config</Name>
</ProjectReference>
<ProjectReference Include="..\Database\Database.vbproj">
<Project>{eaf0ea75-5fa7-485d-89c7-b2d843b03a96}</Project>
<Name>Database</Name>
</ProjectReference>
<ProjectReference Include="..\Filesystem\Filesystem.vbproj">
<Project>{991d0231-4623-496d-8bd0-9ca906029cbc}</Project>
<Name>Filesystem</Name>
</ProjectReference>
<ProjectReference Include="..\Interfaces\Interfaces.vbproj">
<Project>{ab6f09bf-e794-4f6a-94bb-c97c0ba84d64}</Project>
<Name>Interfaces</Name>
</ProjectReference>
<ProjectReference Include="..\Logging\Logging.vbproj">
<Project>{903b2d7d-3b80-4be9-8713-7447b704e1b0}</Project>
<Name>Logging</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="EDMI\ADSync\ADSyncArgs.vb" />
<Compile Include="EDMI\ADSync\ADSyncJob.vb" />
<Compile Include="EDMI\GraphQL\GraphQLArgs.vb" />
<Compile Include="EDMI\GraphQL\GraphQLConfig.vb" />
<Compile Include="EDMI\GraphQL\GraphQLJob.vb" />
<Compile Include="EDMI\GraphQL\GraphQLQuery.vb" />
<Compile Include="EDMI\ZUGFeRD\EmailData.vb" />
<Compile Include="EDMI\ZUGFeRD\EmailFunctions.vb" />
<Compile Include="EDMI\ZUGFeRD\EmailStrings.vb" />
<Compile Include="EDMI\ZUGFeRD\ImportZUGFeRDFiles.vb" />
<Compile Include="EDMI\ZUGFeRD\WorkerArgs.vb" />
<Compile Include="Exceptions.vb" />
<Compile Include="JobInterface.vb" />
<Compile Include="JobBase.vb" />
<Compile Include="JobArgs.vb" />
<Compile Include="JobConfig.vb" />
<Compile Include="JobConfigParser.vb" />
<Compile Include="My Project\AssemblyInfo.vb" />
<Compile Include="My Project\Settings.Designer.vb">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Reference Include="FirebirdSql.Data.FirebirdClient, Version=7.5.0.0, Culture=neutral, PublicKeyToken=3750abcc3150b00c, processorArchitecture=MSIL">
<HintPath>..\packages\FirebirdSql.Data.FirebirdClient.7.5.0\lib\net452\FirebirdSql.Data.FirebirdClient.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.7.15\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Data" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Transactions" />
<Reference Include="System.Xml" />
<Reference Include="System.XML.Linq" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
</Project>

View File

@@ -0,0 +1,34 @@
Imports System
Imports System.Reflection
Imports System.Runtime.InteropServices
' Allgemeine Informationen über eine Assembly werden über die folgenden
' Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
' die einer Assembly zugeordnet sind.
' Werte der Assemblyattribute überprüfen
<Assembly: AssemblyTitle("Modules.Jobs")>
<Assembly: AssemblyDescription("")>
<Assembly: AssemblyCompany("Digital Data")>
<Assembly: AssemblyProduct("Modules.Jobs")>
<Assembly: AssemblyCopyright("Copyright © 2021")>
<Assembly: AssemblyTrademark("")>
<Assembly: ComVisible(False)>
'Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird.
<Assembly: Guid("48b3071f-049c-4c84-b272-f197c1db2652")>
' Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
'
' Hauptversion
' Nebenversion
' Buildnummer
' Revision
'
' Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
' übernehmen, indem Sie "*" eingeben:
<Assembly: AssemblyVersion("1.8.6.0")>
<Assembly: AssemblyFileVersion("1.8.6.0")>

71
Jobs/My Project/Settings.Designer.vb generated Normal file
View File

@@ -0,0 +1,71 @@
'------------------------------------------------------------------------------
' <auto-generated>
' Dieser Code wurde von einem Tool generiert.
' Laufzeitversion:4.0.30319.42000
'
' Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
' der Code erneut generiert wird.
' </auto-generated>
'------------------------------------------------------------------------------
Option Strict On
Option Explicit On
<Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute(), _
Global.System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0"), _
Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
Partial Friend NotInheritable Class Settings
Inherits Global.System.Configuration.ApplicationSettingsBase
Private Shared defaultInstance As Settings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New Settings()),Settings)
#Region "Automatische My.Settings-Speicherfunktion"
#If _MyType = "WindowsForms" Then
Private Shared addedHandler As Boolean
Private Shared addedHandlerLockObject As New Object
<Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
Private Shared Sub AutoSaveSettings(sender As Global.System.Object, e As Global.System.EventArgs)
If My.Application.SaveMySettingsOnExit Then
My.Settings.Save()
End If
End Sub
#End If
#End Region
Public Shared ReadOnly Property [Default]() As Settings
Get
#If _MyType = "WindowsForms" Then
If Not addedHandler Then
SyncLock addedHandlerLockObject
If Not addedHandler Then
AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings
addedHandler = True
End If
End SyncLock
End If
#End If
Return defaultInstance
End Get
End Property
End Class
Namespace My
<Global.Microsoft.VisualBasic.HideModuleNameAttribute(), _
Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _
Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute()> _
Friend Module MySettingsProperty
<Global.System.ComponentModel.Design.HelpKeywordAttribute("My.Settings")> _
Friend ReadOnly Property Settings() As Global.DigitalData.Modules.Jobs.Settings
Get
Return Global.DigitalData.Modules.Jobs.Settings.Default
End Get
End Property
End Module
End Namespace

View File

6
Jobs/packages.config Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FirebirdSql.Data.FirebirdClient" version="7.5.0" targetFramework="net461" />
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net461" />
<package id="NLog" version="4.7.15" targetFramework="net461" />
</packages>