diff --git a/WinLineArtikelnummerGenerator.sln b/WinLineArtikelnummerGenerator.sln
new file mode 100644
index 0000000..09c8101
--- /dev/null
+++ b/WinLineArtikelnummerGenerator.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29728.190
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "WinLineArtikelnummerGenerator", "WinLineArtikelnummerGenerator\WinLineArtikelnummerGenerator.vbproj", "{BA171D0B-6421-4294-B5DC-BF77D93C91E7}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {BA171D0B-6421-4294-B5DC-BF77D93C91E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BA171D0B-6421-4294-B5DC-BF77D93C91E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BA171D0B-6421-4294-B5DC-BF77D93C91E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BA171D0B-6421-4294-B5DC-BF77D93C91E7}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {2FFA2908-335A-4211-8E21-1E5323B7C645}
+ EndGlobalSection
+EndGlobal
diff --git a/WinLineArtikelnummerGenerator/App.config b/WinLineArtikelnummerGenerator/App.config
new file mode 100644
index 0000000..f6744a2
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/App.config
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/WinLineArtikelnummerGenerator/CWLDATEN_MEDSDataSet.Designer.vb b/WinLineArtikelnummerGenerator/CWLDATEN_MEDSDataSet.Designer.vb
new file mode 100644
index 0000000..f0b24e3
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/CWLDATEN_MEDSDataSet.Designer.vb
@@ -0,0 +1,242 @@
+'------------------------------------------------------------------------------
+'
+' 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.
+'
+'------------------------------------------------------------------------------
+
+Option Strict Off
+Option Explicit On
+
+
+
+'''
+'''Represents a strongly typed in-memory cache of data.
+'''
+ _
+Partial Public Class CWLDATEN_MEDSDataSet
+ Inherits Global.System.Data.DataSet
+
+ Private _schemaSerializationMode As Global.System.Data.SchemaSerializationMode = Global.System.Data.SchemaSerializationMode.IncludeSchema
+
+ _
+ Public Sub New()
+ MyBase.New
+ Me.BeginInit
+ Me.InitClass
+ Dim schemaChangedHandler As Global.System.ComponentModel.CollectionChangeEventHandler = AddressOf Me.SchemaChanged
+ AddHandler MyBase.Tables.CollectionChanged, schemaChangedHandler
+ AddHandler MyBase.Relations.CollectionChanged, schemaChangedHandler
+ Me.EndInit
+ End Sub
+
+ _
+ Protected Sub New(ByVal info As Global.System.Runtime.Serialization.SerializationInfo, ByVal context As Global.System.Runtime.Serialization.StreamingContext)
+ MyBase.New(info, context, false)
+ If (Me.IsBinarySerialized(info, context) = true) Then
+ Me.InitVars(false)
+ Dim schemaChangedHandler1 As Global.System.ComponentModel.CollectionChangeEventHandler = AddressOf Me.SchemaChanged
+ AddHandler Me.Tables.CollectionChanged, schemaChangedHandler1
+ AddHandler Me.Relations.CollectionChanged, schemaChangedHandler1
+ Return
+ End If
+ Dim strSchema As String = CType(info.GetValue("XmlSchema", GetType(String)),String)
+ If (Me.DetermineSchemaSerializationMode(info, context) = Global.System.Data.SchemaSerializationMode.IncludeSchema) Then
+ Dim ds As Global.System.Data.DataSet = New Global.System.Data.DataSet()
+ ds.ReadXmlSchema(New Global.System.Xml.XmlTextReader(New Global.System.IO.StringReader(strSchema)))
+ Me.DataSetName = ds.DataSetName
+ Me.Prefix = ds.Prefix
+ Me.Namespace = ds.Namespace
+ Me.Locale = ds.Locale
+ Me.CaseSensitive = ds.CaseSensitive
+ Me.EnforceConstraints = ds.EnforceConstraints
+ Me.Merge(ds, false, Global.System.Data.MissingSchemaAction.Add)
+ Me.InitVars
+ Else
+ Me.ReadXmlSchema(New Global.System.Xml.XmlTextReader(New Global.System.IO.StringReader(strSchema)))
+ End If
+ Me.GetSerializationData(info, context)
+ Dim schemaChangedHandler As Global.System.ComponentModel.CollectionChangeEventHandler = AddressOf Me.SchemaChanged
+ AddHandler MyBase.Tables.CollectionChanged, schemaChangedHandler
+ AddHandler Me.Relations.CollectionChanged, schemaChangedHandler
+ End Sub
+
+ _
+ Public Overrides Property SchemaSerializationMode() As Global.System.Data.SchemaSerializationMode
+ Get
+ Return Me._schemaSerializationMode
+ End Get
+ Set
+ Me._schemaSerializationMode = value
+ End Set
+ End Property
+
+ _
+ Public Shadows ReadOnly Property Tables() As Global.System.Data.DataTableCollection
+ Get
+ Return MyBase.Tables
+ End Get
+ End Property
+
+ _
+ Public Shadows ReadOnly Property Relations() As Global.System.Data.DataRelationCollection
+ Get
+ Return MyBase.Relations
+ End Get
+ End Property
+
+ _
+ Protected Overrides Sub InitializeDerivedDataSet()
+ Me.BeginInit
+ Me.InitClass
+ Me.EndInit
+ End Sub
+
+ _
+ Public Overrides Function Clone() As Global.System.Data.DataSet
+ Dim cln As CWLDATEN_MEDSDataSet = CType(MyBase.Clone,CWLDATEN_MEDSDataSet)
+ cln.InitVars
+ cln.SchemaSerializationMode = Me.SchemaSerializationMode
+ Return cln
+ End Function
+
+ _
+ Protected Overrides Function ShouldSerializeTables() As Boolean
+ Return false
+ End Function
+
+ _
+ Protected Overrides Function ShouldSerializeRelations() As Boolean
+ Return false
+ End Function
+
+ _
+ Protected Overrides Sub ReadXmlSerializable(ByVal reader As Global.System.Xml.XmlReader)
+ If (Me.DetermineSchemaSerializationMode(reader) = Global.System.Data.SchemaSerializationMode.IncludeSchema) Then
+ Me.Reset
+ Dim ds As Global.System.Data.DataSet = New Global.System.Data.DataSet()
+ ds.ReadXml(reader)
+ Me.DataSetName = ds.DataSetName
+ Me.Prefix = ds.Prefix
+ Me.Namespace = ds.Namespace
+ Me.Locale = ds.Locale
+ Me.CaseSensitive = ds.CaseSensitive
+ Me.EnforceConstraints = ds.EnforceConstraints
+ Me.Merge(ds, false, Global.System.Data.MissingSchemaAction.Add)
+ Me.InitVars
+ Else
+ Me.ReadXml(reader)
+ Me.InitVars
+ End If
+ End Sub
+
+ _
+ Protected Overrides Function GetSchemaSerializable() As Global.System.Xml.Schema.XmlSchema
+ Dim stream As Global.System.IO.MemoryStream = New Global.System.IO.MemoryStream()
+ Me.WriteXmlSchema(New Global.System.Xml.XmlTextWriter(stream, Nothing))
+ stream.Position = 0
+ Return Global.System.Xml.Schema.XmlSchema.Read(New Global.System.Xml.XmlTextReader(stream), Nothing)
+ End Function
+
+ _
+ Friend Overloads Sub InitVars()
+ Me.InitVars(true)
+ End Sub
+
+ _
+ Friend Overloads Sub InitVars(ByVal initTable As Boolean)
+ End Sub
+
+ _
+ Private Sub InitClass()
+ Me.DataSetName = "CWLDATEN_MEDSDataSet"
+ Me.Prefix = ""
+ Me.Namespace = "http://tempuri.org/CWLDATEN_MEDSDataSet.xsd"
+ Me.EnforceConstraints = true
+ Me.SchemaSerializationMode = Global.System.Data.SchemaSerializationMode.IncludeSchema
+ End Sub
+
+ _
+ Private Sub SchemaChanged(ByVal sender As Object, ByVal e As Global.System.ComponentModel.CollectionChangeEventArgs)
+ If (e.Action = Global.System.ComponentModel.CollectionChangeAction.Remove) Then
+ Me.InitVars
+ End If
+ End Sub
+
+ _
+ Public Shared Function GetTypedDataSetSchema(ByVal xs As Global.System.Xml.Schema.XmlSchemaSet) As Global.System.Xml.Schema.XmlSchemaComplexType
+ Dim ds As CWLDATEN_MEDSDataSet = New CWLDATEN_MEDSDataSet()
+ Dim type As Global.System.Xml.Schema.XmlSchemaComplexType = New Global.System.Xml.Schema.XmlSchemaComplexType()
+ Dim sequence As Global.System.Xml.Schema.XmlSchemaSequence = New Global.System.Xml.Schema.XmlSchemaSequence()
+ Dim any As Global.System.Xml.Schema.XmlSchemaAny = New Global.System.Xml.Schema.XmlSchemaAny()
+ any.Namespace = ds.Namespace
+ sequence.Items.Add(any)
+ type.Particle = sequence
+ Dim dsSchema As Global.System.Xml.Schema.XmlSchema = ds.GetSchemaSerializable
+ If xs.Contains(dsSchema.TargetNamespace) Then
+ Dim s1 As Global.System.IO.MemoryStream = New Global.System.IO.MemoryStream()
+ Dim s2 As Global.System.IO.MemoryStream = New Global.System.IO.MemoryStream()
+ Try
+ Dim schema As Global.System.Xml.Schema.XmlSchema = Nothing
+ dsSchema.Write(s1)
+ Dim schemas As Global.System.Collections.IEnumerator = xs.Schemas(dsSchema.TargetNamespace).GetEnumerator
+ Do While schemas.MoveNext
+ schema = CType(schemas.Current,Global.System.Xml.Schema.XmlSchema)
+ s2.SetLength(0)
+ schema.Write(s2)
+ If (s1.Length = s2.Length) Then
+ s1.Position = 0
+ s2.Position = 0
+
+ Do While ((s1.Position <> s1.Length) _
+ AndAlso (s1.ReadByte = s2.ReadByte))
+
+
+ Loop
+ If (s1.Position = s1.Length) Then
+ Return type
+ End If
+ End If
+
+ Loop
+ Finally
+ If (Not (s1) Is Nothing) Then
+ s1.Close
+ End If
+ If (Not (s2) Is Nothing) Then
+ s2.Close
+ End If
+ End Try
+ End If
+ xs.Add(dsSchema)
+ Return type
+ End Function
+End Class
diff --git a/WinLineArtikelnummerGenerator/CWLDATEN_MEDSDataSet.xsc b/WinLineArtikelnummerGenerator/CWLDATEN_MEDSDataSet.xsc
new file mode 100644
index 0000000..5f28270
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/CWLDATEN_MEDSDataSet.xsc
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/WinLineArtikelnummerGenerator/CWLDATEN_MEDSDataSet.xsd b/WinLineArtikelnummerGenerator/CWLDATEN_MEDSDataSet.xsd
new file mode 100644
index 0000000..3b44de1
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/CWLDATEN_MEDSDataSet.xsd
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/WinLineArtikelnummerGenerator/CWLDATEN_MEDSDataSet.xss b/WinLineArtikelnummerGenerator/CWLDATEN_MEDSDataSet.xss
new file mode 100644
index 0000000..5f28270
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/CWLDATEN_MEDSDataSet.xss
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/WinLineArtikelnummerGenerator/Config.vb b/WinLineArtikelnummerGenerator/Config.vb
new file mode 100644
index 0000000..eb345e4
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/Config.vb
@@ -0,0 +1,5 @@
+Imports DigitalData.Modules.Config.ConfigAttributes
+
+Public Class Config
+ Public Property ConnectionString As String
+End Class
diff --git a/WinLineArtikelnummerGenerator/Database.vb b/WinLineArtikelnummerGenerator/Database.vb
new file mode 100644
index 0000000..6f90e37
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/Database.vb
@@ -0,0 +1,105 @@
+Imports System.Data.SqlClient
+
+Public Class Database
+ Private _Config As ConfigManager(Of Config)
+ Private _LogConfig As LogConfig
+ Private _Logger As Logger
+
+ Private Const QUERY_TIMEOUT = 120000
+
+ Public Sub New(LogConfig As LogConfig, ConfigManager As ConfigManager(Of Config))
+ _LogConfig = LogConfig
+ _Logger = LogConfig.GetLogger()
+ _Config = ConfigManager
+ End Sub
+
+ Public Function LoadVendors() As DataTable
+ Return GetDatatable("SELECT C229, C003 FROM V050 WHERE C229 IS NOT NULL")
+ End Function
+
+ Public Function LoadVendorIdByCode(VendorCode As String) As String
+ Return GetScalarValue($"SELECT SUBSTRING(C000,0,6) C999 FROM T309 WHERE C001 = '{VendorCode}' AND C002 = 1")
+ End Function
+
+ Public Function LoadGroupsByVendor(VendorId As String) As DataTable
+ Return GetDatatable($"SELECT C001, SUBSTRING(C000,7,5) C999 FROM T309 WHERE C000 LIKE '{VendorId}%' AND C002 = 2 AND C001 LIKE '__ | %'")
+ End Function
+
+ Public Function LoadVersionsByVendorAndGroup(VendorId As String, GroupId As String) As DataTable
+ Return GetDatatable($"SELECT C001 FROM T309 WHERE C000 LIKE '{VendorId}-{GroupId}-%' AND C002 = 3 AND C001 LIKE '__ | %'")
+ End Function
+
+#Region "Database-Access"
+ Private Function GetSQLConnection() As SqlConnection
+ Return GetSQLConnection(_Config.Config.ConnectionString)
+ End Function
+
+ Private Function GetSQLConnection(ConnectionString As String) As SqlConnection
+ Try
+ Dim oConnection As New SqlConnection(ConnectionString)
+ oConnection.Open()
+
+ Dim oMaskedConnectionString = MaskConnectionString(ConnectionString)
+ _Logger.Debug("The Following Connection is open: {0}", oMaskedConnectionString)
+
+ Return oConnection
+ Catch ex As Exception
+ _Logger.Error(ex)
+
+ Return Nothing
+ End Try
+ End Function
+
+ Private Function MaskConnectionString(ConnectionString As String) As String
+ Try
+ Dim oBuilder As New SqlConnectionStringBuilder() With {.ConnectionString = ConnectionString}
+ Dim oConnectionString = ConnectionString.Replace(oBuilder.Password, "XXXXX")
+ Return oConnectionString
+ Catch ex As Exception
+ _Logger.Error(ex)
+ Return "Invalid ConnectionString"
+ End Try
+ End Function
+
+ Public Function GetDatatable(SqlCommand As String) As DataTable
+ Try
+ _Logger.Debug("GetDatatable: Running Query [{0}]", SqlCommand)
+
+ Using oConnection = GetSQLConnection()
+ Using oSQLCOmmand = oConnection.CreateCommand()
+ oSQLCOmmand.CommandText = SqlCommand
+ oSQLCOmmand.CommandTimeout = QUERY_TIMEOUT
+
+ Dim dt As DataTable = New DataTable()
+ Dim oAdapter As SqlDataAdapter = New SqlDataAdapter(oSQLCOmmand)
+ oAdapter.Fill(dt)
+ Return dt
+ End Using
+ End Using
+ Catch ex As Exception
+ _Logger.Warn($"GetDatatable failed SQLCommand [{SqlCommand}] - ERROR: {ex.Message}")
+ Return Nothing
+ End Try
+ End Function
+
+ Public Function GetScalarValue(SQLCommand As String) As Object
+ Try
+ _Logger.Debug("GetScalarValue: Running Query [{0}]", SQLCommand)
+
+ Using oConnection As SqlConnection = GetSQLConnection()
+ Using oSQLCOmmand = oConnection.CreateCommand()
+ oSQLCOmmand.CommandText = SQLCommand
+ oSQLCOmmand.CommandTimeout = QUERY_TIMEOUT
+ Dim oResult As Object = oSQLCOmmand.ExecuteScalar()
+ Return oResult
+ End Using
+ End Using
+ Catch ex As Exception
+ _Logger.Warn($"GetScalarValue failed SQLCommand [{SQLCommand}] - ERROR: {ex.Message}")
+
+ Return Nothing
+ End Try
+ End Function
+#End Region
+
+End Class
diff --git a/WinLineArtikelnummerGenerator/Models/Article.vb b/WinLineArtikelnummerGenerator/Models/Article.vb
new file mode 100644
index 0000000..a07c034
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/Models/Article.vb
@@ -0,0 +1,11 @@
+Public Class Article
+ Public VendorId As String
+ Public ProductGroupId As String
+ Public ProductVersionId As String
+ Public RunningNumber As String
+
+ Public AddedWho As String
+ Public ChangedWho As String
+ Public AddedWhen As Date
+ Public ChangedWhen As Date
+End Class
diff --git a/WinLineArtikelnummerGenerator/Models/ProductGroup.vb b/WinLineArtikelnummerGenerator/Models/ProductGroup.vb
new file mode 100644
index 0000000..0924337
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/Models/ProductGroup.vb
@@ -0,0 +1,11 @@
+Public Class ProductGroup
+ Public Guid As Integer
+ Public Name As String
+ Public NickName As String
+
+ Public Property Versions As List(Of ProductVersion)
+
+ Public Sub New()
+ Versions = New List(Of ProductVersion)
+ End Sub
+End Class
diff --git a/WinLineArtikelnummerGenerator/Models/ProductVersion.vb b/WinLineArtikelnummerGenerator/Models/ProductVersion.vb
new file mode 100644
index 0000000..48406bb
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/Models/ProductVersion.vb
@@ -0,0 +1,4 @@
+Public Class ProductVersion
+ Public Guid As Integer
+ Public Name As String
+End Class
diff --git a/WinLineArtikelnummerGenerator/Models/Vendor.vb b/WinLineArtikelnummerGenerator/Models/Vendor.vb
new file mode 100644
index 0000000..691aa37
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/Models/Vendor.vb
@@ -0,0 +1,5 @@
+Public Class Vendor
+ Public Guid As Integer
+ Public Id As String
+ Public Name As String
+End Class
diff --git a/WinLineArtikelnummerGenerator/Modules/ConfigManager.vb b/WinLineArtikelnummerGenerator/Modules/ConfigManager.vb
new file mode 100644
index 0000000..25fbb52
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/Modules/ConfigManager.vb
@@ -0,0 +1,333 @@
+Imports System.IO
+Imports System.Reflection
+Imports System.Xml.Serialization
+Imports DigitalData.Modules.Logging
+Imports DigitalData.Modules.Config.ConfigAttributes
+
+Public Class ConfigManager(Of T)
+ Private Const USER_CONFIG_NAME As String = "UserConfig.xml"
+ Private Const COMPUTER_CONFIG_NAME As String = "ComputerConfig.xml"
+ Private Const APP_CONFIG_NAME As String = "AppConfig.xml"
+
+ Private ReadOnly _LogConfig As LogConfig
+ Private ReadOnly _Logger As Logger
+ Private ReadOnly _File As File
+
+ Private ReadOnly _UserDirectory As String
+ Private ReadOnly _UserConfigPath As String
+ Private ReadOnly _ComputerDirectory As String
+ Private ReadOnly _ComputerConfigPath As String
+ Private ReadOnly _AppConfigPath As String
+ Private ReadOnly _AppConfigDirectory As String
+
+ Private ReadOnly _TestMode As Boolean = False
+
+ Private ReadOnly _Blueprint As T
+ Private ReadOnly _BlueprintType As Type
+ Private ReadOnly _Serializer As XmlSerializer
+
+ Private ReadOnly _ExcludedAttributes = New List(Of Type) From {
+ GetType(ConnectionStringAttribute),
+ GetType(ConnectionStringTestAttribute),
+ GetType(GlobalSettingAttribute)
+ }
+
+ Private _WriteAllValuesToUserConfig As Boolean = False
+
+ '''
+ ''' Returns the currently loaded config object
+ '''
+ '''
+ Public ReadOnly Property Config As T
+
+ '''
+ ''' Path to the current user config.
+ '''
+ '''
+ Public ReadOnly Property UserConfigPath As String
+ Get
+ Return _UserConfigPath
+ End Get
+ End Property
+
+ '''
+ ''' Path to the current computer config. Maybe the same as `UserConfigPath`
+ '''
+ '''
+ Public ReadOnly Property ComputerConfigPath As String
+ Get
+ Return _ComputerConfigPath
+ End Get
+ End Property
+ Public ReadOnly Property AppConfigPath As String
+ Get
+ Return _AppConfigPath
+ End Get
+ End Property
+
+ '''
+ ''' Creates a new instance of the ConfigManager
+ '''
+ '''
+ ''' LogConfig instance
+ ''' The path to check for a user config file, eg. AppData (Usually Application.UserAppDataPath or Application.LocalUserAppDataPath)
+ ''' The path to check for a computer config file, eg. ProgramData (Usually Application.CommonAppDataPath)
+ ''' The path to check for a third config file. This is useful when running the Application in an environment where AppData/ProgramData directories are not available
+ ''' Override values from ComputerConfig with UserConfig
+ Public Sub New(LogConfig As LogConfig, UserConfigPath As String, ComputerConfigPath As String, Optional ApplicationStartupPath As String = "", Optional ForceUserConfig As Boolean = False)
+ _LogConfig = LogConfig
+ _Logger = LogConfig.GetLogger()
+ _File = New File(_LogConfig)
+
+ _Blueprint = Activator.CreateInstance(Of T)
+ _BlueprintType = _Blueprint.GetType
+ _Serializer = New XmlSerializer(_BlueprintType)
+
+ _UserDirectory = _File.CreateDirectory(UserConfigPath)
+ _UserConfigPath = Path.Combine(_UserDirectory, USER_CONFIG_NAME)
+
+ If ComputerConfigPath <> String.Empty Then
+ _ComputerDirectory = _File.CreateDirectory(ComputerConfigPath)
+ _ComputerConfigPath = Path.Combine(_ComputerDirectory, COMPUTER_CONFIG_NAME)
+ End If
+
+ If ApplicationStartupPath <> String.Empty Then
+ _AppConfigPath = Path.Combine(ApplicationStartupPath, APP_CONFIG_NAME)
+ End If
+
+ _WriteAllValuesToUserConfig = ForceUserConfig
+
+ Config = LoadConfig()
+ End Sub
+
+ '''
+ ''' Creates a new ConfigManager with a single (user)config path
+ '''
+ ''' LogConfig instance
+ ''' The path to check for a user config file, eg. AppData (Usually Application.UserAppDataPath or Application.LocalUserAppDataPath)
+ Public Sub New(LogConfig As LogConfig, ConfigPath As String)
+ MyClass.New(LogConfig, ConfigPath, String.Empty, String.Empty, ForceUserConfig:=True)
+ End Sub
+
+ '''
+ ''' Save the current config object to `UserConfigPath`
+ '''
+ ''' Force saving all attributes including the attributes marked as excluded
+ ''' True if save was successful, False otherwise
+ Public Function Save(Optional ForceAll As Boolean = False) As Boolean
+ Try
+ WriteToFile(Config, _UserConfigPath, ForceAll)
+ Return True
+ Catch ex As Exception
+ _Logger.Error(ex)
+ Return False
+ End Try
+ End Function
+
+ '''
+ ''' Copies all properties from Source to Target, except those who have an attribute
+ ''' listed in ExcludedAttributeTypes
+ '''
+ ''' Source config object
+ ''' Target config object
+ ''' List of Attribute type to exclude
+ Private Sub CopyValues(Source As T, Target As T, Optional ExcludedAttributeTypes As List(Of Type) = Nothing)
+ Dim oType As Type = GetType(T)
+ Dim oExcludedAttributeTypes = IIf(IsNothing(ExcludedAttributeTypes), New List(Of Type), ExcludedAttributeTypes)
+ Dim oProperties = oType.GetProperties().
+ Where(Function(p)
+ Return p.CanRead And p.CanWrite
+ End Function).
+ Where(Function(p)
+ For Each oAttributeType As Type In oExcludedAttributeTypes
+ If Attribute.IsDefined(p, oAttributeType) Then
+ Return False
+ End If
+ Next
+ Return True
+ End Function)
+
+ For Each oProperty As PropertyInfo In oProperties
+ Dim oValue = oProperty.GetValue(Source, Nothing)
+ If Not IsNothing(oValue) Then
+ oProperty.SetValue(Target, oValue, Nothing)
+ End If
+ Next
+ End Sub
+
+ '''
+ ''' Filters a config object by copying all values except `ExcludedAttributeTypes`
+ '''
+ ''' Config object
+ ''' List of Attribute type to exclude
+ '''
+ Private Function FilterValues(ByVal Data As T, ExcludedAttributeTypes As List(Of Type)) As T
+ Dim oResult As T = Activator.CreateInstance(Of T)
+
+ CopyValues(Data, oResult, ExcludedAttributeTypes)
+ Return oResult
+ End Function
+
+ Private Function LoadConfig() As T
+ ' first create an empty/default config object
+ Dim oConfig = Activator.CreateInstance(_BlueprintType)
+
+ ' try to load the special app config
+ oConfig = LoadAppConfig(oConfig)
+
+ ' try to load the computer config
+ oConfig = LoadComputerConfig(oConfig)
+
+ ' now try to load userconfig
+ oConfig = LoadUserConfig(oConfig)
+ Return oConfig
+ End Function
+
+ Private Function LoadAppConfig(ByVal Config As T) As T
+ If Not String.IsNullOrEmpty(_AppConfigPath) AndAlso System.IO.File.Exists(_AppConfigPath) Then
+ Try
+ Dim oAppConfig = ReadFromFile(_AppConfigPath)
+ CopyValues(oAppConfig, Config)
+ Catch ex As Exception
+ _Logger.Error(ex)
+ _Logger.Warn("ApplicationConfig could not be loaded!")
+ End Try
+ _WriteAllValuesToUserConfig = False
+ Else
+ _Logger.Debug("ApplicationConfig does not exist.")
+ _WriteAllValuesToUserConfig = True
+ End If
+
+ Return Config
+ End Function
+
+ Private Function LoadComputerConfig(ByVal Config As T) As T
+ If System.IO.File.Exists(_ComputerConfigPath) Then
+ Try
+ Dim oComputerConfig = ReadFromFile(_ComputerConfigPath)
+ CopyValues(oComputerConfig, Config)
+ Catch ex As Exception
+ _Logger.Error(ex)
+ _Logger.Warn("Computer config could not be loaded!")
+ End Try
+ _WriteAllValuesToUserConfig = False
+ Else
+ _Logger.Debug("Computer config does not exist.")
+ _WriteAllValuesToUserConfig = True
+ End If
+
+ Return Config
+ End Function
+
+ Private Function LoadUserConfig(ByVal Config As T) As T
+ If System.IO.File.Exists(_UserConfigPath) Then
+ Try
+ Dim oUserConfig = ReadFromFile(_UserConfigPath)
+
+ ' if user config exists
+ If Not IsNothing(oUserConfig) Then
+ Dim oExcludedAttributes As New List(Of Type)
+
+ ' Copy values from user config to final config
+ If _WriteAllValuesToUserConfig Then
+ CopyValues(oUserConfig, Config, New List(Of Type))
+ Else
+ CopyValues(oUserConfig, Config, _ExcludedAttributes)
+ End If
+ End If
+ Catch ex As Exception
+ _Logger.Error(ex)
+ _Logger.Warn("User config could not be loaded!")
+ End Try
+ Else
+ _Logger.Debug("User config does not exist.")
+ End If
+
+ Return Config
+ End Function
+
+ Private Function TestHasAttribute(Config As T, AttributeType As Type) As Boolean
+ For Each oProperty As PropertyInfo In Config.GetType.GetProperties()
+ If Attribute.IsDefined(oProperty, GetType(ConnectionStringAttribute)) Then
+ Return True
+ End If
+ Next
+
+ Return False
+ End Function
+
+ '''
+ ''' Serialize a config object to byte array
+ '''
+ '''
+ '''
+ Private Function Serialize(Data As T) As Byte()
+ Try
+ _Logger.Debug("Serializing config object")
+
+ Using oStream = New MemoryStream()
+ _Serializer.Serialize(oStream, Data)
+ Return oStream.ToArray()
+ End Using
+ Catch ex As Exception
+ _Logger.Error(ex)
+ Throw ex
+ End Try
+ End Function
+
+ '''
+ ''' Write an object to disk as xml
+ '''
+ ''' The object to write
+ ''' The file name to write to
+ Private Sub WriteToFile(Data As T, Path As String, ForceAll As Boolean)
+ Try
+ _Logger.Debug("Saving config to: {0}", Path)
+
+ ' If config was loaded from computer config,
+ ' DO NOT save connection string, etc. to user config
+ If _WriteAllValuesToUserConfig = False And ForceAll = False Then
+ Data = FilterValues(Data, _ExcludedAttributes)
+ End If
+
+ Dim oBytes = Serialize(Data)
+
+ Using oFileStream = New FileStream(Path, FileMode.Create, FileAccess.Write)
+ oFileStream.Write(oBytes, 0, oBytes.Length)
+ oFileStream.Flush()
+ End Using
+ Catch ex As Exception
+ _Logger.Warn("Could not save config to {0}", Path)
+ _Logger.Error(ex)
+ Throw ex
+ End Try
+ End Sub
+
+ '''
+ ''' Reads an xml from disk and deserializes to object
+ '''
+ '''
+ Private Function ReadFromFile(Path As String) As T
+ Try
+ _Logger.Debug("Loading config from: {0}", Path)
+ Dim oConfig As T
+
+ Using oReader As New StreamReader(Path)
+ oConfig = _Serializer.Deserialize(oReader)
+ End Using
+
+ ' If oConfig is Nothing, a config file was created but nothing was written to it.
+ ' In this case we need to create oConfig from defaults so we have at least some config object
+ If oConfig Is Nothing Then
+ _Logger.Debug("Config file is valid but empty. Loading default values")
+ oConfig = Activator.CreateInstance(_BlueprintType)
+ End If
+
+ Return oConfig
+ Catch ex As Exception
+ _Logger.Warn("Could not load config from {0}", Path)
+ _Logger.Error(ex)
+ Throw ex
+ End Try
+ End Function
+End Class
diff --git a/WinLineArtikelnummerGenerator/Modules/File.vb b/WinLineArtikelnummerGenerator/Modules/File.vb
new file mode 100644
index 0000000..af4dae4
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/Modules/File.vb
@@ -0,0 +1,267 @@
+Imports System.IO
+Imports System.Text.RegularExpressions
+Imports DigitalData.Modules.Logging
+
+''' File
+''' 0.0.0.1
+''' 11.10.2018
+'''
+''' Module that provides variouse File operations
+'''
+'''
+''' NLog, >= 4.5.8
+'''
+'''
+''' LogConfig, DigitalData.Module.Logging.LogConfig
+''' A LogConfig object
+'''
+'''
+'''
+'''
+'''
+'''
+'''
+Public Class File
+ Private ReadOnly _logger As Logger
+ Private ReadOnly _logConfig As LogConfig
+
+ Private ReadOnly _invalidFilenameChars As String
+ Private ReadOnly _invalidPathChars As String
+
+ Private Const REGEX_CLEAN_FILENAME As String = "[\\/:""<>|\b\0\r\n\t]"
+ Private Const REGEX_CLEAN_PATH As String = "[:""<>|\b\0\r\n\t]"
+
+ Private Const FILE_NAME_ACCESS_TEST = "accessTest.txt"
+
+ Public Sub New(LogConfig As LogConfig)
+ _logConfig = LogConfig
+ _logger = LogConfig.GetLogger()
+
+ _invalidFilenameChars = String.Join("", Path.GetInvalidFileNameChars())
+ _invalidPathChars = String.Join("", Path.GetInvalidPathChars())
+ End Sub
+
+ Public Function GetCleanFilename(FileName As String) As String
+ _logger.Debug("Filename before cleaning: [{0}]", FileName)
+
+ Dim oCleanName As String = FileName
+ oCleanName = Regex.Replace(oCleanName, _invalidFilenameChars, String.Empty)
+ oCleanName = Regex.Replace(oCleanName, REGEX_CLEAN_FILENAME, String.Empty, RegexOptions.Singleline)
+
+ _logger.Debug("Filename after cleaning: [{0}]", oCleanName)
+
+ Return oCleanName
+ End Function
+
+ Public Function GetCleanPath(FilePath As String) As String
+ _logger.Debug("Path before cleaning: [{0}]", FilePath)
+
+ Dim oCleanName As String = FilePath
+ oCleanName = Regex.Replace(oCleanName, _invalidPathChars, String.Empty)
+ oCleanName = Regex.Replace(oCleanName, REGEX_CLEAN_PATH, String.Empty, RegexOptions.Singleline)
+
+ _logger.Debug("Path after cleaning: [{0}]", oCleanName)
+
+ Return oCleanName
+ End Function
+
+ '''
+ ''' Adds fileversions to given filename `Destination` if that file already exists.
+ '''
+ '''
+ '''
+ Public Function GetVersionedFilename(Destination As String) As String
+ Try
+ Dim oFileName As String = Destination
+ Dim oFinalFileName = oFileName
+
+ Dim oDestinationDir = Path.GetDirectoryName(oFileName)
+ Dim oExtension = Path.GetExtension(oFileName)
+
+ Dim oVersionSeparator As Char = "~"c
+ Dim oFileVersion As Integer = Nothing
+
+ ' Split Filename without extension at version separator to:
+ ' - Check if file is already versioned
+ ' - Get the file version of an already versioned file
+ '
+ ' Example:
+ ' test1.pdf --> test1 --> ['test1'] --> no fileversion
+ ' test1~2.pdf --> test1~2 --> ['test1', '2'] --> version 2
+ ' test1~12345~2.pdf --> test1~12345~2 --> ['test1', '12345', '2'] --> still version 2
+ Dim oFileNameWithoutExtension = Path.GetFileNameWithoutExtension(oFileName)
+ Dim oSplitFilename = oFileNameWithoutExtension.Split(oVersionSeparator).ToList()
+
+ ' if file is already versioned, extract file version
+ ' else just use the filename and set version to 1
+ If oSplitFilename.Count > 1 Then
+ Dim oVersion As Integer = 1
+ Try
+ oVersion = Integer.Parse(oSplitFilename.Last())
+ oFileNameWithoutExtension = String.Join("", oSplitFilename.Take(oSplitFilename.Count - 1))
+ Catch ex As Exception
+ ' oFilenameWithoutExtension does NOT change
+ oFileNameWithoutExtension = oFileNameWithoutExtension
+ Finally
+ oFileVersion = oVersion
+ End Try
+ Else
+ oFileVersion = 1
+ End If
+
+ ' while file exists, increment version
+ Do
+ oFinalFileName = Path.Combine(oDestinationDir, GetFilenameWithVersion(oFileNameWithoutExtension, oVersionSeparator, oFileVersion, oExtension))
+ _logger.Debug("Intermediate Filename is {0}", oFinalFileName)
+ _logger.Debug("File version: {0}", oFileVersion)
+ oFileVersion += 1
+ Loop While (IO.File.Exists(oFinalFileName))
+
+ _logger.Debug("Final Filename is {0}", oFinalFileName)
+
+ Return oFinalFileName
+ Catch ex As Exception
+ _logger.Warn("Filename {0} could not be versioned. Original filename will be returned!", Destination)
+ _logger.Error(ex)
+ Return Destination
+ End Try
+ End Function
+
+ Private Function GetFilenameWithVersion(FileNameWithoutExtension As String, VersionSeparator As Char, FileVersion As Integer, Extension As String) As String
+ If FileVersion <= 1 Then
+ Return $"{FileNameWithoutExtension}{Extension}"
+ Else
+ Return $"{FileNameWithoutExtension}{VersionSeparator}{FileVersion}{Extension}"
+ End If
+ End Function
+
+ '''
+ ''' Removes files in a directory filtered by filename, extension and last write date
+ '''
+ ''' The directory in which files will be deleted
+ ''' Only delete files which are older than x days. Must be between 0 and 1000 days.
+ ''' A filename filter which will be checked
+ ''' A file extension which will be checked
+ ''' Should the function continue with deleting when a file could not be deleted?
+ ''' True if all files were deleted or if no files were deleted, otherwise false
+ Public Function RemoveFiles(Path As String, FileKeepTime As Integer, FileBaseName As String, Optional FileExtension As String = "log", Optional ContinueOnError As Boolean = True) As Boolean
+ If Not TestPathIsDirectory(Path) Then
+ Throw New ArgumentException($"Path {Path} is not a directory!")
+ End If
+
+ If Not Directory.Exists(Path) Then
+ Throw New DirectoryNotFoundException($"Path {Path} does not exist!")
+ End If
+
+ If FileKeepTime < 0 Or FileKeepTime > 1000 Then
+ Throw New ArgumentOutOfRangeException("FileKeepTime must be an integer between 0 and 1000!")
+ End If
+
+ Dim oUnableToDeleteCounter = 0
+ Dim oDirectory As New DirectoryInfo(Path)
+ Dim oDateLimit As DateTime = DateTime.Now.AddDays(FileKeepTime)
+ Dim oFiles As List(Of FileInfo) = oDirectory.
+ EnumerateFiles($"*{FileBaseName}*").
+ Where(Function(oFileInfo As FileInfo)
+ Return oFileInfo.Extension = FileExtension And oFileInfo.LastWriteTime < oDateLimit
+ End Function)
+
+ If oFiles.Count = 0 Then
+ _logger.Debug("No files found that match the criterias.")
+ Return True
+ End If
+
+ _logger.Debug("Deleting old files (Found {0}).", oFiles.Count)
+
+ For Each oFile As FileInfo In oFiles
+ Try
+ oFile.Delete()
+ Catch ex As Exception
+ If ContinueOnError = False Then
+ _logger.Warn("Deleting files was aborted at file {0}.", oFile.FullName)
+ Return False
+ End If
+ oUnableToDeleteCounter = oUnableToDeleteCounter + 1
+ _logger.Warn("File {0} could not be deleted!")
+ End Try
+ Next
+
+ If oUnableToDeleteCounter > 0 Then
+ _logger.Debug("Old files partially removed. {0} files could not be removed.", oUnableToDeleteCounter)
+ Else
+ _logger.Debug("Old files removed.")
+ End If
+
+ Return True
+ End Function
+
+
+ Public Sub MoveTo(FilePath As String, Directory As String)
+ Dim oFileInfo As New FileInfo(FilePath)
+ IO.File.Move(FilePath, Path.Combine(Directory, oFileInfo.Name))
+ End Sub
+
+
+ Public Sub MoveTo(FilePath As String, NewFileName As String, Directory As String)
+ IO.File.Move(FilePath, Path.Combine(Directory, NewFileName))
+ End Sub
+
+ '''
+ ''' Tries to create a directory and returns its path.
+ ''' Returns a temp path if `DirectoryPath` can not be created or written to.
+ '''
+ ''' The directory to create
+ ''' Should a write access test be performed?
+ ''' The used path
+ Public Function CreateDirectory(DirectoryPath As String, Optional TestWriteAccess As Boolean = True) As String
+ Dim oFinalPath As String
+ If Directory.Exists(DirectoryPath) Then
+ _logger.Debug("Directory {0} already exists. Skipping.", DirectoryPath)
+ oFinalPath = DirectoryPath
+ Else
+ Try
+ Directory.CreateDirectory(DirectoryPath)
+ oFinalPath = DirectoryPath
+ Catch ex As Exception
+ _logger.Error(ex)
+ _logger.Warn("Directory {0} could not be created. Temp path will be used instead.", DirectoryPath)
+ oFinalPath = Path.GetTempPath()
+ End Try
+ End If
+
+ If TestWriteAccess AndAlso Not TestPathIsWritable(DirectoryPath) Then
+ _logger.Warn("Directory {0} is not writable. Temp path will be used instead.", DirectoryPath)
+ oFinalPath = Path.GetTempPath()
+ Else
+ oFinalPath = DirectoryPath
+ End If
+
+ _logger.Debug("Using path {0}", oFinalPath)
+
+ Return oFinalPath
+ End Function
+
+ Public Function TestPathIsWritable(DirectoryPath As String) As Boolean
+ Try
+ Dim fileAccessPath = Path.Combine(DirectoryPath, FILE_NAME_ACCESS_TEST)
+ Using fs As FileStream = IO.File.Create(fileAccessPath)
+ fs.WriteByte(0)
+ End Using
+
+ IO.File.Delete(fileAccessPath)
+ Return True
+ Catch ex As Exception
+ Return False
+ End Try
+ End Function
+
+ Public Function TestPathIsDirectory(Path As String) As Boolean
+ If Not Directory.Exists(Path) Then
+ Return False
+ End If
+
+ Dim oIsDirectory As Boolean = (System.IO.File.GetAttributes(Path) And FileAttributes.Directory) = FileAttributes.Directory
+ Return oIsDirectory
+ End Function
+
+End Class
diff --git a/WinLineArtikelnummerGenerator/Modules/LogConfig.vb b/WinLineArtikelnummerGenerator/Modules/LogConfig.vb
new file mode 100644
index 0000000..6449dce
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/Modules/LogConfig.vb
@@ -0,0 +1,455 @@
+Imports System.IO
+Imports System.Reflection
+Imports NLog
+Imports NLog.Config
+Imports NLog.Targets
+
+''' LogConfig
+''' 0.0.1.0
+''' 02.10.2018
+'''
+''' Module that writes file-logs to different locations:
+''' local application data, the current directory or a custom path.
+''' Files and directories will be automatically created.
+'''
+'''
+''' NLog, >= 4.5.8
+'''
+'''
+''' Imports DigitalData.Modules.Logging
+'''
+''' Class FooProgram
+''' Private Logger as Logger
+''' Private LogConfig as LogConfig
+'''
+''' Public Sub New()
+''' LogConfig = new LogConfig(args)
+''' Logger = LogConfig.GetLogger()
+''' End Sub
+'''
+''' Public Sub Bar()
+''' Logger.Info("Baz")
+''' End Sub
+''' End Class
+'''
+''' Class FooLib
+''' Private Logger as NLog.Logger
+'''
+''' Public Sub New(LogConfig as LogConfig)
+''' Logger = LogConfig.GetLogger()
+''' End Sub
+'''
+''' Public Sub Bar()
+''' Logger.Info("Baz")
+''' End Sub
+''' End Class
+'''
+'''
+''' If logpath can not be written to, falls back to temp folder as defined in:
+''' https://docs.microsoft.com/de-de/dotnet/api/system.io.path.gettemppath?view=netframework-4.7.2
+'''
+''' If used in a service, LogPath must be set to CustomPath, otherwise the Log will be written to System32!
+'''
+''' For NLog Troubleshooting, set the following Environment variables to write the NLog internal Log:
+''' - NLOG_INTERNAL_LOG_LEVEL: Debug
+''' - NLOG_INTERNAL_LOG_FILE: ex. C:\Temp\Nlog_Internal.log
+'''
+Public Class LogConfig
+#Region "Private Properties"
+ Private Const OPEN_FILE_CACHE_TIMEOUT As Integer = 5
+ Private Const OPEN_FILE_FLUSH_TIMEOUT As Integer = 5
+ Private Const AUTO_FLUSH As Boolean = True
+
+ Private Const KEEP_FILES_OPEN As Boolean = False
+ Private Const KEEP_FILES_OPEN_DEBUG As Boolean = True
+
+ ' MAX_ARCHIVES_FILES works like this (in version 4.5.8):
+ ' 0 = keep ALL archives files
+ ' 1 = only keep latest logfile and NO archive files
+ ' n = keep n archive files
+ Private Const MAX_ARCHIVE_FILES_DEFAULT As Integer = 0
+ Private Const MAX_ARCHIVE_FILES_DEBUG_DETAIL As Integer = 0
+ Private Const ARCHIVE_EVERY As FileArchivePeriod = FileArchivePeriod.Day
+
+ Private Const FILE_NAME_FORMAT_DEFAULT As String = "${shortdate}-${var:product}${var:suffix}.log"
+ Private Const FILE_NAME_FORMAT_DEBUG As String = "${shortdate}-${var:product}${var:suffix}-Debug.log"
+ Private Const FILE_NAME_FORMAT_ERROR As String = "${shortdate}-${var:product}${var:suffix}-Error.log"
+
+ Private Const TARGET_DEFAULT As String = "defaultTarget"
+ Private Const TARGET_ERROR_EX As String = "errorExceptionTarget"
+ Private Const TARGET_ERROR As String = "errorTarget"
+ Private Const TARGET_DEBUG As String = "debugTarget"
+ Private Const TARGET_MEMORY As String = "memoryTarget"
+
+ Private Const DATE_FORMAT_LONG As String = "${longdate}"
+ Private Const DATE_FORMAT_DEFAULT As String = "${date:format=yyyy-MM-dd HH\:mm\:ss}"
+
+ Private Const LOG_FORMAT_BASE As String = DATE_FORMAT_DEFAULT & "|${logger:shortName=True}|${level:uppercase=true}"
+ Private Const LOG_FORMAT_BASE_LONG_DATE As String = DATE_FORMAT_LONG & "|${logger:shortName=True}|${level:uppercase=true}"
+ Private Const LOG_FORMAT_CALLSITE As String = "${callsite:className=false:fileName=true:includeSourcePath=false:methodName=true}"
+
+ Private Const LOG_FORMAT_DEFAULT As String = LOG_FORMAT_BASE & " >> ${message}"
+ Private Const LOG_FORMAT_EXCEPTION As String = LOG_FORMAT_BASE & " >> ${exception:format=Message}${newline}${exception:format=StackTrace}"
+ Private Const LOG_FORMAT_DEBUG As String = LOG_FORMAT_BASE_LONG_DATE & " >> " & LOG_FORMAT_CALLSITE & " -> ${message}"
+ Private Const LOG_FORMAT_MEMORY As String = LOG_FORMAT_BASE_LONG_DATE & " >> ${message}${newline}${exception:format=Message}${newline}${exception:format=StackTrace}"
+
+ Private Const FILE_NAME_ACCESS_TEST = "accessTest.txt"
+ Private Const FOLDER_NAME_LOG = "Log"
+
+ Private ReadOnly failSafePath As String = Path.GetTempPath()
+ Private ReadOnly basePath As String = failSafePath
+
+ Private config As LoggingConfiguration
+ Private isDebug As Boolean = False
+#End Region
+#Region "Public Properties"
+ Public Enum PathType As Integer
+ AppData = 0
+ CurrentDirectory = 1
+ CustomPath = 2
+ Temp = 3
+ End Enum
+
+ '''
+ ''' Returns the NLog.LogFactory object that is used to create Loggers
+ '''
+ ''' LogFactory object
+ Public ReadOnly Property LogFactory As LogFactory
+
+ '''
+ ''' Returns the path to the current default logfile
+ '''
+ ''' Filepath to the logfile
+ Public ReadOnly Property LogFile As String
+
+ '''
+ ''' Returns the path to the current log directory
+ '''
+ ''' Directory path to the log directory
+ Public ReadOnly Property LogDirectory As String
+
+ '''
+ ''' Determines if a debug log will be written
+ '''
+ ''' True, if debug log will be written. False otherwise.
+ Public Property Debug As Boolean
+ Get
+ Return isDebug
+ End Get
+ Set(isDebug As Boolean)
+ Me.isDebug = isDebug
+ 'GetLogger().Debug("=> Debug is now {0}", isDebug)
+ ReloadConfig(isDebug)
+ End Set
+ End Property
+
+ '''
+ ''' Returns Logs in Memory as List(Of String) if Debug is enabled
+ ''' Returns an empty list if debug is disabled
+ '''
+ ''' A list of log messages
+ Public ReadOnly Property Logs As List(Of String)
+ Get
+ Dim oTarget = config.FindTargetByName(Of MemoryTarget)(TARGET_MEMORY)
+ Return oTarget?.Logs.ToList()
+ End Get
+ End Property
+
+ Public ReadOnly Property NLogConfig As LoggingConfiguration
+ Get
+ Return config
+ End Get
+ End Property
+
+#End Region
+
+ '''
+ ''' Initializes a new LogConfig object with a logpath and optinally a filename-suffix.
+ '''
+ ''' The basepath to write logs to. Can be AppData, CurrentDirectory or CustomPath.
+ ''' If `logPath` is set to custom, this defines the custom logPath.
+ ''' If set to anything other than Nothing, extends the logfile name with this suffix.
+ ''' CompanyName is used to construct log-path in when LogPath is set to PathType:AppData
+ ''' ProductName is used to construct log-path in when LogPath is set to PathType:AppData
+ Public Sub New(LogPath As PathType,
+ Optional CustomLogPath As String = Nothing,
+ Optional Suffix As String = Nothing,
+ Optional CompanyName As String = Nothing,
+ Optional ProductName As String = Nothing)
+
+ If LogPath = PathType.AppData And (ProductName Is Nothing Or CompanyName Is Nothing) Then
+ Throw New ArgumentException("Modules.Logging: PathType is AppData and either CompanyName or ProductName was not supplied!")
+ End If
+
+ If LogPath = PathType.CurrentDirectory Then
+ Throw New ArgumentException("Modules.Logging: LogPath.CurrentDirectory is deprecated. Please use LogPath.CustomPath!")
+ End If
+
+ If LogPath = PathType.AppData Then
+ Dim appDataDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
+ basePath = Path.Combine(appDataDir, CompanyName, ProductName, FOLDER_NAME_LOG)
+ ElseIf LogPath = PathType.Temp Then
+ basePath = failSafePath
+ Else 'Custom Path
+ basePath = CustomLogPath
+ End If
+
+ ' If directory does not exist, try to create it!
+ If Not Directory.Exists(basePath) Then
+ Try
+ Directory.CreateDirectory(basePath)
+ Catch ex As Exception
+ ' If creation fails, use failSafe path
+ basePath = failSafePath
+ End Try
+ End If
+
+ ' Try to create a file in `basePath` to check write permissions
+ Try
+ Dim fileAccessPath = Path.Combine(basePath, FILE_NAME_ACCESS_TEST)
+ Using fs As FileStream = System.IO.File.Create(fileAccessPath)
+ fs.WriteByte(0)
+ End Using
+
+ IO.File.Delete(fileAccessPath)
+ Catch ex As Exception
+ ' If creation fails, use failSafe path
+ basePath = failSafePath
+ End Try
+
+ ' Set the suffix to the given string if it exists
+ Dim logFileSuffix As String = String.Empty
+
+ If Suffix IsNot Nothing AndAlso Suffix.Count > 0 Then
+ logFileSuffix = $"-{Suffix}"
+ End If
+
+ Dim oProductName As String = "Main"
+
+ If ProductName IsNot Nothing Then
+ oProductName = ProductName
+ End If
+
+ ' Create config object and initalize it
+ config = GetConfig(oProductName, logFileSuffix)
+
+ ' Save config
+ LogFactory = New LogFactory With {
+ .Configuration = config
+ }
+
+ ' Save log paths for files/directory
+ LogDirectory = basePath
+ LogFile = GetCurrentLogFilePath()
+ End Sub
+
+ Private Sub CheckMyApplication()
+ Dim oAssembly = Assembly.GetEntryAssembly()
+ Dim oMyApp = Nothing
+ For Each oType As Type In oAssembly.DefinedTypes
+ If oType.Name = "MyApplication" Then
+ oMyApp = oType
+ Exit For
+ End If
+ Next
+
+ oMyApp.GetType().GetProperty("")
+ End Sub
+
+ '''
+ ''' Returns the Logger for the calling class
+ '''
+ ''' An object of Logging.Logger
+
+ Public Function GetLogger() As Logger
+ Dim oClassName As String = GetClassFullName()
+ Return LogFactory.GetLogger(Of Logger)(oClassName)
+ End Function
+
+ '''
+ ''' Returns the Logger for a class specified by `ClassName`
+ '''
+ ''' The name of the class the logger belongs to
+ ''' An object of Logging.Logger
+
+ Public Function GetLogger(ClassName As String) As Logger
+ Return LogFactory.GetLogger(Of Logger)(ClassName)
+ End Function
+
+ '''
+ ''' Clears the internal log
+ '''
+ Public Sub ClearLogs()
+ Dim oTarget = config.FindTargetByName(Of MemoryTarget)(TARGET_MEMORY)
+ oTarget?.Logs.Clear()
+ End Sub
+
+ '''
+ ''' Gets the fully qualified name of the class invoking the calling method,
+ ''' including the namespace but Not the assembly.
+ '''
+ ''' The fully qualified class name
+ ''' This method is very resource-intensive!
+
+ Public Shared Function GetClassFullName() As String
+ Dim oFramesToSkip As Integer = 2
+ Dim oClassName As String = String.Empty
+ Dim oStackTrace = Environment.StackTrace
+ Dim oStackTraceLines = oStackTrace.Replace(vbCr, "").Split({vbLf}, StringSplitOptions.RemoveEmptyEntries)
+
+ For i As Integer = 0 To oStackTraceLines.Length - 1
+ Dim oCallingClassAndMethod = oStackTraceLines(i).Split({" ", "<>", "(", ")"}, StringSplitOptions.RemoveEmptyEntries)(1)
+ Dim oMethodStartIndex As Integer = oCallingClassAndMethod.LastIndexOf(".", StringComparison.Ordinal)
+
+ If oMethodStartIndex > 0 Then
+ Dim oCallingClass = oCallingClassAndMethod.Substring(0, oMethodStartIndex)
+ oClassName = oCallingClass.TrimEnd("."c)
+
+ If Not oClassName.StartsWith("System.Environment") AndAlso oFramesToSkip <> 0 Then
+ i += oFramesToSkip - 1
+ oFramesToSkip = 0
+ Continue For
+ End If
+
+ If Not oClassName.StartsWith("System.") Then Exit For
+ End If
+ Next
+
+ Return oClassName
+ End Function
+
+ '''
+ ''' Returns the initial log configuration
+ '''
+ ''' The chosen productname
+ ''' The chosen suffix
+ ''' A NLog.LoggingConfiguration object
+ Private Function GetConfig(productName As String, logFileSuffix As String) As LoggingConfiguration
+ config = New LoggingConfiguration()
+ config.Variables("product") = productName
+ config.Variables("suffix") = logFileSuffix
+
+ ' Add default targets
+ config.AddTarget(TARGET_ERROR_EX, GetErrorExceptionLogTarget(basePath))
+ config.AddTarget(TARGET_ERROR, GetErrorLogTarget(basePath))
+ config.AddTarget(TARGET_DEFAULT, GetDefaultLogTarget(basePath))
+ config.AddTarget(TARGET_DEBUG, GetDebugLogTarget(basePath))
+ config.AddTarget(TARGET_MEMORY, GetMemoryDebugTarget())
+
+ ' Add default rules
+ AddDefaultRules(config)
+
+ Return config
+ End Function
+
+ '''
+ ''' Adds the default rules
+ '''
+ ''' A NLog.LoggingConfiguration object
+ Private Sub AddDefaultRules(ByRef config As LoggingConfiguration)
+ config.AddRuleForOneLevel(LogLevel.Error, TARGET_ERROR_EX)
+ config.AddRuleForOneLevel(LogLevel.Fatal, TARGET_ERROR_EX)
+ config.AddRuleForOneLevel(LogLevel.Warn, TARGET_ERROR)
+ config.AddRuleForOneLevel(LogLevel.Warn, TARGET_DEFAULT)
+ config.AddRuleForOneLevel(LogLevel.Info, TARGET_DEFAULT)
+ End Sub
+
+ '''
+ ''' Returns the full path of the current default log file.
+ '''
+ ''' Full path of the current default log file
+ Private Function GetCurrentLogFilePath()
+ Dim logEventInfo As New LogEventInfo() With {.TimeStamp = Date.Now}
+ Dim target As FileTarget = config.FindTargetByName(TARGET_DEFAULT)
+ Dim fileName As String = target.FileName.Render(logEventInfo)
+
+ Return fileName
+ End Function
+
+ '''
+ ''' Reconfigures and re-adds all loggers, optionally adding the debug rule.
+ '''
+ ''' Adds the Debug rule if true.
+ Private Sub ReloadConfig(Optional Debug As Boolean = False)
+ ' Clear Logging Rules
+ config.LoggingRules.Clear()
+
+ ' Add default rules
+ AddDefaultRules(config)
+
+ ' Add debug rule, if configured
+ If Debug Then
+ config.AddRuleForOneLevel(LogLevel.Debug, TARGET_DEBUG)
+ config.AddRuleForAllLevels(TARGET_MEMORY)
+ End If
+
+ ' Reload all running loggers
+ LogFactory.ReconfigExistingLoggers()
+ End Sub
+
+#Region "Log Targets"
+ Private Function GetDefaultLogTarget(basePath As String) As FileTarget
+ Dim defaultLog As New FileTarget() With {
+ .FileName = Path.Combine(basePath, FILE_NAME_FORMAT_DEFAULT),
+ .Name = TARGET_DEFAULT,
+ .Layout = LOG_FORMAT_DEFAULT,
+ .MaxArchiveFiles = MAX_ARCHIVE_FILES_DEFAULT,
+ .ArchiveEvery = ARCHIVE_EVERY,
+ .KeepFileOpen = KEEP_FILES_OPEN
+ }
+
+ Return defaultLog
+ End Function
+ Private Function GetErrorExceptionLogTarget(basePath As String) As FileTarget
+ Dim errorLogWithExceptions As New FileTarget() With {
+ .FileName = Path.Combine(basePath, FILE_NAME_FORMAT_ERROR),
+ .Name = TARGET_ERROR_EX,
+ .Layout = LOG_FORMAT_EXCEPTION,
+ .MaxArchiveFiles = MAX_ARCHIVE_FILES_DEFAULT,
+ .ArchiveEvery = ARCHIVE_EVERY,
+ .KeepFileOpen = KEEP_FILES_OPEN
+ }
+
+ Return errorLogWithExceptions
+ End Function
+
+ Private Function GetErrorLogTarget(basePath As String) As FileTarget
+ Dim errorLog As New FileTarget() With {
+ .FileName = Path.Combine(basePath, FILE_NAME_FORMAT_ERROR),
+ .Name = TARGET_ERROR,
+ .Layout = LOG_FORMAT_DEFAULT,
+ .MaxArchiveFiles = MAX_ARCHIVE_FILES_DEFAULT,
+ .ArchiveEvery = ARCHIVE_EVERY,
+ .KeepFileOpen = KEEP_FILES_OPEN
+ }
+
+ Return errorLog
+ End Function
+
+ Private Function GetDebugLogTarget(basePath As String) As FileTarget
+ Dim debugLog As New FileTarget() With {
+ .FileName = Path.Combine(basePath, FILE_NAME_FORMAT_DEBUG),
+ .Name = TARGET_DEBUG,
+ .Layout = LOG_FORMAT_DEBUG,
+ .MaxArchiveFiles = MAX_ARCHIVE_FILES_DEBUG_DETAIL,
+ .ArchiveEvery = ARCHIVE_EVERY,
+ .KeepFileOpen = KEEP_FILES_OPEN_DEBUG,
+ .OpenFileCacheTimeout = OPEN_FILE_CACHE_TIMEOUT,
+ .AutoFlush = AUTO_FLUSH,
+ .OpenFileFlushTimeout = OPEN_FILE_FLUSH_TIMEOUT
+ }
+
+ Return debugLog
+ End Function
+
+ Private Function GetMemoryDebugTarget() As MemoryTarget
+ Dim memoryLog As New MemoryTarget() With {
+ .Layout = LOG_FORMAT_MEMORY,
+ .Name = TARGET_MEMORY,
+ .OptimizeBufferReuse = True
+ }
+
+ Return memoryLog
+ End Function
+#End Region
+End Class
diff --git a/WinLineArtikelnummerGenerator/Modules/Logger.vb b/WinLineArtikelnummerGenerator/Modules/Logger.vb
new file mode 100644
index 0000000..c7f56c7
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/Modules/Logger.vb
@@ -0,0 +1,28 @@
+Imports NLog
+
+Public Class Logger
+ Inherits NLog.Logger
+ Implements ILogger
+
+ '''
+ ''' Prints a preformatted Block including a block identifier
+ '''
+ ''' A unique Identifier for this block, eg. DocId, FullPath, ..
+
+ Public Sub NewBlock(blockId As String)
+ Dim message As String = $"-----> Start of Block {blockId}"
+ Dim logEventInfo As New LogEventInfo(LogLevel.Info, Name, message)
+ Dim WrapperType As Type = GetType(Logger)
+
+ Log(WrapperType, logEventInfo)
+ End Sub
+
+
+ Public Sub EndBlock()
+ Dim message As String = $"<----- End of Block"
+ Dim logEventInfo As New LogEventInfo(LogLevel.Info, Name, message)
+ Dim WrapperType As Type = GetType(Logger)
+
+ Log(WrapperType, logEventInfo)
+ End Sub
+End Class
\ No newline at end of file
diff --git a/WinLineArtikelnummerGenerator/My Project/Application.Designer.vb b/WinLineArtikelnummerGenerator/My Project/Application.Designer.vb
new file mode 100644
index 0000000..3319c45
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/My Project/Application.Designer.vb
@@ -0,0 +1,38 @@
+'------------------------------------------------------------------------------
+'
+' This code was generated by a tool.
+' Runtime Version:4.0.30319.42000
+'
+' Changes to this file may cause incorrect behavior and will be lost if
+' the code is regenerated.
+'
+'------------------------------------------------------------------------------
+
+Option Strict On
+Option Explicit On
+
+
+Namespace My
+
+ 'NOTE: This file is auto-generated; do not modify it directly. To make changes,
+ ' or if you encounter build errors in this file, go to the Project Designer
+ ' (go to Project Properties or double-click the My Project node in
+ ' Solution Explorer), and make changes on the Application tab.
+ '
+ Partial Friend Class MyApplication
+
+ _
+ Public Sub New()
+ MyBase.New(Global.Microsoft.VisualBasic.ApplicationServices.AuthenticationMode.Windows)
+ Me.IsSingleInstance = false
+ Me.EnableVisualStyles = true
+ Me.SaveMySettingsOnExit = true
+ Me.ShutDownStyle = Global.Microsoft.VisualBasic.ApplicationServices.ShutdownMode.AfterMainFormCloses
+ End Sub
+
+ _
+ Protected Overrides Sub OnCreateMainForm()
+ Me.MainForm = Global.WinLineArtikelnummerGenerator.frmMain
+ End Sub
+ End Class
+End Namespace
diff --git a/WinLineArtikelnummerGenerator/My Project/Application.myapp b/WinLineArtikelnummerGenerator/My Project/Application.myapp
new file mode 100644
index 0000000..1243847
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/My Project/Application.myapp
@@ -0,0 +1,11 @@
+
+
+ true
+ Form1
+ false
+ 0
+ true
+ 0
+ 0
+ true
+
diff --git a/WinLineArtikelnummerGenerator/My Project/AssemblyInfo.vb b/WinLineArtikelnummerGenerator/My Project/AssemblyInfo.vb
new file mode 100644
index 0000000..a9cd2b2
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/My Project/AssemblyInfo.vb
@@ -0,0 +1,35 @@
+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
+
+
+
+
+
+
+
+
+
+
+'Die folgende GUID wird für die typelib-ID verwendet, wenn dieses Projekt für COM verfügbar gemacht wird.
+
+
+' Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
+'
+' Hauptversion
+' Nebenversion
+' Buildnummer
+' Revision
+'
+' Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
+' indem Sie "*" wie unten gezeigt eingeben:
+'
+
+
+
diff --git a/WinLineArtikelnummerGenerator/My Project/Resources.Designer.vb b/WinLineArtikelnummerGenerator/My Project/Resources.Designer.vb
new file mode 100644
index 0000000..93d083e
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/My Project/Resources.Designer.vb
@@ -0,0 +1,62 @@
+'------------------------------------------------------------------------------
+'
+' This code was generated by a tool.
+' Runtime Version:4.0.30319.42000
+'
+' Changes to this file may cause incorrect behavior and will be lost if
+' the code is regenerated.
+'
+'------------------------------------------------------------------------------
+
+Option Strict On
+Option Explicit On
+
+
+Namespace My.Resources
+
+ 'This class was auto-generated by the StronglyTypedResourceBuilder
+ 'class via a tool like ResGen or Visual Studio.
+ 'To add or remove a member, edit your .ResX file then rerun ResGen
+ 'with the /str option, or rebuild your VS project.
+ '''
+ ''' A strongly-typed resource class, for looking up localized strings, etc.
+ '''
+ _
+ Friend Module Resources
+
+ Private resourceMan As Global.System.Resources.ResourceManager
+
+ Private resourceCulture As Global.System.Globalization.CultureInfo
+
+ '''
+ ''' Returns the cached ResourceManager instance used by this class.
+ '''
+ _
+ Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager
+ Get
+ If Object.ReferenceEquals(resourceMan, Nothing) Then
+ Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("WinLineArtikelnummerGenerator.Resources", GetType(Resources).Assembly)
+ resourceMan = temp
+ End If
+ Return resourceMan
+ End Get
+ End Property
+
+ '''
+ ''' Overrides the current thread's CurrentUICulture property for all
+ ''' resource lookups using this strongly typed resource class.
+ '''
+ _
+ Friend Property Culture() As Global.System.Globalization.CultureInfo
+ Get
+ Return resourceCulture
+ End Get
+ Set(ByVal value As Global.System.Globalization.CultureInfo)
+ resourceCulture = value
+ End Set
+ End Property
+ End Module
+End Namespace
diff --git a/WinLineArtikelnummerGenerator/My Project/Resources.resx b/WinLineArtikelnummerGenerator/My Project/Resources.resx
new file mode 100644
index 0000000..af7dbeb
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/My Project/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/WinLineArtikelnummerGenerator/My Project/Settings.Designer.vb b/WinLineArtikelnummerGenerator/My Project/Settings.Designer.vb
new file mode 100644
index 0000000..06f00a9
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/My Project/Settings.Designer.vb
@@ -0,0 +1,96 @@
+'------------------------------------------------------------------------------
+'
+' 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.
+'
+'------------------------------------------------------------------------------
+
+Option Strict On
+Option Explicit On
+
+
+Namespace My
+
+ _
+ Partial Friend NotInheritable Class MySettings
+ Inherits Global.System.Configuration.ApplicationSettingsBase
+
+ Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings()),MySettings)
+
+#Region "Automatische My.Settings-Speicherfunktion"
+#If _MyType = "WindowsForms" Then
+ Private Shared addedHandler As Boolean
+
+ Private Shared addedHandlerLockObject As New Object
+
+ _
+ 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 MySettings
+ 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
+
+ _
+ Public Property ConnectionString() As String
+ Get
+ Return CType(Me("ConnectionString"),String)
+ End Get
+ Set
+ Me("ConnectionString") = value
+ End Set
+ End Property
+
+ _
+ Public ReadOnly Property MyConnectionString() As String
+ Get
+ Return CType(Me("MyConnectionString"),String)
+ End Get
+ End Property
+ End Class
+End Namespace
+
+Namespace My
+
+ _
+ Friend Module MySettingsProperty
+
+ _
+ Friend ReadOnly Property Settings() As Global.WinLineArtikelnummerGenerator.My.MySettings
+ Get
+ Return Global.WinLineArtikelnummerGenerator.My.MySettings.Default
+ End Get
+ End Property
+ End Module
+End Namespace
diff --git a/WinLineArtikelnummerGenerator/My Project/Settings.settings b/WinLineArtikelnummerGenerator/My Project/Settings.settings
new file mode 100644
index 0000000..493047d
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/My Project/Settings.settings
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+ <?xml version="1.0" encoding="utf-16"?>
+<SerializableConnectionString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <ConnectionString>Data Source=SDD-VMP04-SQL17\MEDACOM;Initial Catalog=CWLDATEN_MEDS;User ID=sa;Password=dd</ConnectionString>
+ <ProviderName>System.Data.SqlClient</ProviderName>
+</SerializableConnectionString>
+ Data Source=SDD-VMP04-SQL17\MEDACOM;Initial Catalog=CWLDATEN_MEDS;User ID=sa;Password=dd
+
+
+
\ No newline at end of file
diff --git a/WinLineArtikelnummerGenerator/My Project/licenses.licx b/WinLineArtikelnummerGenerator/My Project/licenses.licx
new file mode 100644
index 0000000..d39415a
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/My Project/licenses.licx
@@ -0,0 +1,2 @@
+DevExpress.XtraGrid.GridControl, DevExpress.XtraGrid.v20.1, Version=20.1.3.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a
+DevExpress.XtraBars.Ribbon.RibbonControl, DevExpress.XtraBars.v20.1, Version=20.1.3.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a
diff --git a/WinLineArtikelnummerGenerator/WinLineArtikelnummerGenerator.vbproj b/WinLineArtikelnummerGenerator/WinLineArtikelnummerGenerator.vbproj
new file mode 100644
index 0000000..b73b935
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/WinLineArtikelnummerGenerator.vbproj
@@ -0,0 +1,186 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {BA171D0B-6421-4294-B5DC-BF77D93C91E7}
+ WinExe
+ WinLineArtikelnummerGenerator.My.MyApplication
+ WinLineArtikelnummerGenerator
+ WinLineArtikelnummerGenerator
+ 512
+ WindowsForms
+ v4.7.2
+ true
+ true
+
+
+ AnyCPU
+ true
+ full
+ true
+ true
+ bin\Debug\
+ WinLineArtikelnummerGenerator.xml
+ 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022
+
+
+ AnyCPU
+ pdbonly
+ false
+ true
+ true
+ bin\Release\
+ WinLineArtikelnummerGenerator.xml
+ 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022
+
+
+ On
+
+
+ Binary
+
+
+ Off
+
+
+ On
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ..\..\DDMonorepo\Modules.Config\bin\Debug\DigitalData.Modules.Config.dll
+
+
+ ..\..\DDMonorepo\Modules.Database\bin\Debug\DigitalData.Modules.Database.dll
+
+
+ ..\..\DDMonorepo\Modules.Filesystem\bin\Debug\DigitalData.Modules.Filesystem.dll
+
+
+ ..\..\DDMonorepo\Modules.Language\bin\Debug\DigitalData.Modules.Language.dll
+
+
+ ..\..\DDMonorepo\Modules.Logging\bin\Debug\DigitalData.Modules.Logging.dll
+
+
+
+ ..\packages\NLog.4.7.4\lib\net45\NLog.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ CWLDATEN_MEDSDataSet.xsd
+
+
+ frmMain.vb
+
+
+ Form
+
+
+
+
+
+
+
+
+
+ True
+ Application.myapp
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+
+
+ frmMain.vb
+
+
+
+ VbMyResourcesResXFileCodeGenerator
+ Resources.Designer.vb
+ My.Resources
+ Designer
+
+
+
+
+ CWLDATEN_MEDSDataSet.xsd
+
+
+ MSDataSetGenerator
+ CWLDATEN_MEDSDataSet.Designer.vb
+ Designer
+
+
+ CWLDATEN_MEDSDataSet.xsd
+
+
+ MyApplicationCodeGenerator
+ Application.Designer.vb
+
+
+ SettingsSingleFileGenerator
+ My
+ Settings.Designer.vb
+
+
+
+
+
+
\ No newline at end of file
diff --git a/WinLineArtikelnummerGenerator/frmMain.Designer.vb b/WinLineArtikelnummerGenerator/frmMain.Designer.vb
new file mode 100644
index 0000000..5f33330
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/frmMain.Designer.vb
@@ -0,0 +1,161 @@
+ _
+Partial Class frmMain
+ Inherits System.Windows.Forms.Form
+
+ 'Das Formular überschreibt den Löschvorgang, um die Komponentenliste zu bereinigen.
+ _
+ Protected Overrides Sub Dispose(ByVal disposing As Boolean)
+ Try
+ If disposing AndAlso components IsNot Nothing Then
+ components.Dispose()
+ End If
+ Finally
+ MyBase.Dispose(disposing)
+ End Try
+ End Sub
+
+ 'Wird vom Windows Form-Designer benötigt.
+ Private components As System.ComponentModel.IContainer
+
+ 'Hinweis: Die folgende Prozedur ist für den Windows Form-Designer erforderlich.
+ 'Das Bearbeiten ist mit dem Windows Form-Designer möglich.
+ 'Das Bearbeiten mit dem Code-Editor ist nicht möglich.
+ _
+ Private Sub InitializeComponent()
+ Me.listboxVendors = New System.Windows.Forms.ListBox()
+ Me.listboxProductGroups = New System.Windows.Forms.ListBox()
+ Me.Label1 = New System.Windows.Forms.Label()
+ Me.Label2 = New System.Windows.Forms.Label()
+ Me.Button1 = New System.Windows.Forms.Button()
+ Me.Button2 = New System.Windows.Forms.Button()
+ Me.listBoxProductVersion = New System.Windows.Forms.ListBox()
+ Me.Button3 = New System.Windows.Forms.Button()
+ Me.Label3 = New System.Windows.Forms.Label()
+ Me.SuspendLayout()
+ '
+ 'listboxVendors
+ '
+ Me.listboxVendors.Font = New System.Drawing.Font("Consolas", 9.75!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
+ Me.listboxVendors.FormattingEnabled = True
+ Me.listboxVendors.ItemHeight = 15
+ Me.listboxVendors.Location = New System.Drawing.Point(12, 67)
+ Me.listboxVendors.Margin = New System.Windows.Forms.Padding(3, 4, 3, 4)
+ Me.listboxVendors.Name = "listboxVendors"
+ Me.listboxVendors.Size = New System.Drawing.Size(327, 244)
+ Me.listboxVendors.TabIndex = 0
+ '
+ 'listboxProductGroups
+ '
+ Me.listboxProductGroups.Font = New System.Drawing.Font("Consolas", 9.75!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
+ Me.listboxProductGroups.FormattingEnabled = True
+ Me.listboxProductGroups.ItemHeight = 15
+ Me.listboxProductGroups.Location = New System.Drawing.Point(345, 67)
+ Me.listboxProductGroups.Margin = New System.Windows.Forms.Padding(3, 4, 3, 4)
+ Me.listboxProductGroups.Name = "listboxProductGroups"
+ Me.listboxProductGroups.Size = New System.Drawing.Size(327, 244)
+ Me.listboxProductGroups.TabIndex = 0
+ '
+ 'Label1
+ '
+ Me.Label1.AutoSize = True
+ Me.Label1.Font = New System.Drawing.Font("Segoe UI Semibold", 9.0!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
+ Me.Label1.Location = New System.Drawing.Point(9, 18)
+ Me.Label1.Name = "Label1"
+ Me.Label1.Size = New System.Drawing.Size(69, 15)
+ Me.Label1.TabIndex = 1
+ Me.Label1.Text = "Lieferanten:"
+ '
+ 'Label2
+ '
+ Me.Label2.AutoSize = True
+ Me.Label2.Font = New System.Drawing.Font("Segoe UI Semibold", 9.0!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
+ Me.Label2.Location = New System.Drawing.Point(342, 18)
+ Me.Label2.Name = "Label2"
+ Me.Label2.Size = New System.Drawing.Size(97, 15)
+ Me.Label2.TabIndex = 1
+ Me.Label2.Text = "Produktgruppen:"
+ '
+ 'Button1
+ '
+ Me.Button1.Location = New System.Drawing.Point(345, 36)
+ Me.Button1.Name = "Button1"
+ Me.Button1.Size = New System.Drawing.Size(140, 23)
+ Me.Button1.TabIndex = 2
+ Me.Button1.TabStop = False
+ Me.Button1.Text = "Neue Produktgruppe"
+ Me.Button1.UseVisualStyleBackColor = True
+ '
+ 'Button2
+ '
+ Me.Button2.Location = New System.Drawing.Point(12, 37)
+ Me.Button2.Name = "Button2"
+ Me.Button2.Size = New System.Drawing.Size(140, 23)
+ Me.Button2.TabIndex = 2
+ Me.Button2.TabStop = False
+ Me.Button2.Text = "Neuer Lieferant"
+ Me.Button2.UseVisualStyleBackColor = True
+ '
+ 'listBoxProductVersion
+ '
+ Me.listBoxProductVersion.Font = New System.Drawing.Font("Consolas", 9.75!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
+ Me.listBoxProductVersion.FormattingEnabled = True
+ Me.listBoxProductVersion.ItemHeight = 15
+ Me.listBoxProductVersion.Location = New System.Drawing.Point(678, 67)
+ Me.listBoxProductVersion.Margin = New System.Windows.Forms.Padding(3, 4, 3, 4)
+ Me.listBoxProductVersion.Name = "listBoxProductVersion"
+ Me.listBoxProductVersion.Size = New System.Drawing.Size(327, 244)
+ Me.listBoxProductVersion.TabIndex = 0
+ '
+ 'Button3
+ '
+ Me.Button3.Location = New System.Drawing.Point(678, 36)
+ Me.Button3.Name = "Button3"
+ Me.Button3.Size = New System.Drawing.Size(140, 23)
+ Me.Button3.TabIndex = 2
+ Me.Button3.TabStop = False
+ Me.Button3.Text = "Neue Produktversion"
+ Me.Button3.UseVisualStyleBackColor = True
+ '
+ 'Label3
+ '
+ Me.Label3.AutoSize = True
+ Me.Label3.Font = New System.Drawing.Font("Segoe UI Semibold", 9.0!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
+ Me.Label3.Location = New System.Drawing.Point(675, 18)
+ Me.Label3.Name = "Label3"
+ Me.Label3.Size = New System.Drawing.Size(90, 15)
+ Me.Label3.TabIndex = 1
+ Me.Label3.Text = "Produktversion:"
+ '
+ 'frmMain
+ '
+ Me.AutoScaleDimensions = New System.Drawing.SizeF(7.0!, 15.0!)
+ Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
+ Me.ClientSize = New System.Drawing.Size(1105, 430)
+ Me.Controls.Add(Me.Button2)
+ Me.Controls.Add(Me.Button3)
+ Me.Controls.Add(Me.Button1)
+ Me.Controls.Add(Me.Label3)
+ Me.Controls.Add(Me.Label2)
+ Me.Controls.Add(Me.Label1)
+ Me.Controls.Add(Me.listBoxProductVersion)
+ Me.Controls.Add(Me.listboxProductGroups)
+ Me.Controls.Add(Me.listboxVendors)
+ Me.Font = New System.Drawing.Font("Segoe UI", 9.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
+ Me.Margin = New System.Windows.Forms.Padding(3, 4, 3, 4)
+ Me.Name = "frmMain"
+ Me.Text = "frmMain"
+ Me.ResumeLayout(False)
+ Me.PerformLayout()
+
+ End Sub
+
+ Friend WithEvents listboxVendors As ListBox
+ Friend WithEvents listboxProductGroups As ListBox
+ Friend WithEvents Label1 As Label
+ Friend WithEvents Label2 As Label
+ Friend WithEvents Button1 As Button
+ Friend WithEvents Button2 As Button
+ Friend WithEvents listBoxProductVersion As ListBox
+ Friend WithEvents Button3 As Button
+ Friend WithEvents Label3 As Label
+End Class
diff --git a/WinLineArtikelnummerGenerator/frmMain.resx b/WinLineArtikelnummerGenerator/frmMain.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/frmMain.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/WinLineArtikelnummerGenerator/frmMain.vb b/WinLineArtikelnummerGenerator/frmMain.vb
new file mode 100644
index 0000000..9863f9c
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/frmMain.vb
@@ -0,0 +1,60 @@
+Public Class frmMain
+ Private _Logger As Logger
+ Private _LogConfig As LogConfig
+ Private _Config As ConfigManager(Of Config)
+ Private _Database As Database
+
+ Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
+ _LogConfig = New LogConfig(LogPath:=LogConfig.PathType.AppData, CompanyName:="Digital Data", ProductName:="WinLineProductNumberGenerator")
+ _Logger = _LogConfig.GetLogger()
+ _Config = New ConfigManager(Of Config)(_LogConfig, Application.UserAppDataPath)
+ _Database = New Database(_LogConfig, _Config)
+
+ listboxVendors.DataSource = Nothing
+
+ Dim oVendorDT = _Database.LoadVendors()
+ Dim oVendors = New List(Of Vendor)
+
+ listboxVendors.DataSource = oVendorDT
+ listboxVendors.DisplayMember = "c003"
+ listboxVendors.ValueMember = "c229"
+ End Sub
+
+ Private Sub frmMain_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
+ _Config.Save()
+ End Sub
+
+ Private Sub listboxVendors_SelectedIndexChanged(sender As Object, e As EventArgs) Handles listboxVendors.SelectedIndexChanged
+ If listboxVendors.SelectedIndex >= 0 Then
+ Dim oVendor As DataRowView = listboxVendors.SelectedItem()
+ Dim oVendorCode = oVendor.Item("C229")
+ Dim oVendorId = _Database.LoadVendorIdByCode(oVendorCode)
+
+ listboxProductGroups.DataSource = Nothing
+
+ If oVendorId Is Nothing Then
+ _Logger.Warn("VendorId does not match any 'Artikeluntergruppen'.")
+ Exit Sub
+ End If
+
+ Dim oGroups = _Database.LoadGroupsByVendor(oVendorId)
+
+ listboxProductGroups.DataSource = oGroups
+ listboxProductGroups.DisplayMember = "C001"
+ listboxProductGroups.ValueMember = "C999"
+ End If
+ End Sub
+
+ Private Sub listboxProductGroups_SelectedIndexChanged(sender As Object, e As EventArgs) Handles listboxProductGroups.SelectedIndexChanged
+ If listboxProductGroups.SelectedIndex >= 0 Then
+ Dim oVendor As DataRowView = listboxVendors.SelectedItem()
+ Dim oGroup As DataRowView = listboxProductGroups.SelectedItem()
+
+ Dim oVendorCode = oVendor.Item("C229")
+ Dim oVendorId = _Database.LoadVendorIdByCode(oVendorCode)
+ Dim oGroupId = oGroup.Item("C999")
+
+ Dim Versions = _Database.LoadVersionsByVendorAndGroup(oVendorId, oGroupId)
+ End If
+ End Sub
+End Class
\ No newline at end of file
diff --git a/WinLineArtikelnummerGenerator/packages.config b/WinLineArtikelnummerGenerator/packages.config
new file mode 100644
index 0000000..b68a5f8
--- /dev/null
+++ b/WinLineArtikelnummerGenerator/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file