Projektdateien hinzufügen.

This commit is contained in:
2026-03-24 14:43:35 +01:00
parent f43d0bfbdc
commit 06fa68a4e0
13 changed files with 3423 additions and 0 deletions

3
EpiAutoReminder.slnx Normal file
View File

@@ -0,0 +1,3 @@
<Solution>
<Project Path="EpiAutoReminder/EpiAutoReminder.csproj" />
</Solution>

1
EpiAutoReminder/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
appsettings.json

View File

@@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
namespace EpiAutoReminder
{
public class ConfigLoader
{
public AzureAdConfig AzureAd { get; set; }
public GraphConfig Graph { get; set; }
public EpirentConfig Epirent { get; set; }
// Neues Mapping
public Dictionary<string, string> MailMappings { get; set; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
private static readonly string configFilePath = "appsettings.json";
private static ConfigLoader _instance;
public static ConfigLoader Instance => _instance ??= LoadConfig();
public ConfigLoader() { }
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 options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
var config = JsonSerializer.Deserialize<ConfigLoader>(json, options);
if (config == null)
{
Logger.Error("Fehler: Konfiguration konnte nicht deserialisiert werden.");
throw new Exception("Konfiguration konnte nicht geladen werden.");
}
config.MailMappings ??= new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
Logger.Info("Konfiguration erfolgreich geladen.");
return config;
}
catch (Exception ex)
{
Logger.Error("Fehler beim Laden der Konfiguration!", ex);
throw;
}
}
/// <summary>
/// Liefert die Mailadresse zu einem Kürzel, z.B. AR -> axel.reisinger@vt-media.de
/// Gibt null zurück, wenn kein Mapping gefunden wurde.
/// </summary>
public string GetMailAddressByShortcut(string shortcut)
{
if (string.IsNullOrWhiteSpace(shortcut))
return null;
if (MailMappings != null && MailMappings.TryGetValue(shortcut.Trim(), out var mailAddress))
return mailAddress;
return null;
}
/// <summary>
/// Liefert die Mailadresse zu einem Kürzel oder wirft eine Exception, wenn nichts gefunden wurde.
/// </summary>
public string GetRequiredMailAddressByShortcut(string shortcut)
{
var mailAddress = GetMailAddressByShortcut(shortcut);
if (string.IsNullOrWhiteSpace(mailAddress))
throw new KeyNotFoundException($"Für das Kürzel '{shortcut}' wurde keine Mailadresse gefunden.");
return mailAddress;
}
}
public class AzureAdConfig
{
public string ClientId { get; set; }
public string TenantId { get; set; }
public string ClientSecret { get; set; }
}
public class GraphConfig
{
public string BaseUrl { get; set; }
}
public class EpirentConfig
{
public string Server { get; set; }
public int Port { get; set; }
public string Token { get; set; }
public int Mandant { get; set; }
public int ReminderDaysBeforeDispoStart { get; set; }
public int OfferDateOlderThan { get; set; }
}
}

View File

