This commit is contained in:
Developer01
2026-09-17 12:35:49 +02:00
7 changed files with 3620 additions and 16 deletions

View File

@@ -265,6 +265,7 @@
<Compile Include="GrapQLInterface\LoginData.vb" />
<Compile Include="GrapQLInterface\LogoutData.vb" />
<Compile Include="GrapQLInterface\QueryData.vb" />
<Compile Include="ZUGFeRDInterface\ebInterface6.1\InvoiceType.vb" />
<Compile Include="ZUGFeRDInterface\Enums\ErrorCodes.vb" />
<Compile Include="ZUGFeRDInterface\Enums\Item_Types.vb" />
<Compile Include="ZUGFeRDInterface\Exceptions.vb" />

View File

@@ -3,6 +3,7 @@ Imports System.Xml
Imports System.Xml.Serialization
Imports DigitalData.Modules.Interfaces.Exceptions
Imports DigitalData.Modules.Interfaces.PDFEmbeds
Imports DigitalData.Modules.Interfaces.PropertyValues
Imports DigitalData.Modules.Interfaces.ZUGFeRD
Imports DigitalData.Modules.Logging
Imports GdPicture14
@@ -21,8 +22,6 @@ Public Class ZUGFeRDInterface
Public Const ZUGFERD_SPEC_2x = "ZUGFERD_2x"
Public Const ZUGFERD_SPEC_2_3x = "ZUGFERD_2_3x"
Public Const UBL_SPEC_21 = "UBL_21"
Public Const XMLSCHEMA_ZUGFERD_10 = "Version1_0"
Public Const XMLSCHEMA_ZUGFERD_20 = "Version2_0"
Public Const XMLSCHEMA_ZUGFERD_211 = "Version2_1_1"
@@ -31,9 +30,14 @@ Public Class ZUGFeRDInterface
Public Const XMLSCHEMA_ZUGFERD_233 = "Version2_3_3_FacturX"
Public Const XMLSCHEMA_ZUGFERD_24 = "Version2_4_FacturX"
Public Const XMLSCHEMA_ZUGFERD_25 = "Version2_5_FacturX"
Public Const UBL_SPEC_21 = "UBL_21"
Public Const XMLSCHEMA_UBL_21_INVOICE = "UBL2_1_INVOICE"
Public Const XMLSCHEMA_UBL_21_CREDITNOTE = "UBL2_1_CREDITNOTE"
Public Const EBINTERFACE_SPEC_61 = "EBINTERFACE_61"
Public Const XMLSCHEMA_EBINTERFACE_61 = "Version6_1"
Public Const RECEIPT_TYPE_XML = "XML"
Public Const RECEIPT_TYPE_PDF = "PDF"
@@ -67,6 +71,7 @@ Public Class ZUGFeRDInterface
Public Property AllowZugferd_2_x_Schema As Boolean = True
Public Property AllowZugferd_2_3_x_Schema As Boolean = True
Public Property AllowPeppol_3_x_Schema As Boolean = False
Public Property AllowEBInterface_Schema As Boolean = False
End Class
Public Class ZugferdResult
@@ -152,6 +157,20 @@ Public Class ZUGFeRDInterface
Where(Function(kv) kv.Specification = pSpecification Or kv.Specification = ZUGFERD_SPEC_DEFAULT).
ToDictionary(Function(kv) kv.XMLPath, Function(kv) kv)
Catch ex As Exception
_logger.Error(ex)
End Try
ElseIf pSpecification = EBINTERFACE_SPEC_61 Then
Try
' root-Elemente werden hier herausgefiltert
Dim countElements As Integer = pPropertyMapList.Where(Function(x) x.Specification = pSpecification And Not x.XMLPath.StartsWith("root.")).Count
_logger.Debug("Property map list contains [{0}] elements for specification [{1}]", countElements, pSpecification)
Return pPropertyMapList.
Where(Function(kv) kv.Specification = pSpecification And Not kv.XMLPath.StartsWith("root.")).
ToDictionary(Function(kv) kv.XMLPath, Function(kv) kv)
Catch ex As Exception
_logger.Error(ex)
End Try
@@ -174,6 +193,28 @@ Public Class ZUGFeRDInterface
Return Nothing
End Function
Public Function FilterRootPropertyMap(pPropertyMapList As List(Of XmlItemProperty), pSpecification As String) As Dictionary(Of String, XmlItemProperty)
' Aktuell verwendet nur ebeInterafce attribute im root-Knoten
If pSpecification = EBINTERFACE_SPEC_61 Then
Try
' root-Elemente werden hier herausgefiltert
Dim countElements As Integer = pPropertyMapList.Where(Function(x) x.Specification = pSpecification And x.XMLPath.StartsWith("root.")).Count
_logger.Debug("Property map list contains [{0}] root attribute elements for specification [{1}]", countElements, pSpecification)
Return pPropertyMapList.
Where(Function(kv) kv.Specification = pSpecification And kv.XMLPath.StartsWith("root.")).
ToDictionary(Function(kv) kv.XMLPath, Function(kv) kv)
Catch ex As Exception
_logger.Error(ex)
End Try
Else
Return Nothing
End If
End Function
Public Function GetSerializedXMLContentFromFile(oFileInfo As FileInfo) As ZugferdResult
Dim oResult = New ZugferdResult()
Dim oMessage As String = String.Empty
@@ -455,6 +496,16 @@ Public Class ZUGFeRDInterface
})
End If
If _Options.AllowEBInterface_Schema Then
oAllowedTypes.AddRange(New List(Of AllowedType) From {
New AllowedType With {
.SchemaType = GetType(EBINTERFACE_61.InvoiceType),
.Specification = EBINTERFACE_SPEC_61,
.XMLSchema = XMLSCHEMA_EBINTERFACE_61
}
})
End If
For Each oType In oAllowedTypes
Dim oTypeName As String = oType.SchemaType.FullName
Dim oSerializer As New XmlSerializer(oType.SchemaType)
@@ -497,4 +548,5 @@ Public Class ZUGFeRDInterface
Throw New ZUGFeRDExecption(ErrorCodes.UnsupportedFerdException, oMessage)
End Try
End Function
End Class

