Interfaces/Job: Add check for currencyId format in ZUGFeRD documents
This commit is contained in:
parent
f491d4dd24
commit
76cba215fe
@ -1,4 +1,6 @@
|
|||||||
Imports System.IO
|
Imports System.Collections.Generic
|
||||||
|
Imports System.IO
|
||||||
|
Imports System.Reflection.Emit
|
||||||
Imports System.Xml
|
Imports System.Xml
|
||||||
Imports System.Xml.Serialization
|
Imports System.Xml.Serialization
|
||||||
Imports System.Xml.XPath
|
Imports System.Xml.XPath
|
||||||
@ -54,6 +56,13 @@ Public Class ZUGFeRDInterface
|
|||||||
Public Property XPathObject As XPathDocument
|
Public Property XPathObject As XPathDocument
|
||||||
Public Property SchemaObject As Object
|
Public Property SchemaObject As Object
|
||||||
Public Property Specification As String
|
Public Property Specification As String
|
||||||
|
Public Property ValidationErrors As New List(Of ZugferdValidationError)
|
||||||
|
End Class
|
||||||
|
|
||||||
|
Public Class ZugferdValidationError
|
||||||
|
Public ElementName As String
|
||||||
|
Public ElementValue As String
|
||||||
|
Public ErrorMessage As String
|
||||||
End Class
|
End Class
|
||||||
|
|
||||||
''' <summary>
|
''' <summary>
|
||||||
@ -125,9 +134,10 @@ Public Class ZUGFeRDInterface
|
|||||||
''' <exception cref="ZUGFeRDExecption"></exception>
|
''' <exception cref="ZUGFeRDExecption"></exception>
|
||||||
Public Function ExtractZUGFeRDFileWithGDPicture(Path As String) As ZugferdResult
|
Public Function ExtractZUGFeRDFileWithGDPicture(Path As String) As ZugferdResult
|
||||||
Dim oResult = ValidateZUGFeRDFileWithGDPicture(Path)
|
Dim oResult = ValidateZUGFeRDFileWithGDPicture(Path)
|
||||||
|
oResult = ValidateZUGFeRDDocument(oResult)
|
||||||
|
|
||||||
'If IsNothing(oResult.SchemaObject) Then
|
'If IsNothing(oResult.SchemaObject) Then
|
||||||
' Throw New ZUGFeRDExecption(ErrorType.NoZugferd, "Datei ist keine ZUGFeRD Datei.")
|
Throw New ZUGFeRDExecption(ErrorType.NoZugferd, "Datei ist keine ZUGFeRD Datei.")
|
||||||
'End If
|
'End If
|
||||||
|
|
||||||
Return SerializeZUGFeRDDocument(oResult)
|
Return SerializeZUGFeRDDocument(oResult)
|
||||||
@ -140,6 +150,7 @@ Public Class ZUGFeRDInterface
|
|||||||
''' <exception cref="ZUGFeRDExecption"></exception>
|
''' <exception cref="ZUGFeRDExecption"></exception>
|
||||||
Public Function ExtractZUGFeRDFileWithGDPicture(Stream As Stream) As ZugferdResult
|
Public Function ExtractZUGFeRDFileWithGDPicture(Stream As Stream) As ZugferdResult
|
||||||
Dim oResult = ValidateZUGFeRDFileWithGDPicture(Stream)
|
Dim oResult = ValidateZUGFeRDFileWithGDPicture(Stream)
|
||||||
|
oResult = ValidateZUGFeRDDocument(oResult)
|
||||||
|
|
||||||
'If IsNothing(oResult.SchemaObject) Then
|
'If IsNothing(oResult.SchemaObject) Then
|
||||||
' Throw New ZUGFeRDExecption(ErrorType.NoZugferd, "Datei ist keine ZUGFeRD Datei.")
|
' Throw New ZUGFeRDExecption(ErrorType.NoZugferd, "Datei ist keine ZUGFeRD Datei.")
|
||||||
@ -256,6 +267,72 @@ Public Class ZUGFeRDInterface
|
|||||||
Public Specification As String
|
Public Specification As String
|
||||||
End Class
|
End Class
|
||||||
|
|
||||||
|
Public Function ValidateZUGFeRDDocument(pResult As ZugferdResult) As ZugferdResult
|
||||||
|
Dim oNavigator As XPathNavigator = pResult.XPathObject.CreateNavigator()
|
||||||
|
Dim oNamespaceManager As New XmlNamespaceManager(oNavigator.NameTable)
|
||||||
|
|
||||||
|
oNamespaceManager.AddNamespace("ram", "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:12")
|
||||||
|
oNamespaceManager.AddNamespace("rsm", "urn:ferd:CrossIndustryDocument:invoice:1p0")
|
||||||
|
|
||||||
|
' CurrencyCode Nodes
|
||||||
|
Try
|
||||||
|
Dim oCurrencyCodeIterator As XPathNodeIterator = oNavigator.
|
||||||
|
Select("//ram:InvoiceCurrencyCode | //ram:TaxCurrencyCode | //ram:TaxCurrencyCode | //ram:SourceCurrencyCode", oNamespaceManager)
|
||||||
|
|
||||||
|
While oCurrencyCodeIterator.MoveNext()
|
||||||
|
Dim oNode As XPathNavigator = oCurrencyCodeIterator.Current
|
||||||
|
Dim oValid = ValidateCurrencyCode(oNode.Value)
|
||||||
|
If oValid = False Then
|
||||||
|
pResult.ValidationErrors.Add(New ZugferdValidationError() With {
|
||||||
|
.ElementName = oNode.Name,
|
||||||
|
.ElementValue = oNode.Value,
|
||||||
|
.ErrorMessage = "Invalid CurrencyCode. Only 3-Character codes are allowed."
|
||||||
|
})
|
||||||
|
End If
|
||||||
|
End While
|
||||||
|
Catch ex As Exception
|
||||||
|
_logger.Error(ex)
|
||||||
|
End Try
|
||||||
|
|
||||||
|
' currencyID
|
||||||
|
Try
|
||||||
|
Dim oCurrencyIDIterator As XPathNodeIterator = oNavigator.Select("//*[@currencyID]")
|
||||||
|
|
||||||
|
While oCurrencyIDIterator.MoveNext()
|
||||||
|
Dim oNode As XPathNavigator = oCurrencyIDIterator.Current
|
||||||
|
Dim oCurrencyID As String = oNode.GetAttribute("currencyID", "")
|
||||||
|
|
||||||
|
' CurrencyID is optional per spec
|
||||||
|
If String.IsNullOrWhiteSpace(oCurrencyID) Then
|
||||||
|
Continue While
|
||||||
|
End If
|
||||||
|
|
||||||
|
Dim oValid = ValidateCurrencyCode(oCurrencyID)
|
||||||
|
If oValid = False Then
|
||||||
|
pResult.ValidationErrors.Add(New ZugferdValidationError() With {
|
||||||
|
.ElementName = oNode.Name,
|
||||||
|
.ElementValue = oCurrencyID,
|
||||||
|
.ErrorMessage = "Invalid currencyID. Only 3-Character codes or empty values are allowed."
|
||||||
|
})
|
||||||
|
End If
|
||||||
|
End While
|
||||||
|
Catch ex As Exception
|
||||||
|
_logger.Error(ex)
|
||||||
|
End Try
|
||||||
|
|
||||||
|
Return pResult
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function ValidateCurrencyCode(pValue As String) As Boolean
|
||||||
|
Dim oValueRegex As New Text.RegularExpressions.Regex("[A-Z]{3}")
|
||||||
|
|
||||||
|
If oValueRegex.IsMatch(pValue) = False Then
|
||||||
|
Return False
|
||||||
|
End If
|
||||||
|
|
||||||
|
Return True
|
||||||
|
End Function
|
||||||
|
|
||||||
Public Function SerializeZUGFeRDDocument(pResult As ZugferdResult) As ZugferdResult
|
Public Function SerializeZUGFeRDDocument(pResult As ZugferdResult) As ZugferdResult
|
||||||
Try
|
Try
|
||||||
Dim oNavigator As XPathNavigator = pResult.XPathObject.CreateNavigator()
|
Dim oNavigator As XPathNavigator = pResult.XPathObject.CreateNavigator()
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
Imports System.IO
|
Imports System.Collections.Generic
|
||||||
|
Imports System.IO
|
||||||
|
Imports DigitalData.Modules.Interfaces.ZUGFeRDInterface
|
||||||
|
|
||||||
Public Class Exceptions
|
Public Class Exceptions
|
||||||
Public Class MissingValueException
|
Public Class MissingValueException
|
||||||
@ -75,4 +77,14 @@ Public Class Exceptions
|
|||||||
MyBase.New(pInfo)
|
MyBase.New(pInfo)
|
||||||
End Sub
|
End Sub
|
||||||
End Class
|
End Class
|
||||||
|
|
||||||
|
Public Class ValidationException
|
||||||
|
Inherits ApplicationException
|
||||||
|
|
||||||
|
Public ValidationErrors As List(Of ZugferdValidationError)
|
||||||
|
|
||||||
|
Public Sub New()
|
||||||
|
MyBase.New("ZUGFeRD document found but validation failed!")
|
||||||
|
End Sub
|
||||||
|
End Class
|
||||||
End Class
|
End Class
|
||||||
|
|||||||
@ -20,6 +20,10 @@
|
|||||||
|
|
||||||
Public Const EMAIL_MD5_ERROR = "<p>Die von Ihnen gesendete Rechnung wurde bereits von unserem System verarbeitet.</p>"
|
Public Const EMAIL_MD5_ERROR = "<p>Die von Ihnen gesendete Rechnung wurde bereits von unserem System verarbeitet.</p>"
|
||||||
|
|
||||||
|
Public Const EMAIL_VALIDATION_ERROR = "
|
||||||
|
<p>Die von Ihnen gesendete Rechnung hat die ZUGFeRD Validierung nicht bestanden.</p>
|
||||||
|
<p>Die folgenden Felder sind nicht korrekt:<ul>{0}</ul></p>"
|
||||||
|
|
||||||
Public Const EMAIL_TOO_MUCH_FERDS = "<p>In Ihrer Email ({0}) sind mehr als ein ZUGFeRD Dokument enthalten. Bitte prüfen Sie Rechnung an Anhänge. Nur eine Rechnung darf das ZUGFeRD-Format enthalten</p>"
|
Public Const EMAIL_TOO_MUCH_FERDS = "<p>In Ihrer Email ({0}) sind mehr als ein ZUGFeRD Dokument enthalten. Bitte prüfen Sie Rechnung an Anhänge. Nur eine Rechnung darf das ZUGFeRD-Format enthalten</p>"
|
||||||
|
|
||||||
Public Const EMAIL_NO_FERDS = "<p>Ihre Email ({0}) enthielt keine ZUGFeRD-Dokumente.</p>"
|
Public Const EMAIL_NO_FERDS = "<p>Ihre Email ({0}) enthielt keine ZUGFeRD-Dokumente.</p>"
|
||||||
|
|||||||
@ -254,6 +254,7 @@ Public Class ImportZUGFeRDFiles
|
|||||||
|
|
||||||
Try
|
Try
|
||||||
oDocument = _zugferd.ExtractZUGFeRDFileWithGDPicture(oFile.FullName)
|
oDocument = _zugferd.ExtractZUGFeRDFileWithGDPicture(oFile.FullName)
|
||||||
|
|
||||||
Catch ex As ZUGFeRDExecption
|
Catch ex As ZUGFeRDExecption
|
||||||
Select Case ex.ErrorType
|
Select Case ex.ErrorType
|
||||||
Case ZUGFeRDInterface.ErrorType.NoZugferd
|
Case ZUGFeRDInterface.ErrorType.NoZugferd
|
||||||
@ -275,6 +276,17 @@ Public Class ImportZUGFeRDFiles
|
|||||||
End Select
|
End Select
|
||||||
End Try
|
End Try
|
||||||
|
|
||||||
|
' These validation errors check the document according to the specification.
|
||||||
|
' Things like this will be checked:
|
||||||
|
' - Currency Codes
|
||||||
|
' - Country Codes
|
||||||
|
' - DateTime Formats
|
||||||
|
If oDocument.ValidationErrors.Any() Then
|
||||||
|
Throw New ValidationException() With {
|
||||||
|
.ValidationErrors = oDocument.ValidationErrors
|
||||||
|
}
|
||||||
|
End If
|
||||||
|
|
||||||
' Extract all attachments with the extensions specified in `AllowedExtensions`.
|
' Extract all attachments with the extensions specified in `AllowedExtensions`.
|
||||||
' If you need to extract and use embedded xml files, you need to filter out the zugferd-invoice.xml yourself.
|
' If you need to extract and use embedded xml files, you need to filter out the zugferd-invoice.xml yourself.
|
||||||
' Right now the zugferd-invoice.xml is filtered out because `AllowedExtensions` does not contain `xml`.
|
' Right now the zugferd-invoice.xml is filtered out because `AllowedExtensions` does not contain `xml`.
|
||||||
@ -375,6 +387,24 @@ Public Class ImportZUGFeRDFiles
|
|||||||
oIsSuccess = True
|
oIsSuccess = True
|
||||||
oMoveDirectory = oArgs.SuccessDirectory
|
oMoveDirectory = oArgs.SuccessDirectory
|
||||||
|
|
||||||
|
Catch ex As ValidationException
|
||||||
|
_logger.Error(ex)
|
||||||
|
|
||||||
|
Dim oErrors = ex.ValidationErrors
|
||||||
|
Dim oMessage = "REJECTED - ZUGFeRD yes but formal validation failed!"
|
||||||
|
Update_HistoryEntry(oMessageId, oMD5CheckSum, oMessage, oFBTransaction)
|
||||||
|
|
||||||
|
Dim oErrorList As String = ""
|
||||||
|
For Each oError In oErrors
|
||||||
|
oErrorList += $"<li>Element '{oError.ElementName}' mit Wert '{oError.ElementValue}': {oError.ErrorMessage}</li>"
|
||||||
|
Next
|
||||||
|
|
||||||
|
Dim oBody = String.Format(EmailStrings.EMAIL_VALIDATION_ERROR, oErrorList)
|
||||||
|
Dim oEmailData = MoveAndRenameEmailToRejected(oArgs, oMessageId)
|
||||||
|
|
||||||
|
_email.AddToEmailQueueMSSQL(oMessageId, oBody, oEmailData, "ValidationException", _EmailOutAccountId, oArgs.NamePortal)
|
||||||
|
AddRejectedState(oMessageId, "ValidationException", "Die Rechnungsvalidierung ist fehlgeschlagen!", "", oSQLTransaction)
|
||||||
|
|
||||||
Catch ex As MD5HashException
|
Catch ex As MD5HashException
|
||||||
_logger.Error(ex)
|
_logger.Error(ex)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user