@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="log4net" Version="3.3.0" />
<PackageReference Include="Microsoft.Graph" Version="5.103.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.83.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="RestSharp" Version="114.0.0" />
</ItemGroup>
<ItemGroup>
<None Update=".gitignore">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="appsettings.CHANGEME.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="log4net.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,56 @@
using Newtonsoft.Json;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace EpiAutoReminder
{
internal class EpiOrders
{
ConfigLoader Configuration = ConfigLoader.Instance;
RestClient epirentserver;
public EpiOrders()
{
epirentserver = new RestClient("http://" + Configuration.Epirent.Server + ":" + Configuration.Epirent.Port);
}
public OrderList getOrderList()
{
OrderList orderlist = JsonConvert.DeserializeObject<OrderList>(getOrderJson());
return orderlist;
}
public ContactDetail getContact(int ContactPrimaryKey)
{
RestRequest request = new RestRequest("/v1/contact/" + ContactPrimaryKey + "?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 JsonConvert.DeserializeObject<ContactDetail>(epirentserver.ExecuteGet(request).Content);
}
public ContactPersonDetail getContactPersonDetail(int ContactPersonPrimaryKey)
{
RestRequest request = new RestRequest("/v1/cperson/" + ContactPersonPrimaryKey + "?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 JsonConvert.DeserializeObject<ContactPersonDetail>(epirentserver.ExecuteGet(request).Content);
}
private String getOrderJson()
{
RestRequest request = new RestRequest("/v1/order/filter?cl="+ Configuration.Epirent.Mandant + "&ir=true&ia=false&icl=false&ico=false&icv=true", 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;
}
}
}

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using log4net.Config;
using log4net;
namespace EpiAutoReminder
{
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);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,239 @@
// <auto-generated />
//
// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do:
//
// using EpiAutoReminder;
//
// var contactPersonDetail = ContactPersonDetail.FromJson(jsonString);
namespace EpiAutoReminder
{
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
public partial class ContactPersonDetail
{
[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<ContactPersonDetailPayload> Payload { get; set; }
[JsonProperty("request_duration")]
public long RequestDuration { get; set; }
// shared types from OrderList.cs
[JsonProperty("req_datetime")]
public ReqDatetime ReqDatetime { get; set; }
[JsonProperty("_dbg_request_data")]
public DbgRequestData DbgRequestData { get; set; }
[JsonProperty("_dbg_user_info")]
public UserInfo DbgUserInfo { get; set; }
public static ContactPersonDetail FromJson(string json)
=> JsonConvert.DeserializeObject<ContactPersonDetail>(json, EpiAutoReminder.Converter.Settings);
}
public partial class ContactPersonDetailPayload
{
[JsonProperty("primary_key")]
public long PrimaryKey { get; set; }
[JsonProperty("contact_pk")]
public long ContactPk { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("first_name")]
public string FirstName { get; set; }
[JsonProperty("last_name")]
public string LastName { get; set; }
[JsonProperty("nickname")]
public string Nickname { get; set; }
[JsonProperty("position")]
public string Position { get; set; }
[JsonProperty("date_of_birth")]
public string DateOfBirth { get; set; }
[JsonProperty("department")]
public string Department { get; set; }
[JsonProperty("salutation")]
public string Salutation { get; set; }
[JsonProperty("grade")]
public string Grade { get; set; }
[JsonProperty("has_image")]
public bool HasImage { get; set; }
[JsonProperty("communication")]
public List<ContactPersonDetailCommunication> Communication { get; set; }
[JsonProperty("contact_chrono")]
public List<ContactPersonDetailChrono> ContactChrono { get; set; }
[JsonProperty("searchindex")]
public List<object> Searchindex { get; set; }
[JsonProperty("date_changed")]
[JsonConverter(typeof(SafeDateTimeOffsetConverter))]
public DateTimeOffset? DateChanged { get; set; }
[JsonProperty("time_changed")]
public long TimeChanged { get; set; }
[JsonProperty("user_changed")]
public string UserChanged { get; set; }
[JsonProperty("date_created")]
[JsonConverter(typeof(SafeDateTimeOffsetConverter))]
public DateTimeOffset? DateCreated { get; set; }
[JsonProperty("time_created")]
public long TimeCreated { get; set; }
[JsonProperty("user_created")]
public string UserCreated { get; set; }
[JsonProperty("_communication_length")]
public long CommunicationLength { get; set; }
[JsonProperty("_contact_chrono_length")]
public long ContactChronoLength { get; set; }
[JsonIgnore]
public DateTimeOffset? CreatedAt => EpiDateHelper.CombineDateAndSeconds(DateCreated, TimeCreated);
[JsonIgnore]
public DateTimeOffset? ChangedAt => EpiDateHelper.CombineDateAndSeconds(DateChanged, TimeChanged);
}
public partial class ContactPersonDetailCommunication
{
[JsonProperty("primary_key")]
public long PrimaryKey { get; set; }
[JsonProperty("contact_pk")]
public long ContactPk { get; set; }
[JsonProperty("contact_person_pk")]
public long ContactPersonPk { get; set; }
[JsonProperty("uplink")]
public string Uplink { get; set; }
[JsonProperty("type")]
public long Type { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("phone_match_number")]
public string PhoneMatchNumber { get; set; }
[JsonProperty("position")]
public long Position { get; set; }
[JsonProperty("is_newsletter")]
public bool IsNewsletter { get; set; }
[JsonProperty("is_invoice")]
public bool IsInvoice { get; set; }
}
public partial class ContactPersonDetailChrono
{
[JsonProperty("primary_key")]
public long PrimaryKey { get; set; }
[JsonProperty("contact_pk")]
public long ContactPk { get; set; }
[JsonProperty("contact_person_pk")]
public long ContactPersonPk { get; set; }
[JsonProperty("keyword")]
public string Keyword { get; set; }
[JsonProperty("contact_partner")]
public string ContactPartner { get; set; }
[JsonProperty("date")]
[JsonConverter(typeof(SafeDateTimeOffsetConverter))]
public DateTimeOffset? Date { get; set; }
[JsonProperty("time")]
public long Time { get; set; }
[JsonProperty("duration")]
public long Duration { get; set; }
[JsonProperty("com_type")]
public long ComType { get; set; }
[JsonProperty("com_type_str")]
public string ComTypeStr { get; set; }
[JsonProperty("uplink")]
public string Uplink { get; set; }
[JsonProperty("_ref_contact_chrono")]
public string RefContactChrono { get; set; }
[JsonProperty("date_changed")]
[JsonConverter(typeof(SafeDateTimeOffsetConverter))]
public DateTimeOffset? DateChanged { get; set; }
[JsonProperty("time_changed")]
public long TimeChanged { get; set; }
[JsonProperty("user_changed")]
public string UserChanged { get; set; }
[JsonProperty("date_created")]
[JsonConverter(typeof(SafeDateTimeOffsetConverter))]
public DateTimeOffset? DateCreated { get; set; }
[JsonProperty("time_created")]
public long TimeCreated { get; set; }
[JsonProperty("user_created")]
public string UserCreated { get; set; }
[JsonIgnore]
public DateTimeOffset? CreatedAt => EpiDateHelper.CombineDateAndSeconds(DateCreated, TimeCreated);
[JsonIgnore]
public DateTimeOffset? ChangedAt => EpiDateHelper.CombineDateAndSeconds(DateChanged, TimeChanged);
}
public static class ContactPersonDetailSerialize
{
public static string ToJson(this ContactPersonDetail self)
=> JsonConvert.SerializeObject(self, EpiAutoReminder.Converter.Settings);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using log4net;
using Microsoft.Graph;
using Microsoft.Graph.Models;
using Microsoft.Identity.Client;
using Microsoft.Kiota.Abstractions.Authentication;
namespace EpiAutoReminder
{
internal class O365Connector
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(O365Connector));
private readonly string clientID;
private readonly string clientSecret;
private readonly string tenantID;
private readonly string baseUrl;
public O365Connector(string clientID, string clientSecret, string tenantID, string baseUrl)
{
this.clientID = clientID;
this.clientSecret = clientSecret;
this.tenantID = tenantID;
this.baseUrl = baseUrl;
}
public async Task<string> CreateDraftMailAsync(
string to,
string sender,
string subject,
string body,
string cc = null,
string bcc = null)
{
try
{
Logger.Info("CreateDraftMailAsync gestartet.");
if (string.IsNullOrWhiteSpace(to))
throw new ArgumentException("TO darf nicht leer sein.", nameof(to));
if (string.IsNullOrWhiteSpace(sender))
throw new ArgumentException("Sender darf nicht leer sein.", nameof(sender));
var graphClient = await GetGraphClient();
var message = new Message
{
Subject = subject ?? string.Empty,
Body = new ItemBody
{
ContentType = BodyType.Text,
Content = body ?? string.Empty
},
ToRecipients = new List<Recipient>
{
new Recipient
{
EmailAddress = new EmailAddress
{
Address = to.Trim()
}
}
},
CcRecipients = string.IsNullOrWhiteSpace(cc)
? new List<Recipient>()
: new List<Recipient>
{
new Recipient
{
EmailAddress = new EmailAddress
{
Address = cc.Trim()
}
}
},
BccRecipients = string.IsNullOrWhiteSpace(bcc)
? new List<Recipient>()
: new List<Recipient>
{
new Recipient
{
EmailAddress = new EmailAddress
{
Address = bcc.Trim()
}
}
}
};
Logger.Info($"Erstelle Draft für Sender '{sender}' an '{to}'.");
var createdDraft = await graphClient
.Users[sender]
.Messages
.PostAsync(message);
if (createdDraft == null || string.IsNullOrWhiteSpace(createdDraft.Id))
throw new Exception("Entwurf konnte nicht erstellt werden.");
Logger.Info($"Draft mail created for '{sender}' with id '{createdDraft.Id}'.");
return createdDraft.Id;
}
catch (Exception ex)
{
Logger.Error("Fehler beim Draft erstellen", ex);
throw;
}
}
private async Task<GraphServiceClient> GetGraphClient()
{
try
{
Logger.Info("Initialisiere GraphServiceClient...");
var clientApp = ConfidentialClientApplicationBuilder.Create(clientID)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri($"{baseUrl}/{tenantID}"))
.Build();
var accessTokenProvider = new TokenAcquisitionProvider(clientApp);
var authProvider = new BaseBearerTokenAuthenticationProvider(accessTokenProvider);
Logger.Info("GraphServiceClient erfolgreich initialisiert.");
return new GraphServiceClient(authProvider);
}
catch (Exception ex)
{
Logger.Error("Error initializing GraphServiceClient: " + ex.Message, ex);
throw;
}
}
}
public class TokenAcquisitionProvider : IAccessTokenProvider
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(TokenAcquisitionProvider));
private readonly IConfidentialClientApplication _clientApp;
public TokenAcquisitionProvider(IConfidentialClientApplication clientApp)
{
_clientApp = clientApp;
}
public async Task<string> GetAuthorizationTokenAsync(
Uri uri,
Dictionary<string, object>? additionalAuthenticationContext = null,
CancellationToken cancellationToken = default)
{
try
{
Logger.Info("Fordere Access Token für Microsoft Graph an...");
var result = await _clientApp
.AcquireTokenForClient(new[] { "https://graph.microsoft.com/.default" })
.ExecuteAsync(cancellationToken);
Logger.Info("Access Token erfolgreich geholt.");
return result.AccessToken;
}
catch (Exception ex)
{
Logger.Error("Error acquiring token: " + ex.Message, ex);
throw;
}
}
public AllowedHostsValidator AllowedHostsValidator { get; } = new AllowedHostsValidator();
}
}

183
EpiAutoReminder/Program.cs Normal file
View File

@@ -0,0 +1,183 @@
using Microsoft.Graph.Models;
using Microsoft.Graph.Models.IdentityGovernance;
using System.ClientModel.Primitives;
using System.Reflection;
namespace EpiAutoReminder
{
internal class Program
{
private static ConfigLoader Configuration;
private static OrderList OrderList;
private static O365Connector O365;
static void Main(string[] args)
{
try
{
Logger.Info("Initializing configuration.");
Configuration = ConfigLoader.Instance;
Logger.Info("Configuration loaded successfully.");
}
catch (Exception ex)
{
Logger.Error("Error loading configuration: " + ex.Message);
return;
}
try
{
Logger.Info("Initializing O365 connector.");
O365 = new O365Connector(Configuration.AzureAd.ClientId, Configuration.AzureAd.ClientSecret, Configuration.AzureAd.TenantId, Configuration.Graph.BaseUrl);
Logger.Info("O365 connector initialized.");
}
catch (Exception ex)
{
Logger.Error("Error initializing O365 connector: " + ex.Message);
return;
}
try
{
Logger.Info("Fetching Epirent Orders.");
OrderList = new EpiOrders().getOrderList();
}
catch (Exception ex)
{
Logger.Error("Error fetching contacts: " + ex.Message);
return;
}
//Prozessroutine
foreach (Payload Order in OrderList.Payload)
{
try
{
var dispo = Order.OrderSchedule.Find(x => x.Name == "Dispo");
var offerdate = Order.OrderDate;
if (dispo?.DateStart == null)
{
continue;
}
if (offerdate == null)
{
continue;
}
var today = DateTime.Now.Date;
var dispoDate = dispo.DateStart.Value.Date;
var offerDate = offerdate.Value.Date;
var daysUntilDispo = (dispoDate - today).Days;
var offerAgeDays = (today - offerDate).Days;
if (daysUntilDispo != Configuration.Epirent.ReminderDaysBeforeDispoStart)
{
continue;
}
if (offerAgeDays < Configuration.Epirent.OfferDateOlderThan)
{
continue;
}
Logger.Info($"Order found: {Order.Event}. Dispo in {daysUntilDispo} days. Offer is {offerAgeDays} days old.");
// Jetzt Daten für die Mail zusammensuchen.
// Dazu brauchen wir die Ansprechpartnermailadresse, den Bearbeiter inkl. Kontaktdaten
// und ein paar Infos über den Job wie Name, ID und evtl. Bestellnummer.
ContactPersonDetailPayload epiContactPerson = null;
ContactDetailPayload epiContactDetail = null;
string recipientMailAddress = "";
string recipientNameAndSalutation = "";
string pronoun = "";
Logger.Info($"Versuche Kontakte zu finden");
// Erst Kontaktperson versuchen
if (Order.ContactPerson != null)
{
Logger.Info($"Aktive Kontaktperson gefunden");
epiContactPerson = new EpiOrders()
.getContactPersonDetail((int)Order.ContactPerson.PrimaryKey)?
.Payload?
.FirstOrDefault();
Logger.Info($"Kontaktperson: " + epiContactPerson.Name);
var personMail = epiContactPerson?.Communication?
.FirstOrDefault(x => x.Type == 3);
if (personMail != null && !string.IsNullOrWhiteSpace(personMail.Uplink))
{
Logger.Info($"Kontaktperson hat Mail: " + personMail.Uplink);
recipientMailAddress = personMail.Uplink;
recipientNameAndSalutation = $"Hallo {epiContactPerson.FirstName} {epiContactPerson.LastName}";
pronoun = "dir";
}
else
{
epiContactPerson = null; // Fallback auf Kontakt
Logger.Info("Kontaktperson hat keine mail - falle zurück auf Kontakt");
}
}
// Fallback: normalen Kontakt verwenden
if (epiContactPerson == null)
{
Logger.Info($"Nutze Kontaktdatensatz.");
epiContactDetail = new EpiOrders()
.getContact((int)Order.Contact.PrimaryKey)?
.Payload?
.FirstOrDefault();
var contactMail = epiContactDetail?.Communication?
.FirstOrDefault(x => x.Type == 3);
if (contactMail == null || string.IsNullOrWhiteSpace(contactMail.Uplink))
{
// kein Mailkontakt möglich
Logger.Info("Keine Mailadresse im Kontakt gefunden - kein Mailkontakt möglich");
continue;
}
recipientMailAddress = contactMail.Uplink;
recipientNameAndSalutation = $"Liebes Team von {epiContactDetail.Name}";
pronoun = "euch";
Logger.Info("Mailadressen gefunden.");
}
var senderMail = ConfigLoader.Instance.GetMailAddressByShortcut(Order.EditorShort);
Logger.Info("Versuche Mail an " + recipientNameAndSalutation + " (" + recipientMailAddress + ") von " + senderMail + " zu senden");
var bodyText =
recipientNameAndSalutation + "," + Environment.NewLine + Environment.NewLine +
$"ich hoffe, es geht {pronoun} gut." + Environment.NewLine +
$"vor einiger Zeit habe ich {pronoun} unser Angebot \"{Order.Event}\" (Angebotsnummer: {Order.OrderNoFmt}) zukommen lassen. \r\nDa ich bisher noch nichts gehört habe, wollte ich kurz nachfragen, ob das Angebot noch aktuell ist oder ob ich bei der weiteren Planung unterstützen kann." + Environment.NewLine + Environment.NewLine +
"Gerne stehe ich für Rückfragen, Anpassungen oder weitere Abstimmungen jederzeit zur Verfügung." + Environment.NewLine + Environment.NewLine +
"Ich freue mich auf eine Rückmeldung.";
var draftId = O365.CreateDraftMailAsync(
recipientMailAddress,
senderMail,
"Erinnerung zum offenen Angebot " + Order.OrderNoFmt + " " + Order.Event,
bodyText, null, senderMail).GetAwaiter().GetResult();
Logger.Info("Draft erstellt mit ID: " + draftId);
}
catch (Exception ex)
{
Logger.Error("Error processing order " + Order.Event + ": " + ex.Message);
}
}
}
}
}

View File

@@ -0,0 +1,24 @@
{
"AzureAd": {
"ClientId": "xxx",
"TenantId": "xxx",
"ClientSecret": "xxx"
},
"Graph": {
"BaseUrl": "https://login.microsoftonline.com"
},
"Epirent": {
"Server": "xxx",
"Port": 8080,
"Token": "xxx",
"Mandant": 0,
"ReminderDaysBeforeDispoStart": 21,
"OfferDateOlderThan": 14
},
"MailMappings": {
"TU": "test.user@vt-media.de",
}
}

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="logs/app.log" />
<appendToFile value="true" />
<rollingStyle value="Date" />
<datePattern value="yyyy-MM-dd'.log'" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="[%date] [%level] %logger - %message%newline" />
</layout>
</appender>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="[%date] [%level] %logger - %message%newline" />
</layout>
</appender>
<root>
<level value="ALL" />
<appender-ref ref="RollingFileAppender" />
<appender-ref ref="ConsoleAppender" />
</root>
</log4net>