Imports DigitalData.Modules.Logging Imports DigitalData.Modules.Database Imports MultiTool.Shared.Winline.Entities Imports System.Text.RegularExpressions Imports MultiTool.Shared.Templates Imports MultiTool.Shared.Constants Namespace Winline Public Class WinlineData Inherits BaseClass Private ReadOnly Property Database As MSSQLServer Private ReadOnly Property Config As GeneralConfig Private ReadOnly Property MandatorConfig As MandatorConfig Private ReadOnly Property MappingConfig As MappingConfig Public Property Articles As New List(Of Article) Public Property Accounts As New List(Of Account) Public Property Mandators As New List(Of Mandator) Public Property DocumentKinds As New List(Of DocumentKind) Public Years As List(Of Integer) Public Sub New(pLogConfig As LogConfig, pDatabase As MSSQLServer, pConfig As GeneralConfig, pMappingConfig As MappingConfig, pMandatorConfig As MandatorConfig) MyBase.New(pLogConfig, pLogConfig.GetLogger()) Database = pDatabase Config = pConfig MandatorConfig = pMandatorConfig MappingConfig = pMappingConfig End Sub Public Function GetWinLineYear(pYear As Integer) Return (pYear - 1900) * 12 End Function Public Function GetWinLineYear() Return GetWinLineYear(Config.GetYear) End Function Public Async Function LoadArticles(pMandator As Mandator) As Task Logger.Info("Loading Articles for Mandator [{0}]", pMandator) Dim oYear = GetWinLineYear() Try Dim oSQL = $" SELECT DISTINCT [c002], -- Artikelnummer [c003], -- Bezeichnung [c075] -- EAN FROM [{pMandator.Server}].[{pMandator.Database}].[dbo].[v021] WHERE mesocomp = '{pMandator.Id}' AND mesoyear = {oYear}" Dim oTable = Await Database.GetDatatableAsync(oSQL) Dim oArticles As New List(Of Article) For Each oRow As DataRow In oTable.Rows Dim oArticleId As String = ItemEx(oRow, V21_ARTICLENUMBER, String.Empty) Dim oArticleDescription As String = ItemEx(oRow, V21_ARTICLEDESCRIPTION, String.Empty) Dim oEAN As String = ItemEx(oRow, V21_EAN, String.Empty) oArticles.Add(New Article With { .Id = oArticleId, .Name = oArticleDescription, .EAN = oEAN, .Mandator = pMandator }) Next Articles.AddRange(oArticles) Logger.Info("[{0}] Articles loaded for Mandator [{1}]", oArticles.Count, pMandator) Catch ex As Exception Logger.Warn("Could not load Articles for Mandator [{0}]", pMandator) Logger.Error(ex) End Try End Function Public Async Function LoadAccounts(pMandator As Mandator) As Task Logger.Info("Loading Accounts for Mandator [{0}]", pMandator) Dim oYear = GetWinLineYear() Try Dim oSQL = $" SELECT DISTINCT [c002], -- Kundennummer [c003], -- Kundenname [c050], -- Straße [c052], -- Ort [c051], -- PLZ [c260] -- GLN FROM [{pMandator.Server}].[{pMandator.Database}].[dbo].[v050] WHERE c139 IS NULL AND mesocomp = '{pMandator.Id}' AND mesoyear = {oYear}" Dim oTable = Await Database.GetDatatableAsync(oSQL) Dim oAccounts As New List(Of Account) For Each oRow As DataRow In oTable.Rows Dim oAccountNumber As String = ItemEx(oRow, V50_ACCOUNTNUMBER, String.Empty) Dim oAccountName As String = ItemEx(oRow, V50_ACCOUNTNAME, String.Empty) Dim oStreetName As String = ItemEx(oRow, V50_STREETNAME, String.Empty) Dim oZipCode As String = ItemEx(oRow, V50_ZIPCODE, String.Empty) Dim oCityName As String = ItemEx(oRow, V50_CITYNAME, String.Empty) Dim oGLN As String = ItemEx(oRow, V50_GLN, String.Empty) oAccounts.Add(New Account With { .Id = oAccountNumber, .Name = oAccountName, .StreetName = oStreetName, .ZipCode = oZipCode, .CityName = oCityName, .GLN = oGLN, .Mandator = pMandator }) Next Accounts.AddRange(oAccounts) Logger.Info("[{0}] Accounts loaded for Mandator [{1}]", oAccounts.Count, pMandator) Catch ex As Exception Logger.Warn("Could not load Accounts for Mandator [{0}]", pMandator) Logger.Error(ex) End Try End Function Public Async Function LoadMandators() As Task Try Dim oSQL = "SELECT [c000], [c003], [c004] FROM [cwlsystem].[dbo].[T001SRV] (NOLOCK)" Dim oTable = Await Database.GetDatatableAsync(oSQL) Mandators.Clear() For Each oRow As DataRow In oTable.Rows Dim oDbInfo = SplitConnectionInfo(oRow) Dim oMandator = New Mandator With { .Id = ItemEx(oRow, T01_MANDATORID, String.Empty), .Name = ItemEx(oRow, T01_MANDATORNAME, String.Empty), .Database = oDbInfo.Item1, .Server = oDbInfo.Item2 } Dim oMandatorConfig As MandatorConfigItem = MandatorConfig.Items. Where(Function(item) item.Name = oMandator.Id). SingleOrDefault() If oMandatorConfig IsNot Nothing Then oMandator.IsWhitelisted = True oMandator.Order = oMandatorConfig.OrderKey End If Mandators.Add(oMandator) Next Logger.Info("[{0}] Mandators loaded", Mandators.Count) Catch ex As Exception Logger.Warn("Could not load Mandators") Logger.Error(ex) End Try End Function Public Sub LoadEconomicYears() Dim oCurrentYear = Now.Year Dim oRange As IEnumerable(Of Integer) = Enumerable.Range(oCurrentYear - 10, 12).ToList() Years = oRange End Sub Public Async Function LoadDocumentKinds(pMandator As Mandator) As Task Dim oYear As Integer = GetWinLineYear() Try ' TODO: This is Schaum specific, maybe move to config later Dim oSQL = $" SELECT [c030], [c001], [mesocomp] FROM [{pMandator.Database}].[dbo].[t357] (NOLOCK) WHERE ( [c001] LIKE 'Werk%(VK)' OR [c001] LIKE 'Werk%(WK)' ) AND [mesocomp] = '{pMandator.Id}' AND [mesoyear] = {oYear}" Dim oTable As DataTable = Await Database.GetDatatableAsync(oSQL) Dim oKinds As New List(Of DocumentKind) For Each oRow As DataRow In oTable.Rows oKinds.Add(New DocumentKind With { .Id = ItemEx(oRow, T357_KINDID, String.Empty), .Name = ItemEx(oRow, T357_KINDNAME, String.Empty), .Mandator = pMandator }) Next DocumentKinds.AddRange(oKinds) Logger.Info("[{0}] DocumentKinds loaded for [{1}]", Mandators.Count, pMandator) Catch ex As Exception Logger.Warn("Could not load DocumentKinds") Logger.Error(ex) End Try End Function Public Function TryGetAccount(pGLN As String, pMandator As Mandator) As Account Try If pGLN Is Nothing OrElse pGLN = String.Empty Then Return Nothing End If Dim oYear As Integer = GetWinLineYear() Dim oSQL = $" SELECT [c002], -- Kundennummer [c003], -- Kundenname [c050], -- Straße [c052], -- Ort [c051] -- PLZ FROM [{pMandator.Database}].[dbo].[v050] WHERE [c004] = 2 -- KontoTyp AND [c260] = '{pGLN}' AND [mesocomp] = '{pMandator.Id}' and [mesoyear] = {oYear}" Dim oTable As DataTable = Database.GetDatatable(oSQL) ' GLN not found in this Mandator, continue to next one If oTable.Rows.Count = 0 Then Logger.Debug("GLN [{0}] was not found in Mandator: [{1}]", pGLN, pMandator.Id) Return Nothing End If ' Duplicate GLN, exit and do nothing about this number If oTable.Rows.Count > 1 Then Logger.Warn("GLN [{0}] was found more than once in Mandator: [{1}]", pGLN, pMandator.Id) Return Nothing End If Dim oRow As DataRow = oTable.Rows.Item(0) Dim oAccountNumber As String = ItemEx(oRow, V50_ACCOUNTNUMBER, String.Empty) Dim oAccountName As String = ItemEx(oRow, V50_ACCOUNTNAME, String.Empty) Dim oStreetName As String = ItemEx(oRow, V50_STREETNAME, String.Empty) Dim oZipCode As String = ItemEx(oRow, V50_ZIPCODE, String.Empty) Dim oCityName As String = ItemEx(oRow, V50_CITYNAME, String.Empty) Return New Account With { .Id = oAccountNumber, .Name = oAccountName, .StreetName = oStreetName, .CityName = oCityName, .ZipCode = oZipCode, .Mandator = pMandator } Catch ex As Exception Logger.Warn("Error while trying to get account for GLN [{0}]", pGLN) Logger.Error(ex) Return Nothing End Try End Function Public Function TryGetArticleNumber(pEAN As String, pMandator As Mandator) As String Try Dim oYear As Integer = GetWinLineYear() Dim oSQL As String = $" SELECT [c011], -- Artikelnummer [c003], -- Artikelbezeichnung [c075], -- EAN-Nummer [c123] -- Ersatzartikelnummer FROM [{pMandator.Database}].[dbo].[v021] WHERE [c075] = '{pEAN}' AND [mesocomp] = '{pMandator.Id}' AND [mesoyear] = {oYear}" Dim oTable As DataTable = Database.GetDatatable(oSQL) ' EAN not found in this Mandator, continue to next one If oTable.Rows.Count = 0 Then Logger.Debug("EAN [{0}] was not found in Mandator: [{1}]", pEAN, pMandator.Id) Return Nothing End If ' Duplicate EAN, exit and do nothing about this number If oTable.Rows.Count > 1 Then Logger.Warn("EAN [{0}] was found more than once", pEAN) Return Nothing End If Dim oRow As DataRow = oTable.Rows.Item(0) Dim oArticleNumber As String = ItemEx(oRow, V21_MAINARTICLENUMBER, String.Empty) Return oArticleNumber Catch ex As Exception Logger.Error(ex) Return Nothing End Try End Function Public Function GetContacts(pAccountNumber As String, pMandator As Mandator) As List(Of Contact) Try Dim oContacts As New List(Of Contact) Dim oYear As Integer = GetWinLineYear() Dim oSQL As String = $" SELECT [c000], -- Key [c001], -- Name [c063] -- Kontaktnummer FROM [{pMandator.Database}].[dbo].[t045] WHERE [c063] LIKE '{pAccountNumber}-%' AND [mesocomp] = '{pMandator.Id}' AND [mesoyear] = {oYear}" Dim oTable As DataTable = Database.GetDatatable(oSQL) ' Contact not found in this Mandator, continue to next one If oTable.Rows.Count = 0 Then Logger.Debug("Contact for Account [{0}] was not found in Mandator: [{1}]", pAccountNumber, pMandator.Id) Return Nothing End If For Each oRow In oTable.Rows oContacts.Add(New Contact With { .Id = ItemEx(Of String)(oRow, T45_KEY, Nothing), .Name = ItemEx(Of String)(oRow, T45_NAME, Nothing), .Number = ItemEx(Of String)(oRow, T45_CONTACTNUMBER, Nothing) }) Next Return oContacts Catch ex As Exception Logger.Error(ex) Return Nothing End Try End Function Public Function GetReplacementArticleNumber(pArticleNumber As String, pMandator As Mandator) Try Dim oYear As Integer = GetWinLineYear() Dim oSQL As String = $" SELECT [c011], -- Artikelnummer [c003], -- Artikelbezeichnung [c075], -- EAN-Nummer [c123] -- Ersatzartikelnummer FROM [{pMandator.Database}].[dbo].[v021] WHERE [c011] = '{pArticleNumber}' AND [mesocomp] = '{pMandator.Id}' AND [mesoyear] = {oYear}" Dim oTable As DataTable = Database.GetDatatable(oSQL) ' ArticleNumber not found in this Mandator, continue to next one If oTable.Rows.Count = 0 Then Logger.Debug("ArticleNumber [{0}] was not found in Mandator: [{1}]", pArticleNumber, pMandator.Id) Return Nothing End If ' Duplicate EAN, exit and do nothing about this number If oTable.Rows.Count > 1 Then Logger.Warn("ArticleNumber [{0}] was found more than once", pArticleNumber) Return Nothing End If Dim oRow As DataRow = oTable.Rows.Item(0) Dim oReplacementArticleNumber = ItemEx(Of String)(oRow, V21_REPLACEMENTARTICLENUMBER, Nothing) If oReplacementArticleNumber Is Nothing Then Return pArticleNumber End If Return GetReplacementArticleNumber(oReplacementArticleNumber, pMandator) Catch ex As Exception Logger.Error(ex) Return Nothing End Try End Function Public Function FindMatchingMandatorFromOrder(pData As Documents.Document) As Mandator Dim oPositions = pData.Rows. Where(Function(r) r.Name.ToUpper.EndsWith("T026")). ToList() Dim oEANNumbers = oPositions. Select(Function(p) If p.Fields.ContainsKey("Artikelnummer") Then Return p.Fields.Item("Artikelnummer").Original Else Return Nothing End If End Function). Where(Function(ean) ean IsNot Nothing). Distinct(). ToList() Dim oYear = GetWinLineYear() Dim oMandatorId As String = String.Empty ' TODO: Instead load whitelisted mandators from database Dim oWhitelistedMandators = Mandators. Where(Function(m) m.IsWhitelisted = True). OrderBy(Function(m) m.Order). ToList() For Each oEANNumber In oEANNumbers For Each oMandator In oWhitelistedMandators Dim oSQL As String = $" SELECT [c011], -- Artikelnummer [c003], -- Artikelbezeichnung [c075], -- EAN-Nummer [c123] -- Ersatzartikelnummer FROM [{oMandator.Database}].[dbo].[v021] WHERE [c075] = '{oEANNumber}' AND [mesocomp] = '{oMandator.Id}' AND [mesoyear] = {oYear}" Dim oTable As DataTable = Database.GetDatatable(oSQL) ' EAN not found in this Mandator, continue to next one If oTable.Rows.Count = 0 Then Logger.Debug("EAN [{0}] was not found in Mandator: [{1}]", oEANNumber, oMandator.Id) Continue For End If ' Duplicate EAN, exit and do nothing about this manda If oTable.Rows.Count > 1 Then Logger.Warn("EAN [{0}] was found more than once. Skipping Mandator [{1}]", oEANNumber, oMandator.Id) Exit For End If Dim oRow As DataRow = oTable.Rows.Item(0) Dim oArticleNumber As String = ItemEx(oRow, V21_MAINARTICLENUMBER, String.Empty) ' EAN was found, now we need to check it against the Regex of the current Mandantor, if one exists Dim oMappingConfigItem = MappingConfig.Items. Where(Function(item) item.DestinationName = "MANDATOR" And item.DestinationValue = oMandator.Id And item.SourceName = "ARTICLE"). SingleOrDefault() If oMappingConfigItem IsNot Nothing Then Try Dim oRegex As New Regex(oMappingConfigItem.SourceRegex) Dim oMatch = oRegex.Match(oArticleNumber) ' If ArticleNumber matches the regex, we assign it and exit If oMatch.Success Then Return oMandator Else ' If it does not match, continue to the next mandator End If Catch ex As Exception Logger.Error(ex) Logger.Warn("Regex [{0}] could not be parsed. Skipping.", oMandator.Regex) End Try Else Continue For End If Next Next Return Nothing End Function ''' ''' Turns a database info like "SQLCWLDATEN on SERVER\INSTANCE" into a Tuple of two strings ''' ''' ''' Private Function SplitConnectionInfo(pRow As DataRow) As Tuple(Of String, String) Dim oDbInfo = pRow.Item(T01_DATABASEINFO).ToString() Dim oSplittedInfo = SplitAtString(oDbInfo.ToUpper, "ON") Dim oServer = oSplittedInfo.Item(1).Trim() Dim oDatabase = oSplittedInfo.Item(0).Trim() If oDatabase.StartsWith("SQL") Then oDatabase = oDatabase.Remove(0, 3) End If Return New Tuple(Of String, String)(oDatabase, oServer) End Function Private Function SplitAtString(pStringToSplit As String, pDelimiter As String) As List(Of String) Dim oDelimiter As String() = New String(0) {pDelimiter} Return pStringToSplit. Split(oDelimiter, StringSplitOptions.None). ToList() End Function End Class End Namespace