using System; using System.IO; using System.Linq; using System.Collections.Generic; 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; namespace ZUGFeRDRESTService.Controllers { [Route("api/[controller]")] [ApiController] public class ValidationController : ControllerBase { public const string RESPONSE_OK = "OK"; public const string RESPONSE_ERROR = "ERROR"; public const string ADDED_WHO = "ZUGFeRD REST Service"; public const string MESSAGEID_DOMAIN = "test.wisag.de"; private const int MAX_FILE_SIZE_DEFAULT = 25; private readonly ZUGFeRDInterface _zugferd; private readonly IDatabase _database; private readonly DigitalData.Modules.Logging.LogConfig _logConfig; private readonly DigitalData.Modules.Logging.Logger _logger; private readonly DigitalData.Modules.Filesystem.File _file; private readonly PropertyValues _props; private readonly Dictionary _propertyMap; private readonly int _MaxFileSizeInMegabytes; public ValidationController(ILogging logging, IDatabase database, IConfiguration Config) { _logConfig = logging.LogConfig; _logger = _logConfig.GetLogger(); _file = new DigitalData.Modules.Filesystem.File(_logConfig); _logger.Debug("Validation Controller initializing"); _database = database; var oGDPictureKey = database.GetGDPictureKey(); var oPropertyMap = database.GetPropertyMap(); _propertyMap = oPropertyMap; _zugferd = new ZUGFeRDInterface(_logConfig, oGDPictureKey); _props = new PropertyValues(_logConfig); var oAppConfig = Config.GetSection("Config"); if (int.TryParse(oAppConfig["MaxFileSizeInMegabytes"], out _MaxFileSizeInMegabytes)) { _logger.Info("Configuration MaxFileSizeInMegabytes was not set. Using default value [{0}]", MAX_FILE_SIZE_DEFAULT); _MaxFileSizeInMegabytes = MAX_FILE_SIZE_DEFAULT; } _logger.Debug("Validation Controller initialized!"); } /// /// POST: /api/validation /// /// This parameter's name needs to correspond to the html form's file-input name /// This is the email address which the user supplied [HttpPost] public ValidationResponse Post(IFormFile file, string user_id) { _logger.Info("Start processing request to ValidationController"); object oDocument; CheckPropertyValuesResult oResult = new CheckPropertyValuesResult(); try { using Stream oStream = file.OpenReadStream(); { _logger.Info("Checking Filesize of file [{0}]", file.FileName); long maxFileSize = _MaxFileSizeInMegabytes * 1024 * 1024; bool oFileSizeIsOK = file.Length < maxFileSize; if (oFileSizeIsOK == false) { throw new ZUGFeRDExecption(ErrorType.FileTooBig, string.Format("Die hochgeladene Datei überschreitet die zulässige Dateigröße [{0}].", _MaxFileSizeInMegabytes)); } _logger.Info("Extracting ZUGFeRD Data from file [{0}]", file.FileName); oDocument = _zugferd.ExtractZUGFeRDFileWithGDPicture(oStream); _logger.Info("Starting structural check against the database."); oResult = _props.CheckPropertyValues(oDocument, _propertyMap, "MESSAGEID"); var oRequiredProperties = _propertyMap. Where(prop => { return prop.Value.IsRequired == true; }). Select(prop => { return prop.Value.Description; }) .ToList(); _logger.Debug("Found [{0}] required properties", oRequiredProperties.Count); _logger.Debug(string.Join(",", oRequiredProperties.ToArray())); _logger.Info("Result of checking against the database: {0} valid properties, {1} missing properties", oResult.ValidProperties.Count, oResult.MissingProperties.Count); if (oResult.MissingProperties.Count > 0) { throw new ZUGFeRDExecption(ErrorType.MissingProperties, "Die hochgeladene Datei ist eine gültige ZUGFeRD-Rechnung, allerdings fehlen benötigte Daten."); } Tuple oValidateResult = ValidateBuyerOrderReference(oResult.ValidProperties); if (oValidateResult.Item1 == false) { throw new ZUGFeRDExecption(ErrorType.UnknownError, "Die hochgeladene Datei kann nicht validiert werden, weil ein unbekannter Fehler aufgetreten ist."); } string oValidateResultString = oValidateResult.Item2; if (oValidateResultString == "ALL REFERENCES CHECKED POSITIVE") { string oMessage = "Die hochgeladene Datei ist eine gültige-ZUGFeRD Rechnung"; _logger.Info($"Responding with message: [{oMessage}]"); return new ValidationResponse() { status = RESPONSE_OK, message = oMessage }; } else { string oMessage = oValidateResultString; _logger.Info($"Responding with message: [{oMessage}]"); return new ValidationResponse() { status = RESPONSE_ERROR, message = oMessage }; } }; } catch (ZUGFeRDExecption ex) { _logger.Error(ex); // Determine which message should be sent in the response string oMessage = ex.ErrorType switch { ErrorType.NoValidFile => "Die hochgeladene Datei ist keine gültige Datei.", ErrorType.NoZugferd => "Die hochgeladene Datei ist keine ZUGFeRD-Rechnung.", ErrorType.NoValidZugferd => "Die hochgeladene Datei ist keine gültige ZUGFeRD-Rechnung.", ErrorType.MissingProperties => "Die hochgeladene Datei ist keine gültige ZUGFeRD-Rechnung, es fehlen einige Metadaten", ErrorType.FileTooBig => string.Format("Die hochgeladene Datei überschreitet die zulässige Dateigröße [{0}].", _MaxFileSizeInMegabytes), _ => "Die hochgeladene Datei kann nicht validiert werden.", }; // Determine if any errors should be sent in the response List oErrors = ex.ErrorType switch { // Errors contains the list of missing fields ErrorType.MissingProperties => oResult.MissingProperties, _ => new List() }; _logger.Info($"Responding with message: [{oMessage}]"); return new ValidationResponse() { status = RESPONSE_ERROR, message = oMessage, errors = oErrors }; } catch (Exception ex) { _logger.Error(ex); string oMessage = "Die hochgeladene Datei kann nicht validiert werden, weil ein unbekannter Fehler aufgetreten ist."; _logger.Info($"Responding with message: [{oMessage}]"); return new ValidationResponse() { status = RESPONSE_ERROR, message = oMessage }; } } private Tuple ValidateBuyerOrderReference(List pProperties) { var oMessageId = GetMessageId(); _logger.Debug("Created new MessageId: [{0}]", oMessageId); _logger.Debug("Inserting properties into database."); foreach (var oItem in pProperties) { var oResult = InsertPropertyMap(oItem, oMessageId); if (oResult == false) { _logger.Warn("Error while inserting the Property [{0}] into the Database!", oItem.Description); } } _logger.Debug("Calling validation prodecure."); try { using SqlCommand oCommand = new SqlCommand("PRCUST_INV_CHECK_FROM_PORTAL"); oCommand.CommandType = System.Data.CommandType.StoredProcedure; oCommand.Parameters.Clear(); oCommand.Parameters.Add("@REF_GUID", System.Data.SqlDbType.VarChar, 250).Value = oMessageId; oCommand.Parameters.Add("@MSG_OUTPUT", System.Data.SqlDbType.VarChar, 500).Direction = System.Data.ParameterDirection.Output; if (_database.MSSQL.ExecuteNonQuery(oCommand)) { string oReturnValue = (string)oCommand.Parameters["@MSG_OUTPUT"].Value; return new Tuple(true, oReturnValue); } else { return new Tuple(false, string.Empty); } } catch (Exception e) { _logger.Error(e); return new Tuple(false, string.Empty); } } public string GetMessageId() { return $"{Guid.NewGuid()}@{MESSAGEID_DOMAIN}"; } public bool InsertPropertyMap(ValidProperty pProperty, string pMessageId) { try { 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)"; var oParams = new SqlParameter[] { new SqlParameter("@REFERENCE_GUID", pMessageId), new SqlParameter("@ITEM_DESCRIPTION", pProperty.Description), new SqlParameter("@ITEM_VALUE", pProperty.Value), 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); } catch (Exception ex) { _logger.Error(ex); return false; } } } }