diff --git a/ValidacionDocumentosIdentidadVIES.vb b/ValidacionDocumentosIdentidadVIES.vb
new file mode 100644
index 0000000..0e4810d
--- /dev/null
+++ b/ValidacionDocumentosIdentidadVIES.vb
@@ -0,0 +1,127 @@
+Imports System.Net.Http
+Imports System.Text.Json
+Imports System.Text.RegularExpressions
+Imports System.Threading.Tasks
+
+Public Class VatValidationResult
+ Public Property IsValid As Boolean
+ Public Property Name As String
+ Public Property Address As String
+ Public Property RawJson As String
+ Public Property ErrorMessage As String
+End Class
+
+Public Class ValidacionDocumentosIdentidad
+
+ ' -----------------------------
+ ' 1) Validación de formato VAT
+ ' -----------------------------
+ Private Shared ReadOnly Patterns As New Dictionary(Of String, String) From {
+ {"AT", "^[A-Z]U[0-9]{8}$"},
+ {"BE", "^[0-9]{10}$"},
+ {"BG", "^[0-9]{9,10}$"},
+ {"CY", "^[0-9]{8}[A-Z]$"},
+ {"CZ", "^[0-9]{8,10}$"},
+ {"DE", "^[0-9]{9}$"},
+ {"DK", "^[0-9]{8}$"},
+ {"EE", "^[0-9]{9}$"},
+ {"EL", "^[0-9]{9}$"},
+ {"ES", "^[A-Z0-9][0-9]{7}[A-Z0-9]$"},
+ {"FI", "^[0-9]{8}$"},
+ {"FR", "^[A-Z0-9]{2}[0-9]{9}$"},
+ {"HR", "^[0-9]{11}$"},
+ {"HU", "^[0-9]{8}$"},
+ {"IE", "^[0-9]{7}[A-W][A-I]?$"},
+ {"IT", "^[0-9]{11}$"},
+ {"LT", "^[0-9]{9}|[0-9]{12}$"},
+ {"LU", "^[0-9]{8}$"},
+ {"LV", "^[0-9]{11}$"},
+ {"MT", "^[0-9]{8}$"},
+ {"NL", "^[0-9]{9}B[0-9]{2}$"},
+ {"PL", "^[0-9]{10}$"},
+ {"PT", "^[0-9]{9}$"},
+ {"RO", "^[0-9]{2,10}$"},
+ {"SE", "^[0-9]{12}$"},
+ {"SI", "^[0-9]{8}$"},
+ {"SK", "^[0-9]{10}$"},
+ {"XI", "^[0-9]{9}$"} ' Irlanda del Norte
+ }
+
+ Public Shared Function IsValidVatFormat(countryCode As String,
+ vatNumber As String) As Boolean
+
+ countryCode = countryCode.ToUpper().Trim()
+ vatNumber = vatNumber.ToUpper().Trim()
+
+ If Not Patterns.ContainsKey(countryCode) Then
+ Return False
+ End If
+
+ Return Regex.IsMatch(vatNumber, Patterns(countryCode))
+ End Function
+
+
+ ' -----------------------------
+ ' 2) Cliente HTTP compartido
+ ' -----------------------------
+ Private Shared ReadOnly _http As New HttpClient() With {
+ .Timeout = TimeSpan.FromSeconds(10)
+ }
+
+
+ ' -----------------------------
+ ' 3) Validación VIES vía REST (ASÍNCRONA)
+ ' -----------------------------
+ Public Shared Async Function ValidateVatAsync(countryCode As String,
+ vatNumber As String) As Task(Of VatValidationResult)
+
+ Dim result As New VatValidationResult()
+
+ Try
+ ' Validación de formato previa
+ If Not IsValidVatFormat(countryCode, vatNumber) Then
+ result.IsValid = False
+ result.ErrorMessage = $"Formato VAT inválido para {countryCode}"
+ Return result
+ End If
+
+ Dim url = $"https://ec.europa.eu/taxation_customs/vies/rest-api/ms/{countryCode}/vat/{vatNumber}"
+
+ Dim json As String = Await _http.GetStringAsync(url)
+ result.RawJson = json
+
+ Dim doc = JsonDocument.Parse(json)
+ Dim root = doc.RootElement
+
+ result.IsValid = root.GetProperty("isValid").GetBoolean()
+
+ If result.IsValid Then
+ result.Name = root.GetProperty("traderName").GetString()
+ result.Address = root.GetProperty("traderAddress").GetString()
+ End If
+
+ Catch ex As HttpRequestException
+ result.ErrorMessage = "Error HTTP: " & ex.Message
+
+ Catch ex As TaskCanceledException
+ result.ErrorMessage = "Timeout al consultar VIES"
+
+ Catch ex As Exception
+ result.ErrorMessage = "Error inesperado: " & ex.Message
+ End Try
+
+ Return result
+ End Function
+
+
+ ' -----------------------------
+ ' 4) Validación VIES vía REST (SINCRONA)
+ ' -----------------------------
+ Public Shared Function ValidateVat(countryCode As String,
+ vatNumber As String) As VatValidationResult
+
+ Return ValidateVatAsync(countryCode, vatNumber).GetAwaiter().GetResult()
+
+ End Function
+
+End Class
diff --git a/tsUtilidades.vbproj b/tsUtilidades.vbproj
index 89333c4..54d52bc 100644
--- a/tsUtilidades.vbproj
+++ b/tsUtilidades.vbproj
@@ -16,11 +16,12 @@
net8.0
tsUtilidades
net8.0, libreria
- 1.1.18
+ 1.1.19
Manuel
Tecnosis S.A
Utilidades Varias
+ - 2026-07-22 1.1.19 Nueva Clase ValidacionDocumentosIdentidad para documentos extranjeros
- 2026-05-29 1.1.18 Corrección rutina LeeMysql y LeeMysqlBytArray para que como maximo se admitan 2048 caracteres string
- 2026-05-29 1.1.17 Corrección rutina LeeMysql para que como maximo se admitan 2048 caracteres string
- 2026-05-19 1.1.16 Corrección multiplataforma tsNotificacionesClient