View File

@@ -1,8 +1,8 @@
Public Enum Item_Types
StringType '0
DateType '1
MoneyType '2
FileType '3
ListType '4 - vgl. Währung
StringType = 0 '0
DateType = 1 '1
MoneyType = 2 '2
FileType = 3 '3
ListType = 4 '4 - vgl. Währung
End Enum

View File

@@ -1,7 +1,9 @@
Imports System.Globalization
Imports System.Reflection
Imports System.Text.RegularExpressions
Imports System.Xml
Imports DigitalData.Modules.Logging
Imports GdPicture.Internal.MSOfficeBinary.translator.Tools
Public Class PropertyValues
Private _logger As Logger
@@ -122,12 +124,13 @@ Public Class PropertyValues
' Returns nothing if oColumn.Value contains an empty list
Dim oPropertyValue = oColumn.Value.ElementAtOrDefault(oRowIndex)
_logger.Debug("Processing itemColumn *TableColumn* [{0}].", oTableColumn)
If oTableColumn = "INVOICE_SELLER_EMAIL" Then
Console.WriteLine("INVOICE_SELLER_EMAIL")
ElseIf oTableColumn = "INVOICE_POSITION_ARTICLE" Then
Console.WriteLine("INVOICE_POSITION_ARTICLE")
End If
'_logger.Debug("Processing itemColumn *TableColumn* [{0}].", oTableColumn)
'If oTableColumn = "INVOICE_SELLER_EMAIL" Then
' Console.WriteLine("INVOICE_SELLER_EMAIL")
'ElseIf oTableColumn = "INVOICE_POSITION_ARTICLE" Then
' Console.WriteLine("INVOICE_POSITION_ARTICLE")
'End If
If IsNothing(oPropertyValue) OrElse String.IsNullOrEmpty(oPropertyValue) Then
If oColumn.Key.IsRequired Then
_logger.Warn($"{MessageId} - oPropertyValue for column [{oTableColumn}] is empty or not found but is required. Continuing with Empty String.")
@@ -154,7 +157,11 @@ Public Class PropertyValues
End If
If oTableColumn = "INVOICE_CURRENCY" Or oItemType = 4 Then
If oItemType = Item_Types.MoneyType Then
oPropertyValue = GetFormattedDecimalValue(oPropertyValue)
End If
If oTableColumn = "INVOICE_CURRENCY" Or oItemType = Item_Types.ListType Then
Dim oValuestring = oPropertyValue.ToString()
' Bei Listenelementen entfernen wir den String Item, um den Wert zu erhalten
@@ -247,8 +254,12 @@ Public Class PropertyValues
End If
End If
If oItemType = Item_Types.MoneyType Then
oPropertyValue = GetFormattedDecimalValue(oPropertyValue)
End If
' Statt dem Zahlenwert des Enums, wollen wir die Währungsbezeichnung
If oTableColumn = "INVOICE_CURRENCY" Or oItemType = 4 Then
If oTableColumn = "INVOICE_CURRENCY" Or oItemType = Item_Types.ListType Then
Dim oValuestring = oPropertyValue.ToString()
' Bei Listenelementen entfernen wir den String Item, um den Wert zu erhalten
@@ -275,6 +286,167 @@ Public Class PropertyValues
Return oResult
End Function
Private Function GetFormattedDecimalValue(pPropertyValue As Object) As Object
' wir versuchen den gefundenen Wert in einen decimal mit 2 Nachommastellen umzuwandeln
' Eingaben können mit . oder mit , als Dezimal-Trenner kommen: 19.00 = 19,00
If IsNothing(pPropertyValue) OrElse String.IsNullOrEmpty(pPropertyValue) Then
Return String.Empty
End If
Try
Dim stringValue = pPropertyValue.ToString()
Dim deCulture As New CultureInfo("de-DE")
Dim usCulture As New CultureInfo("en-US")
Dim decimalValue As Decimal
If stringValue.Contains(".") AndAlso Not stringValue.Contains(",") Then
' Wir wandeln mit en-US um
If Decimal.TryParse(pPropertyValue, NumberStyles.Any, usCulture, decimalValue) Then
decimalValue = Math.Round(decimalValue, 2)
pPropertyValue = decimalValue.ToString("F2", CultureInfo.InvariantCulture)
End If
ElseIf stringValue.Contains(",") AndAlso Not stringValue.Contains(".") Then
' Wir wandeln mit de-DE um, wenn Komma als Trennzeichen enthalten ist
If Decimal.TryParse(pPropertyValue, NumberStyles.Any, deCulture, decimalValue) Then
decimalValue = Math.Round(decimalValue, 2)
pPropertyValue = decimalValue.ToString("F2", CultureInfo.InvariantCulture)
End If
ElseIf Not stringValue.Contains(",") AndAlso Not stringValue.Contains(".") Then
' Wir wandeln mit en-US um, wenn KEIN Trennzeichen enthalten ist
If Decimal.TryParse(pPropertyValue, NumberStyles.Any, usCulture, decimalValue) Then
decimalValue = Math.Round(decimalValue, 2)
pPropertyValue = decimalValue.ToString("F2", CultureInfo.InvariantCulture)
End If
End If
Return pPropertyValue
Catch ex As Exception
_logger.Error(ex)
Return Nothing
End Try
End Function
Public Function CheckRootAttributeValues(pDocument As Object, pPropertyMap As Dictionary(Of String, XmlItemProperty), pMessageId As String, pCheckResult As CheckPropertyValuesResult) As CheckPropertyValuesResult
If pPropertyMap Is Nothing Then
Return pCheckResult
End If
' PropertyMap items with `IsGrouped = False` are handled normally
Dim oRootAttributeProperties As Dictionary(Of String, XmlItemProperty) = pPropertyMap.
ToDictionary(Function(Item) Item.Key,
Function(Item) Item.Value)
For Each oItem As KeyValuePair(Of String, XmlItemProperty) In oRootAttributeProperties
Dim oTableColumn As String = oItem.Value.TableColumn
Dim oPropertyDescription As String = oItem.Value.Description
Dim oPropertyPath As String = oItem.Value.XMLPath
Dim oPropertyValue As Object = Nothing
Dim oTableName = oItem.Value.TableName
Dim oIsRequired = oItem.Value.IsRequired
Dim oDescription = oItem.Value.Description
Dim oItemType = oItem.Value.ItemType
Dim oEN16931_ID = oItem.Value.EN16931_ID
Try
oPropertyValue = GetRootAttributeValue(pDocument, oItem.Key)
Catch ex As Exception
_logger.Error(ex)
oPropertyValue = Nothing
End Try
If IsNothing(oPropertyValue) OrElse String.IsNullOrEmpty(oPropertyValue) Then
If oItem.Value.IsRequired Then
_logger.Warn("Column [{0}] is empty, but marked as required! Adding MissingProperty.", oTableColumn)
Dim oMissingProperty = New MissingProperty With
{
.EN16931_ID = oEN16931_ID,
.Description = oPropertyDescription,
.XMLPath = oPropertyPath
}
pCheckResult.MissingProperties.Add(oMissingProperty)
Continue For
Else
_logger.Debug("oPropertyValue for Column [{0}] is empty or not found. Skipping.", oTableColumn)
Continue For
End If
End If
If pDocument.Specification = "EBINTERFACE_61" Then
' ebInterface DocumentType in UNTDID 1001 — Document type
If oTableColumn = "INVOICE_TYPE" Then
oPropertyValue = GetUNTDID1001_DocumentType(oPropertyValue)
_logger.Debug("oPropertyValue for Column [{0}] in EBINTERFACE_61 with value [{1}] ", oTableColumn, oPropertyValue)
End If
End If
pCheckResult.ValidProperties.Add(New ValidProperty() With {
.MessageId = pMessageId,
.Description = oPropertyDescription,
.Value = oPropertyValue,
.GroupCounter = 0,
.TableName = oTableName,
.TableColumn = oTableColumn,
.IsRequired = oIsRequired,
.XMLPath = oPropertyPath,
.ItemType = oItemType,
.EN16931_ID = oEN16931_ID
})
Next
Return pCheckResult
End Function
Private Function GetUNTDID1001_DocumentType(oPropertyValue As String) As String
Select Case oPropertyValue
Case "CreditMemo"
Return 381 ' Credit note
Case "FinalSettlement"
Return 877 ' Final construction invoice
Case "Invoice"
Return 380 ' Commercial invoice
Case "InvoiceForAdvancePayment"
Return 386 ' Prepayment invoice
Case "InvoiceForPartialDelivery"
Return 381 ' Partial invoice
Case "SelfBilling"
Return 261 ' Self billed credit note
Case "SubsequentCredit"
Return 83 ' Credit note related to financial adjustments
Case "SubsequentDebit"
Return 84 ' Debit note related to financial adjustments
Case Else
Return 0
End Select
End Function
Private Function GetRootAttributeValue(pDocument As Object, pXmlPath As String) As String
If pDocument.XElementObject Is Nothing Then
Return Nothing
End If
Try
Dim attributeName As String = pXmlPath.Replace("root.", "")
Dim attributeValue As String = pDocument.XElementObject.Attribute(attributeName).Value
Return attributeValue
Catch ex As Exception
_logger.Error(ex)
Return Nothing
End Try
End Function
Public Function GetPropValue(Obj As Object, PropertyName As String) As List(Of Object)
Dim oNameParts As String() = PropertyName.Split("."c)

