Add support for multiple functions per field, add ADDRESS function

This commit is contained in:
Jonathan Jenne 2023-06-26 11:27:51 +02:00
parent 7846e660b9
commit 68e4c59e63
9 changed files with 274 additions and 154 deletions

View File

@ -9,6 +9,7 @@
Public Const FUNCTION_SQL = "SQL" Public Const FUNCTION_SQL = "SQL"
Public Const FUNCTION_FIELD = "FIELD" Public Const FUNCTION_FIELD = "FIELD"
Public Const FUNCTION_RUNNINGNUMBER = "RUNNINGNUMBER" Public Const FUNCTION_RUNNINGNUMBER = "RUNNINGNUMBER"
Public Const FUNCTION_ADDRESS = "ADDRESS"
Public Const PLACEHOLDER_CONST = "CONST" Public Const PLACEHOLDER_CONST = "CONST"
Public Const PLACEHOLDER_FIELD = "FIELD" Public Const PLACEHOLDER_FIELD = "FIELD"
@ -65,6 +66,8 @@
MissingValue MissingValue
AccountNotFound AccountNotFound
ArticleNotFound ArticleNotFound
PriceNotCalculated
MissingParameter
End Enum End Enum
Public Class FieldError Public Class FieldError

View File

@ -8,6 +8,7 @@ Imports MultiTool.Common.Winline
Imports MultiTool.Common.Winline.Entities Imports MultiTool.Common.Winline.Entities
Imports MultiTool.Common.Constants Imports MultiTool.Common.Constants
Imports DigitalData.Modules.Database Imports DigitalData.Modules.Database
Imports DevExpress.Utils.CommonDialogs
Namespace Documents Namespace Documents
Public Class DocumentLoader Public Class DocumentLoader
@ -88,7 +89,7 @@ Namespace Documents
Public Async Function LoadFile(pFileInfo As FileInfo, pTemplate As Template, pMandator As Mandator) As Task(Of Document) 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) Logger.Debug("Creating new Document object for file [{0}]", pFileInfo.Name)
Dim oDocument As Document = New Document With { Dim oDocument As New Document With {
.File = pFileInfo, .File = pFileInfo,
.Schema = pTemplate .Schema = pTemplate
} }
@ -349,6 +350,12 @@ Namespace Documents
Private Function ApplySQLFunctionForImport(pDocument As Document, pSQLConfig As List(Of FieldConfig)) As Document Private Function ApplySQLFunctionForImport(pDocument As Document, pSQLConfig As List(Of FieldConfig)) As Document
For Each oSQLConfigItem In pSQLConfig 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 ' FieldList is a list of fields that will be changed
' Example: Setting SQL for Article StorageLocation will invoke the sql for each row ' Example: Setting SQL for Article StorageLocation will invoke the sql for each row
Dim oRowList = pDocument.Rows. Dim oRowList = pDocument.Rows.
@ -356,7 +363,8 @@ Namespace Documents
ToList() ToList()
For Each oRow As DocumentRow In oRowList For Each oRow As DocumentRow In oRowList
Dim oSQL = oSQLConfigItem.Function.Params
Dim oSQL = oFunction.Params
Dim oField = oRow.Fields. Dim oField = oRow.Fields.
Where(Function(field) field.Key = oSQLConfigItem.Name). Where(Function(field) field.Key = oSQLConfigItem.Name).
SingleOrDefault() SingleOrDefault()
@ -368,7 +376,11 @@ Namespace Documents
If oValue IsNot Nothing Then If oValue IsNot Nothing Then
oField.Value.SetExternalValue(oValue) oField.Value.SetExternalValue(oValue)
End If End If
Next Next
Next
Next Next
Return pDocument Return pDocument
@ -400,8 +412,12 @@ Namespace Documents
Continue For Continue For
End If End If
Dim oFunctionName = oColumn.Config.FunctionName For Each oFunction As FieldConfig.ColumnFunction In oColumn.Config.Functions
Dim oFunctionParams = oColumn.Config.FunctionParams
Dim oFunctionName = oFunction.Name
Dim oFunctionParams = oFunction.Params
If oFunctionName = String.Empty Then If oFunctionName = String.Empty Then
Continue For Continue For
@ -418,6 +434,7 @@ Namespace Documents
End If End If
Next Next
Next Next
Next
Return pDocument Return pDocument
End Function End Function
@ -431,14 +448,17 @@ Namespace Documents
Exit For Exit For
End If End If
Dim oColumn = oTable.Columns.Where(Function(c) c.Name = oField.Key).SingleOrDefault() Dim oItemName As String = oField.Key
Dim oColumn = oTable.Columns.Where(Function(c) c.Name = oItemName).SingleOrDefault()
If oColumn Is Nothing Then If oColumn Is Nothing Then
Continue For Continue For
End If End If
Dim oFunctionName = oColumn.Config.FunctionName For Each oFunction As FieldConfig.ColumnFunction In oColumn.Config.Functions
Dim oFunctionParams = oColumn.Config.FunctionParams
Dim oFunctionName = oFunction.Name
Dim oFunctionParams = oFunction.Params
' The code below needs a defined function ' The code below needs a defined function
If oFunctionName = String.Empty Then If oFunctionName = String.Empty Then
@ -449,22 +469,28 @@ Namespace Documents
' The main identifier will be checked for String.empty and not required. ' The main identifier will be checked for String.empty and not required.
' This makes sure that optional fields do not generate errors. ' This makes sure that optional fields do not generate errors.
Dim oIdentifier As DocumentRow.FieldValue = oRow.Fields.GetOrDefault(oField.Key) Dim oIdentifier As DocumentRow.FieldValue = oRow.Fields.GetOrDefault(oItemName)
If oIdentifier.Original = String.Empty And oIdentifier.IsRequired = False Then If oIdentifier.Original = String.Empty And oIdentifier.IsRequired = False Then
Continue For Continue For
End If End If
Select Case oFunctionName Select Case oFunctionName
Case FUNCTION_GLN Case FUNCTION_GLN
SetAccountByGLN(oRow, pMandator, oField.Key, Nothing, oParamsDict) SetAccountByGLN(oRow, pMandator, oItemName, Nothing, oParamsDict)
Case FUNCTION_EAN Case FUNCTION_EAN
SetArticleByEAN(oRow, pMandator, oField.Key) SetArticleByEAN(oRow, pMandator, oItemName)
Case FUNCTION_RUNNINGNUMBER Case FUNCTION_RUNNINGNUMBER
Await SetVersionedRunningNumber(pDocument, oRow, pMandator, oField.Key, oParamsDict) Await SetVersionedRunningNumber(pDocument, oRow, pMandator, oItemName, oParamsDict)
Case FUNCTION_ADDRESS
Await SetAddressByAccountNumber(pDocument, oRow, pMandator, oItemName, oParamsDict)
End Select End Select
Next
Next Next
Next Next
@ -489,11 +515,13 @@ Namespace Documents
End If End If
Dim oFunctionName = oColumn.Config.FunctionName For Each oFunction As FieldConfig.ColumnFunction In oColumn.Config.Functions
Dim oFunctionParams = oColumn.Config.FunctionParams
Dim oFunctionName = oFunction.Name
Dim oFunctionParams = oFunction.Params
Dim oParamsDict = Parameters.Parse(oFunctionParams) Dim oParamsDict = Parameters.Parse(oFunctionParams)
If oFunctionName = Constants.FUNCTION_FIELD Then If oFunctionName = FUNCTION_FIELD Then
Try Try
Logger.Debug("Applying function FIELD to field [{0}]", oField.Key) Logger.Debug("Applying function FIELD to field [{0}]", oField.Key)
@ -532,6 +560,9 @@ Namespace Documents
End Try End Try
End If End If
Next Next
Next
Next Next
Return pDocument Return pDocument
@ -629,6 +660,7 @@ Namespace Documents
Logger.Info("Price for Item [{0}] set to [{1}]", pPriceField, oArticlePrice) Logger.Info("Price for Item [{0}] set to [{1}]", pPriceField, oArticlePrice)
Else Else
Logger.Warn("Price for Item [{0}] could not be found!", pPriceField) 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 If
End Function End Function
@ -662,8 +694,8 @@ Namespace Documents
End If End If
' Try to find an account that matches the GLN ' Try to find an account that matches the GLN
Dim oAlternateField = pParams.GetOrDefault("AltField", String.Empty) Dim oAlternateField As String = pParams.GetOrDefault("AltField", String.Empty)
Dim oAccount = Winline.TryGetAccount(oNumberItem.Original, pMandator, "c260", oAlternateField) Dim oAccount As Account = Winline.TryGetAccount(oNumberItem.Original, pMandator, "c260", oAlternateField)
' If an account was found, set it for External and Final value ' If an account was found, set it for External and Final value
If oAccount IsNot Nothing Then If oAccount IsNot Nothing Then
@ -715,6 +747,68 @@ Namespace Documents
Throw ex Throw ex
End Try End Try
End Function 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 Class
End Namespace End Namespace

