From cccbb667a8d0ba0b7261c28746f5835f84429f0c Mon Sep 17 00:00:00 2001 From: dev-mkoebis Date: Thu, 23 Apr 2026 14:26:26 +0200 Subject: [PATCH] Initila commit --- AssemblyInfo.cs | 21 +++ Business/ExcelReader.cs | 85 ++++++++++++ Business/FileOperations.cs | 200 ++++++++++++++++++++++++++++ Business/StructureException.cs | 25 ++++ Business/Vorgang.cs | 142 ++++++++++++++++++++ Business/VorgangCM.cs | 59 +++++++++ Business/VorgangInpro.cs | 160 ++++++++++++++++++++++ Business/VorgangStandort.cs | 58 ++++++++ Data/DataOperations.cs | 234 +++++++++++++++++++++++++++++++++ MergeCMInpro.csproj | 44 +++++++ MergeCMInpro.sln | 41 ++++++ NLog.config | 12 ++ Program.cs | 209 +++++++++++++++++++++++++++++ config.txt | 3 + 14 files changed, 1293 insertions(+) create mode 100644 AssemblyInfo.cs create mode 100644 Business/ExcelReader.cs create mode 100644 Business/FileOperations.cs create mode 100644 Business/StructureException.cs create mode 100644 Business/Vorgang.cs create mode 100644 Business/VorgangCM.cs create mode 100644 Business/VorgangInpro.cs create mode 100644 Business/VorgangStandort.cs create mode 100644 Data/DataOperations.cs create mode 100644 MergeCMInpro.csproj create mode 100644 MergeCMInpro.sln create mode 100644 NLog.config create mode 100644 Program.cs create mode 100644 config.txt diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs new file mode 100644 index 0000000..f715493 --- /dev/null +++ b/AssemblyInfo.cs @@ -0,0 +1,21 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// In Projekten im SDK-Stil wie dem vorliegenden, bei dem verschiedene Assemblyattribute +// üblicherweise in dieser Datei definiert wurden, werden diese Attribute jetzt während +// der Builderstellung automatisch hinzugefügt und mit Werten aufgefüllt, die in den +// Projekteigenschaften definiert sind. Informationen dazu, welche Attribute einbezogen +// werden und wie dieser Prozess angepasst werden kann, finden Sie unter https://aka.ms/assembly-info-properties. + + +// Wenn "ComVisible" auf FALSE festgelegt wird, sind die Typen in dieser Assembly +// für COM-Komponenten nicht sichtbar. Wenn Sie von COM aus auf einen Typ in dieser +// Assembly zugreifen müssen, legen Sie das ComVisible-Attribut für den betreffenden +// Typ auf TRUE fest. + +[assembly: ComVisible(false)] +[assembly: InternalsVisibleTo("MergeCMInpro.Testing")] +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM +// bereitgestellt wird. + +[assembly: Guid("d03111fe-db60-4d6b-9ac0-8bce91794c5d")] diff --git a/Business/ExcelReader.cs b/Business/ExcelReader.cs new file mode 100644 index 0000000..1162a1d --- /dev/null +++ b/Business/ExcelReader.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using Excel = Microsoft.Office.Interop.Excel; + +namespace MergeCMInpro.Business +{ + internal class ExcelReader + { + public static string ReadExcel(string filename, string vorgangId) + { + Excel.Application excel = null; + excel = new Excel.Application(); + excel.Visible = true; + Excel.Workbook wkb = null; + + wkb = Open(excel, filename); + Excel.Range searchedRange = excel.get_Range("A1", "XFD1048576"); + Excel.Range currentFind = searchedRange.Find(vorgangId); + Excel.Sheets excelSheets = wkb.Worksheets; + Excel.Worksheet excelWorksheet = (Excel.Worksheet)excelSheets.get_Item(1); + string res = string.Empty; + + if (currentFind != null) + { + var cellValue = Convert.ToString((excelWorksheet.Cells[currentFind.Row, currentFind.Column+10] as Excel.Range).Value); + DateTime dt = DateTime.Parse(cellValue); + res = dt.ToShortDateString(); + } + else + { + res = "01.01.1900"; + //TODO: Logger, falls nicht gefunden. Und sonst wird 01.01.1900 als default gesetzt + } + + wkb.Close(true); + excel.Quit(); + return res; + } + + public static Excel.Workbook Open(Excel.Application excelInstance, + string fileName, bool readOnly = false, bool editable = true, + bool updateLinks = true) + { + Excel.Workbook book = excelInstance.Workbooks.Open( + fileName, updateLinks, readOnly, + Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, + Type.Missing, editable, Type.Missing, Type.Missing, Type.Missing, + Type.Missing, Type.Missing); + return book; + } + + public static void ReleaseComObjects(ref List comObjects) + { + for (int i = 0; i < comObjects.Count; i++) + { + object obj = comObjects[i]; + if (Marshal.IsComObject(obj)) + { + while (obj != null && Marshal.ReleaseComObject(obj) != 0) ; + while (obj != null && Marshal.FinalReleaseComObject(obj) != 0) ; + } + obj = null; + } + + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + + public static int GetColumnNumber(string column)//A --> 1 B --> 2 CI --> 87 + { + int col = 0; + char[] temp = column.ToUpper().ToCharArray(); + for (int i = temp.Length - 1; i >= 0; i--) + { + int num = Convert.ToInt32(temp[i]) - 64; + col += num * Convert.ToInt32(Math.Pow(26, temp.Length - 1 - i)); + } + return col; + } + } + +} + diff --git a/Business/FileOperations.cs b/Business/FileOperations.cs new file mode 100644 index 0000000..f5ce93d --- /dev/null +++ b/Business/FileOperations.cs @@ -0,0 +1,200 @@ +using MergeCMInpro.Data; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace MergeCMInpro.Business +{ + public static class FileOperations + { + public static string[] ExtractFilePath(string path) + { + var splittedAbsolutePath = path.Split("\\").SkipLast(2).LastOrDefault(); + return splittedAbsolutePath.Split(" "); + } + + /// + /// Kopiert Quelldateien an Zielort + /// + /// Quellpfad + /// Zielordner + /// Zielname + /// Zielpfad + public static void CopySourcePdfFileToDestination(string sourceFilePath, string destFolderName, string destFileName, string archivNetPath) + { + string destPath = Path.Combine(archivNetPath, destFolderName); + if (!Directory.Exists(destPath)) + Directory.CreateDirectory(destPath); + File.Copy(sourceFilePath, Path.Combine(destPath, destFileName), true); + } + + public static void CopyJPMFilesToDestination(string sourcePath, string archivNetPath) + { + string pattern = "(\\.)"; + + if (Directory.GetDirectories(sourcePath).Length > 0 && Directory.GetFiles(sourcePath, "*.jpm").Length == 0) + { + foreach (var item in Directory.GetDirectories(sourcePath)) + { + CopyJPMFilesToDestination(item, archivNetPath); + } + } + else + { + var temp = sourcePath.Split("\\"); + string extractedPath = string.Empty; + string archivPath = Path.Combine(archivNetPath, "Archiv"); + foreach (var item in temp) + { + if (Regex.Match(item, pattern).Success) + { + extractedPath = string.Concat("Sicherungen jpm ", item.Split(" ").FirstOrDefault(), " bis ", item.Split(" ").ElementAt(2)); + break; + } + } + if (!Directory.Exists(Path.Combine(archivPath, extractedPath))) + { + Directory.CreateDirectory(Path.Combine(archivPath, extractedPath)); + } + int count = 0; + foreach (var t in Directory.GetFiles(sourcePath, "*.jpm")) + { + FileInfo fi = new FileInfo(t); + if (!File.Exists(Path.Combine(archivPath, extractedPath, fi.Name))) + { + File.Copy(t, Path.Combine(archivPath, extractedPath, fi.Name)); + count++; + Console.Write("\r{0} files copied", count); + } + else + { + Console.Write("\rskipped!"); + } + } + } + } + + public static DateTime GetCreationDate(string file) + { + FileInfo fi = new FileInfo(file); + return fi.LastWriteTime; + } + + public static string GetFullFilePath(string file, string csvPath) + { + string currentDirectory = Directory.GetParent(csvPath).FullName; + string res = string.Empty; + res = Directory.GetFiles(currentDirectory, file, SearchOption.AllDirectories).FirstOrDefault(); + if (res == null) + return string.Empty; + else + { + return res; + } + } + + //Splittet Informationen der Zeilen in Dienstleister-Datei (Rohdaten) + public static void SplitRow(string path, string archivNetPath) + { + int count = 0; + int countRows = File.ReadAllLines(path).Length; + + using (StreamReader sr = new StreamReader(path, System.Text.Encoding.GetEncoding("ISO-8859-1"))) + { + string[] arr = new string[10]; + while (!sr.EndOfStream) + { + count++; + if (count != countRows) + { + Console.Write("\r{0}/{1} ", count, countRows); + } + else + { + Console.WriteLine("\r{0}/{1} ", count, countRows); + } + arr = sr.ReadLine().Split(";"); + bool jpmFiles = arr[2].Contains(".jpm"); + //Erstelle Basis-Vorgang aus den SAGA-Daten + Vorgang v; + + if(arr[2].Contains("70/")) + { + //arr[0]=ID z.B. 815922, arr[2]=AZ z.B. 61/5-3-048105, arr[3]=Strasse z.B. Westheck, arr[5]=DocType z.B. SCHRIFTVERKEHR, arr[6]=Teil z.B. 001, arr[8]=PDF z.B. 815922.pdf + if (jpmFiles) + { + v = new Vorgang(arr[0], arr[2], string.Concat(arr[5], " Teil ", arr[6]), arr[6], arr[7], arr[3], arr[4]); + } + else + { + v = new Vorgang(arr[0], arr[2], string.Concat(arr[5], " Teil ", arr[6]), arr[6], arr[7], arr[3], arr[4]); + } + } + else + { + if (jpmFiles) + { + v = new Vorgang(arr[0], arr[2], string.Concat(arr[5], " Teil ", arr[6]), arr[6], arr[8]); + } + else + { + v = new Vorgang(arr[0], arr[2], string.Concat(arr[5], " Teil ", arr[6]), arr[6], arr[7]); + } + } + + #region PDF + if(jpmFiles) + { + v.AbsoluteFilePath = GetFullFilePath(arr[8], path); //arr[7]=jpm-Dateien + } + else + { + v.AbsoluteFilePath = GetFullFilePath(arr[7], path); + } + + if (v.AbsoluteFilePath == string.Empty) + { + Console.WriteLine("Keine PDF-Dateien gefunden."); + return; + } + v.CreationDate = GetCreationDate(v.AbsoluteFilePath); + #endregion + + //Ergänze Basis-Vorgang mit den Daten aus der DB + DataOperations.ConnectAndExecute(v, archivNetPath); + } + } + } + + + + public static string LowerDocumentType(string value) + { + switch (value) + { + case "SCHRIFTVERKEHR": + value = "01"; + break; + case "ZEICHNUNGEN": + value = "02"; + break; + case "STATIK": + value = "03"; + break; + case "SONSTIGES": + value = "04"; + break; + case "GENEHMIGUNG": + value = "05"; + break; + default: + break; + } + return value; + } + + } +} diff --git a/Business/StructureException.cs b/Business/StructureException.cs new file mode 100644 index 0000000..c0b70f6 --- /dev/null +++ b/Business/StructureException.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MergeCMInpro.Business +{ + internal class StructureException : Exception + { + public StructureException() + { + + } + + public StructureException(string foldername) : base(string.Format("No Folder with name {0} available", foldername)) + { + + } + + public StructureException(string message, Exception inner):base(message, inner) + { + + } + + } +} diff --git a/Business/Vorgang.cs b/Business/Vorgang.cs new file mode 100644 index 0000000..e8caa08 --- /dev/null +++ b/Business/Vorgang.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace MergeCMInpro.Business +{ + public class Vorgang + { + #region Fields + private string aktenzeichen; + #endregion + + #region Properties + + public string VorgangsDefinition { get; set; } + + public int Id { get; set; } + + public string Aktenzeichen + { + get + { + return aktenzeichen; + } + set + { + aktenzeichen = value; + if (!aktenzeichen.Contains("/")) + { + aktenzeichen = ReplaceHiphenWithSlash(value); + } + } + } + + public string FameId { get; set; } + + public string DocType { get; set; } + + public string NamePDF { get; set; } + + public static Dictionary Strassen { get; set; } + + public string Strasse1 { get; set; } + + public string Teil { get; set; } + + public string Hausnummer1 { get; set; } + + public string Strasse2 { get; set; } = string.Empty; + + public string Hausnummer2 { get; set; } = string.Empty; + + public string AbsoluteFilePath { get; set; } + + public DateTime CreationDate { get; set; } + + public DateTime Enddatum { get; set; } + + #endregion Properties + + #region Constructor + + public Vorgang(string id, string aktenzeichen, string docType, string teil, string namePDF, string strasse1, string hausnummer1) + { + this.DocType = docType; + this.Teil = teil; + this.Aktenzeichen = aktenzeichen; + this.NamePDF = namePDF; + this.Id = Convert.ToInt32(id); + this.FameId = GenerateFameId(); + this.Strasse1 = strasse1; + this.Hausnummer1 = hausnummer1; + } + + public Vorgang(string id, string aktenzeichen, string docType, string teil, string namePDF) + { + this.DocType = docType; + this.Teil = teil; + this.Aktenzeichen = aktenzeichen; + this.NamePDF = namePDF; + this.Id = Convert.ToInt32(id); + this.FameId = GenerateFameId(); + } + + public Vorgang() + { + + } + #endregion + + #region Methods + string ReplaceHiphenWithSlash(string aktenzeichen) + { + var regex = new Regex(Regex.Escape("-")); + string res = regex.Replace(aktenzeichen, "/", 1); + return res; + } + + protected string GenerateFameId() + { + string dt = DateTime.Now.ToString("ddMMyyhhmmssfff"); + byte[] tmpDt; + byte[] tmpHash; + + tmpDt = ASCIIEncoding.ASCII.GetBytes(dt); + tmpHash = new MD5CryptoServiceProvider().ComputeHash(tmpDt); + + StringBuilder res = new StringBuilder(tmpHash.Length); + for (int i = 0; i < tmpHash.Length; i++) + { + res.Append(tmpHash[i].ToString("X2")); + } + string tmp = res.ToString().ToLower().Substring(res.Length - 12); + + return tmp; + } + + public virtual string GenerateVorgang(Vorgang v) + { + return String.Empty; + } + + protected string GenerateLangbezeichnungFromPath(string path) + { + var temp = path.Split('\\').SkipLast(2).LastOrDefault().Split(' ').Skip(3); + string result = string.Concat(temp.ElementAt(0), " ", temp.ElementAt(1), " ", temp.ElementAt(2), " Datensatz "); + + //#region TESTING + //string testing = "TEST"; + //string result = testing; + //#endregion + + return result; + } + #endregion + } +} diff --git a/Business/VorgangCM.cs b/Business/VorgangCM.cs new file mode 100644 index 0000000..d6fd9ab --- /dev/null +++ b/Business/VorgangCM.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace MergeCMInpro.Business +{ + public class VorgangCM : Vorgang + { + public string BeschreibungVorgang { get; set; } + + public VorgangCM() + { + + } + + public override string GenerateVorgang(Vorgang v) + { + StringBuilder sbCM = new StringBuilder(); + sbCM.Append(string.Concat(v.Id, ";")); + sbCM.Append(string.Concat(v.Aktenzeichen, ";")); + sbCM.Append(string.Concat(v.VorgangsDefinition.Trim(), ';')); + sbCM.Append(string.Concat(this.BeschreibungVorgang, ";")); + sbCM.Append(string.Concat(v.Strasse1, ";")); + sbCM.Append(string.Concat(v.Hausnummer1, ";")); + sbCM.Append(string.Concat(v.Strasse2.Trim(), ";")); + sbCM.Append(string.Concat(v.Hausnummer2.Trim(), ';')); + sbCM.Append(string.Concat(this.Enddatum.ToShortDateString(), ";")); + sbCM.Append(string.Concat(v.FameId, ";")); + sbCM.Append(string.Concat(v.CreationDate.ToShortDateString(), ";")); + //Langbezeichnung + string documentType = FileOperations.LowerDocumentType(v.DocType.Split(" ").FirstOrDefault()); + sbCM.Append(string.Concat(documentType, " ", string.Concat(v.DocType.Split(" ").FirstOrDefault().Substring(0, 1), v.DocType.Split(" ").FirstOrDefault().Substring(1).ToLower()), " ", v.DocType.Split(" ", 2).Skip(1).FirstOrDefault(), ";")); + + string langBez = string.Empty; + + try + { + langBez = base.GenerateLangbezeichnungFromPath(v.AbsoluteFilePath); + } + catch (ArgumentOutOfRangeException ex) + { + Console.WriteLine("ERROR: Wrong Path {0}. Please provide folder like '843.818 - 847.187 SAGA HD 28 2023_12_18'", Path.GetDirectoryName(v.AbsoluteFilePath)); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + //string langBez = base.GenerateLangbezeichnungFromPath(v.AbsoluteFilePath); + sbCM.Append(string.Concat(langBez, ";")); + + sbCM.Append(string.Concat(v.FameId, ".pdf")); + + return sbCM.ToString(); + + } + } +} diff --git a/Business/VorgangInpro.cs b/Business/VorgangInpro.cs new file mode 100644 index 0000000..904c378 --- /dev/null +++ b/Business/VorgangInpro.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MergeCMInpro.Business +{ + internal class VorgangInpro : Vorgang + { + #region Properties + public string Abteilung { get; set; } + + public string IdStrasse { get; set; } + + public string Hausnummer1Von { get; set; } + + public string Hausnummer1Bis { get; set; } + + public string HsNrVonZusatz { get; set; } + + public string HsNrBisZusatz { get; set; } + + public DateTime Eingangsdatum { get; set; } + + public string Beschreibung1 { get; set; } + + public string Beschreibung2 { get; set; } + + public string Bezeichnung2 { get; set; } + + public string Checkliste { get; set; } + + public string DocsSZ { get; set; } + + + #endregion + + public VorgangInpro() + { + + } + + public override string GenerateVorgang(Vorgang v) + { + string documentType = FileOperations.LowerDocumentType(v.DocType.Split(" ").FirstOrDefault()); + string band = string.Concat(string.Concat(documentType, " ", string.Concat(v.DocType.Split(" ").FirstOrDefault().Substring(0, 1), v.DocType.Split(" ").FirstOrDefault().Substring(1).ToLower()), " ", v.DocType.Split(" ", 2).Skip(1).FirstOrDefault())); + + StringBuilder sbInpro = new StringBuilder(); + sbInpro.Append(string.Concat(v.Id, ";"));//Barcode + sbInpro.Append(string.Concat(v.Aktenzeichen, ";"));//Vorgangsnummer + sbInpro.Append(string.Concat(string.Empty, ";"));//Abteilung + sbInpro.Append(string.Concat(this.IdStrasse, ";"));//Strasse1ID + sbInpro.Append(string.Concat(v.Strasse1, ";").Trim());//Strasse1 + + + sbInpro.Append(string.Concat(v.Hausnummer1, ";").Trim());//Hausnummer1 + sbInpro.Append(string.Concat(this.Hausnummer1Von, ";").Trim());//Hausnummer1Von + sbInpro.Append(string.Concat(this.HsNrVonZusatz, ";").Trim());//Hausnummer1VonZusatz + sbInpro.Append(string.Concat(this.Hausnummer1Bis, ";").Trim());//Hausnummer1Bis + sbInpro.Append(string.Concat(this.HsNrBisZusatz, ";").Trim());//Hausnummer1BisZusatz + + + sbInpro.Append(string.Concat(this.Strasse2, ";").Trim());//Strasse2 + sbInpro.Append(string.Concat(this.Hausnummer2, ";").Trim());//Hausnummer2 + + sbInpro.Append(string.Concat(band, ";"));//Bezeichnung1 bzw. Band + sbInpro.Append(string.Concat(this.Eingangsdatum.ToShortDateString(), ";"));//Eingangsdatum + sbInpro.Append(string.Concat("Digitalisierte Akte", ";"));//Beschreibung1 + sbInpro.Append(string.Concat(string.Empty, ";")); //Beschreibung2 + sbInpro.Append(string.Concat(string.Empty, ";"));//Bezeichnung2 + sbInpro.Append(string.Concat(this.DocsSZ, ";"));//Bezeichnung3 + sbInpro.Append(string.Concat(v.FameId, ".pdf", ";"));//Dateiname + sbInpro.Append(string.Concat(v.FameId, ";"));//FameId + sbInpro.Append(string.Concat(v.VorgangsDefinition, ";").Trim());//Vorgangsart + sbInpro.Append(string.Concat(this.Checkliste, ";").Trim());//Checkliste + sbInpro.Append(string.Concat(this.Enddatum.ToShortDateString(), ";"));//Abschlussdatum + sbInpro.Append(string.Concat(base.GenerateLangbezeichnungFromPath(v.AbsoluteFilePath), v.Id)); + return sbInpro.ToString(); + } + + //string GenerateLangbezeichnungFromPath(string path) + //{ + // var temp = path.Split('\\').SkipLast(2).LastOrDefault().Split(' ').Skip(3); + // string result = string.Concat(temp.ElementAt(0), " ", temp.ElementAt(1), " ", temp.ElementAt(2), " Datensatz "); + + // //#region TESTING + // //string testing = "TEST"; + // //string result = testing; + // //#endregion + + // return result; + //} + + + public void SplittedHausnummerZusatz(Vorgang v) + { + string hausnummerZusatzVon = string.Empty; + string hausnummerZusatzBis = string.Empty; + + Tuple result; + + if (v.Hausnummer1.Contains('-')) //13-15, 13a-15 + { + hausnummerZusatzVon = v.Hausnummer1.Split('-').FirstOrDefault(); + hausnummerZusatzBis = v.Hausnummer1.Split('-').LastOrDefault(); + result = ExtractingZusatzFromHausnummer(hausnummerZusatzVon); + this.Hausnummer1Von = result.Item1; + this.HsNrVonZusatz = result.Item2; + result = ExtractingZusatzFromHausnummer(hausnummerZusatzBis); + this.Hausnummer1Bis = result.Item1; + this.HsNrBisZusatz = result.Item2; + + if (this.HsNrBisZusatz.Length >= 2 || this.HsNrVonZusatz.Length >= 2) + { + this.HsNrVonZusatz = this.HsNrVonZusatz != string.Empty ? this.HsNrVonZusatz.FirstOrDefault().ToString() : string.Empty; + this.HsNrBisZusatz = this.HsNrBisZusatz != string.Empty ? this.HsNrBisZusatz.LastOrDefault().ToString() : string.Empty; + } + } + else if (v.Hausnummer1.Contains("bis")) + { + //Vorkommen von "bis" + int firstBisIndex = v.Hausnummer1.IndexOf("bis"); + int lastBisIndex = v.Hausnummer1.LastIndexOf("bis") + "bis".Length; + this.Hausnummer1Von = v.Hausnummer1.Substring(0, firstBisIndex); + result = ExtractingZusatzFromHausnummer(this.Hausnummer1Von); + this.Hausnummer1Von = result.Item1; + this.HsNrVonZusatz = result.Item2; + this.Hausnummer1Bis = v.Hausnummer1.Substring(lastBisIndex, v.Hausnummer1.Length - lastBisIndex); + result = ExtractingZusatzFromHausnummer(this.Hausnummer1Bis); + this.Hausnummer1Bis = result.Item1; + this.HsNrBisZusatz = result.Item2; + } + else //13 + { + result = ExtractingZusatzFromHausnummer(v.Hausnummer1); + this.Hausnummer1Von = result.Item1; + this.HsNrVonZusatz = result.Item2; + } + } + + public static Tuple ExtractingZusatzFromHausnummer(string input) + { + StringBuilder sbHausnummer = new StringBuilder(); + StringBuilder sbZusatz = new StringBuilder(); + + for (int i = 0; i < input.Length; i++) + { + if (char.IsDigit(input[i])) + { + sbHausnummer.Append(input[i]); + } + else + { + sbZusatz.Append(input[i]); + } + } + return new Tuple(sbHausnummer.ToString(), sbZusatz.ToString()); + } + } +} diff --git a/Business/VorgangStandort.cs b/Business/VorgangStandort.cs new file mode 100644 index 0000000..c5a0275 --- /dev/null +++ b/Business/VorgangStandort.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MergeCMInpro.Business +{ + internal class VorgangStandort : Vorgang + { + public string CID { get; set; } + + public string Sachbearbeiter { get; set; } + + public string Bemerkung { get; set; } + + public string Standort { get; set; } + + public DateTime Entnahmedatum { get; set; } + + public string Entnahmezeit { get; set; } + + + public VorgangStandort(string csvPath) + { + this.CID = "0001"; + this.Sachbearbeiter = string.Empty; + this.Bemerkung = "Digitalisierte Akte s. E-Akte-Ordner Altakte"; + this.Standort = "61/1-1 Archiv"; + this.Entnahmedatum = GetEntnahmedatum(csvPath); + this.Entnahmezeit = "18:00:00"; + } + + public override string GenerateVorgang(Vorgang v) + { + + StringBuilder sbStandort = new StringBuilder(); + sbStandort.Append(string.Concat(this.CID, ";"));//CID + sbStandort.Append(string.Concat(v.Aktenzeichen, ";"));//Vorgangsnummer + sbStandort.Append(string.Concat(this.Sachbearbeiter, ";"));//Sachbearbeiter + sbStandort.Append(string.Concat(this.Standort, ";"));//Standort + sbStandort.Append(string.Concat(this.Entnahmedatum.ToShortDateString(), ";"));//Entnahmedatum -> Datum Tabelle + sbStandort.Append(string.Concat(this.Entnahmezeit, ";"));//Entnahmezeit + sbStandort.Append(string.Concat(this.Entnahmedatum.ToShortDateString(), ";"));//Rückgabedatum=Entnahmedatum + sbStandort.Append(string.Concat(this.Entnahmezeit, ";"));//Rückgabezeit=Entnahmezeit + + return sbStandort.ToString(); + } + + public DateTime GetEntnahmedatum(string pathFile) + { + string splitted = pathFile.Split('\\').LastOrDefault().Split('_').LastOrDefault(); + + DateTime dt = new DateTime(Convert.ToInt32(splitted.Substring(0,4)),Convert.ToInt32(splitted.Substring(4,2)),Convert.ToInt32(splitted.Substring(6,2))); + return dt; + } + } +} + diff --git a/Data/DataOperations.cs b/Data/DataOperations.cs new file mode 100644 index 0000000..534acef --- /dev/null +++ b/Data/DataOperations.cs @@ -0,0 +1,234 @@ +using MergeCMInpro.Business; +using System; +using System.Collections.Generic; +using System.Text; +using NLog; +using System.Linq; +using System.IO; + +namespace MergeCMInpro.Data +{ + public static class DataOperations + { + private static readonly Logger _logger = NLog.LogManager.GetCurrentClassLogger(); + + public static string conString = string.Empty; + public static string kanalAktePath = string.Empty; + public static string csvPath = string.Empty; + + public static List vorgängeInpro = new List(); + public static List vorgängeCM = new List(); + + public static List vorgängeStandort = new List(); + + public static string GenerateQuery(string az) + { + string sqlQuery = "SELECT " + + "INPROP.PMGTVVORGANG.VORGANGID, " + + "INPROP.PMGTVVORGANG.VORGANGDEFID, " + + "INPROP.PMVBCHECKLIST.CHECKLISTID, " + + "INPROP.PMVBCHECKLIST.DESCRIPTION, " + + "INPROP.PMGTVVORGANG.STRASSE1ID, " + + "INPROP.PMGTVVORGANG.STRASSE1, " + + "INPROP.PMGTVVORGANG.HAUSNUMMER1, " + + "INPROP.PMGTVVORGANG.HSNRVON, " + + "INPROP.PMGTVVORGANG.HSNRVONZUSATZ, " + + "INPROP.PMGTVVORGANG.HSNRBIS, " + + "INPROP.PMGTVVORGANG.HSNRBISZUSATZ, " + + "INPROP.PMGTVVORGANG.STRASSE2, " + + "INPROP.PMGTVVORGANG.HAUSNUMMER2, " + + "INPROP.PMGTVVORGANG.DATUMEINGANG, " + + "INPROP.PMGTVVORGANG.DATUMABSCHLUSS " + + "FROM INPROP.PMGTVVORGANG " + + "INNER JOIN INPROP.PMVBCHECKLIST ON INPROP.PMGTVVORGANG.CHECKID = INPROP.PMVBCHECKLIST.CHECKLISTID " + + "WHERE INPROP.PMGTVVORGANG.VORGANGID='" + az + "'"; + return sqlQuery; + } + + private static string KanalakteQueryForStrasseID(string strasse) + { + string sqlQuery = "SELECT " + + "INPROP.PMGTVSTRASSE.BEZEICHNUNG, " + + "INPROP.PMGTVSTRASSE.STRASSENNR " + + "FROM INPROP.PMGTVSTRASSE " + + "WHERE INPROP.PMGTVSTRASSE.BEZEICHNUNG='" + strasse.TrimEnd() + "'"; + return sqlQuery; + } + + + static Tuple FillVorgangObjects(Oracle.ManagedDataAccess.Client.OracleConnection con, Oracle.ManagedDataAccess.Client.OracleCommand cmd, Vorgang v) + { + con.Open(); + using (Oracle.ManagedDataAccess.Client.OracleDataReader reader = cmd.ExecuteReader()) + { + VorgangInpro vorgangInpro = new VorgangInpro(); + VorgangCM vorgangCM = new VorgangCM(); + + while (reader.Read()) + { + vorgangInpro.Checkliste = reader.GetString(2); + v.VorgangsDefinition = reader.GetString(1); + vorgangCM.BeschreibungVorgang = reader.GetString(3); + var tempDate = reader.GetValue(13).ToString(); + if (tempDate == "null" || tempDate.Equals(string.Empty)) + { + vorgangInpro.Eingangsdatum = new DateTime(1900, 1, 1); + _logger.Error("Wrong date " + v.Aktenzeichen); + } + else + { + vorgangInpro.Eingangsdatum = Convert.ToDateTime(tempDate); + } + tempDate = reader.GetValue(14).ToString(); + if (tempDate == "null" || tempDate.Equals(string.Empty)) + { + vorgangInpro.Enddatum = new DateTime(1900, 1, 1); + vorgangCM.Enddatum = vorgangInpro.Enddatum; + _logger.Error("Wrong date " + v.Aktenzeichen); + } + else + { + vorgangInpro.Enddatum = Convert.ToDateTime(tempDate); + vorgangCM.Enddatum = vorgangInpro.Enddatum; + } + var strasseId = reader.GetValue(4); + if (strasseId.ToString() == "") + { + vorgangInpro.IdStrasse = "null"; + _logger.Error("No ID for Street: " + v.Strasse1); + } + else + { + vorgangInpro.IdStrasse = strasseId.ToString(); + } + v.Strasse1 = reader.GetValue(5).ToString(); + v.Hausnummer1 = reader.GetValue(6).ToString(); + + vorgangInpro.Hausnummer1Von = reader.GetValue(7).ToString(); + vorgangInpro.HsNrVonZusatz = reader.GetValue(8).ToString(); + vorgangInpro.Hausnummer1Bis = reader.GetValue(9).ToString(); + vorgangInpro.HsNrBisZusatz = reader.GetValue(10).ToString(); + v.Strasse2 = reader.GetValue(11).ToString(); + v.Hausnummer2 = reader.GetValue(12).ToString(); + } + return new Tuple(vorgangCM, vorgangInpro); + } + } + + internal static Tuple FillVorgangObjectsKanalakte(Vorgang v) + { + VorgangInpro vorgangInpro = new VorgangInpro(); + VorgangCM vorgangCM = new VorgangCM(); + + vorgangInpro.Checkliste = "0000000198"; + v.VorgangsDefinition = "AA"; + vorgangCM.BeschreibungVorgang = "Kanalakte"; + + vorgangInpro.Eingangsdatum = DateTime.Parse(ExcelReader.ReadExcel(kanalAktePath, v.Aktenzeichen)); + vorgangInpro.Enddatum = DateTime.Parse(ExcelReader.ReadExcel(kanalAktePath, v.Aktenzeichen)); + + vorgangCM.Enddatum = vorgangInpro.Enddatum; + //_logger.Error("Wrong date " + v.Aktenzeichen); + + #region Strasse + using (Oracle.ManagedDataAccess.Client.OracleConnection oracle = new Oracle.ManagedDataAccess.Client.OracleConnection()) + { + oracle.ConnectionString = conString; + + string queryStrasse = KanalakteQueryForStrasseID(v.Strasse1); + oracle.Open(); + + using (Oracle.ManagedDataAccess.Client.OracleCommand com = new Oracle.ManagedDataAccess.Client.OracleCommand(queryStrasse, oracle)) + { + using (Oracle.ManagedDataAccess.Client.OracleDataReader reader = com.ExecuteReader()) + { + while (reader.Read()) + { + var strasseId = reader.GetValue(1).ToString(); + if (strasseId.ToString() == "") + { + vorgangInpro.IdStrasse = "null"; + _logger.Error("No ID for Street: " + v.Strasse1); + } + else + { + vorgangInpro.Strasse1 = reader.GetValue(0).ToString(); + vorgangInpro.IdStrasse = strasseId.ToString(); + } + } + } + } + } + #endregion + //TODO: Hausnummer muss gesplittet werden + vorgangInpro.SplittedHausnummerZusatz(v); + return new Tuple(vorgangCM, vorgangInpro); + } + + #region Extract Strassen + + + #endregion + + + public static void ConnectAndExecute(Vorgang v, string archivNetPath) + { + Tuple tuple; + try + { + #region Kanalakten + if (v.Aktenzeichen.StartsWith("70/")) + { + tuple = FillVorgangObjectsKanalakte(v); + + //CM + vorgängeCM.Add(tuple.Item1.GenerateVorgang(v)); + + //Inpro + vorgängeInpro.Add(tuple.Item2.GenerateVorgang(v)); + + //Copy PDF to Destination on NetShare + var tmp = FileOperations.ExtractFilePath(v.AbsoluteFilePath); + var destFolder = string.Concat("pdf", tmp.ElementAt(5), " ", tmp.ElementAt(0), " bis ", tmp.ElementAt(2)); + FileOperations.CopySourcePdfFileToDestination(v.AbsoluteFilePath, destFolder, string.Concat(v.FameId, ".pdf"), archivNetPath); + return; + } + #endregion + + using (Oracle.ManagedDataAccess.Client.OracleConnection oracle = new Oracle.ManagedDataAccess.Client.OracleConnection()) + { + oracle.ConnectionString = conString; + + string sqlQuery = GenerateQuery(v.Aktenzeichen); + + using (Oracle.ManagedDataAccess.Client.OracleCommand com = new Oracle.ManagedDataAccess.Client.OracleCommand(sqlQuery, oracle)) + { + //Erstelle Inpro und CM-Objekt + tuple = FillVorgangObjects(oracle, com, v); + + //Füge CM-Objekt Liste hinzu + vorgängeCM.Add(tuple.Item1.GenerateVorgang(v)); + + //Für Inpro-Objekt Liste hinzu + vorgängeInpro.Add(tuple.Item2.GenerateVorgang(v)); + + //Standort + var vStandort = new VorgangStandort(csvPath); + vorgängeStandort.Add(vStandort.GenerateVorgang(v)); + + //Copy PDF to Destination on NetShare + var tmp = FileOperations.ExtractFilePath(v.AbsoluteFilePath); + var destFolder = string.Concat("pdf", tmp.ElementAt(5), " ", tmp.ElementAt(0), " bis ", tmp.ElementAt(2)); + FileOperations.CopySourcePdfFileToDestination(v.AbsoluteFilePath, destFolder, string.Concat(v.FameId, ".pdf"), archivNetPath); + } + } + } + catch (Exception ex) + { + Console.WriteLine("Error {0}:{1}", v.Aktenzeichen, ex.Message); + _logger.Error(ex.Message + " " + v.Aktenzeichen); + } + } + + } +} diff --git a/MergeCMInpro.csproj b/MergeCMInpro.csproj new file mode 100644 index 0000000..c4fa1b7 --- /dev/null +++ b/MergeCMInpro.csproj @@ -0,0 +1,44 @@ + + + + Exe + netcoreapp3.1 + embedded + AnyCPU;x86 + + + + + + + + + tlbimp + 8 + 1 + 00020813-0000-0000-c000-000000000046 + 0 + false + true + + + + + + Always + + + + + + + + + + + + Always + + + + diff --git a/MergeCMInpro.sln b/MergeCMInpro.sln new file mode 100644 index 0000000..53fe1e7 --- /dev/null +++ b/MergeCMInpro.sln @@ -0,0 +1,41 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32126.317 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MergeCMInpro", "MergeCMInpro.csproj", "{0798D653-CA13-4E5D-8A9E-4D8A3ADA4D1A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MergeCMInpro.Test", "..\TestProject1\MergeCMInpro.Test.csproj", "{E67A9AAD-585E-4F23-8A85-665A49285C61}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0798D653-CA13-4E5D-8A9E-4D8A3ADA4D1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0798D653-CA13-4E5D-8A9E-4D8A3ADA4D1A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0798D653-CA13-4E5D-8A9E-4D8A3ADA4D1A}.Debug|x86.ActiveCfg = Debug|x86 + {0798D653-CA13-4E5D-8A9E-4D8A3ADA4D1A}.Debug|x86.Build.0 = Debug|x86 + {0798D653-CA13-4E5D-8A9E-4D8A3ADA4D1A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0798D653-CA13-4E5D-8A9E-4D8A3ADA4D1A}.Release|Any CPU.Build.0 = Release|Any CPU + {0798D653-CA13-4E5D-8A9E-4D8A3ADA4D1A}.Release|x86.ActiveCfg = Release|x86 + {0798D653-CA13-4E5D-8A9E-4D8A3ADA4D1A}.Release|x86.Build.0 = Release|x86 + {E67A9AAD-585E-4F23-8A85-665A49285C61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E67A9AAD-585E-4F23-8A85-665A49285C61}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E67A9AAD-585E-4F23-8A85-665A49285C61}.Debug|x86.ActiveCfg = Debug|Any CPU + {E67A9AAD-585E-4F23-8A85-665A49285C61}.Debug|x86.Build.0 = Debug|Any CPU + {E67A9AAD-585E-4F23-8A85-665A49285C61}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E67A9AAD-585E-4F23-8A85-665A49285C61}.Release|Any CPU.Build.0 = Release|Any CPU + {E67A9AAD-585E-4F23-8A85-665A49285C61}.Release|x86.ActiveCfg = Release|Any CPU + {E67A9AAD-585E-4F23-8A85-665A49285C61}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {512F540C-4375-4C7D-B8F4-9D45AE50AA94} + EndGlobalSection +EndGlobal diff --git a/NLog.config b/NLog.config new file mode 100644 index 0000000..7b5bee7 --- /dev/null +++ b/NLog.config @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..2b47dbf --- /dev/null +++ b/Program.cs @@ -0,0 +1,209 @@ +using System; +using System.IO; +using System.Linq; +using MergeCMInpro.Business; +using MergeCMInpro.Data; +using NLog; +using Sentry; +using static System.Net.WebRequestMethods; + + +namespace MergeCMInpro +{ + static class Program + { + private static readonly Logger _logger = NLog.LogManager.GetCurrentClassLogger(); + + static string csvPath = String.Empty; + + static void Main(string[] args) + { + try + { + CallSentry(); + Console.ForegroundColor = ConsoleColor.White; + + //productive + //csvPath = Path.Combine(Directory.GetFiles(Environment.CurrentDirectory, "*.csv").FirstOrDefault()); + + //testing + //csvPath = Path.Combine(Directory.GetFiles(@"\\fsdeg002\DatenStA61_S\Bauvorhaben\0_SAGA\839.867 - 843.817 SAGA HD 27 2023_11_08", "*.csv").FirstOrDefault()); + //csvPath = Path.Combine(Directory.GetFiles(@"C:\Testing\MergeInpro", "*.csv").FirstOrDefault()); + //csvPath = Path.Combine(Directory.GetFiles(@"C:\TEMP\SAGA\839.867 - 843.817 SAGA HD 27 2023_11_08", "*.csv").FirstOrDefault()); + //csvPath = Path.Combine(Directory.GetFiles(@"C:\TEMP\SAGA\843.818 - 847.187 SAGA HD 28 2023_12_18", "*.csv").FirstOrDefault()); + csvPath = Path.Combine(Directory.GetFiles(@"E:\TestData\MergeCMInpro", "*.csv").FirstOrDefault()); + + + string pathOutputFileCM = Path.Combine(Directory.GetParent(csvPath).FullName, "outputCM.csv"); + string pathOutputFileInpro = Path.Combine(Directory.GetParent(csvPath).FullName, "outputInpro.csv"); + string pathOutputFileStandort = Path.Combine(Directory.GetParent(csvPath).FullName, "outputStandort.csv"); + + if (CheckFileExist()) + { + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine("WARNING: import files already exist."); + Console.ResetColor(); + return; + } + + + if(!CheckFolderStructure(csvPath)) + { + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine("WARNING: folder structure wrong. Please note folder with name '{0}' has to contain all pdf files.", Path.GetFileNameWithoutExtension(csvPath)); + Console.ResetColor(); + return; + } + + var config = System.IO.File.ReadLines(@".\config.txt").ToArray(); + DataOperations.conString = config[0]; + DataOperations.kanalAktePath = config[2]; + DataOperations.csvPath = csvPath; + + if (!System.IO.File.Exists(DataOperations.kanalAktePath)) + throw new FileNotFoundException("Kanalaktendatei nicht gefunden."); + + //Testing + string archivNetPath = Path.Combine(@"E:\TestData\ArchivPath"); + + //productive + //string archivNetPath = Path.Combine(config[1]); + + Console.WriteLine("Working, please wait... or get yourself a tea."); + + FileOperations.SplitRow(csvPath, archivNetPath); + + //CM + using (StreamWriter sw = new StreamWriter(pathOutputFileCM)) + { + foreach (var item in DataOperations.vorgängeCM) + { + sw.WriteLine(item.ToString()); + } + } + + //Inpro + using (StreamWriter sw = new StreamWriter(pathOutputFileInpro)) + { + foreach (var item in DataOperations.vorgängeInpro) + { + sw.WriteLine(item.ToString()); + } + } + + //Standort + using (StreamWriter sw = new StreamWriter(pathOutputFileStandort)) + { + foreach (var item in DataOperations.vorgängeStandort.Distinct()) + { + sw.WriteLine(item.ToString()); + } + } + + + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine("{0} created.", pathOutputFileCM); + + FileInfo fi = new FileInfo(pathOutputFileInpro); + if(fi.Length!=0) + { + Console.WriteLine("{0} created.", pathOutputFileInpro); + Console.WriteLine("{0} created.", pathOutputFileStandort); + } + + Console.ResetColor(); + + //#region Backup JPM-Files + //Console.WriteLine("Start copying jpm-files, please wait..."); + //FileInfo fi = new FileInfo(csvPath); + //FileOperations.CopyJPMFilesToDestination(fi.Directory.FullName, archivNetPath); + //Console.WriteLine(""); + //Console.ForegroundColor = ConsoleColor.Green; + //Console.WriteLine("Done. You may close this window now."); + //#endregion + + Console.ReadLine(); + + } + catch (DirectoryNotFoundException) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("ERROR: source directory not found."); + Console.ReadLine(); + } + catch (FileNotFoundException ex) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("ERROR: " + ex.Message); + Console.ReadLine(); + } + catch (Exception ex) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("ERROR: unknown error occured. Maybe no csv-File? Please check!\r\n{0}", ex.InnerException); + Console.ReadLine(); + } + } + + static string CallSentry() + { + try + { + var res = SentrySdk.Init(options => + { + // A Sentry Data Source Name (DSN) is required. + // See https://docs.sentry.io/product/sentry-basics/dsn-explainer/ + // You can set it in the SENTRY_DSN environment variable, or you can set it in code here. + options.Dsn = "https://73fd45bffc79360f9de9b7a7a622d96f@sentry.katyes.dynv6.net/3"; + + // When debug is enabled, the Sentry client will emit detailed debugging information to the console. + // This might be helpful, or might interfere with the normal operation of your application. + // We enable it here for demonstration purposes when first trying Sentry. + // You shouldn't do this in your applications unless you're troubleshooting issues with Sentry. + options.Debug = true; + + // This option is recommended. It enables Sentry's "Release Health" feature. + options.AutoSessionTracking = true; + }); + return res.ToString(); + + } + catch(Exception ex) + { + SentrySdk.CaptureMessage("Something went wrong"); + return ex.Message; + } + } + + private static bool CheckFolderStructure(string csvName) + { + string fileName = Path.GetFileNameWithoutExtension(csvName); + bool result = false; + string dir = Path.GetDirectoryName(csvName); + DirectoryInfo di = new DirectoryInfo(dir); + if(di.GetFiles("*.pdf").Length > 0) + { + return result; + } + if (Directory.Exists(Path.Combine(dir, fileName))) + { + result = true; + } + return result; + } + + public static bool CheckFileExist() + { + FileInfo fi = new FileInfo(Path.Combine(Directory.GetParent(csvPath).FullName, "outputInpro.csv")); + + if ((System.IO.File.Exists(Path.Combine(Directory.GetParent(csvPath).FullName, "outputInpro.csv")) && fi.Length!=0) || System.IO.File.Exists(Path.Combine(Directory.GetParent(csvPath).FullName, "outputCM.csv"))) + { + return true; + } + else + { + return false; + } + } + } +} diff --git a/config.txt b/config.txt new file mode 100644 index 0000000..a575443 --- /dev/null +++ b/config.txt @@ -0,0 +1,3 @@ +Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=dborap04.stadtdo.de)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=bgvp.stadtdo.de)));user id=leser;password=leser; +\\asbgvp02\Archivtransfer\ +\\fsdeg002\DatenStA61_T\StA 61\Digitalisierung\Kanalakten\Kanalakten insgesamt.xlsx \ No newline at end of file