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 diff --git a/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs b/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs index 0ac2c0e8..790c3623 100644 --- a/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs +++ b/WEBSERVICES/ZUGFeRDRESTService/Controllers/ValidationController.cs @@ -1,18 +1,21 @@ -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.CodeAnalysis.CSharp.Syntax; using Microsoft.Extensions.Configuration; -using System.Xml.Linq; -using Newtonsoft.Json.Linq; +using Microsoft.VisualStudio.Web.CodeGeneration.Contracts.Messaging; +using System; +using System.Collections.Generic; +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; +using static DigitalData.Modules.Interfaces.PropertyValues; +using static DigitalData.Modules.Interfaces.ZUGFeRDInterface; namespace ZUGFeRDRESTService.Controllers { @@ -26,6 +29,37 @@ 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_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"; + 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"; + + 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 = GERMAN; + private const int MAX_FILE_SIZE_DEFAULT = 25; private readonly ZUGFeRDInterface _zugferd; @@ -33,10 +67,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(); @@ -54,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 @@ -82,7 +113,37 @@ 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 + }; + + _AllowedLanguageCodes = new List() + { + GERMAN, + ENGLISH, + FRENCH, + SPAIN, + }; + _logger.Debug("Validation Controller initialized!"); } @@ -146,14 +207,58 @@ 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(IFormCollection collection) { _logger.Info("Start processing request to ValidationController"); ZugferdResult oZugferdResult = null; CheckPropertyValuesResult oPropertyResult = new CheckPropertyValuesResult(); + var oUserId = string.Empty; + var oLanguageId = GERMAN; + + IFormFile file = collection.Files[0]; + + foreach (var keyItem in collection.Keys) + { + 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(oLanguageId)) + { + _logger.Info("Language code was empty. Set to default 'de-DE'"); + // DEFAULT-Sprache = Deutsch de-DE + _UserLanguageCode = GERMAN; + } + else if (_AllowedLanguageCodes.Where(i => i.Equals(oLanguageId, StringComparison.OrdinalIgnoreCase)).FirstOrDefault() != null) + { + _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'", oLanguageId); + _UserLanguageCode = GERMAN; + } + try { using Stream oStream = file.OpenReadStream(); @@ -165,7 +270,6 @@ namespace ZUGFeRDRESTService.Controllers if (oFileSizeIsOK == false) { - //throw new ZUGFeRDExecption(ErrorType.FileTooBig, "FileTooBig"); throw new ZUGFeRDExecption(ErrorCodes.FileSizeLimitReachedException, _MaxFileSizeInMegabytes.ToString(), string.Empty, "FileTooBig"); } @@ -174,6 +278,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)) { @@ -185,23 +290,23 @@ namespace ZUGFeRDRESTService.Controllers }; oZugferdResult = _zugferd.SerializeZUGFeRDDocument(oResult); + oZugferdResult.ReceiptFileType = "XML"; } _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"); @@ -219,24 +324,21 @@ 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"); } 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 +346,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 +358,8 @@ namespace ZUGFeRDRESTService.Controllers message = oMessage }; } - }; + } + } catch (ZUGFeRDExecption ex) { @@ -282,26 +385,24 @@ 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)) - { - oMessage = Regex.Replace(oMessage, "@REJECTION_CODE", "Ablehnungscode: " + rejectionCodeNumber, RegexOptions.IgnoreCase); - } - // Determine if any errors should be sent in the response 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; } } - else + else { - oMessage = "Alte Logik. Meldung nicht gefunden"; + oMessage = "Alte Logik. Meldung nicht gefunden"; } _logger.Info($"Responding with message: [{oMessage}]"); @@ -315,21 +416,61 @@ namespace ZUGFeRDRESTService.Controllers } catch (ValidationException ex) { + _logger.Error(ex); + var rejectionCodeNumber = this.GetRejectionCodeNumber(ex.ErrorCode); // 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) @@ -348,20 +489,73 @@ namespace ZUGFeRDRESTService.Controllers } } - private Tuple ValidateBuyerOrderReference(List pProperties) + 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 + /// + /// + 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. + /// + private Tuple ValidateBuyerOrderReference(List pProperties, ZugferdResult pZugferdResult) { 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) + 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."); @@ -377,6 +571,57 @@ 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; + } + + break; + } + } + } + + _logger.Debug("Validation terminal Result message: " + oReturnValue); return new Tuple(true, oReturnValue); } @@ -390,86 +635,391 @@ namespace ZUGFeRDRESTService.Controllers { _logger.Error(e); return new Tuple(false, string.Empty); - } + } } - public string GetMessageId() + 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; + } + + private string GetMessageId() { 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; + } + + private bool DeleteExistingPropertyValues(string pMessageId) { try { - if (pProperty.ItemType == 3) + var delItemValueSQL = "DELETE FROM TBEDMI_ITEM_VALUE WHERE REFERENCE_GUID = '" + pMessageId + "';"; + var oCommand = new SqlCommand(delItemValueSQL); + var retValue = _database.MSSQL.ExecuteNonQuery(oCommand); + + if (!retValue) { - // Wir speichern keine Attachment-Werte in die DB - return true; + _logger.Warn("DELETE FROM TBEDMI_ITEM_VALUE NOT successfull"); + return false; } - 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); + _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); + 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) + { + _logger.Error(ex); + return false; + } + + 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 = "ZUGFERD_Rejection_" + pErrorCode + "_Web"; + var searchTitle = string.Empty; - var messageItem = _RecjectionMessageList.Where(i => i.Title.Equals(searchTitle, StringComparison.OrdinalIgnoreCase) && i.Language.Equals(language, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); - if (messageItem != null) + if (pErrorCode.Contains("2000")) { - _logger.Info("GetRejectionMessage() - messageItem: '" + messageItem.String1 + "'"); - return messageItem.String1; + 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 + { + 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(); + } + + if (messageItem != null && !string.IsNullOrEmpty(pErrorCode)) + { + 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 { @@ -478,6 +1028,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) 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 @@

    - + +

    + + +

    + 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