View File

@ -112,7 +112,8 @@ Public Class ReportGenerator(Of TReport As IReport)
ToList() ToList()
For Each oRow As DocumentRow In oRowList For Each oRow As DocumentRow In oRowList
Dim oSQL = oSQLConfigItem.Function.Params For Each oFunction In oSQLConfigItem.Functions
Dim oSQL = oFunction.Params
Dim oField = oRow.Fields. Dim oField = oRow.Fields.
Where(Function(field) field.Key = oSQLConfigItem.Name). Where(Function(field) field.Key = oSQLConfigItem.Name).
SingleOrDefault() SingleOrDefault()
@ -126,6 +127,7 @@ Public Class ReportGenerator(Of TReport As IReport)
End If End If
Next Next
Next Next
Next
Return pDocument Return pDocument
End Function End Function

View File

@ -3,6 +3,7 @@ Imports DigitalData.Modules.Language
Namespace Templates Namespace Templates
Public Class FieldConfig Public Class FieldConfig
Public Property Id As Integer
Public Property Name As String Public Property Name As String
Public Property Table As String Public Property Table As String
Public Property Type As ColumnType Public Property Type As ColumnType
@ -16,19 +17,7 @@ Namespace Templates
Public Property IsVirtual As Boolean Public Property IsVirtual As Boolean
Public Property PreferExternalValue As Boolean Public Property PreferExternalValue As Boolean
Public Property [Function] As ColumnFunction Public Property Functions As New List(Of ColumnFunction)
Public ReadOnly Property FunctionName As String
Get
Return Utils.NotNull([Function]?.Name, String.Empty)
End Get
End Property
Public ReadOnly Property FunctionParams As String
Get
Return Utils.NotNull([Function]?.Params, String.Empty)
End Get
End Property
Public Class ColumnFunction Public Class ColumnFunction
Public Id As XmlFunction Public Id As XmlFunction

