383 lines
16 KiB
VB.net
383 lines
16 KiB
VB.net
Imports System.Globalization
|
|
Imports System.IO
|
|
Imports DigitalData.Modules.Logging
|
|
Imports MultiTool.Shared.Exceptions
|
|
Imports MultiTool.Shared.Schemas
|
|
Imports MultiTool.Shared.Winline
|
|
|
|
Namespace Documents
|
|
Public Class DocumentLoader
|
|
Inherits BaseClass
|
|
|
|
Private ReadOnly Winline As WinlineData
|
|
|
|
Public Property Files As New List(Of Document)
|
|
Public Event FileLoadComplete As EventHandler(Of FileLoadInfo)
|
|
|
|
Public Structure FileLoadInfo
|
|
Public FilesLoaded As Integer
|
|
Public FilesTotal As Integer
|
|
End Structure
|
|
|
|
Public Sub New(pLogConfig As LogConfig, pWinline As WinlineData)
|
|
MyBase.New(pLogConfig, pLogConfig.GetLogger())
|
|
Winline = pWinline
|
|
End Sub
|
|
|
|
|
|
Public Function LoadFiles(pInputDirectory As String, pSchema As Schema, pMandator As Mandator) As Boolean
|
|
If pInputDirectory = String.Empty Then
|
|
Throw New ArgumentNullException("InputDirectory")
|
|
End If
|
|
|
|
Logger.Info("Loading files from directory [{0}]", pInputDirectory)
|
|
Files.Clear()
|
|
|
|
Try
|
|
Dim oDirectory As New DirectoryInfo(pInputDirectory)
|
|
Dim oFiles = oDirectory.GetFiles()
|
|
|
|
Logger.Debug("Found [{0}] files in directory [{1}]", oFiles.Count, oDirectory)
|
|
|
|
For Each oFile In oFiles
|
|
Try
|
|
Dim oDocument = LoadFile(oFile, pSchema, pMandator)
|
|
Files.Add(oDocument)
|
|
|
|
Dim oInfo As FileLoadInfo
|
|
oInfo.FilesLoaded = Files.Count
|
|
oInfo.FilesTotal = oFiles.Count
|
|
|
|
RaiseEvent FileLoadComplete(Me, oInfo)
|
|
Catch ex As MissingAttributeException
|
|
Logger.Error(ex)
|
|
Throw New DocumentLoaderException($"Missing Attribute '{ex.Message}' in File '{oFile.Name}'")
|
|
|
|
Catch ex As Exception
|
|
Logger.Error(ex)
|
|
Throw ex
|
|
End Try
|
|
Next
|
|
|
|
Return True
|
|
|
|
Catch ex As Exception
|
|
Logger.Error(ex)
|
|
Throw ex
|
|
|
|
End Try
|
|
End Function
|
|
|
|
Public Function LoadFile(pFileInfo As FileInfo, pSchema As Schema, pMandator As Mandator) As Document
|
|
Dim oFileList As New List(Of FileInfo) From {pFileInfo}
|
|
Logger.Info("Loading file [{0}]", pFileInfo.Name)
|
|
|
|
Try
|
|
Return oFileList.
|
|
Select(AddressOf WrapFileInfo).
|
|
Select(Function(d) IncludeSchema(d, pSchema)).
|
|
Select(Function(d) LoadDocumentData(d, pSchema)).
|
|
Select(Function(d) MatchDataFromWinLine(d, Winline.Mandators, pMandator, pSchema)).
|
|
SingleOrDefault()
|
|
Catch ex As Exception
|
|
Logger.Error(ex)
|
|
Throw ex
|
|
End Try
|
|
End Function
|
|
|
|
|
|
Private Function IncludeSchema(pDocument As Document, pSchema As Schema) As Document
|
|
pDocument.Schema = pSchema
|
|
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, pSchema As Schema) As Document
|
|
Dim oText As String = IO.File.ReadAllText(pDocument.FullName)
|
|
Dim oDoc = XDocument.Parse(oText)
|
|
|
|
Dim oRootElement As XElement = XmlData.GetElement(oDoc, "MESOWebService")
|
|
If oRootElement Is Nothing Then
|
|
Throw New Exceptions.MalformedXmlException("Datei enthält kein MESOWebService-Element")
|
|
End If
|
|
|
|
Dim oTemplateName = XmlData.GetElementAttribute(oRootElement, "Template")
|
|
If oTemplateName Is Nothing Then
|
|
Throw New Exceptions.MalformedXmlException("Datei enthält kein Template-Attribut")
|
|
End If
|
|
|
|
Dim oTemplateType = XmlData.GetElementAttribute(oRootElement, "TemplateType")
|
|
If oTemplateType Is Nothing Then
|
|
Throw New Exceptions.MalformedXmlException("Datei enthält kein TemplateType-Attribut")
|
|
End If
|
|
|
|
Dim oOption = XmlData.GetElementAttribute(oRootElement, "option")
|
|
If oOption Is Nothing Then
|
|
Throw New Exceptions.MalformedXmlException("Datei enthält kein option-Attribut")
|
|
End If
|
|
|
|
Dim oPrintVoucher = XmlData.GetElementAttribute(oRootElement, "printVoucher")
|
|
If oPrintVoucher Is Nothing Then
|
|
Throw New Exceptions.MalformedXmlException("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 oSortKey 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 oFields As New Dictionary(Of String, DocumentRow.FieldValue)
|
|
Dim oSubElements = oTopLevelElement.Descendants().ToList()
|
|
Dim oTable = pSchema.Tables.
|
|
Where(Function(t) t.Name = oTopLevelElement.Name).
|
|
FirstOrDefault()
|
|
|
|
For Each oColumn In oTable.Columns
|
|
Dim oSubElement = oSubElements.
|
|
Where(Function(e) e.Name = oColumn.Name).
|
|
SingleOrDefault()
|
|
|
|
If oSubElement IsNot Nothing Then
|
|
Dim oRequired = oColumn.IsRequired
|
|
Dim oValue = oSubElement.Value.Trim()
|
|
|
|
' TODO: Needed when we have time for date times
|
|
'If oSchemaField.DataType = Constants.ColumnType.Date Then
|
|
' Dim oDate = Date.ParseExact(oValue, "yyyy-MM-dd", CultureInfo.InvariantCulture)
|
|
' oValue = oDate.ToString("d")
|
|
'End If
|
|
|
|
oFields.Add(oSubElement.Name.ToString, New DocumentRow.FieldValue With {
|
|
.Original = oValue,
|
|
.Final = oValue,
|
|
.DataType = oColumn.DataType,
|
|
.Required = oRequired
|
|
})
|
|
Else
|
|
Dim oColumnError = DocumentRow.FieldError.None
|
|
If oColumn.Config?.IsRequired Then
|
|
oColumnError = DocumentRow.FieldError.MissingValue
|
|
End If
|
|
|
|
oFields.Add(oColumn.Name, New DocumentRow.FieldValue With {
|
|
.[Error] = oColumnError
|
|
})
|
|
End If
|
|
Next
|
|
|
|
'For Each oSubElement As XElement In oSubElements
|
|
' Dim oSchemaField = oTable.Columns.
|
|
' Where(Function(c) c.Name = oSubElement.Name).
|
|
' SingleOrDefault()
|
|
|
|
' Dim oRequired = oSchemaField.IsRequired
|
|
' Dim oValue = oSubElement.Value.Trim()
|
|
|
|
' ' TODO: Needed when we have time for date times
|
|
' 'If oSchemaField.DataType = Constants.ColumnType.Date Then
|
|
' ' Dim oDate = Date.ParseExact(oValue, "yyyy-MM-dd", CultureInfo.InvariantCulture)
|
|
' ' oValue = oDate.ToString("d")
|
|
' 'End If
|
|
|
|
' oFields.Add(oSubElement.Name.ToString, New DocumentRow.FieldValue With {
|
|
' .Original = oValue,
|
|
' .Final = oValue,
|
|
' .DataType = oSchemaField.DataType,
|
|
' .Required = oRequired
|
|
' })
|
|
|
|
'Next
|
|
|
|
'' All fields in the schema are generated,
|
|
'' only creating the ones with values leads to wrong visual cues when asking for
|
|
'' docs/rows/fields with errors
|
|
'For Each oColumn In oTable.Columns
|
|
' If oFields.ContainsKey(oColumn.Name) Then
|
|
' Continue For
|
|
' End If
|
|
|
|
' Dim oColumnError = DocumentRow.FieldError.None
|
|
' If oColumn.Config?.IsRequired Then
|
|
' oColumnError = DocumentRow.FieldError.MissingValue
|
|
' End If
|
|
|
|
' oFields.Add(oColumn.Name, New DocumentRow.FieldValue With {
|
|
' .[Error] = oColumnError
|
|
' })
|
|
'Next
|
|
|
|
' Create a DocumentRow object for each Top Level Element
|
|
Dim oRow = New DocumentRow With {
|
|
.SortKey = oSortKey,
|
|
.Name = oTopLevelElement.Name.ToString,
|
|
.Fields = oFields
|
|
}
|
|
|
|
oSortKey += 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
|
|
|
|
|
|
Private Function MatchDataFromWinLine(pDocument As Document, pMandators As List(Of Mandator), pMandator As Mandator, pSchema As Schema) As Document
|
|
Dim oMandators As List(Of Winline.Mandator) = pMandators.
|
|
Where(Function(m) m.IsWhitelisted = True).
|
|
OrderBy(Function(m) m.Order).
|
|
ToList()
|
|
|
|
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)
|
|
Throw New Exceptions.NoMandatorException($"Mandator not found for file [{pDocument.File.Name}]")
|
|
End If
|
|
|
|
pDocument = MatchDocumentData(pDocument, oMandator, pSchema)
|
|
pDocument.Mandator = oMandator
|
|
|
|
Return pDocument
|
|
End Function
|
|
|
|
Private Function MatchDocumentData(pDocument As Document, pMandator As Winline.Mandator, pSchema As Schema) As Document
|
|
Dim oYear = Winline.GetWinLineYear()
|
|
|
|
If pMandator Is Nothing Then
|
|
Return pDocument
|
|
End If
|
|
|
|
For Each oRow As DocumentRow In pDocument.Rows
|
|
Dim oTable = pSchema.Tables.Where(Function(t) t.Name = oRow.Name).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
|
|
|
|
Dim oFunctionName = oColumn.Config?.Function?.Name
|
|
|
|
If oFunctionName = "GLN" Then
|
|
SetAccountByGLN(oRow, pMandator, oField.Key, Nothing)
|
|
End If
|
|
|
|
If oFunctionName = "EAN" Then
|
|
Dim oNumberItem As DocumentRow.FieldValue = oRow.Fields.GetOrDefault(oField.Key)
|
|
Dim oArticleNumber = Winline.TryGetArticleNumber(oNumberItem.Original, pMandator)
|
|
If oArticleNumber IsNot Nothing Then
|
|
oNumberItem.External = oArticleNumber
|
|
oNumberItem.Final = oArticleNumber
|
|
Else
|
|
oNumberItem.Error = DocumentRow.FieldError.ArticleNotFound
|
|
End If
|
|
End If
|
|
Next
|
|
Next
|
|
|
|
'Dim oHead As DocumentRow = pDocument.Rows.
|
|
' Where(Function(r) r.Name.ToUpper.EndsWith("T025")).
|
|
' SetValue(Sub(r As DocumentRow) SetAccountByGLN(r, pMandator, "Fakt_Kontonummer", "Fakt_Name")).
|
|
' SetValue(Sub(r As DocumentRow) SetAccountByGLN(r, pMandator, "Lief_Kontonummer", "Lief_Name")).
|
|
' FirstOrDefault()
|
|
|
|
'Dim oPositions As List(Of DocumentRow) = pDocument.Rows.
|
|
' Where(Function(r) r.Name.ToUpper.EndsWith("T026")).
|
|
' SetValue(Sub(oRow As DocumentRow)
|
|
' Dim oNumberItem As DocumentRow.FieldValue = oRow.Fields.GetOrDefault("Artikelnummer")
|
|
' If oNumberItem Is Nothing Then
|
|
' Exit Sub
|
|
' End If
|
|
|
|
' Dim oArticleNumber = Winline.TryGetArticleNumber(oNumberItem.Original, pMandator)
|
|
' If oArticleNumber IsNot Nothing Then
|
|
' oNumberItem.External = oArticleNumber
|
|
' oNumberItem.Final = oArticleNumber
|
|
' Else
|
|
' oNumberItem.Error = DocumentRow.FieldError.ArticleNotFound
|
|
' End If
|
|
' End Sub).
|
|
' ToList()
|
|
|
|
'Dim oList As New List(Of DocumentRow) From {oHead}
|
|
'pDocument.Rows = oList.Concat(oPositions).ToList()
|
|
|
|
Return pDocument
|
|
End Function
|
|
|
|
Private Sub SetAccountByGLN(oRow As DocumentRow, pMandator As Winline.Mandator, pNumberField As String, pNameField As String)
|
|
' 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 oAccount = Winline.TryGetAccount(oNumberItem.Original, pMandator)
|
|
|
|
' If an account was found, set it for External and Final value
|
|
If oAccount IsNot Nothing Then
|
|
oNumberItem.External = oAccount.Id
|
|
oNumberItem.Final = oAccount.Id
|
|
|
|
If oContainsAccountName Then
|
|
oNameItem.External = oAccount.Name
|
|
oNameItem.Final = 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
|
|
oNumberItem.Error = DocumentRow.FieldError.AccountNotFound
|
|
End If
|
|
End Sub
|
|
|
|
Private Function WrapFileInfo(pFileInfo As FileInfo) As Document
|
|
Return New Document With {.File = pFileInfo}
|
|
End Function
|
|
End Class
|
|
|
|
End Namespace |