From 24dbed32ccd6ee79754de2c6ec54a3084ca39773 Mon Sep 17 00:00:00 2001 From: pitzm Date: Mon, 2 Feb 2026 14:40:55 +0100 Subject: [PATCH 01/11] ZUGFeRdRESTService - Validation Controller: REFERENCES_Rejection-Messages auswerten und als WebMessage ausgeben --- .../Controllers/ValidationController.cs | 292 ++++++++++++++++-- .../appsettings.Development.json | 10 +- 2 files changed, 269 insertions(+), 33 deletions(-) diff --git a/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs b/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs index 0ac2c0e8..ae616ab0 100644 --- a/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs +++ b/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs @@ -1,18 +1,17 @@ -using System; -using System.IO; -using System.Linq; -using System.Collections.Generic; +using DigitalData.Modules.Interfaces; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using DigitalData.Modules.Interfaces; -using static DigitalData.Modules.Interfaces.Exceptions; -using static DigitalData.Modules.Interfaces.ZUGFeRDInterface; -using static DigitalData.Modules.Interfaces.PropertyValues; -using System.Data.SqlClient; using Microsoft.Extensions.Configuration; -using System.Xml.Linq; -using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Data.SqlClient; +using System.IO; +using System.Linq; using System.Text.RegularExpressions; +using System.Xml.Linq; +using static DigitalData.Modules.Interfaces.Exceptions; +using static DigitalData.Modules.Interfaces.PropertyValues; +using static DigitalData.Modules.Interfaces.ZUGFeRDInterface; namespace ZUGFeRDRESTService.Controllers { @@ -26,6 +25,29 @@ namespace ZUGFeRDRESTService.Controllers public const string ADDED_WHO = "ZUGFeRD REST Service"; public const string MESSAGEID_DOMAIN = "test.wisag.de"; + public const string VALIDATION_SUCCESS = "VALIDATION SUCCESS"; + public const string REFERENCES_Rejection_30001 = "REFERENCES_Rejection_30001"; + public const string REFERENCES_Rejection_30002 = "REFERENCES_Rejection_30002"; + public const string REFERENCES_Rejection_30003_1 = "REFERENCES_Rejection_30003_1"; + public const string REFERENCES_Rejection_30003_2 = "REFERENCES_Rejection_30003_2"; + public const string REFERENCES_Rejection_30003_3 = "REFERENCES_Rejection_30003_3"; + public const string REFERENCES_Rejection_30004_1 = "REFERENCES_Rejection_30004_1"; + public const string REFERENCES_Rejection_30004_2 = "REFERENCES_Rejection_30004_2"; + public const string REFERENCES_Rejection_30004_3 = "REFERENCES_Rejection_30004_3"; + public const string REFERENCES_Rejection_30005_1 = "REFERENCES_Rejection_30005_1"; + public const string REFERENCES_Rejection_30005_2 = "REFERENCES_Rejection_30005_2"; + public const string REFERENCES_Rejection_30006 = "REFERENCES_Rejection_30006" ; + public const string REFERENCES_Rejection_30007 = "REFERENCES_Rejection_30007" ; + public const string REFERENCES_Rejection_30007_1 = "REFERENCES_Rejection_30007_1"; + public const string REFERENCES_Rejection_30008 = "REFERENCES_Rejection_30008" ; + public const string REFERENCES_Rejection_30009 = "REFERENCES_Rejection_30009" ; + public const string REFERENCES_Rejection_30010 = "REFERENCES_Rejection_30010" ; + public const string REFERENCES_Rejection_30011 = "REFERENCES_Rejection_30011" ; + public const string REFERENCES_Rejection_30012 = "REFERENCES_Rejection_30012" ; + public const string AMOUNT_CALC_REJECTION = "AMOUNT_CALC_REJECTION"; + + private List _ValidationErrors; + private const int MAX_FILE_SIZE_DEFAULT = 25; private readonly ZUGFeRDInterface _zugferd; @@ -82,7 +104,29 @@ namespace ZUGFeRDRESTService.Controllers _logger.Debug("Property Map list initial: [{0}] entries found. [{1}] entries will be available.", oPropertyMapList.Count, _propertyMapList.Count); _RecjectionMessageList = database.GetRejectionMessageList(); - + + _ValidationErrors = new List() { + REFERENCES_Rejection_30001, + REFERENCES_Rejection_30002, + REFERENCES_Rejection_30003_1, + REFERENCES_Rejection_30003_2, + REFERENCES_Rejection_30003_3, + REFERENCES_Rejection_30004_1, + REFERENCES_Rejection_30004_2, + REFERENCES_Rejection_30004_3, + REFERENCES_Rejection_30005_1, + REFERENCES_Rejection_30005_2, + REFERENCES_Rejection_30006, + REFERENCES_Rejection_30007, + REFERENCES_Rejection_30007_1, + REFERENCES_Rejection_30008, + REFERENCES_Rejection_30009, + REFERENCES_Rejection_30010, + REFERENCES_Rejection_30011, + REFERENCES_Rejection_30012, + AMOUNT_CALC_REJECTION + }; + _logger.Debug("Validation Controller initialized!"); } @@ -189,19 +233,19 @@ namespace ZUGFeRDRESTService.Controllers _logger.Info("Detected Specification was: [{0}]", oZugferdResult.Specification); - var oFilteredPropertyMap = _zugferd.FilterPropertyMap(_propertyMapList, oZugferdResult.Specification); + var oFilteredPropertyMap = _zugferd.FilterPropertyMap(_propertyMapList, oZugferdResult.Specification); if (oFilteredPropertyMap.Count == 0) { _logger.Warn("No properties found in property map for specification [{0}]", oZugferdResult.Specification); //throw new ZUGFeRDExecption(ErrorType.UnsupportedFormat, "Unsupported Format"); throw new ZUGFeRDExecption(ErrorCodes.UnsupportedFerdException, "Unsupported Format"); - } + } else { _logger.Debug("Property map contains [{0}] entries for specification [{1}]", oFilteredPropertyMap.Count, oZugferdResult.Specification); } - + _logger.Info("Starting structural check against the database."); oPropertyResult = _props.CheckPropertyValues(oZugferdResult.SchemaObject, oFilteredPropertyMap, "MESSAGEID"); @@ -221,7 +265,7 @@ namespace ZUGFeRDRESTService.Controllers { //throw new ZUGFeRDExecption(ErrorType.MissingProperties, "Missing Properties"); throw new ZUGFeRDExecption(ErrorCodes.MissingValueException, "Missing Properties"); - } + } Tuple oValidateResult = ValidateBuyerOrderReference(oPropertyResult.ValidProperties); @@ -233,10 +277,9 @@ namespace ZUGFeRDRESTService.Controllers string oValidateResultString = oValidateResult.Item2; - if (oValidateResultString == "ALL REFERENCES CHECKED POSITIVE") + if (oValidateResultString.Equals(VALIDATION_SUCCESS)) { string oMessage = "Die hochgeladene Datei ist eine gültige-ZUGFeRD Rechnung"; - _logger.Info($"Responding with message: [{oMessage}]"); return new ValidationResponse() @@ -244,10 +287,10 @@ namespace ZUGFeRDRESTService.Controllers status = RESPONSE_OK, message = oMessage }; - } else + } + else { string oMessage = oValidateResultString; - _logger.Info($"Responding with message: [{oMessage}]"); return new ValidationResponse() @@ -256,7 +299,8 @@ namespace ZUGFeRDRESTService.Controllers message = oMessage }; } - }; + } + } catch (ZUGFeRDExecption ex) { @@ -299,9 +343,9 @@ namespace ZUGFeRDRESTService.Controllers break; } } - else + else { - oMessage = "Alte Logik. Meldung nicht gefunden"; + oMessage = "Alte Logik. Meldung nicht gefunden"; } _logger.Info($"Responding with message: [{oMessage}]"); @@ -315,6 +359,8 @@ namespace ZUGFeRDRESTService.Controllers } catch (ValidationException ex) { + _logger.Error(ex); + var rejectionCodeNumber = this.GetRejectionCodeNumber(ex.ErrorCode); // Der gesamte Ausgabetext muss anhand des ErrorCodes ermittelt werden @@ -348,10 +394,14 @@ namespace ZUGFeRDRESTService.Controllers } } + /// + /// Hier wird eine externe Prozedur gerufen, PRCUST_INV_CHECK_FROM_PORTAL, + /// die das Ergebnis der Referenzpruefung liefert. + /// private Tuple ValidateBuyerOrderReference(List pProperties) { var oMessageId = GetMessageId(); - _logger.Debug("Created new MessageId: [{0}]", oMessageId); + _logger.Debug("Created new MessageId: [{0}]", oMessageId); _logger.Debug("Inserting properties into database."); foreach (var oItem in pProperties) @@ -377,6 +427,59 @@ namespace ZUGFeRDRESTService.Controllers if (_database.MSSQL.ExecuteNonQuery(oCommand)) { string oReturnValue = (string)oCommand.Parameters["@MSG_OUTPUT"].Value; + + _logger.Debug("Validation Result message from DB: " + oReturnValue); + + if (oReturnValue.Equals("ALL REFERENCES CHECKED POSITIVE", StringComparison.OrdinalIgnoreCase)) + { + _logger.Debug("Validation Success"); + oReturnValue = VALIDATION_SUCCESS; + } + else + { + // Gehe durch die möglichen Fehler, und ermittle Rückmeldung + foreach (var oRejectionItem in _ValidationErrors) + { + if (oReturnValue.Contains(oRejectionItem, StringComparison.OrdinalIgnoreCase)) + { + _logger.Debug("oRejectionItem match: " + oRejectionItem); + + var oDbMessage = this.GetRejectionMessage(oRejectionItem); + + //Jetzt müssen ggf Platzhalter ersetzt werden. + if (oRejectionItem == REFERENCES_Rejection_30003_2) + { + string oReplaceParam1 = GetReplaceText1_30003_2(pProperties); + string oReplaceParam2 = GetReplaceText2_30003_2(pProperties); + + oReturnValue = oDbMessage.Replace("@REPLACE_PARAM1", oReplaceParam1); + oReturnValue = oReturnValue.Replace("@REPLACE_PARAM2", oReplaceParam2); + + } + else if (oRejectionItem == REFERENCES_Rejection_30003_3) + { + string oReplaceParam1 = GetReplaceText1_30003_3(pProperties); + + oReturnValue = oDbMessage.Replace("@REPLACE_PARAM1", oReplaceParam1); + } + else if (oRejectionItem == REFERENCES_Rejection_30001) + { + // TODO - BUKR? + oReturnValue = oDbMessage.Replace("@REPLACE_PARAM1", "9999"); + } + else + { + oReturnValue = oDbMessage; + } + + // Für alle den RejectionCode setzen + oReturnValue = oReturnValue.Replace("@REJECTION_CODE", oRejectionItem); + break; + } + } + } + + _logger.Debug("Validation terminal Result message: " + oReturnValue); return new Tuple(true, oReturnValue); } @@ -390,7 +493,122 @@ namespace ZUGFeRDRESTService.Controllers { _logger.Error(e); return new Tuple(false, string.Empty); - } + } + } + + private string GetReplaceText1_30003_3(List pProperties) + { + string oReplaceParam1 = string.Empty; + + // BuyerOrderReferencedDocument + var itemBT13 = pProperties.Where(i => i.TableColumn == "INVOICE_REFERENCE").FirstOrDefault(); + string valueBt13 = "-"; + if (itemBT13 != null) + { + valueBt13 = string.IsNullOrEmpty(itemBT13.Value) ? "-" : itemBT13.Value; + } + oReplaceParam1 += "
  • [BuyerOrderReferencedDocument] (BT-13) = [" + valueBt13 + "]
  • "; + + + // BuyerReference + var itemBT10 = pProperties.Where(i => i.TableColumn == "INVOICE_REFERENCE3").FirstOrDefault(); + string valueBt10 = "-"; + if (itemBT10 != null) + { + valueBt10 = string.IsNullOrEmpty(itemBT10.Value) ? "-" : itemBT10.Value; + } + oReplaceParam1 += "
  • [BuyerReference] (BT-10) = [" + valueBt10 + "]
  • "; + + + // CostCenter + var itemBT19 = pProperties.Where(i => i.TableColumn == "INVOICE_COST_CENTER").FirstOrDefault(); + string valueBt19 = "-"; + if (itemBT19 != null) + { + valueBt19 = string.IsNullOrEmpty(itemBT19.Value) ? "-" : itemBT19.Value; + } + oReplaceParam1 += "
  • [CostCenter] (BT-19) = [" + valueBt19 + "]
  • "; + + // BuyerID + var itemBT46 = pProperties.Where(i => i.TableColumn == "INVOICE_BUYER_ID").FirstOrDefault(); + string valueBt46 = "-"; + if (itemBT46 != null) + { + valueBt46 = string.IsNullOrEmpty(itemBT46.Value) ? "-" : itemBT46.Value; + } + oReplaceParam1 += "
  • [BuyerTradeParty] (BT-46) = [" + valueBt46 + "]
  • "; + + oReplaceParam1 = "
      " + oReplaceParam1 + "
    "; + _logger.Debug("oReplaceParam1-Text: " + oReplaceParam1); + + return oReplaceParam1; + } + + private string GetReplaceText2_30003_2(List pProperties) + { + string oReplaceParam2 = string.Empty; + + // BuyerTradeParty.Name + var itemBT44 = pProperties.Where(i => i.TableColumn == "INVOICE_BUYER_NAME").FirstOrDefault(); + string valueBt44 = "-"; + if (itemBT44 != null) + { + valueBt44 = string.IsNullOrEmpty(itemBT44.Value) ? "-" : itemBT44.Value; + } + oReplaceParam2 += "
  • [BuyerTradeParty.Name] (BT-44) = [" + valueBt44 + "]
  • "; + + // BuyerTradeParty.PostalTradeAddress.LineTwo + var itemBT51 = pProperties.Where(i => i.TableColumn == "INVOICE_BUYER_ADRESS2").FirstOrDefault(); + string valueBt51 = "-"; + if (itemBT51 != null) + { + valueBt51 = string.IsNullOrEmpty(itemBT51.Value) ? "-" : itemBT51.Value; + } + oReplaceParam2 += "
  • [BuyerTradeParty.PostalTradeAddress.LineTwo] (BT-51) = [" + valueBt51 + "]
  • "; + + oReplaceParam2 = "
      " + oReplaceParam2 + "
    "; + _logger.Debug("oReplaceParam2-Text: " + oReplaceParam2); + + return oReplaceParam2; + } + + private string GetReplaceText1_30003_2(List pProperties) + { + string oReplaceParam1 = string.Empty; + + // BuyerOrderReferencedDocument + var itemBT13 = pProperties.Where(i => i.TableColumn == "INVOICE_REFERENCE").FirstOrDefault(); + string valueBt13 = "-"; + if (itemBT13 != null) + { + valueBt13 = string.IsNullOrEmpty(itemBT13.Value) ? "-" : itemBT13.Value; + } + oReplaceParam1 += "
  • [BuyerOrderReferencedDocument] (BT-13) = [" + valueBt13 + "]
  • "; + + + // BuyerReference + var itemBT10 = pProperties.Where(i => i.TableColumn == "INVOICE_REFERENCE3").FirstOrDefault(); + string valueBt10 = "-"; + if (itemBT10 != null) + { + valueBt10 = string.IsNullOrEmpty(itemBT10.Value) ? "-" : itemBT10.Value; + } + oReplaceParam1 += "
  • [BuyerReference] (BT-10) = [" + valueBt10 + "]
  • "; + + + // CostCenter + var itemBT19 = pProperties.Where(i => i.TableColumn == "INVOICE_COST_CENTER").FirstOrDefault(); + string valueBt19 = "-"; + if (itemBT19 != null) + { + valueBt19 = string.IsNullOrEmpty(itemBT19.Value) ? "-" : itemBT19.Value; + } + oReplaceParam1 += "
  • [CostCenter] (BT-19) = [" + valueBt19 + "]
  • "; + + oReplaceParam1 = "
      " + oReplaceParam1 + "
    "; + _logger.Debug("oReplaceParam1-Text: " + oReplaceParam1); + + return oReplaceParam1; } public string GetMessageId() @@ -428,7 +646,7 @@ namespace ZUGFeRDRESTService.Controllers { itemValue = pProperty.Value; } - + var oParams = new SqlParameter[] { new SqlParameter("@REFERENCE_GUID", pMessageId), @@ -449,7 +667,7 @@ namespace ZUGFeRDRESTService.Controllers { _logger.Error(ex); return false; - } + } } /// @@ -463,7 +681,25 @@ namespace ZUGFeRDRESTService.Controllers // Sprache wird man vielleicht mal auswählen können var language = "de-DE"; - var searchTitle = "ZUGFERD_Rejection_" + pErrorCode + "_Web"; + var searchTitle = string.Empty; + + if (pErrorCode.Contains("2000")) + { + searchTitle = "ZUGFERD_Rejection_" + pErrorCode + "_Web"; + } + else if (pErrorCode.Contains("REFERENCES_Rejection_3000")) + { + searchTitle = pErrorCode + "_Web"; + } + else if (pErrorCode.Contains("AMOUNT_CALC")) + { + searchTitle = "AMOUNT_CALC_REJECTION_Web"; + } + else + { + // else-Fall muss noch geklärt werden. + searchTitle = "AMOUNT_CALC_REJECTION_Web"; + } var messageItem = _RecjectionMessageList.Where(i => i.Title.Equals(searchTitle, StringComparison.OrdinalIgnoreCase) && i.Language.Equals(language, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); if (messageItem != null) diff --git a/WEBSERVICES/ZUGFeRDRESTService/appsettings.Development.json b/WEBSERVICES/ZUGFeRDRESTService/appsettings.Development.json index b6613161..1bf7cf67 100644 --- a/WEBSERVICES/ZUGFeRDRESTService/appsettings.Development.json +++ b/WEBSERVICES/ZUGFeRDRESTService/appsettings.Development.json @@ -20,11 +20,11 @@ }, "Zugferd": { "AllowZugferd10": true, - "AllowZugferd2x": false, - "AllowZugferd23x": false, - "AllowFacturX": false, - "AllowXRechnung": false, - "AllowPeppolBISBill3x": false + "AllowZugferd2x": true, + "AllowZugferd23x": true, + "AllowFacturX": true, + "AllowXRechnung": true, + "AllowPeppolBISBill3x": true }, "GDPictureVersion": "", "MaxFileSizeInMegabytes": 25 From d1dbe8096d73955588b02af355154a94cb9fd4d5 Mon Sep 17 00:00:00 2001 From: pitzm Date: Wed, 4 Feb 2026 14:13:11 +0100 Subject: [PATCH 02/11] =?UTF-8?q?ValidationController:=20BulkInsert,=20XML?= =?UTF-8?q?-Schema=20etc=20f=C3=BCr=20die=20Datenbank=20mitnehmen.=20Korre?= =?UTF-8?q?kturen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/ValidationController.cs | 275 +++++++++++++----- 1 file changed, 210 insertions(+), 65 deletions(-) diff --git a/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs b/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs index ae616ab0..49ff5c5d 100644 --- a/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs +++ b/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using System; using System.Collections.Generic; +using System.Data; using System.Data.SqlClient; using System.IO; using System.Linq; @@ -36,14 +37,14 @@ namespace ZUGFeRDRESTService.Controllers public const string REFERENCES_Rejection_30004_3 = "REFERENCES_Rejection_30004_3"; public const string REFERENCES_Rejection_30005_1 = "REFERENCES_Rejection_30005_1"; public const string REFERENCES_Rejection_30005_2 = "REFERENCES_Rejection_30005_2"; - public const string REFERENCES_Rejection_30006 = "REFERENCES_Rejection_30006" ; - public const string REFERENCES_Rejection_30007 = "REFERENCES_Rejection_30007" ; + public const string REFERENCES_Rejection_30006 = "REFERENCES_Rejection_30006"; + public const string REFERENCES_Rejection_30007 = "REFERENCES_Rejection_30007"; public const string REFERENCES_Rejection_30007_1 = "REFERENCES_Rejection_30007_1"; - public const string REFERENCES_Rejection_30008 = "REFERENCES_Rejection_30008" ; - public const string REFERENCES_Rejection_30009 = "REFERENCES_Rejection_30009" ; - public const string REFERENCES_Rejection_30010 = "REFERENCES_Rejection_30010" ; - public const string REFERENCES_Rejection_30011 = "REFERENCES_Rejection_30011" ; - public const string REFERENCES_Rejection_30012 = "REFERENCES_Rejection_30012" ; + public const string REFERENCES_Rejection_30008 = "REFERENCES_Rejection_30008"; + public const string REFERENCES_Rejection_30009 = "REFERENCES_Rejection_30009"; + public const string REFERENCES_Rejection_30010 = "REFERENCES_Rejection_30010"; + public const string REFERENCES_Rejection_30011 = "REFERENCES_Rejection_30011"; + public const string REFERENCES_Rejection_30012 = "REFERENCES_Rejection_30012"; public const string AMOUNT_CALC_REJECTION = "AMOUNT_CALC_REJECTION"; private List _ValidationErrors; @@ -209,7 +210,6 @@ namespace ZUGFeRDRESTService.Controllers if (oFileSizeIsOK == false) { - //throw new ZUGFeRDExecption(ErrorType.FileTooBig, "FileTooBig"); throw new ZUGFeRDExecption(ErrorCodes.FileSizeLimitReachedException, _MaxFileSizeInMegabytes.ToString(), string.Empty, "FileTooBig"); } @@ -218,6 +218,7 @@ namespace ZUGFeRDRESTService.Controllers if (file.FileName.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase)) { oZugferdResult = _zugferd.ExtractZUGFeRDFileWithGDPicture(oStream); + oZugferdResult.ReceiptFileType = "PDF"; } else if (file.FileName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase)) { @@ -229,6 +230,7 @@ namespace ZUGFeRDRESTService.Controllers }; oZugferdResult = _zugferd.SerializeZUGFeRDDocument(oResult); + oZugferdResult.ReceiptFileType = "XML"; } _logger.Info("Detected Specification was: [{0}]", oZugferdResult.Specification); @@ -238,7 +240,6 @@ namespace ZUGFeRDRESTService.Controllers if (oFilteredPropertyMap.Count == 0) { _logger.Warn("No properties found in property map for specification [{0}]", oZugferdResult.Specification); - //throw new ZUGFeRDExecption(ErrorType.UnsupportedFormat, "Unsupported Format"); throw new ZUGFeRDExecption(ErrorCodes.UnsupportedFerdException, "Unsupported Format"); } else @@ -263,15 +264,13 @@ namespace ZUGFeRDRESTService.Controllers if (oPropertyResult.MissingProperties.Count > 0) { - //throw new ZUGFeRDExecption(ErrorType.MissingProperties, "Missing Properties"); throw new ZUGFeRDExecption(ErrorCodes.MissingValueException, "Missing Properties"); } - Tuple oValidateResult = ValidateBuyerOrderReference(oPropertyResult.ValidProperties); + Tuple oValidateResult = ValidateBuyerOrderReference(oPropertyResult.ValidProperties, oZugferdResult); if (oValidateResult.Item1 == false) { - //throw new ZUGFeRDExecption(ErrorType.UnknownError, "Unknown Error"); throw new ZUGFeRDExecption(ErrorCodes.UnhandledException, "Unknown Error"); } @@ -398,20 +397,16 @@ namespace ZUGFeRDRESTService.Controllers /// Hier wird eine externe Prozedur gerufen, PRCUST_INV_CHECK_FROM_PORTAL, /// die das Ergebnis der Referenzpruefung liefert. /// - private Tuple ValidateBuyerOrderReference(List pProperties) + private Tuple ValidateBuyerOrderReference(List pProperties, ZugferdResult pZugferdResult) { var oMessageId = GetMessageId(); _logger.Debug("Created new MessageId: [{0}]", oMessageId); _logger.Debug("Inserting properties into database."); - foreach (var oItem in pProperties) + if (!BulkInsertDataToDatabase(oMessageId, pProperties, pZugferdResult)) { - var oResult = InsertPropertyMap(oItem, oMessageId); - - if (oResult == false) - { - _logger.Warn("Error while inserting the Property [{0}] into the Database!", oItem.Description); - } + _logger.Warn("Error while Inserting properties into database!"); + return new Tuple(false, string.Empty); } _logger.Debug("Calling validation prodecure."); @@ -472,8 +467,13 @@ namespace ZUGFeRDRESTService.Controllers oReturnValue = oDbMessage; } - // Für alle den RejectionCode setzen - oReturnValue = oReturnValue.Replace("@REJECTION_CODE", oRejectionItem); + // Der REJECTION-Code wird in alle Meldungen eingefügt. + if (!string.IsNullOrEmpty(oRejectionItem) && oReturnValue.Contains("@REJECTION_CODE", StringComparison.OrdinalIgnoreCase)) + { + var rejectionCodeNumber = GetRejectionCodeNumber(oRejectionItem); + oReturnValue = Regex.Replace(oReturnValue, "@REJECTION_CODE", "Ablehnungscode: " + rejectionCodeNumber, RegexOptions.IgnoreCase); + } + break; } } @@ -616,60 +616,194 @@ namespace ZUGFeRDRESTService.Controllers return $"{Guid.NewGuid()}@{MESSAGEID_DOMAIN}"; } - public bool InsertPropertyMap(ValidProperty pProperty, string pMessageId) + private bool BulkInsertDataToDatabase(string pMessageId, List pProperties, ZugferdResult pZugferdResult) + { + if (string.IsNullOrEmpty(pMessageId)) + { + return false; + } + + if (!DeleteExistingPropertyValues(pMessageId)) + { + return false; + } + + DataTable oDataTable = PrepareDataTable(pMessageId, pProperties, pZugferdResult); + + // ColumnList initialisieren + List oColumnNames = new List { + "REFERENCE_GUID", + "ITEM_DESCRIPTION", + "ITEM_VALUE", + "GROUP_COUNTER", + "SPEC_NAME", + "IS_REQUIRED" + }; + + bool oBulkResult = BulkInsert(oDataTable, "TBEDMI_ITEM_VALUE", oColumnNames); + + if (!oBulkResult) + { + _logger.Error("Bulk Insert for MessageId [{0}] failed!", pMessageId); + return false; + } + + _logger.Info("Bulk Insert finished. [{0}] rows inserted for MessageId [{1}].", oDataTable.Rows.Count, pMessageId); + return true; + } + + private bool BulkInsert(DataTable pDataTable, string pDestinationTable, List pColumnNames) + { + using (var oSqlBulkCopy = new SqlBulkCopy(_database.MSSQL.GetConnection())) + { + oSqlBulkCopy.DestinationTableName = pDestinationTable; + foreach (var columnItem in pColumnNames) + { + oSqlBulkCopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping(columnItem, columnItem)); + } + + try + { + oSqlBulkCopy.WriteToServer(pDataTable); + } + catch (Exception ex) + { + _logger.Error(ex); + return false; + } + } + + return true; + } + + private DataTable PrepareDataTable(string pMessageId, List pProperties, ZugferdResult pDocument) + { + DataTable oDataTable = new DataTable(); + oDataTable.Columns.Add(new DataColumn("REFERENCE_GUID", typeof(string))); + oDataTable.Columns.Add(new DataColumn("ITEM_DESCRIPTION", typeof(string))); + oDataTable.Columns.Add(new DataColumn("ITEM_VALUE", typeof(string))); + oDataTable.Columns.Add(new DataColumn("GROUP_COUNTER", typeof(Int32))); + oDataTable.Columns.Add(new DataColumn("SPEC_NAME", typeof(string))); + oDataTable.Columns.Add(new DataColumn("IS_REQUIRED", typeof(bool))); + + // Erste Zeile enthält die Spezifikation + DataRow oFirstRow = oDataTable.NewRow(); + oFirstRow["REFERENCE_GUID"] = pMessageId; + oFirstRow["ITEM_DESCRIPTION"] = "Verwendete Spezifikation der E-Rechnung"; + oFirstRow["ITEM_VALUE"] = pDocument.Specification; + oFirstRow["GROUP_COUNTER"] = 0; + oFirstRow["SPEC_NAME"] = "ZUGFERD_SPECIFICATION"; + oFirstRow["IS_REQUIRED"] = false; + + _logger.Debug("Mapping Property [ZUGFERD_SPECIFICATION] with value [{0}]", pDocument.Specification); + oDataTable.Rows.Add(oFirstRow); + + //' Zweite Zeile enthält das verwendete XML Schema + DataRow oSecondRow = oDataTable.NewRow(); + oSecondRow["REFERENCE_GUID"] = pMessageId; + oSecondRow["ITEM_DESCRIPTION"] = "Verwendetes XML-Schema (XSD) der E-Rechnung"; + oSecondRow["ITEM_VALUE"] = pDocument.UsedXMLSchema; + oSecondRow["GROUP_COUNTER"] = 0; + oSecondRow["SPEC_NAME"] = "ZUGFERD_XML_SCHEMA"; + oSecondRow["IS_REQUIRED"] = false; + + _logger.Debug("Mapping Property [ZUGFERD_XML_SCHEMA] with value [{0}]", pDocument.UsedXMLSchema); + oDataTable.Rows.Add(oSecondRow); + + //' Dritte Zeile enthält das verwendete Datei-Format des Belegs (PDF/XML) + if (pDocument.ReceiptFileType != null) + { + DataRow oThirdRow = oDataTable.NewRow(); + oThirdRow["REFERENCE_GUID"] = pMessageId; + oThirdRow["ITEM_DESCRIPTION"] = "Dateityp der E-Rechnung"; + oThirdRow["ITEM_VALUE"] = pDocument.ReceiptFileType; + oThirdRow["GROUP_COUNTER"] = 0; + oThirdRow["SPEC_NAME"] = "RECEIPT_FILE_TYPE"; + oThirdRow["IS_REQUIRED"] = false; + + _logger.Debug("Mapping Property [RECEIPT_FILE_TYPE] with value [{0}]", pDocument.ReceiptFileType); + oDataTable.Rows.Add(oThirdRow); + } + + foreach (var propertyItem in pProperties) + { + + // ItemType = 3 => eingebettete Datei, nicht den base64 speichern + if (propertyItem.ItemType == 3) + { + continue; + } + + // ItemType = 0 (normale texte) dürfen nicht leer sein - leere Werte werden überlesen + if (propertyItem.ItemType == 0 && string.IsNullOrEmpty(propertyItem.Value)) + { + continue; + } + + // If GroupCounter is -1, it means this is a default property that can only occur once. + // Set the actual inserted value to 0 + var oGroupCounterValue = propertyItem.GroupCounter < 0 ? 0 : propertyItem.GroupCounter; + + if (propertyItem.Value.Length > 4000) + { + _logger.Warn("Value for field [{0}] is longer than 4000 characters, will be truncated!", propertyItem.TableColumn); + propertyItem.Value = propertyItem.Value.Substring(0, 4000); + } + + // Description mit BT-Feld aufbereiten + var betterDescription = propertyItem.Description; + if (!string.IsNullOrEmpty(propertyItem.EN16931_ID)) + { + betterDescription = propertyItem.EN16931_ID + " (" + propertyItem.Description + ")"; + } + + DataRow newRow = oDataTable.NewRow(); + newRow["REFERENCE_GUID"] = pMessageId; + newRow["ITEM_DESCRIPTION"] = betterDescription; + newRow["ITEM_VALUE"] = propertyItem.Value; + newRow["GROUP_COUNTER"] = oGroupCounterValue; + newRow["SPEC_NAME"] = propertyItem.TableColumn; + newRow["IS_REQUIRED"] = propertyItem.IsRequired; + + _logger.Debug("Mapping Property [{0}] with value [{1}]", propertyItem.TableColumn, propertyItem.Value); + oDataTable.Rows.Add(newRow); + } + + return oDataTable; + } + + public bool DeleteExistingPropertyValues(string pMessageId) { try { - if (pProperty.ItemType == 3) - { - // Wir speichern keine Attachment-Werte in die DB - return true; - } - - if (pProperty.ItemType == 0 && string.IsNullOrEmpty(pProperty.Value.ToString())) - { - // Leere Texte speichern wir nicht in der DB - return true; - } - - var oSql = $"INSERT INTO {pProperty.TableName} " + - "(REFERENCE_GUID, ITEM_DESCRIPTION, ITEM_VALUE, CREATEDWHO, SPEC_NAME, GROUP_COUNTER, IS_REQUIRED) VALUES " + - "(@REFERENCE_GUID, @ITEM_DESCRIPTION, @ITEM_VALUE, @CREATEDWHO, @SPEC_NAME, @GROUP_COUNTER, @IS_REQUIRED)"; - - string itemValue = string.Empty; - if (pProperty.Value.Length > 4000) - { - itemValue = pProperty.Value.Substring(1, 4000); - _logger.Warn("Value for field [{0}] is longer than 4000 characters, will be truncated!", pProperty.TableColumn); - } - else - { - itemValue = pProperty.Value; - } - - var oParams = new SqlParameter[] - { - new SqlParameter("@REFERENCE_GUID", pMessageId), - new SqlParameter("@ITEM_DESCRIPTION", pProperty.Description), - new SqlParameter("@ITEM_VALUE", itemValue.Replace("'", "''")), - new SqlParameter("@CREATEDWHO", ADDED_WHO), - new SqlParameter("@GROUP_COUNTER", pProperty.GroupCounter), - new SqlParameter("@SPEC_NAME", pProperty.TableColumn), - new SqlParameter("@IS_REQUIRED", pProperty.IsRequired) - }; - - var oCommand = new SqlCommand(oSql); - oCommand.Parameters.AddRange(oParams); - - return _database.MSSQL.ExecuteNonQuery(oCommand); + var delItemValueSQL = "DELETE FROM TBEDMI_ITEM_VALUE WHERE REFERENCE_GUID '" + pMessageId + "';"; + var oCommand = new SqlCommand(delItemValueSQL); + _database.MSSQL.ExecuteNonQuery(oCommand); + _logger.Debug("DELETE FROM TBEDMI_ITEM_VALUE successfull"); } catch (Exception ex) { _logger.Error(ex); return false; } + + try + { + var delItemFilesSQL = "DELETE FROM TBEDMI_ITEM_FILES WHERE REFERENCE_GUID '" + pMessageId + "';"; + var oCommand = new SqlCommand(delItemFilesSQL); + _database.MSSQL.ExecuteNonQuery(oCommand); + _logger.Debug("DELETE FROM TBEDMI_ITEM_FILES successfull"); + } + catch (Exception ex) + { + _logger.Error(ex); + return false; + } + + return true; } + /// /// Ermittelt die Ausgabe-nachricht für einen Fehlercode /// @@ -744,5 +878,16 @@ namespace ZUGFeRDRESTService.Controllers return "20010"; } } + + private string GetRejectionCodeNumber(string rejectionCode) + { + if (rejectionCode.StartsWith("REFERENCES_Rejection_")) + { + var retValue = rejectionCode.Replace("REFERENCES_Rejection_", ""); + return retValue; + } + + return rejectionCode; + } } } From 538e2fcde14cb965b192fd19a4cee40d837ec294 Mon Sep 17 00:00:00 2001 From: pitzm Date: Wed, 4 Feb 2026 15:09:46 +0100 Subject: [PATCH 03/11] ValidationController: Korrekturen --- .../Controllers/ValidationController.cs | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs b/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs index 49ff5c5d..b629122b 100644 --- a/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs +++ b/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs @@ -776,9 +776,16 @@ namespace ZUGFeRDRESTService.Controllers { try { - var delItemValueSQL = "DELETE FROM TBEDMI_ITEM_VALUE WHERE REFERENCE_GUID '" + pMessageId + "';"; + var delItemValueSQL = "DELETE FROM TBEDMI_ITEM_VALUE WHERE REFERENCE_GUID = '" + pMessageId + "';"; var oCommand = new SqlCommand(delItemValueSQL); - _database.MSSQL.ExecuteNonQuery(oCommand); + var retValue = _database.MSSQL.ExecuteNonQuery(oCommand); + + if (!retValue) + { + _logger.Warn("DELETE FROM TBEDMI_ITEM_VALUE NOT successfull"); + return false; + } + _logger.Debug("DELETE FROM TBEDMI_ITEM_VALUE successfull"); } catch (Exception ex) @@ -789,9 +796,15 @@ namespace ZUGFeRDRESTService.Controllers try { - var delItemFilesSQL = "DELETE FROM TBEDMI_ITEM_FILES WHERE REFERENCE_GUID '" + pMessageId + "';"; + var delItemFilesSQL = "DELETE FROM TBEDMI_ITEM_FILES WHERE REFERENCE_GUID = '" + pMessageId + "';"; var oCommand = new SqlCommand(delItemFilesSQL); - _database.MSSQL.ExecuteNonQuery(oCommand); + var retValue = _database.MSSQL.ExecuteNonQuery(oCommand); + + if (!retValue) + { + _logger.Warn("DELETE FROM TBEDMI_ITEM_FILES NOT successfull"); + return false; + } _logger.Debug("DELETE FROM TBEDMI_ITEM_FILES successfull"); } catch (Exception ex) From 7173c89b772d8897bf7d0bf4bb1b64ad7ced14f5 Mon Sep 17 00:00:00 2001 From: pitzm Date: Fri, 6 Mar 2026 11:17:06 +0100 Subject: [PATCH 04/11] =?UTF-8?q?GUIs.Common:=20Formatierbare=20Datumsspal?= =?UTF-8?q?ten-Funktionen=20erg=C3=A4nzt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GUIs.Common/GridBuilder.vb | 54 +++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/GUIs.Common/GridBuilder.vb b/GUIs.Common/GridBuilder.vb index 3ab51893..9a0226f6 100644 --- a/GUIs.Common/GridBuilder.vb +++ b/GUIs.Common/GridBuilder.vb @@ -82,7 +82,7 @@ Public Class GridBuilder ToList() For Each oDateCol In oDateColumns - SetDateTimeColumn(oDateCol) + SetDateTimeColumn(oDateCol, "g") Next End Sub @@ -96,19 +96,61 @@ Public Class GridBuilder ToList() For Each oDateCol In oDateColumns - SetDateTimeColumn(oDateCol) + SetDateTimeColumn(oDateCol, "g") Next End Sub - Private Sub SetDateTimeColumn(pColumn As GridColumn) + ''' + ''' Applies a proper datetime format string to all columns of the view. + ''' + ''' The view's columns need to be loaded for this to work! + Public Sub SetDateTimeColumns(pView As GridView, pFormatString As String) + If pView.Columns Is Nothing Then + Exit Sub + End If + + Dim oDateColumns = pView.Columns.AsEnumerable. + Where(Function(column As GridColumn) column.ColumnType = GetType(Date)). + ToList() + + For Each oDateCol In oDateColumns + SetDateTimeColumn(oDateCol, pFormatString) + Next + End Sub + + Public Sub SetDateTimeColumns(pTreeList As TreeList, pFormatString As String) + If pTreeList.Columns Is Nothing Then + Exit Sub + End If + + Dim oDateColumns = pTreeList.Columns.AsEnumerable. + Where(Function(column As TreeListColumn) column.ColumnType = GetType(Date)). + ToList() + + For Each oDateCol In oDateColumns + SetDateTimeColumn(oDateCol, pFormatString) + Next + End Sub + + Private Sub SetDateTimeColumn(pColumn As GridColumn, pFormatString As String) + + If String.IsNullOrEmpty(pFormatString) Then + pFormatString = "g" + End If + pColumn.DisplayFormat.FormatType = FormatType.Custom - pColumn.DisplayFormat.FormatString = "g" + pColumn.DisplayFormat.FormatString = pFormatString pColumn.DisplayFormat.Format = DateTimeFormatInfo.CurrentInfo End Sub - Private Sub SetDateTimeColumn(pColumn As TreeListColumn) + Private Sub SetDateTimeColumn(pColumn As TreeListColumn, pFormatString As String) + + If String.IsNullOrEmpty(pFormatString) Then + pFormatString = "g" + End If + pColumn.Format.FormatType = FormatType.Custom - pColumn.Format.FormatString = "g" + pColumn.Format.FormatString = pFormatString pColumn.Format.Format = DateTimeFormatInfo.CurrentInfo End Sub From 6936d452092be716c45551fa1edd8fec3d4ec642 Mon Sep 17 00:00:00 2001 From: pitzm Date: Fri, 6 Mar 2026 11:18:04 +0100 Subject: [PATCH 05/11] GUIs.Common: Version 2.7.1.0 --- GUIs.Common/My Project/AssemblyInfo.vb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/GUIs.Common/My Project/AssemblyInfo.vb b/GUIs.Common/My Project/AssemblyInfo.vb index 497148cb..8e3be4b5 100644 --- a/GUIs.Common/My Project/AssemblyInfo.vb +++ b/GUIs.Common/My Project/AssemblyInfo.vb @@ -13,7 +13,7 @@ Imports System.Runtime.InteropServices - + @@ -31,5 +31,5 @@ Imports System.Runtime.InteropServices ' übernehmen, indem Sie "*" eingeben: ' - - + + From 64ae4e9c7683b95d3759b23f142f49e497b4b69f Mon Sep 17 00:00:00 2001 From: pitzm Date: Tue, 17 Mar 2026 10:30:08 +0100 Subject: [PATCH 06/11] ZUGFeRD REST Service: LanguageID in HTTPPost-Methode --- .../Controllers/ValidationController.cs | 74 ++++++++++++++++--- .../ZUGFeRDRESTService/Pages/Index.cshtml | 7 +- 2 files changed, 69 insertions(+), 12 deletions(-) diff --git a/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs b/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs index b629122b..c7029f79 100644 --- a/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs +++ b/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs @@ -1,6 +1,7 @@ using DigitalData.Modules.Interfaces; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.Extensions.Configuration; using System; using System.Collections.Generic; @@ -47,7 +48,14 @@ namespace ZUGFeRDRESTService.Controllers public const string REFERENCES_Rejection_30012 = "REFERENCES_Rejection_30012"; public const string AMOUNT_CALC_REJECTION = "AMOUNT_CALC_REJECTION"; + public const string GERMAN = "de-DE"; + public const string ENGLISH = "en-US"; + public const string FRENCH = "fr-FR"; + public const string SPAIN = "es-ES"; + private List _ValidationErrors; + private List _AllowedLanguageCodes; + private string _UserLanguageCode = "de-DE"; private const int MAX_FILE_SIZE_DEFAULT = 25; @@ -128,6 +136,14 @@ namespace ZUGFeRDRESTService.Controllers AMOUNT_CALC_REJECTION }; + _AllowedLanguageCodes = new List() + { + GERMAN, + ENGLISH, + FRENCH, + SPAIN, + }; + _logger.Debug("Validation Controller initialized!"); } @@ -191,14 +207,41 @@ namespace ZUGFeRDRESTService.Controllers /// /// This parameter's name needs to correspond to the html form's file-input name /// This is the email address which the user supplied + /// This is language code which the user supplied (en-US, de-DE) [HttpPost] - public ValidationResponse Post(IFormFile file, string user_id) + public ValidationResponse Post(IFormFile file, string user_id, string language_id = null) { _logger.Info("Start processing request to ValidationController"); ZugferdResult oZugferdResult = null; CheckPropertyValuesResult oPropertyResult = new CheckPropertyValuesResult(); + if (!string.IsNullOrEmpty(user_id)) + { + _logger.Info("UserID set to [{0}].", user_id); + } + else + { + _logger.Info("UserID is empty!"); + } + + if (string.IsNullOrEmpty(language_id)) + { + _logger.Info("Language code was empty. Set to default 'de-DE'"); + // DEFAULT-Sprache = Deutsch de-DE + _UserLanguageCode = GERMAN; + } + else if (_AllowedLanguageCodes.Contains(language_id)) + { + _logger.Info("Language code is allowed. Set to [{0}].", language_id); + _UserLanguageCode = language_id; + } + else + { + _logger.Info("Language code was unknown: [{0}]. Set to default 'de-DE'", language_id); + _UserLanguageCode = GERMAN; + } + try { using Stream oStream = file.OpenReadStream(); @@ -299,7 +342,7 @@ namespace ZUGFeRDRESTService.Controllers }; } } - + } catch (ZUGFeRDExecption ex) { @@ -611,7 +654,7 @@ namespace ZUGFeRDRESTService.Controllers return oReplaceParam1; } - public string GetMessageId() + private string GetMessageId() { return $"{Guid.NewGuid()}@{MESSAGEID_DOMAIN}"; } @@ -772,7 +815,7 @@ namespace ZUGFeRDRESTService.Controllers return oDataTable; } - public bool DeleteExistingPropertyValues(string pMessageId) + private bool DeleteExistingPropertyValues(string pMessageId) { try { @@ -816,18 +859,15 @@ namespace ZUGFeRDRESTService.Controllers return true; } - /// /// Ermittelt die Ausgabe-nachricht für einen Fehlercode /// - public string GetRejectionMessage(string pErrorCode) + private string GetRejectionMessage(string pErrorCode) { _logger.Info("GetRejectionMessage() - errorCode.ToString(): '" + pErrorCode.ToString() + "'"); if (_RecjectionMessageList == null) return string.Empty; - // Sprache wird man vielleicht mal auswählen können - var language = "de-DE"; var searchTitle = string.Empty; if (pErrorCode.Contains("2000")) @@ -844,11 +884,23 @@ namespace ZUGFeRDRESTService.Controllers } else { - // else-Fall muss noch geklärt werden. - searchTitle = "AMOUNT_CALC_REJECTION_Web"; + searchTitle = "ZUGFERD_Rejection_20006_Web"; + } + + // Sprachgenauen Text suchen. + var messageItem = _RecjectionMessageList.Where(i => i.Title.Equals(searchTitle, StringComparison.OrdinalIgnoreCase) && + i.Language.Equals(_UserLanguageCode, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); + + if (messageItem == null && + !_UserLanguageCode.Equals(GERMAN, StringComparison.OrdinalIgnoreCase)) { + + _logger.Info("GetRejectionMessage() - Es wurde kein passender Text für die Sprache [{0}] gefunden.", _UserLanguageCode); + + // Wenn kein sprachgenauer Text vorliegt, hole den deutschen Text. + messageItem = _RecjectionMessageList.Where(i => i.Title.Equals(searchTitle, StringComparison.OrdinalIgnoreCase) && + i.Language.Equals(GERMAN, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); } - var messageItem = _RecjectionMessageList.Where(i => i.Title.Equals(searchTitle, StringComparison.OrdinalIgnoreCase) && i.Language.Equals(language, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); if (messageItem != null) { _logger.Info("GetRejectionMessage() - messageItem: '" + messageItem.String1 + "'"); diff --git a/WEBSERVICES/ZUGFeRDRESTService/Pages/Index.cshtml b/WEBSERVICES/ZUGFeRDRESTService/Pages/Index.cshtml index d941e9ef..1b7cf371 100644 --- a/WEBSERVICES/ZUGFeRDRESTService/Pages/Index.cshtml +++ b/WEBSERVICES/ZUGFeRDRESTService/Pages/Index.cshtml @@ -15,7 +15,12 @@

    - + +

    + + +

    + From 25e0eddfdb2d5ba3d99b5e0a4025e932c43a122d Mon Sep 17 00:00:00 2001 From: pitzm Date: Tue, 17 Mar 2026 11:29:03 +0100 Subject: [PATCH 07/11] =?UTF-8?q?ZUGFeRDRESTService:=20HttpPost-Parameter?= =?UTF-8?q?=20anders=20abfragen.=20Es=20ist=20immer=20nur=20ein=20Paramete?= =?UTF-8?q?r=20m=C3=B6glich?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/ValidationController.cs | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs b/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs index c7029f79..4bd3751c 100644 --- a/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs +++ b/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs @@ -9,6 +9,7 @@ using System.Data; using System.Data.SqlClient; using System.IO; using System.Linq; +using System.Net.Http; using System.Text.RegularExpressions; using System.Xml.Linq; using static DigitalData.Modules.Interfaces.Exceptions; @@ -209,36 +210,54 @@ namespace ZUGFeRDRESTService.Controllers /// This is the email address which the user supplied /// This is language code which the user supplied (en-US, de-DE) [HttpPost] - public ValidationResponse Post(IFormFile file, string user_id, string language_id = null) + //public ValidationResponse Post(IFormFile file, StringContent user_id, StringContent language_id = null) + public ValidationResponse Post(IFormCollection collection) { _logger.Info("Start processing request to ValidationController"); ZugferdResult oZugferdResult = null; CheckPropertyValuesResult oPropertyResult = new CheckPropertyValuesResult(); - if (!string.IsNullOrEmpty(user_id)) + var oUserId = string.Empty; // user_id == null ? string.Empty : user_id.ToString(); + var oLanguageId = GERMAN; // language_id == null ? GERMAN : language_id.ToString(); + + IFormFile file = collection.Files[0]; + + foreach (var keyItem in collection.Keys) { - _logger.Info("UserID set to [{0}].", user_id); + if (keyItem == "user_id") + { + oUserId = collection[keyItem]; + } + else if (keyItem == "language_id") + { + oLanguageId = collection[keyItem]; + } + } + + if (!string.IsNullOrEmpty(oUserId)) + { + _logger.Info("UserID set to [{0}].", oUserId); } else { _logger.Info("UserID is empty!"); } - if (string.IsNullOrEmpty(language_id)) + if (string.IsNullOrEmpty(oLanguageId)) { _logger.Info("Language code was empty. Set to default 'de-DE'"); // DEFAULT-Sprache = Deutsch de-DE _UserLanguageCode = GERMAN; } - else if (_AllowedLanguageCodes.Contains(language_id)) + else if (_AllowedLanguageCodes.Contains(oLanguageId)) { - _logger.Info("Language code is allowed. Set to [{0}].", language_id); - _UserLanguageCode = language_id; + _logger.Info("Language code is allowed. Set to [{0}].", oLanguageId); + _UserLanguageCode = oLanguageId; } else { - _logger.Info("Language code was unknown: [{0}]. Set to default 'de-DE'", language_id); + _logger.Info("Language code was unknown: [{0}]. Set to default 'de-DE'", oLanguageId); _UserLanguageCode = GERMAN; } From dc56d783b3c35281b583a9b756822256f0b867ee Mon Sep 17 00:00:00 2001 From: pitzm Date: Mon, 23 Mar 2026 13:36:43 +0100 Subject: [PATCH 08/11] ZUGFeRD REST Service: "Ablehnungscode" mehrsprachige Ausgabe --- .../Controllers/ValidationController.cs | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs b/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs index 4bd3751c..50e0f5ff 100644 --- a/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs +++ b/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs @@ -31,6 +31,7 @@ namespace ZUGFeRDRESTService.Controllers public const string VALIDATION_SUCCESS = "VALIDATION SUCCESS"; public const string REFERENCES_Rejection_30001 = "REFERENCES_Rejection_30001"; public const string REFERENCES_Rejection_30002 = "REFERENCES_Rejection_30002"; + public const string REFERENCES_Rejection_30002_1 = "REFERENCES_Rejection_30002_1"; public const string REFERENCES_Rejection_30003_1 = "REFERENCES_Rejection_30003_1"; public const string REFERENCES_Rejection_30003_2 = "REFERENCES_Rejection_30003_2"; public const string REFERENCES_Rejection_30003_3 = "REFERENCES_Rejection_30003_3"; @@ -56,7 +57,7 @@ namespace ZUGFeRDRESTService.Controllers private List _ValidationErrors; private List _AllowedLanguageCodes; - private string _UserLanguageCode = "de-DE"; + private string _UserLanguageCode = GERMAN; private const int MAX_FILE_SIZE_DEFAULT = 25; @@ -65,10 +66,9 @@ namespace ZUGFeRDRESTService.Controllers private readonly DigitalData.Modules.Logging.LogConfig _logConfig; private readonly DigitalData.Modules.Logging.Logger _logger; - private readonly DigitalData.Modules.Filesystem.File _file; + //private readonly DigitalData.Modules.Filesystem.File _file; private readonly PropertyValues _props; - //private readonly Dictionary _propertyMap = new Dictionary(); private readonly List _propertyMapList = new List(); private readonly List _RecjectionMessageList = new List(); @@ -86,7 +86,7 @@ namespace ZUGFeRDRESTService.Controllers { _logConfig = logging.LogConfig; _logger = _logConfig.GetLogger(); - _file = new DigitalData.Modules.Filesystem.File(_logConfig); + //_file = new DigitalData.Modules.Filesystem.File(_logConfig); _logger.Debug("Validation Controller initializing"); @@ -390,7 +390,11 @@ namespace ZUGFeRDRESTService.Controllers // Der REJECTION-Code wird in alle Meldungen eingefügt. if (!string.IsNullOrEmpty(rejectionCodeNumber) && oMessage.Contains("@REJECTION_CODE", StringComparison.OrdinalIgnoreCase)) { - oMessage = Regex.Replace(oMessage, "@REJECTION_CODE", "Ablehnungscode: " + rejectionCodeNumber, RegexOptions.IgnoreCase); + var substituteText = this.GetLabelText("Ablehnungscode", _UserLanguageCode); + substituteText = string.IsNullOrEmpty(substituteText) ? "Ablehnungscode" : substituteText; + substituteText = substituteText + ": " + rejectionCodeNumber; + + oMessage = Regex.Replace(oMessage, "@REJECTION_CODE", substituteText, RegexOptions.IgnoreCase); } // Determine if any errors should be sent in the response @@ -533,7 +537,12 @@ namespace ZUGFeRDRESTService.Controllers if (!string.IsNullOrEmpty(oRejectionItem) && oReturnValue.Contains("@REJECTION_CODE", StringComparison.OrdinalIgnoreCase)) { var rejectionCodeNumber = GetRejectionCodeNumber(oRejectionItem); - oReturnValue = Regex.Replace(oReturnValue, "@REJECTION_CODE", "Ablehnungscode: " + rejectionCodeNumber, RegexOptions.IgnoreCase); + + var substituteText = this.GetLabelText("Ablehnungscode", _UserLanguageCode); + substituteText = string.IsNullOrEmpty(substituteText) ? "Ablehnungscode" : substituteText; + substituteText = substituteText + ": " + rejectionCodeNumber; + + oReturnValue = Regex.Replace(oReturnValue, "@REJECTION_CODE", substituteText, RegexOptions.IgnoreCase); } break; @@ -932,6 +941,15 @@ namespace ZUGFeRDRESTService.Controllers } } + private string GetLabelText(string pLabel, string pLanguage) + { + // Sprachgenauen Text suchen. + var messageItem = _RecjectionMessageList.Where(i => i.Title.Equals(pLabel, StringComparison.OrdinalIgnoreCase) && + i.Language.Equals(pLanguage, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); + + return messageItem != null ? messageItem.String1 : string.Empty; + } + private string GetRejectionCodeNumber(ErrorCodes rejectionCode) { switch (rejectionCode) From 4cfc8cd46b95296a47816683f55f72c2e046173b Mon Sep 17 00:00:00 2001 From: pitzm Date: Wed, 25 Mar 2026 08:47:45 +0100 Subject: [PATCH 09/11] Texte austauschen --- .../Controllers/ValidationController.cs | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs b/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs index 50e0f5ff..2e625d83 100644 --- a/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs +++ b/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.Extensions.Configuration; +using Microsoft.VisualStudio.Web.CodeGeneration.Contracts.Messaging; using System; using System.Collections.Generic; using System.Data; @@ -387,16 +388,6 @@ namespace ZUGFeRDRESTService.Controllers oMessage = Regex.Replace(oMessage, "@REPLACE_PARAM2", ex.Param2, RegexOptions.IgnoreCase); } - // Der REJECTION-Code wird in alle Meldungen eingefügt. - if (!string.IsNullOrEmpty(rejectionCodeNumber) && oMessage.Contains("@REJECTION_CODE", StringComparison.OrdinalIgnoreCase)) - { - var substituteText = this.GetLabelText("Ablehnungscode", _UserLanguageCode); - substituteText = string.IsNullOrEmpty(substituteText) ? "Ablehnungscode" : substituteText; - substituteText = substituteText + ": " + rejectionCodeNumber; - - oMessage = Regex.Replace(oMessage, "@REJECTION_CODE", substituteText, RegexOptions.IgnoreCase); - } - // Determine if any errors should be sent in the response switch (ex.ErrorCode) { @@ -533,18 +524,6 @@ namespace ZUGFeRDRESTService.Controllers oReturnValue = oDbMessage; } - // Der REJECTION-Code wird in alle Meldungen eingefügt. - if (!string.IsNullOrEmpty(oRejectionItem) && oReturnValue.Contains("@REJECTION_CODE", StringComparison.OrdinalIgnoreCase)) - { - var rejectionCodeNumber = GetRejectionCodeNumber(oRejectionItem); - - var substituteText = this.GetLabelText("Ablehnungscode", _UserLanguageCode); - substituteText = string.IsNullOrEmpty(substituteText) ? "Ablehnungscode" : substituteText; - substituteText = substituteText + ": " + rejectionCodeNumber; - - oReturnValue = Regex.Replace(oReturnValue, "@REJECTION_CODE", substituteText, RegexOptions.IgnoreCase); - } - break; } } @@ -929,10 +908,26 @@ namespace ZUGFeRDRESTService.Controllers i.Language.Equals(GERMAN, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); } - if (messageItem != null) + if (messageItem != null && !string.IsNullOrEmpty(pErrorCode)) { - _logger.Info("GetRejectionMessage() - messageItem: '" + messageItem.String1 + "'"); - return messageItem.String1; + var resultText = string.Empty; + + // Der REJECTION-Code wird in allen Meldungen ausgetauscht. + if (messageItem.String1.Contains("@REJECTION_CODE", StringComparison.OrdinalIgnoreCase)) + { + var substituteText = this.GetLabelText("Ablehnungscode", _UserLanguageCode); + substituteText = string.IsNullOrEmpty(substituteText) ? "Ablehnungscode" : substituteText; + substituteText = substituteText + ": " + pErrorCode; + + resultText = Regex.Replace(messageItem.String1, "@REJECTION_CODE", substituteText, RegexOptions.IgnoreCase); + } + else + { + resultText = messageItem.String1; + } + + _logger.Info("GetRejectionMessage() - messageItem: '" + resultText + "'"); + return resultText; } else { From 3f58acea7a24e0810acb977831122dc69f44f727 Mon Sep 17 00:00:00 2001 From: pitzm Date: Wed, 25 Mar 2026 14:41:58 +0100 Subject: [PATCH 10/11] ValidationException - Auswertung der Exception-Items - mehrsprachig --- .../Controllers/ValidationController.cs | 101 ++++++++++++++---- 1 file changed, 79 insertions(+), 22 deletions(-) diff --git a/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs b/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs index 2e625d83..fb4e33be 100644 --- a/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs +++ b/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs @@ -87,8 +87,6 @@ namespace ZUGFeRDRESTService.Controllers { _logConfig = logging.LogConfig; _logger = _logConfig.GetLogger(); - //_file = new DigitalData.Modules.Filesystem.File(_logConfig); - _logger.Debug("Validation Controller initializing"); // Read config file and assign all option flags related to @@ -211,7 +209,6 @@ namespace ZUGFeRDRESTService.Controllers /// This is the email address which the user supplied /// This is language code which the user supplied (en-US, de-DE) [HttpPost] - //public ValidationResponse Post(IFormFile file, StringContent user_id, StringContent language_id = null) public ValidationResponse Post(IFormCollection collection) { _logger.Info("Start processing request to ValidationController"); @@ -219,8 +216,8 @@ namespace ZUGFeRDRESTService.Controllers ZugferdResult oZugferdResult = null; CheckPropertyValuesResult oPropertyResult = new CheckPropertyValuesResult(); - var oUserId = string.Empty; // user_id == null ? string.Empty : user_id.ToString(); - var oLanguageId = GERMAN; // language_id == null ? GERMAN : language_id.ToString(); + var oUserId = string.Empty; + var oLanguageId = GERMAN; IFormFile file = collection.Files[0]; @@ -422,16 +419,54 @@ namespace ZUGFeRDRESTService.Controllers // Der gesamte Ausgabetext muss anhand des ErrorCodes ermittelt werden string oMessage = this.GetRejectionMessage(rejectionCodeNumber); - List oErrors = ex.ValidationErrors.Select(e => - { - return $"Element '{e.ElementName}' mit Wert '{e.ElementValue}': {e.ErrorMessageDE}"; - }).ToList(); + // Wenn es ValidationErrors gibt, werden diese nun übersetzt und in eine Liste übertragen werden + var mainText = GetTextByToken("ItemValueText"); + var resultList = new List(); + foreach (var errorItem in ex.ValidationErrors) + { + var resultString = mainText; + + if (!string.IsNullOrEmpty(errorItem.ElementName)) + { + // replace @ITEM_NAME => e.ElementName + resultString = resultString.Replace("@ITEM_NAME", errorItem.ElementName, StringComparison.OrdinalIgnoreCase); + } + + if (!string.IsNullOrEmpty(errorItem.ElementValue)) + { + // replace ITEM_VALUE => e.ElementValue + resultString = resultString.Replace("@ITEM_VALUE", errorItem.ElementValue, StringComparison.OrdinalIgnoreCase); + } + + var itemErrorText = GetTextByToken(errorItem.ErrorMessageToken); + if (!string.IsNullOrEmpty(itemErrorText)) + { + // attach itemErrorText + resultString += " " + itemErrorText; + } + + _logger.Debug($"resultString: [{resultString}]"); + resultList.Add(resultString); + } + + // wenn ergebnisse vorliegen, in html transformieren und anhängen + if (resultList.Count > 0) + { + var htmlResultString = "
      "; + foreach (var resultItem in resultList) + { + htmlResultString += "
    • " + resultItem + "
    • " + Environment.NewLine; + } + htmlResultString += "
    "; + + oMessage += " " + htmlResultString; + } + return new ValidationResponse() { status = RESPONSE_ERROR, - message = oMessage, - errors = oErrors + message = oMessage }; } catch (Exception ex) @@ -450,6 +485,39 @@ namespace ZUGFeRDRESTService.Controllers } } + /// + /// Holt sprachgenauen Text anhand eines Titel-Tokens + /// + /// + private string GetTextByToken(string tokenValue) + { + RejectionStringRow stringRow = null; + + if (!string.IsNullOrEmpty(tokenValue)) + { + stringRow = _RecjectionMessageList.Where(i => i.Title.Equals(tokenValue, StringComparison.OrdinalIgnoreCase) && + i.Language.Equals(_UserLanguageCode, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); + + if (stringRow == null && + !_UserLanguageCode.Equals(GERMAN, StringComparison.OrdinalIgnoreCase)) + { + // Wenn kein sprachgenauer Text vorliegt, hole den deutschen Text. + stringRow = _RecjectionMessageList.Where(i => i.Title.Equals(tokenValue, StringComparison.OrdinalIgnoreCase) && + i.Language.Equals(GERMAN, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); + } + } + + string retValue = string.Empty; + + if (stringRow != null) + { + retValue = !string.IsNullOrEmpty(stringRow.String1) ? stringRow.String1.Trim() : string.Empty; + } + + _logger.Debug($"Token [{tokenValue}] - String1 [{retValue}]"); + return retValue; + } + /// /// Hier wird eine externe Prozedur gerufen, PRCUST_INV_CHECK_FROM_PORTAL, /// die das Ergebnis der Referenzpruefung liefert. @@ -975,16 +1043,5 @@ namespace ZUGFeRDRESTService.Controllers return "20010"; } } - - private string GetRejectionCodeNumber(string rejectionCode) - { - if (rejectionCode.StartsWith("REFERENCES_Rejection_")) - { - var retValue = rejectionCode.Replace("REFERENCES_Rejection_", ""); - return retValue; - } - - return rejectionCode; - } } } From b4eecbdb86f650af00a400c1d2759ef4dcb4b9dd Mon Sep 17 00:00:00 2001 From: pitzm Date: Thu, 2 Apr 2026 11:46:20 +0200 Subject: [PATCH 11/11] ZUGFERD 20007 - BT-Felder-Liste anzeigen, mehrsprachig --- .../Controllers/ValidationController.cs | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs b/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs index fb4e33be..790c3623 100644 --- a/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs +++ b/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs @@ -248,7 +248,7 @@ namespace ZUGFeRDRESTService.Controllers // DEFAULT-Sprache = Deutsch de-DE _UserLanguageCode = GERMAN; } - else if (_AllowedLanguageCodes.Contains(oLanguageId)) + else if (_AllowedLanguageCodes.Where(i => i.Equals(oLanguageId, StringComparison.OrdinalIgnoreCase)).FirstOrDefault() != null) { _logger.Info("Language code is allowed. Set to [{0}].", oLanguageId); _UserLanguageCode = oLanguageId; @@ -389,8 +389,12 @@ namespace ZUGFeRDRESTService.Controllers switch (ex.ErrorCode) { case ErrorCodes.MissingValueException: - oErrors.AddRange(from item in oPropertyResult.MissingProperties - select (item.EN16931_ID + " (" + item.Description + ")")); + + var oErrorsText = GetMissingValuesListString(oPropertyResult.MissingProperties); + if (!string.IsNullOrEmpty(oErrorsText) && oMessage.Contains("@REPLACE_PARAM3", StringComparison.OrdinalIgnoreCase)) + { + oMessage = Regex.Replace(oMessage, "@REPLACE_PARAM3", oErrorsText, RegexOptions.IgnoreCase); + } break; default: break; @@ -485,6 +489,26 @@ namespace ZUGFeRDRESTService.Controllers } } + private string GetMissingValuesListString(List missingProperties) + { + if (missingProperties == null || missingProperties.Count == 0) + { + return string.Empty; + } + + string retValue = "
      "; + foreach (var missingProperty in missingProperties) + { + var searchToken = missingProperty.EN16931_ID + "_Description"; + var descriptionTranslated = GetTextByToken(searchToken); + var rowString = "
    • " + missingProperty.EN16931_ID + " (" + descriptionTranslated + ")
    • "; + retValue += rowString; + } + retValue += "
    "; + + return retValue; + } + /// /// Holt sprachgenauen Text anhand eines Titel-Tokens ///