View File

@ -11,7 +11,7 @@ Namespace Templates
Public ReadOnly Property SqlItems As List(Of FieldConfig) Public ReadOnly Property SqlItems As List(Of FieldConfig)
Get Get
Return Items. Return Items.
Where(Function(item) item.Function.Name = Constants.FUNCTION_SQL). Where(Function(item) item.Functions.Any(Function(f) f.Name = Constants.FUNCTION_SQL)).
ToList() ToList()
End Get End Get
End Property End Property

View File

@ -19,6 +19,7 @@ Namespace Templates
Private Const SQL_TBMT_FILTERS = "SELECT * FROM [DD_ECM].[dbo].[VWMT_FILTERS]" Private Const SQL_TBMT_FILTERS = "SELECT * FROM [DD_ECM].[dbo].[VWMT_FILTERS]"
Private Const SQL_VWMT_ITEMS = "SELECT * FROM [DD_ECM].[dbo].[VWMT_ITEMS] ORDER BY TEMPLATE_NAME, TABLE_NAME" Private Const SQL_VWMT_ITEMS = "SELECT * FROM [DD_ECM].[dbo].[VWMT_ITEMS] ORDER BY TEMPLATE_NAME, TABLE_NAME"
Private Const SQL_VWMT_FUNCTIONS = "SELECT * FROM [DD_ECM].[dbo].[VWMT_FUNCTIONS] ORDER BY ITEM_ID, SEQUENCE"
Private Const SQL_VWMT_MAPPING = "SELECT * FROM [DD_ECM].[dbo].[VWMT_MAPPING]" Private Const SQL_VWMT_MAPPING = "SELECT * FROM [DD_ECM].[dbo].[VWMT_MAPPING]"
Private Const SQL_TBMT_MANDATORS = "SELECT * FROM [DD_ECM].[dbo].[TBMT_MANDATORS] ORDER BY ORDER_KEY" Private Const SQL_TBMT_MANDATORS = "SELECT * FROM [DD_ECM].[dbo].[TBMT_MANDATORS] ORDER BY ORDER_KEY"
Private Const SQL_TBMT_CONFIG = "SELECT * FROM [DD_ECM].[dbo].[TBMT_CONFIG]" Private Const SQL_TBMT_CONFIG = "SELECT * FROM [DD_ECM].[dbo].[TBMT_CONFIG]"
@ -191,6 +192,7 @@ Namespace Templates
For Each oRow As DataRow In oTable.Rows For Each oRow As DataRow In oTable.Rows
Dim oColumn As New FieldConfig() With { Dim oColumn As New FieldConfig() With {
.Id = oRow.ItemEx("ITEM_ID", 0),
.Template = oRow.ItemEx("TEMPLATE_NAME", String.Empty), .Template = oRow.ItemEx("TEMPLATE_NAME", String.Empty),
.Table = oRow.ItemEx("TABLE_NAME", String.Empty), .Table = oRow.ItemEx("TABLE_NAME", String.Empty),
.Name = oRow.ItemEx("ITEM_NAME", String.Empty), .Name = oRow.ItemEx("ITEM_NAME", String.Empty),
@ -202,11 +204,7 @@ Namespace Templates
.IsVirtual = oRow.ItemEx("IS_VIRTUAL", False), .IsVirtual = oRow.ItemEx("IS_VIRTUAL", False),
.IsHead = oRow.ItemEx("IS_HEAD", True), .IsHead = oRow.ItemEx("IS_HEAD", True),
.PreferExternalValue = oRow.ItemEx("PREFER_EXTERNAL", True), .PreferExternalValue = oRow.ItemEx("PREFER_EXTERNAL", True),
.[Function] = New FieldConfig.ColumnFunction With { .Functions = New List(Of FieldConfig.ColumnFunction)
.Id = oRow.ItemEx("FUNCTION_ID", 0),
.Name = oRow.ItemEx("FUNCTION_NAME", String.Empty),
.Params = oRow.ItemEx("FUNCTION_PARAMETERS", String.Empty)
}
} }
Logger.Debug("Creating Template Item for Table [{0}]: [{1}]", oColumn.Table, oColumn.Name) Logger.Debug("Creating Template Item for Table [{0}]: [{1}]", oColumn.Table, oColumn.Name)
@ -227,6 +225,37 @@ Namespace Templates
End Try End Try
End Function End Function
Public Async Function LoadTemplateFunctions() As Task(Of Boolean)
Try
Dim oTable As DataTable = Await Database.GetDatatableAsync(SQL_VWMT_FUNCTIONS)
Dim oItems As New List(Of FieldConfig)
For Each oRow As DataRow In oTable.Rows
Dim oTemplateItemId = oRow.ItemEx("ITEM_ID", 0)
Dim oTemplateItem = TemplateConfiguration.Items.SingleOrDefault(Function(i) i.Id = oTemplateItemId)
If oTemplateItem Is Nothing Then
Logger.Warn("Function configuration could not be assigned to an existing template item, item id was [{0}]", oTemplateItemId)
Continue For
End If
oTemplateItem.Functions.Add(New FieldConfig.ColumnFunction With {
.Id = oRow.ItemEx("FUNCTION_ID", 0),
.Name = oRow.ItemEx("FUNCTION_NAME", String.Empty),
.Params = oRow.ItemEx("FUNCTION_PARAMETERS", String.Empty)
})
Next
Return True
Catch ex As Exception
Logger.Error(ex)
Return False
End Try
End Function
Public Function UpdateTemplateFromFile(pTemplate As Template, pInputDirectory As String) As Template Public Function UpdateTemplateFromFile(pTemplate As Template, pInputDirectory As String) As Template
Dim oFullPath = Path.Combine(pInputDirectory, pTemplate.FileName) Dim oFullPath = Path.Combine(pInputDirectory, pTemplate.FileName)

