ValidationController: BulkInsert, XML-Schema etc für die Datenbank mitnehmen. Korrekturen

This commit is contained in:
2026-02-04 14:13:11 +01:00
parent 24dbed32cc
commit d1dbe8096d

View File

@@ -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<string> _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<bool, string> oValidateResult = ValidateBuyerOrderReference(oPropertyResult.ValidProperties);
Tuple<bool, string> 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.
/// </summary>
private Tuple<bool, string> ValidateBuyerOrderReference(List<ValidProperty> pProperties)
private Tuple<bool, string> ValidateBuyerOrderReference(List<ValidProperty> 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<bool, string>(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<ValidProperty> pProperties, ZugferdResult pZugferdResult)
{
if (string.IsNullOrEmpty(pMessageId))
{
return false;
}
if (!DeleteExistingPropertyValues(pMessageId))
{
return false;
}
DataTable oDataTable = PrepareDataTable(pMessageId, pProperties, pZugferdResult);
// ColumnList initialisieren
List<string> oColumnNames = new List<string> {
"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<string> 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<ValidProperty> 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;
}
/// <summary>
/// Ermittelt die Ausgabe-nachricht für einen Fehlercode
/// </summary>
@@ -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;
}
}
}