File diff suppressed because it is too large Load Diff

View File

@@ -120,7 +120,8 @@ Public Class ImportZUGFeRDFiles
_zugferd = New ZUGFeRDInterface(_logConfig, _gdpictureLicenseKey, New ZUGFeRDInterface.ZugferdOptions() With {
.AllowFacturX_Filename = oArgs.AllowFacturX,
.AllowXRechnung_Filename = oArgs.AllowXRechnung,
.AllowPeppol_3_x_Schema = oArgs.AllowPeppolBISBill3x
.AllowPeppol_3_x_Schema = oArgs.AllowPeppolBISBill3x,
.AllowEBInterface_Schema = oArgs.AllowEBInterface
})
_logger.Debug("Starting Job {0}", [GetType].Name)
@@ -719,6 +720,10 @@ Public Class ImportZUGFeRDFiles
Dim oPropertyMap As Dictionary(Of String, XmlItemProperty) = _zugferd.FilterPropertyMap(pArgs.PropertyMapList, pDocument.Specification)
Dim oCheckResult = _zugferd.PropertyValues.CheckPropertyValues(pDocument.SchemaObject, oPropertyMap, pMessageId)
' root-Attribute falls konfiguriert auslesen, Ergebnis im oCheckResult enthalten
Dim oRootAttributePropertyMap As Dictionary(Of String, XmlItemProperty) = _zugferd.FilterRootPropertyMap(pArgs.PropertyMapList, pDocument.Specification)
oCheckResult = _zugferd.PropertyValues.CheckRootAttributeValues(pDocument, oRootAttributePropertyMap, pMessageId, oCheckResult)
_logger.Info("Properties checked: [{0}] missing properties / [{1}] valid properties found.", oCheckResult.MissingProperties.Count, oCheckResult.ValidProperties.Count)
If oCheckResult.MissingProperties.Count > 0 Then

View File

@@ -33,5 +33,6 @@ Public Class WorkerArgs
Public AllowZugferd2x As Boolean = True
Public AllowZugferd23x As Boolean = True
Public AllowPeppolBISBill3x As Boolean = False
Public AllowEBInterface As Boolean = False
End Class