View File

@ -375,34 +375,33 @@ Namespace Winline
For Each oTable In pTemplate.Tables For Each oTable In pTemplate.Tables
Logger.Debug("Processing Table [{0}]", oTable.Name) Logger.Debug("Processing Table [{0}]", oTable.Name)
For Each oItem As Template.Column In oTable.Columns For Each oColumn As Template.Column In oTable.Columns
Dim oTableName As String = oTable.Name Dim oTableName As String = oTable.Name
Dim oItemName As String = oItem.Name Dim oItemName As String = oColumn.Name
Logger.Debug("Processing item [{0}]", oItemName) Logger.Debug("Processing item [{0}]", oItemName)
If oItem.Config.Function Is Nothing Then For Each oFunction As FieldConfig.ColumnFunction In oColumn.Config.Functions
Continue For
End If
Dim oFunction = oItem.Config.Function.Name Dim oFunctionName = oFunction.Name
Dim oFunctionParams = oFunction.Params
Dim oPath = $"//MESOWebService/{oTableName}/{oItemName}" Dim oPath = $"//MESOWebService/{oTableName}/{oItemName}"
Dim oNodes As XmlNodeList = oXMLDocument.SelectNodes(oPath) Dim oNodes As XmlNodeList = oXMLDocument.SelectNodes(oPath)
Logger.Debug("Calling function [{0}] on node [{1}]", oFunction, oPath) Logger.Debug("Calling function [{0}] on node [{1}]", oFunctionName, oPath)
For Each oNode As XmlNode In oNodes For Each oNode As XmlNode In oNodes
If oItem.Config.Function.Name = "GLN" Then If oFunctionName = Constants.FUNCTION_GLN Then
Dim oGLN = Winline.TryGetGLN(oNode.InnerText, pMandator) Dim oGLN = Winline.TryGetGLN(oNode.InnerText, pMandator)
If oGLN Is Nothing Then If oGLN Is Nothing Then
Throw New MissingAttributeException("GLN") Throw New MissingAttributeException(Constants.FUNCTION_GLN)
End If End If
oNode.InnerText = oGLN oNode.InnerText = oGLN
ElseIf oItem.Config.Function.Name = "EAN" Then ElseIf oFunctionName = Constants.FUNCTION_EAN Then
Dim oEAN = Winline.TryGetEAN(oNode.InnerText, pMandator) Dim oEAN = Winline.TryGetEAN(oNode.InnerText, pMandator)
If oEAN Is Nothing Then If oEAN Is Nothing Then
@ -416,12 +415,12 @@ Namespace Winline
oNode.InnerText = oEAN oNode.InnerText = oEAN
ElseIf oItem.Config.Function.Name = "SQL" Then ElseIf oFunctionName = Constants.FUNCTION_SQL Then
Dim oSQL = Patterns.ReplaceForExport(pDocument, pMandator, oItem.Config.Function.Params) Dim oSQL = Patterns.ReplaceForExport(pDocument, pMandator, oFunctionParams)
Dim oValue = Database.GetScalarValue(oSQL) Dim oValue = Database.GetScalarValue(oSQL)
If oValue Is Nothing Then If oValue Is Nothing Then
Throw New MissingAttributeException("SQL") Throw New MissingAttributeException(Constants.FUNCTION_SQL)
End If End If
oNode.InnerText = oValue oNode.InnerText = oValue
@ -429,6 +428,9 @@ Namespace Winline
End If End If
Next Next
Next Next
Next
Next Next
Return oXMLDocument Return oXMLDocument

