diff --git a/.gitignore b/.gitignore
index 9491a2f..1654d18 100644
--- a/.gitignore
+++ b/.gitignore
@@ -360,4 +360,5 @@ MigrationBackup/
.ionide/
# Fody - auto-generated XML schema
-FodyWeavers.xsd
\ No newline at end of file
+FodyWeavers.xsd
+/Epi2O365/appsettings.json
diff --git a/Epi2O365.sln b/Epi2O365.sln
new file mode 100644
index 0000000..284d88c
--- /dev/null
+++ b/Epi2O365.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.11.35327.3
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Epi2O365", "Epi2O365\Epi2O365.csproj", "{4D90F254-A69D-41CB-853D-77DE7DE56770}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {4D90F254-A69D-41CB-853D-77DE7DE56770}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4D90F254-A69D-41CB-853D-77DE7DE56770}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4D90F254-A69D-41CB-853D-77DE7DE56770}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4D90F254-A69D-41CB-853D-77DE7DE56770}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {E759632A-6321-4221-8A39-15C4E3DE0583}
+ EndGlobalSection
+EndGlobal
diff --git a/Epi2O365/ConfigLoader.cs b/Epi2O365/ConfigLoader.cs
new file mode 100644
index 0000000..cec342f
--- /dev/null
+++ b/Epi2O365/ConfigLoader.cs
@@ -0,0 +1,91 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System;
+using System.IO;
+using System.Text.Json;
+
+namespace Epi2O365
+{
+ public class ConfigLoader
+ {
+ public AzureAdConfig AzureAd { get; set; }
+ public GraphConfig Graph { get; set; }
+ public EpirentConfig Epirent { get; set; }
+
+ private static readonly string configFilePath = "appsettings.json";
+ private static ConfigLoader _instance;
+
+ // Singleton-Instanz für globale Nutzung
+ public static ConfigLoader Instance => _instance ??= LoadConfig();
+
+ public ConfigLoader() { }
+
+ ///
+ /// Lädt die Konfiguration aus der appsettings.json Datei
+ ///
+ public static ConfigLoader LoadConfig()
+ {
+ Logger.Info("Lade Konfigurationsdatei...");
+
+ try
+ {
+ if (!File.Exists(configFilePath))
+ {
+ Logger.Error($"Konfigurationsdatei '{configFilePath}' wurde nicht gefunden!");
+ throw new FileNotFoundException($"Config-Datei '{configFilePath}' wurde nicht gefunden!");
+ }
+
+ string json = File.ReadAllText(configFilePath);
+ var config = JsonSerializer.Deserialize(json);
+
+ if (config == null)
+ {
+ Logger.Error("Fehler: Konfiguration konnte nicht deserialisiert werden.");
+ throw new Exception("Konfiguration konnte nicht geladen werden.");
+ }
+
+ Logger.Info("Konfiguration erfolgreich geladen.");
+ return config;
+ }
+ catch (Exception ex)
+ {
+ Logger.Error("Fehler beim Laden der Konfiguration!", ex);
+ throw;
+ }
+ }
+ }
+
+ ///
+ /// Azure AD Konfiguration (für Microsoft Graph API)
+ ///
+ public class AzureAdConfig
+ {
+ public string ClientId { get; set; }
+ public string TenantId { get; set; }
+ public string ClientSecret { get; set; }
+ }
+
+ ///
+ /// Microsoft Graph API Konfiguration
+ ///
+ public class GraphConfig
+ {
+ public string BaseUrl { get; set; }
+ public List Users { get; set; }
+ }
+
+ ///
+ /// Epirent Konfiguration
+ ///
+ public class EpirentConfig
+ {
+ public string Server { get; set; }
+ public int Port { get; set; }
+ public string Token { get; set; }
+ public int Mandant { get; set; }
+
+ }
+}
\ No newline at end of file
diff --git a/Epi2O365/Epi2O365.csproj b/Epi2O365/Epi2O365.csproj
new file mode 100644
index 0000000..09c0732
--- /dev/null
+++ b/Epi2O365/Epi2O365.csproj
@@ -0,0 +1,34 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+ Leopold Strobl
+ VT-Media
+ Epirent to O365 Syncher
+ 2025 VT-Media
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+
+
diff --git a/Epi2O365/EpiContacts.cs b/Epi2O365/EpiContacts.cs
new file mode 100644
index 0000000..2ddaedf
--- /dev/null
+++ b/Epi2O365/EpiContacts.cs
@@ -0,0 +1,89 @@
+using Microsoft.Graph.Models.ExternalConnectors;
+using Microsoft.Kiota.Abstractions;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using RestSharp;
+using Newtonsoft.Json;
+
+namespace Epi2O365
+{
+ class EpiContacts
+ {
+ ConfigLoader Configuration = ConfigLoader.Instance;
+ RestClient epirentserver;
+ public EpiContacts()
+ {
+ epirentserver = new RestClient("http://" + Configuration.Epirent.Server + ":" + Configuration.Epirent.Port);
+ }
+
+
+ private String getContactJson()
+ {
+ RestRequest request = new RestRequest("/v1/contact/filter?ia=1&cl=" + Configuration.Epirent.Mandant, RestSharp.Method.Get);
+ request.AddHeader("X-EPI-NO-SESSION", "True");
+ request.AddHeader("X-EPI-ACC-TOK", Configuration.Epirent.Token);
+ request.RequestFormat = DataFormat.Json;
+ return epirentserver.ExecuteGet(request).Content;
+
+ }
+ private String getContactDetailJson(long ContactPrimaryKey)
+ {
+ RestRequest request = new RestRequest("/v1/contact/"+ContactPrimaryKey+"/filter?ia=1&cl=" + Configuration.Epirent.Mandant, RestSharp.Method.Get);
+ request.AddHeader("X-EPI-NO-SESSION", "True");
+ request.AddHeader("X-EPI-ACC-TOK", Configuration.Epirent.Token);
+ request.RequestFormat = DataFormat.Json;
+
+ return epirentserver.ExecuteGet(request).Content;
+
+ }
+ private String getContactPersonJson(long ContactPersonPrimaryKey)
+ {
+ RestRequest request = new RestRequest("/v1/cperson/" + ContactPersonPrimaryKey + "/filter?ia=1&cl=" + Configuration.Epirent.Mandant, RestSharp.Method.Get);
+ request.AddHeader("X-EPI-NO-SESSION", "True");
+ request.AddHeader("X-EPI-ACC-TOK", Configuration.Epirent.Token);
+ request.RequestFormat = DataFormat.Json;
+
+ return epirentserver.ExecuteGet(request).Content;
+
+ }
+
+ public Model.ContactList getContactList()
+ {
+ Model.ContactList contactList = JsonConvert.DeserializeObject(getContactJson());
+ return contactList;
+ }
+
+ public Model.ContactList getContactList(DateTime FilterTime)
+ {
+ Model.ContactList contactList = JsonConvert.DeserializeObject(getContactJson());
+
+ contactList.Payload.RemoveAll(p => p.getDateTimeChanged() < FilterTime);
+
+ return contactList;
+
+ }
+
+
+ public Model.ContactDetail GetContactDetail(long primaryKey)
+ {
+
+ Model.ContactDetail contactDetail = JsonConvert.DeserializeObject(getContactDetailJson(primaryKey));
+ return contactDetail;
+ }
+
+ public Model.ContactPersonDetail GetContactPersonDetail(long primaryKey)
+ {
+
+ Model.ContactPersonDetail cPersonDetail = JsonConvert.DeserializeObject(getContactPersonJson(primaryKey));
+ return cPersonDetail;
+ }
+
+
+
+
+ }
+}
diff --git a/Epi2O365/Logger.cs b/Epi2O365/Logger.cs
new file mode 100644
index 0000000..deb9a57
--- /dev/null
+++ b/Epi2O365/Logger.cs
@@ -0,0 +1,27 @@
+using log4net.Config;
+using log4net;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Epi2O365
+{
+ public static class Logger
+ {
+ private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ static Logger()
+ {
+ var logRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
+ XmlConfigurator.Configure(logRepository, new FileInfo("log4net.config"));
+ }
+
+ public static void Info(string message) => log.Info(message);
+ public static void Warn(string message) => log.Warn(message);
+ public static void Error(string message, Exception ex = null) => log.Error(message, ex);
+ public static void Debug(string message) => log.Debug(message);
+ }
+}
diff --git a/Epi2O365/Model/ContactDetail.cs b/Epi2O365/Model/ContactDetail.cs
new file mode 100644
index 0000000..38c2724
--- /dev/null
+++ b/Epi2O365/Model/ContactDetail.cs
@@ -0,0 +1,303 @@
+using Microsoft.Graph.Models;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Epi2O365.Model
+{
+ public partial class ContactDetail
+ {
+ [JsonProperty("success")]
+ public bool Success { get; set; }
+
+ [JsonProperty("namespace")]
+ public string Namespace { get; set; }
+
+ [JsonProperty("action")]
+ public string Action { get; set; }
+
+ [JsonProperty("templateSignature")]
+ public string TemplateSignature { get; set; }
+
+ [JsonProperty("payload_length")]
+ public long PayloadLength { get; set; }
+
+ [JsonProperty("is_send_payload")]
+ public bool IsSendPayload { get; set; }
+
+ [JsonProperty("payload")]
+ public List Payload { get; set; }
+
+
+ }
+
+
+
+ public partial class ContactDetailPayload
+ {
+ [JsonProperty("primary_key")]
+ public long PrimaryKey { get; set; }
+
+ [JsonProperty("customer_no")]
+ public long CustomerNo { get; set; }
+
+ [JsonProperty("supplier_no")]
+ public long SupplierNo { get; set; }
+
+ [JsonProperty("is_single_person")]
+ public bool IsSinglePerson { get; set; }
+
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonProperty("name1")]
+ public string Name1 { get; set; }
+
+ [JsonProperty("name2")]
+ public string Name2 { get; set; }
+
+ [JsonProperty("name3")]
+ public string Name3 { get; set; }
+
+ [JsonProperty("matchcode")]
+ public string Matchcode { get; set; }
+
+ [JsonProperty("is_interested")]
+ public bool IsInterested { get; set; }
+
+ [JsonProperty("is_customer")]
+ public bool IsCustomer { get; set; }
+
+ [JsonProperty("is_supplier")]
+ public bool IsSupplier { get; set; }
+
+ [JsonProperty("is_location")]
+ public bool IsLocation { get; set; }
+
+ [JsonProperty("is_vip")]
+ public bool IsVip { get; set; }
+
+ [JsonProperty("is_colleague")]
+ public bool IsColleague { get; set; }
+
+ [JsonProperty("salutation")]
+ public string Salutation { get; set; }
+
+ [JsonProperty("grade")]
+ public string Grade { get; set; }
+
+ [JsonProperty("notes_positive")]
+ public string NotesPositive { get; set; }
+
+ [JsonProperty("notes_negative")]
+ public string NotesNegative { get; set; }
+
+ [JsonProperty("date_of_birth")]
+ public string DateOfBirth { get; set; }
+
+ [JsonProperty("date_changed")]
+ public DateTime DateChanged { get; set; }
+
+ [JsonProperty("time_changed")]
+ public long TimeChanged { get; set; }
+
+ [JsonProperty("date_created")]
+ public DateTime DateCreated { get; set; }
+
+ [JsonProperty("time_created")]
+ public long TimeCreated { get; set; }
+
+ public DateTime getDateTimeCreated()
+ {
+ TimeSpan time = TimeSpan.FromSeconds(TimeCreated);
+ return DateCreated.Date + time; // Combine date and time
+ }
+
+ public DateTime getDateTimeChanged()
+ {
+ TimeSpan time = TimeSpan.FromSeconds(TimeChanged);
+ return DateChanged.Date + time; // Combine date and time
+ }
+
+ [JsonProperty("is_active")]
+ public bool IsActive { get; set; }
+
+ [JsonProperty("terms_of_payment_pk")]
+ public long TermsOfPaymentPk { get; set; }
+
+ [JsonProperty("is_sysstd_terms_of_payment")]
+ public bool IsSysstdTermsOfPayment { get; set; }
+
+ [JsonProperty("is_blocked_order")]
+ public bool IsBlockedOrder { get; set; }
+
+ [JsonProperty("is_separate_invoice_address")]
+ public bool IsSeparateInvoiceAddress { get; set; }
+
+ [JsonProperty("has_image")]
+ public bool HasImage { get; set; }
+
+ [JsonProperty("address")]
+ public Address Address { get; set; }
+
+ [JsonProperty("address_list")]
+ public List AddressList { get; set; }
+
+ [JsonProperty("address_delivery")]
+ public object AddressDelivery { get; set; }
+
+ [JsonProperty("communication")]
+ public List Communication { get; set; }
+
+ [JsonProperty("_communication_length")]
+ public long CommunicationLength { get; set; }
+
+ [JsonProperty("contact_person")]
+ public List ContactPerson { get; set; }
+
+ [JsonProperty("_contact_person_length")]
+ public long ContactPersonLength { get; set; }
+
+
+
+ [JsonProperty("_contact_chrono_length")]
+ public long ContactChronoLength { get; set; }
+
+
+
+ [JsonProperty("_order_length")]
+ public long OrderLength { get; set; }
+
+
+
+ [JsonProperty("searchindex")]
+ public List