2023-11-07 10:19:30 +01:00

815 lines
36 KiB
VB.net

Imports System.IO
Imports System.Text.RegularExpressions
Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.Language
Imports MultiTool.Common.Exceptions
Imports MultiTool.Common.Templates
Imports MultiTool.Common.Winline
Imports MultiTool.Common.Winline.Entities
Imports MultiTool.Common.Constants
Imports DigitalData.Modules.Database
Imports DevExpress.Utils.CommonDialogs
Imports DigitalData.Modules.Base
Namespace Documents
Public Class DocumentLoader
Inherits BaseClass
Private ReadOnly Winline As WinlineData
Private ReadOnly MappingConfig As MappingConfig
Private ReadOnly TemplateConfig As TemplateConfig
Private ReadOnly ApplicationConfig As Config
Private ReadOnly Patterns As Patterns
Public Property Files As New List(Of Document)
Public Property FilesTotal As Integer = 0
Public Property FilesLoaded As Integer = 0
Public Event FileLoadComplete As EventHandler(Of FileLoadInfo)
Public Event FileLoadProgress As EventHandler(Of FileLoadProgressInfo)
Public Class FileLoadInfo
Public FilesLoaded As Integer
Public FilesTotal As Integer
Public Sub New(pTotal As Integer, pLoaded As Integer)
FilesTotal = pTotal
FilesLoaded = pLoaded
End Sub
End Class
Public Class FileLoadProgressInfo
Inherits FileLoadInfo
Public RunningFunction As String
Public Sub New(pTotal As Integer, pLoaded As Integer)
MyBase.New(pTotal, pLoaded)
End Sub
End Class
Public Sub New(pLogConfig As LogConfig, pWinline As WinlineData, pDatabase As MSSQLServer, pMappingConfig As MappingConfig, pTemplateConfig As TemplateConfig, pGeneralConfig As GeneralConfig, pApplicationConfig As Config)
MyBase.New(pLogConfig, pDatabase)
Winline = pWinline
MappingConfig = pMappingConfig
TemplateConfig = pTemplateConfig
ApplicationConfig = pApplicationConfig
Patterns = New Patterns(pLogConfig, pGeneralConfig)
End Sub
Public Async Function LoadFiles(pTemplate As Template, pMandator As Mandator) As Task(Of Boolean)
Logger.Info("Loading files from directory [{0}]", pTemplate.InputDirectory)
Files.Clear()
Try
Dim oDirectory As New DirectoryInfo(pTemplate.InputDirectory)
Dim oFiles = oDirectory.GetFiles()
FilesTotal = oFiles.Count
Logger.Debug("Found [{0}] files in directory [{1}]", oFiles.Count, oDirectory)
For Each oFile In oFiles
Logger.Info("Loading file [{0}]", oFile.Name)
Dim oDocument = Await LoadFile(oFile, pTemplate, pMandator)
Files.Add(oDocument)
FilesLoaded = Files.Count
Dim oInfo As New FileLoadInfo(FilesTotal, FilesLoaded)
RaiseEvent FileLoadComplete(Me, oInfo)
Next
Return True
Catch ex As Exception
Logger.Error(ex)
Throw ex
End Try
End Function
Public Async Function LoadFile(pFileInfo As FileInfo, pTemplate As Template, pMandator As Mandator) As Task(Of Document)
Logger.Debug("Creating new Document object for file [{0}]", pFileInfo.Name)
Dim oDocument As New Document With {
.File = pFileInfo,
.Schema = pTemplate
}
Try
oDocument = LoadDocumentData(oDocument, pTemplate, TemplateConfig)
Catch ex As Exception
Logger.Error(ex)
Throw ex
End Try
Try
oDocument = Await MatchDataFromWinLine(oDocument, Winline.Mandators, pMandator, pTemplate)
Catch ex As Exception
Logger.Error(ex)
Throw ex
End Try
Try
oDocument = MarkRequiredFields(oDocument)
Catch ex As Exception
Logger.Error(ex)
Throw ex
End Try
Return oDocument
End Function
Public Function MarkRequiredFields(pDocument As Document) As Document
For Each oRow In pDocument.Rows
For Each oField In oRow.Fields
If oField.Value.Final = String.Empty And oField.Value.IsRequired Then
oField.Value.AddFieldError(FieldErrorType.MissingValue, $"Attribut '{oField.Key}' ist ein Pflichtfeld, wurde aber nicht gefüllt.")
End If
Next
Next
Return pDocument
End Function
Public Sub ReplaceDocument(pDocument As Document)
Dim oIndex = Files.IndexOf(pDocument)
Files.Item(oIndex) = pDocument
End Sub
Private Function IncludeSchema(pDocument As Document, pTemplate As Template) As Document
Logger.Debug("Adding schema to Document object.")
pDocument.Schema = pTemplate
Return pDocument
End Function
''' <summary>
''' Loads a single document from the FullName Property in the Document Object
''' </summary>
''' <example>
''' A document might look like this:
''' <MESOWebService>
''' <Row1></Row1>
''' <Row2></Row2>
''' <Row3></Row3>
''' </MESOWebService>
''' </example>
Private Function LoadDocumentData(pDocument As Document, pTemplate As Template, pTemplateConfig As TemplateConfig) As Document
Logger.Debug("Loading file contents")
Dim oText As String
Dim oDoc As XDocument
Try
Logger.Debug("Loading file from fs..")
oText = IO.File.ReadAllText(pDocument.FullName)
Logger.Debug("Parsing file..")
oDoc = XDocument.Parse(oText)
Catch ex As Exception
Logger.Error(ex)
Throw ex
End Try
Logger.Debug("File Loaded.")
Dim oRootElement As XElement = XmlData.GetElement(oDoc, "MESOWebService")
If oRootElement Is Nothing Then
pDocument.AddDocumentError(DocumentErrorType.MissingXmlAttribute, "Datei enthält kein MESOWebService-Attribut")
End If
Dim oTemplateName = XmlData.GetElementAttribute(oRootElement, "Template")
If oTemplateName Is Nothing Then
pDocument.AddDocumentError(DocumentErrorType.MissingXmlAttribute, "Datei enthält kein Template-Attribut")
End If
Dim oTemplateType = XmlData.GetElementAttribute(oRootElement, "TemplateType")
If oTemplateType Is Nothing Then
pDocument.AddDocumentError(DocumentErrorType.MissingXmlAttribute, "Datei enthält kein TemplateType-Attribut")
End If
Dim oOption = XmlData.GetElementAttribute(oRootElement, "option")
If oOption Is Nothing Then
pDocument.AddDocumentError(DocumentErrorType.MissingXmlAttribute, "Datei enthält kein option-Attribut")
End If
Dim oPrintVoucher = XmlData.GetElementAttribute(oRootElement, "printVoucher")
If oPrintVoucher Is Nothing Then
pDocument.AddDocumentError(DocumentErrorType.MissingXmlAttribute, "Datei enthält kein printVoucher-Attribut")
End If
' The first level of Elements are the document Rows
Dim oTopLevelElements As List(Of XElement) = oRootElement.Elements.ToList
Dim oDocumentRows As New List(Of DocumentRow)
Dim oRowSortKey As Integer = 0
' TODO: Somehow add all fields in the correct order
'
' Right now, the method of
' - first the filled field from xml
' - then the rest from schema
'
' leads to unordered fields.
For Each oTopLevelElement As XElement In oTopLevelElements
Dim oColumnSortKey = 0
Dim oFields As New Dictionary(Of String, DocumentRow.FieldValue)
Dim oSubElements = oTopLevelElement.Descendants().ToList()
Dim oTable = pTemplate.Tables.
Where(Function(t) t.Name = oTopLevelElement.Name).
FirstOrDefault()
Logger.Debug("Creating fields from [{0}] columns for Table [{1}]", oTable.Columns.Count, oTable.Name)
For Each oColumn In oTable.Columns
Dim oQuery = oSubElements.Where(Function(e) e.Name = oColumn.Name)
Dim oElementCount = oQuery.Count()
Dim oSubElement = oQuery.FirstOrDefault()
If oElementCount > 1 Then
Logger.Warn("Found [{0}] elements for column [{1}]. Using first occurrence.")
End If
If oSubElement IsNot Nothing Then
Dim oRequired = oColumn.IsRequired
Dim oValue = oSubElement.Value.Trim()
Logger.Debug("Creating existing field from Element: [{0}]", oSubElement.Name.ToString)
' TODO: Needed when we have time for date times
'If oTemplateField.DataType = Constants.ColumnType.Date Then
' Dim oDate = Date.ParseExact(oValue, "yyyy-MM-dd", CultureInfo.InvariantCulture)
' oValue = oDate.ToString("d")
'End If
Dim oFieldValue = GetFieldValueFromColumn(oColumn, oColumnSortKey)
oFieldValue.SetOriginalValue(oValue)
oFields.Add(oSubElement.Name.ToString, oFieldValue)
Else
Logger.Debug("Creating new field from Configuration: [{0}]", oColumn.Name)
Dim oFieldValue = GetFieldValueFromColumn(oColumn, oColumnSortKey)
If oColumn.Config?.IsRequired Then
oFieldValue.AddFieldError(FieldErrorType.MissingValue, $"Attribut {oSubElement.Name} wird benötigt, ist aber nicht gefüllt.")
End If
oFields.Add(oColumn.Name, oFieldValue)
End If
oColumnSortKey += 1
Next
' Create a DocumentRow object for each Top Level Element
Dim oRow = New DocumentRow With {
.SortKey = oRowSortKey,
.TableName = oTopLevelElement.Name.ToString,
.Fields = oFields
}
oRowSortKey += 1
oDocumentRows.Add(oRow)
Next
' Update the document
pDocument.TemplateName = oTemplateName
pDocument.TemplateType = oTemplateType
pDocument.Option = oOption
pDocument.PrintVoucher = oPrintVoucher
pDocument.Rows = oDocumentRows
Return pDocument
End Function
Public Function GetFieldValueFromColumn(pColumn As Template.Column, pSortKey As Integer) As DocumentRow.FieldValue
Return New DocumentRow.FieldValue(LogConfig) With {
.DataType = pColumn.DataType,
.IsRequired = pColumn.IsRequired,
.IsVirtual = pColumn.Config.IsVirtual,
.PreferExternalValue = pColumn.Config.PreferExternalValue,
.SortKey = pSortKey
}
End Function
Private Async Function MatchDataFromWinLine(pDocument As Document, pMandators As List(Of Mandator), pMandator As Mandator, pTemplate As Template) As Task(Of Document)
Dim oMandators As List(Of Mandator) = pMandators.
Where(Function(m) m.IsWhitelisted = True).
OrderBy(Function(m) m.Order).
ToList()
Dim oInfo As New FileLoadProgressInfo(FilesTotal, FilesLoaded) With {.RunningFunction = "Mandant finden"}
RaiseEvent FileLoadProgress(Me, oInfo)
Dim oMandator As Mandator = Nothing
If pMandator IsNot Nothing Then
oMandator = pMandator
Else
oMandator = Winline.FindMatchingMandatorFromOrder(pDocument)
End If
If oMandator Is Nothing Then
Logger.Warn("Mandator not found for File [{0}]", pDocument.File.Name)
' Without mandator, we just exit, life is meaningless.
pDocument.AddDocumentError(DocumentErrorType.MandatorNotFound, "Mandant nicht gefunden. Verarbeitung wurde abgebrochen.")
Else
' Set mandator befor applying any functions that depend on a valid mandator
pDocument.Mandator = oMandator
RaiseEvent FileLoadProgress(Me, New FileLoadProgressInfo(FilesTotal, FilesLoaded) With {.RunningFunction = "Winline-Funktionen"})
pDocument = Await ApplyDefinedItemFunctionsForImportAsync(pDocument, oMandator, pTemplate)
pDocument = ApplyDynamicItemFunctionsForImport(pDocument, oMandator)
RaiseEvent FileLoadProgress(Me, New FileLoadProgressInfo(FilesTotal, FilesLoaded) With {.RunningFunction = "Preis-Funktionen"})
If ApplicationConfig.AutomaticPriceCalculation = True Then
' These functions will only be applied if the document does not have errors
pDocument = Await MaybeApplyPriceFunctions(pDocument, oMandator, pTemplate)
End If
RaiseEvent FileLoadProgress(Me, New FileLoadProgressInfo(FilesTotal, FilesLoaded) With {.RunningFunction = "Feld-Funktionen"})
pDocument = ApplySQLFunctionForImport(pDocument, TemplateConfig.SqlItems)
' This function needs to be the last one because
' it can relate to any previously set value
pDocument = ApplyFieldFunctionForImport(pDocument, oMandator, pTemplate)
End If
Return pDocument
End Function
Private Function ApplySQLFunctionForImport(pDocument As Document, pSQLConfig As List(Of FieldConfig)) As Document
For Each oSQLConfigItem In pSQLConfig
For Each oFunction In oSQLConfigItem.Functions
If Not oFunction.Name = FUNCTION_SQL Then
Continue For
End If
' FieldList is a list of fields that will be changed
' Example: Setting SQL for Article StorageLocation will invoke the sql for each row
Dim oRowList = pDocument.Rows.
Where(Function(row) row.Fields.Any(Function(field) field.Key = oSQLConfigItem.Name)).
ToList()
For Each oRow As DocumentRow In oRowList
Dim oSQL = oFunction.Params
Dim oField = oRow.Fields.
Where(Function(field) field.Key = oSQLConfigItem.Name).
SingleOrDefault()
oSQL = Patterns.ReplaceForImport(pDocument, oRow, oSQL)
Dim oValue = Database.GetScalarValue(oSQL)
If oValue IsNot Nothing Then
oField.Value.SetExternalValue(oValue)
End If
Next
Next
Next
Return pDocument
End Function
''' <summary>
''' Apply price calculation to the documents products
'''
''' This needs to be strictly seperated from `ApplyDefinedItemFunctionsForImport`
''' because prices can only be calculated if GLN and EAN functions were already (successfully) processed
''' </summary>
Public Async Function MaybeApplyPriceFunctions(pDocument As Document, pMandator As Mandator, pTemplate As Template) As Task(Of Document)
If pDocument.HasErrors Then
pDocument.AddDocumentError(DocumentErrorType.PriceNotCalculated, "Der Preis konnte für mindestens eine Zeile nicht ermittelt werden, weil es noch Fehler im Dokument gibt")
Return pDocument
End If
For Each oRow As DocumentRow In pDocument.Rows
Dim oTable = pTemplate.Tables.Where(Function(table) table.Name = oRow.TableName).SingleOrDefault()
For Each oField In oRow.Fields
If oTable Is Nothing Then
Exit For
End If
Dim oColumn = oTable.Columns.Where(Function(c) c.Name = oField.Key).SingleOrDefault()
If oColumn Is Nothing Then
Continue For
End If
For Each oFunction As FieldConfig.ColumnFunction In oColumn.Config.Functions
Dim oFunctionName = oFunction.Name
Dim oFunctionParams = oFunction.Params
If oFunctionName = String.Empty Then
Continue For
End If
Logger.Debug("Running Function: [{0}]", oFunctionName)
Logger.Debug("With Parameters: [{0}]", oFunctionParams)
Dim oParamsDict = Parameters.Parse(oFunctionParams)
If oFunctionName = Constants.FUNCTION_PRICE Then
Await SetPrice(oRow, oField.Key, oParamsDict, pDocument, pMandator, pTemplate)
End If
Next
Next
Next
Return pDocument
End Function
Private Async Function ApplyDefinedItemFunctionsForImportAsync(pDocument As Document, pMandator As Mandator, pTemplate As Template) As Task(Of Document)
For Each oRow As DocumentRow In pDocument.Rows
Dim oTable = pTemplate.Tables.Where(Function(table) table.Name = oRow.TableName).SingleOrDefault()
For Each oField In oRow.Fields
If oTable Is Nothing Then
Exit For
End If
Dim oItemName As String = oField.Key
Dim oColumn = oTable.Columns.Where(Function(c) c.Name = oItemName).SingleOrDefault()
If oColumn Is Nothing Then
Continue For
End If
For Each oFunction As FieldConfig.ColumnFunction In oColumn.Config.Functions
Dim oFunctionName = oFunction.Name
Dim oFunctionParams = oFunction.Params
' The code below needs a defined function
If oFunctionName = String.Empty Then
Continue For
End If
Dim oParamsDict = Parameters.Parse(oFunctionParams)
' The main identifier will be checked for String.empty and not required.
' This makes sure that optional fields do not generate errors.
Dim oIdentifier As DocumentRow.FieldValue = oRow.Fields.GetOrDefault(oItemName)
If oIdentifier.Original = String.Empty And oIdentifier.IsRequired = False Then
Continue For
End If
Select Case oFunctionName
Case FUNCTION_GLN
SetAccountByGLN(oRow, pMandator, oItemName, Nothing, oParamsDict)
Case FUNCTION_EAN
SetArticleByEAN(oRow, pMandator, oItemName)
Case FUNCTION_RUNNINGNUMBER
Await SetVersionedRunningNumber(pDocument, oRow, pMandator, oItemName, oParamsDict)
Case FUNCTION_ADDRESS
Await SetAddressByAccountNumber(pDocument, oRow, pMandator, oItemName, oParamsDict)
End Select
Next
Next
Next
Return pDocument
End Function
Private Function ApplyFieldFunctionForImport(pDocument As Document, pMandator As Mandator, pTemplate As Template) As Document
For Each oRow As DocumentRow In pDocument.Rows
Dim oTable = pDocument.Schema.Tables.Where(Function(table) table.Name = oRow.TableName).SingleOrDefault()
For Each oField In oRow.Fields
If oTable Is Nothing Then
Logger.Warn("Table [{0}] was not found in the Schema. Exiting.", oRow.TableName)
Exit For
End If
Dim oColumn = oTable.Columns.Where(Function(c) c.Name = oField.Key).SingleOrDefault()
If oColumn Is Nothing Then
Logger.Warn("Column [{0}] was not found in Table [{0}]. Skipping.", oField.Key, oTable.Name)
Continue For
End If
For Each oFunction As FieldConfig.ColumnFunction In oColumn.Config.Functions
Dim oFunctionName = oFunction.Name
Dim oFunctionParams = oFunction.Params
Dim oParamsDict = Parameters.Parse(oFunctionParams)
If oFunctionName = FUNCTION_FIELD Then
Try
Logger.Debug("Applying function FIELD to field [{0}]", oField.Key)
Dim oParam = oParamsDict.FirstOrDefault()
If IsNothing(oParam) Then
Logger.Warn("Function FIELD needs exactly one parameter. Skipping")
Continue For
End If
Dim oFieldName = oParam.Key
Dim oSubKey = oParam.Value
Dim oReferencedField = oRow.Fields.
Where(Function(field) field.Key = oFieldName).
FirstOrDefault()
If IsNothing(oReferencedField) = False Then
Dim oRawValue = oReferencedField.Value?.GetValue(oSubKey)
Dim oValue As String = ObjectEx.NotNull(oRawValue, String.Empty)
If oValue <> String.Empty Then
oField.Value.SetExternalValue(oValue)
End If
Else
Logger.Warn("Referenced Field [{0}] was not found. Skipping.", oFieldName)
Continue For
End If
Catch ex As Exception
Logger.Warn("Function FIELD could not be applied to field [{0}]. Skipping.", oField.Key)
Continue For
End Try
End If
Next
Next
Next
Return pDocument
End Function
''' <summary>
''' Execute Mappings defined in TBMT_MAPPING_CONFIG,
''' for example mapping Article Numbers to Winline Mandators
''' </summary>
Private Function ApplyDynamicItemFunctionsForImport(pDocument As Document, pMandator As Mandator) As Document
' We only want the mapping config for things in the xml file.
' that excludes things like setting the mandator.
Dim oFilteredMappingConfig = MappingConfig.Items.
Where(Function(item) item.DestinationItem <> String.Empty).
ToList()
For Each oMapping As MappingConfigItem In oFilteredMappingConfig
' Get Source Value
Dim oField As KeyValuePair(Of String, DocumentRow.FieldValue) = pDocument.Rows.
SelectMany(Function(row) row.Fields).
Where(Function(field) field.Key = oMapping.SourceItem).
FirstOrDefault()
' Test on Regex
Dim oRegex As New Regex(oMapping.SourceRegex)
If oRegex.IsMatch(oField.Value.Final) Then
pDocument.Rows.
SelectMany(Function(row) row.Fields).
Where(Function(field) field.Key = oMapping.DestinationItem).
SetValue(Sub(field) field.Value.SetExternalValue(oMapping.DestinationValue))
Else
' don't do anything
End If
Next
Return pDocument
End Function
Private Async Function SetPrice(pRow As DocumentRow, pPriceField As String, pParamMap As Dictionary(Of String, String), pDocument As Document, pMandator As Mandator, pTemplate As Template) As Task
Dim oPriceItem As DocumentRow.FieldValue = pRow.Fields.GetOrDefault(pPriceField)
' These fields are fetched from the current row
Const PARAMETER_ARTICLE = "Article"
Dim oArticleNumberField As String = pParamMap.GetOrDefault(PARAMETER_ARTICLE, Nothing)
If oArticleNumberField Is Nothing Then
Logger.Warn("Parameter '{0}' not found for Function PRICE", PARAMETER_ARTICLE)
End If
If pRow.Fields.ContainsKey(oArticleNumberField) = False Then
Logger.Warn("Value '{0}' for Parameter '{1}' not found for Function PRICE", oArticleNumberField, PARAMETER_ARTICLE)
End If
Dim oArticleNumber As String = pRow.Fields.Item(oArticleNumberField).Final
Const PARAMETER_QUANTITY = "Quantity"
Dim oQuantityField As String = pParamMap.GetOrDefault(PARAMETER_QUANTITY, Nothing)
If oQuantityField Is Nothing Then
Logger.Warn("Parameter '{0}' not found for Function PRICE", PARAMETER_QUANTITY)
End If
Dim oQuantity As Integer = 1
If Integer.TryParse(pRow.Fields.Item(oQuantityField).Final, oQuantity) = False Then
Logger.Warn("Value for parameter '{0}' could not be parsed. Setting to 1.", PARAMETER_QUANTITY)
End If
' These fields a fetched from the head row, ie. the first row
Dim oAccountNumberField As String = pParamMap.GetOrDefault("Account", Nothing)
Dim oAccountNumber = pDocument.GetFieldValue(oAccountNumberField)
Dim oDocumentDateField As String = pParamMap.GetOrDefault("DocumentDate", Nothing)
Dim oDocumentDate = pDocument.GetFieldValue(oDocumentDateField)
Dim oDocumentKindField As String = pParamMap.GetOrDefault("DocumentKind", Nothing)
Dim oDocumentKind As Integer = 0
If Integer.TryParse(pDocument.GetFieldValue(oDocumentKindField), oDocumentKind) = False Then
Logger.Warn("Value for parameter DocumentKind could not be parsed. Setting to 0.")
End If
' TODO: Add Field Names as Constants
' TODO: Check for missing values
' TODO: This function should not be hardcoded, but be replaced with virtual field or something..
' It is related to customer SCHAUM and nothing general
Dim oWaitingDays As Integer = Await Winline.TryGetWaitingDaysAsync(oDocumentKind, pMandator)
' END TODO
Dim oArticlePrice As Double = Await Winline.TryGetArticlePriceAsync(oArticleNumber, oAccountNumber, oQuantity, oDocumentDate, pMandator, pTemplate, oWaitingDays)
If oArticlePrice > 0 Then
oPriceItem.SetExternalValue(oArticlePrice)
Logger.Info("Price for Item [{0}] set to [{1}]", pPriceField, oArticlePrice)
Else
Logger.Warn("Price for Item [{0}] could not be found!", pPriceField)
oPriceItem.AddFieldError(FieldErrorType.PriceNotCalculated, "Der Preis für diese Position konnte nicht ermittelt werden.")
End If
End Function
Private Sub SetArticleByEAN(pRow As DocumentRow, pMandator As Mandator, pArticleField As String)
Dim oNumberItem As DocumentRow.FieldValue = pRow.Fields.GetOrDefault(pArticleField)
If oNumberItem Is Nothing Then
Exit Sub
End If
Dim oArticleNumber = Winline.TryGetArticleNumber(oNumberItem.Original, pMandator)
If oArticleNumber IsNot Nothing Then
oNumberItem.SetExternalValue(oArticleNumber)
Logger.Info("EAN [{0}] resolved to ArticleNumber [{1}]", oNumberItem.Original, oArticleNumber)
Else
oNumberItem.AddFieldError(FieldErrorType.ArticleNotFound, $"EAN in Attribut '{pArticleField}' konnte nicht aufgelöst werden.")
Logger.Warn("EAN [{0}] could not be resolved ArticleNumber", oNumberItem.Original)
End If
End Sub
Private Sub SetAccountByGLN(oRow As DocumentRow, pMandator As Mandator, pNumberField As String, pNameField As String, pParams As Dictionary(Of String, String))
Try
' Try to read the Account number (which is a GLN really) and account Name
Dim oNumberItem As DocumentRow.FieldValue = oRow.Fields.GetOrDefault(pNumberField)
Dim oNameItem As DocumentRow.FieldValue = oRow.Fields.GetOrDefault(pNameField)
Dim oContainsAccountName As Boolean = Not IsNothing(oNameItem)
If oNumberItem Is Nothing Then
Exit Sub
End If
' Try to find an account that matches the GLN
Dim oAlternateField As String = pParams.GetOrDefault("AltField", String.Empty)
Dim oAccount As Account = Winline.TryGetAccount(oNumberItem.Original, pMandator, "c260", oAlternateField)
' If an account was found, set it for External and Final value
If oAccount IsNot Nothing Then
oNumberItem.SetExternalValue(oAccount.Id)
If oContainsAccountName Then
oNameItem.SetExternalValue(oAccount.Name)
Else
' TODO: What to to if name field is missing or not set?
'oRow.Fields.Add(pNameField, New DocumentRow.FieldValue() With {
' .External = oAccount.Name,
' .Final = oAccount.Name
'})
End If
Else
' If no account was found, mark it as error.
oNumberItem.AddFieldError(FieldErrorType.AccountNotFound, $"GLN in Attribut '{pNumberField}' konnte nicht aufgelöst werden.")
End If
Catch ex As Exception
Logger.Error(ex)
Throw ex
End Try
End Sub
Public Async Function SetVersionedRunningNumber(pDocument As Document, pRow As DocumentRow, pMandator As Mandator, pNumberField As String, pParams As Dictionary(Of String, String)) As Task
Try
Const PARAMETER_ACCOUNT = "Account"
Dim oAccountField As String = pParams.GetOrDefault(PARAMETER_ACCOUNT, Nothing)
If oAccountField Is Nothing Then
Logger.Warn("Parameter '{0}' not found for Function RUNNINGNUMBER", PARAMETER_ACCOUNT)
End If
Dim oRunningNumberItem As DocumentRow.FieldValue = pRow.Fields.GetOrDefault(pNumberField)
Dim oRunningNumber = oRunningNumberItem.Original
' We use the Final value to work with the actual account number, not the GLN
Dim oAccountNumberItem As DocumentRow.FieldValue = pRow.Fields.GetOrDefault(oAccountField)
Dim oAccountNumber = oAccountNumberItem.Final
Dim oVersionedNumber = Await Winline.GetVersionedRunningNumberAsync(pDocument, pMandator, oAccountNumber, oRunningNumber)
If oVersionedNumber <> oRunningNumber Then
oRunningNumberItem.SetExternalValue(oVersionedNumber)
End If
Catch ex As Exception
Logger.Error(ex)
Throw ex
End Try
End Function
Public Function SetAddressByAccountNumber(pDocument As Document, pRow As DocumentRow, pMandator As Mandator, pAccountField As String, pParams As Dictionary(Of String, String)) As Task
Try
Const PARAMETER_NAME = "Name"
Const PARAMETER_STREET = "Street"
Const PARAMETER_ZIP = "Zip"
Const PARAMETER_CITY = "City"
Dim oAccountNumberItem As DocumentRow.FieldValue = pRow.Fields.GetOrDefault(pAccountField)
Dim oAccountNumber = oAccountNumberItem.Final
Dim oNameField As String = pParams.GetOrDefault(PARAMETER_NAME, Nothing)
Dim oNameFieldItem As DocumentRow.FieldValue = pRow.Fields.GetOrDefault(oNameField)
If oNameField Is Nothing Then
Logger.Warn("Parameter '{0}' not found for Function ADDRESS", PARAMETER_NAME)
oAccountNumberItem.AddFieldError(FieldErrorType.MissingParameter, $"Parameter '{PARAMETER_NAME}' wurde nicht gefüllt.")
End If
Dim oStreetField As String = pParams.GetOrDefault(PARAMETER_STREET, Nothing)
Dim oStreetFieldItem As DocumentRow.FieldValue = pRow.Fields.GetOrDefault(oStreetField)
If oStreetField Is Nothing Then
Logger.Warn("Parameter '{0}' not found for Function ADDRESS", PARAMETER_STREET)
oAccountNumberItem.AddFieldError(FieldErrorType.MissingParameter, $"Parameter '{PARAMETER_STREET}' wurde nicht gefüllt.")
End If
Dim oZipField As String = pParams.GetOrDefault(PARAMETER_ZIP, Nothing)
Dim oZipFieldItem As DocumentRow.FieldValue = pRow.Fields.GetOrDefault(oZipField)
If oZipField Is Nothing Then
Logger.Warn("Parameter '{0}' not found for Function ADDRESS", PARAMETER_ZIP)
oAccountNumberItem.AddFieldError(FieldErrorType.MissingParameter, $"Parameter '{PARAMETER_ZIP}' wurde nicht gefüllt.")
End If
Dim oCityField As String = pParams.GetOrDefault(PARAMETER_CITY, Nothing)
Dim oCityFieldItem As DocumentRow.FieldValue = pRow.Fields.GetOrDefault(oCityField)
If oCityField Is Nothing Then
Logger.Warn("Parameter '{0}' not found for Function ADDRESS", PARAMETER_CITY)
oAccountNumberItem.AddFieldError(FieldErrorType.MissingParameter, $"Parameter '{PARAMETER_CITY}' wurde nicht gefüllt.")
End If
Dim oAccount = Winline.Accounts.Where(Function(a) a.Id = oAccountNumber And a.Mandator.Equals(pMandator)).SingleOrDefault()
If oAccount Is Nothing Then
Logger.Warn("Account with Id [{0}] in Mandator [{1}] could not be found.", oAccountNumber, pMandator.Id)
End If
oNameFieldItem.SetExternalValue(oAccount.Name)
oStreetFieldItem.SetExternalValue(oAccount.StreetName)
oZipFieldItem.SetExternalValue(oAccount.ZipCode)
oCityFieldItem.SetExternalValue(oAccount.CityName)
Catch ex As Exception
Logger.Error(ex)
Throw ex
End Try
Return Task.CompletedTask
End Function
End Class
End Namespace