View File

@ -110,6 +110,7 @@ Public Class frmMain
TemplateLoader = New TemplateLoader(LogConfig, Database) TemplateLoader = New TemplateLoader(LogConfig, Database)
Await TemplateLoader.LoadTemplates() Await TemplateLoader.LoadTemplates()
Await TemplateLoader.LoadTemplateConfiguration() Await TemplateLoader.LoadTemplateConfiguration()
Await TemplateLoader.LoadTemplateFunctions()
Await TemplateLoader.LoadGeneralConfiguration() Await TemplateLoader.LoadGeneralConfiguration()
Await TemplateLoader.LoadMappingConfiguration() Await TemplateLoader.LoadMappingConfiguration()
Await TemplateLoader.LoadMandatorConfiguration() Await TemplateLoader.LoadMandatorConfiguration()

View File

@ -272,11 +272,11 @@ Public Class frmRowEditor
Exit Sub Exit Sub
End If End If
If oColumn.Config?.Function?.Name = FUNCTION_GLN Then If oColumn.Config?.Functions.Any(Function(f) f.Name = FUNCTION_GLN) Then
e.RepositoryItem = AccountPicker e.RepositoryItem = AccountPicker
End If End If
If oColumn.Config?.Function?.Name = FUNCTION_EAN Then If oColumn.Config?.Functions.Any(Function(f) f.Name = FUNCTION_EAN) Then
e.RepositoryItem = ArticlePicker e.RepositoryItem = ArticlePicker
End If End If