Просмотр исходного кода

Benachrichtigung beinahe abgeschlossen!

Arne Diekmann 8 лет назад
Родитель
Сommit
170281be9a

+ 10 - 2
GreenTree.Nachtragsmanagement.Core/Authentication/UserHelper.cs

@@ -13,6 +13,14 @@ namespace GreenTree.Nachtragsmanagement.Core.Authentication
 {
     public class UserHelper : IUserHelper
     {
+        /// <summary>
+        /// Default serializer settings
+        /// </summary>
+        private static JsonSerializerSettings _jsonSerializerSettigs = new JsonSerializerSettings
+        {
+            ReferenceLoopHandling = ReferenceLoopHandling.Ignore
+        };
+
         /// <summary>
         /// Evaluates the current user from request cookies
         /// </summary>
@@ -66,7 +74,7 @@ namespace GreenTree.Nachtragsmanagement.Core.Authentication
 
             var authCookie = HttpContext.Current.Request.Cookies["auth"];
 
-            var userJson = JsonConvert.SerializeObject(user);
+            var userJson = JsonConvert.SerializeObject(user, _jsonSerializerSettigs);
 
             authCookie = new HttpCookie("auth", StaticHelper.CompressString(userJson))
             {
@@ -101,7 +109,7 @@ namespace GreenTree.Nachtragsmanagement.Core.Authentication
 
             var authCookie = HttpContext.Current.Request.Cookies["auth"];
 
-            var userJson = JsonConvert.SerializeObject(user);
+            var userJson = JsonConvert.SerializeObject(user, _jsonSerializerSettigs);
 
             authCookie = new HttpCookie("auth", StaticHelper.CompressString(userJson))
             {

+ 26 - 0
GreenTree.Nachtragsmanagement.Core/Domain/Config/ConfigItem.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GreenTree.Nachtragsmanagement.Core.Domain.Config
+{
+    public class ConfigItem : BaseEntity
+    {
+        /// <summary>
+        /// Name
+        /// </summary>
+        public string Name { get; set; }
+
+        /// <summary>
+        /// Fullname of the datatype
+        /// </summary>
+        public string TypeFullName { get; set; }
+
+        /// <summary>
+        /// Value
+        /// </summary>
+        public string Value { get; set; }
+    }
+}

+ 2 - 12
GreenTree.Nachtragsmanagement.Core/Domain/Misc/MailNotification.cs

@@ -28,19 +28,9 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.Misc
         public string NotificationPluginSystemName { get; set; }
 
         /// <summary>
-        /// Determines if data is daily summarized before notificated
+        /// The system name of the job that should be processed by the corresponding notification plugin
         /// </summary>
-        public bool IsDailySummary { get; set; }
-
-        /// <summary>
-        /// Determines if data is weekly summarized before notificated
-        /// </summary>
-        public bool IsWeeklySummary { get; set; }
-
-        /// <summary>
-        /// Determines if data is monthly summarized before notificated
-        /// </summary>
-        public bool IsMonthlySummary { get; set; }
+        public string NotificationJobSystemName { get; set; }
 
         /// <summary>
         /// Users assigned to the notification

+ 2 - 0
GreenTree.Nachtragsmanagement.Core/GreenTree.Nachtragsmanagement.Core.csproj

@@ -91,6 +91,7 @@
     <Compile Include="Domain\Appendix\Category.cs" />
     <Compile Include="Domain\Appendix\CategoryValue.cs" />
     <Compile Include="Domain\Appendix\State.cs" />
+    <Compile Include="Domain\Config\ConfigItem.cs" />
     <Compile Include="Domain\Deviation\Deviation.cs" />
     <Compile Include="Domain\Deviation\Disturbance.cs" />
     <Compile Include="Domain\Deviation\DisturbanceValue.cs" />
@@ -110,6 +111,7 @@
     <Compile Include="Plugins\IPlugin.cs" />
     <Compile Include="Plugins\IPluginFinder.cs" />
     <Compile Include="Plugins\LoadPluginsMode.cs" />
+    <Compile Include="Plugins\NotificationJob.cs" />
     <Compile Include="Plugins\PluginDescriptor.cs" />
     <Compile Include="Plugins\PluginExtensions.cs" />
     <Compile Include="Plugins\PluginFileParser.cs" />

+ 5 - 0
GreenTree.Nachtragsmanagement.Core/Plugins/INotificationPlugin.cs

@@ -19,6 +19,11 @@ namespace GreenTree.Nachtragsmanagement.Core.Plugins
         /// </summary>
         string SystemName { get; }
 
+        /// <summary>
+        /// List of available notification jobs
+        /// </summary>
+        List<NotificationJob> AvailableNotificationJobs { get; }
+
         /// <summary>
         /// Displayed name
         /// </summary>

+ 42 - 0
GreenTree.Nachtragsmanagement.Core/Plugins/NotificationJob.cs

@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GreenTree.Nachtragsmanagement.Core.Plugins
+{
+    public class NotificationJob
+    {
+        /// <summary>
+        /// Unique id for the job
+        /// </summary>
+        public Guid Id { get; private set; }
+
+        /// <summary>
+        /// System name
+        /// </summary>
+        public string SystemName { get; private set; }
+
+        /// <summary>
+        /// Displayed name
+        /// </summary>
+        public string Name { get; private set; }
+
+        /// <summary>
+        /// Further description on how this plugin works
+        /// </summary>
+        public string Description { get; private set; }
+
+        /// <summary>
+        /// Initializes a new instance of the NotificationJob class
+        /// </summary>
+        public NotificationJob(Guid id, string systemName, string name, string description)
+        {
+            Id = id;
+            SystemName = systemName;
+            Name = name;
+            Description = description;
+        }
+    }
+}

+ 6 - 1
GreenTree.Nachtragsmanagement.Data/AppendixObjectContext.cs

@@ -20,6 +20,8 @@ using GreenTree.Nachtragsmanagement.Data.Mapping.Misc;
 using GreenTree.Nachtragsmanagement.Data.Mapping.Site;
 using GreenTree.Nachtragsmanagement.Data.Mapping.User;
 using System.Data.Entity.Migrations;
+using GreenTree.Nachtragsmanagement.Core.Domain.Config;
+using GreenTree.Nachtragsmanagement.Data.Mapping.Config;
 
 namespace GreenTree.Nachtragsmanagement.Data
 {
@@ -54,6 +56,7 @@ namespace GreenTree.Nachtragsmanagement.Data
             var appendixSet = Set<Appendix>();
             var mailNotificationSet = Set<MailNotification>();
             var dbContextSpecSet = Set<DbContextSpec>();
+            var configItemSet = Set<ConfigItem>();
 
             _dbSets.AddRange(
                 new object[] 
@@ -71,7 +74,8 @@ namespace GreenTree.Nachtragsmanagement.Data
                     stateSet,
                     appendixSet,
                     mailNotificationSet,
-                    dbContextSpecSet
+                    dbContextSpecSet,
+                    configItemSet
                 }
             );
 
@@ -106,6 +110,7 @@ namespace GreenTree.Nachtragsmanagement.Data
             modelBuilder.Configurations.Add(new AppendixMap());
             modelBuilder.Configurations.Add(new MailNotificationMap());
             modelBuilder.Configurations.Add(new DbContextSpecMap());
+            modelBuilder.Configurations.Add(new ConfigItemMap());
 
             base.OnModelCreating(modelBuilder);
         }

+ 1 - 0
GreenTree.Nachtragsmanagement.Data/GreenTree.Nachtragsmanagement.Data.csproj

@@ -54,6 +54,7 @@
     <Compile Include="Mapping\Appendix\CategoryMap.cs" />
     <Compile Include="Mapping\Appendix\CategoryValueMap.cs" />
     <Compile Include="Mapping\Appendix\StateMap.cs" />
+    <Compile Include="Mapping\Config\ConfigItemMap.cs" />
     <Compile Include="Mapping\Deviation\DisturbanceValueMap.cs" />
     <Compile Include="Mapping\Deviation\DeviationMap.cs" />
     <Compile Include="Mapping\Deviation\DisturbanceMap.cs" />

+ 23 - 0
GreenTree.Nachtragsmanagement.Data/Mapping/Config/ConfigItemMap.cs

@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Data.Entity.ModelConfiguration;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GreenTree.Nachtragsmanagement.Data.Mapping.Config
+{
+    public class ConfigItemMap : EntityTypeConfiguration<Core.Domain.Config.ConfigItem>
+    {
+        public ConfigItemMap()
+        {
+            ToTable("ConfigItem");
+
+            HasKey(f => f.Id);
+
+            Property(f => f.Name);
+            Property(f => f.TypeFullName);
+            Property(f => f.Value);
+        }
+    }
+}

+ 5 - 3
GreenTree.Nachtragsmanagement.Data/Mapping/Misc/MailNotificationMap.cs

@@ -18,9 +18,11 @@ namespace GreenTree.Nachtragsmanagement.Data.Mapping.Misc
 
             Property(m => m.CronExpression);
             Property(m => m.NotificationPluginSystemName);
-            Property(m => m.IsDailySummary);
-            Property(m => m.IsWeeklySummary);
-            Property(m => m.IsMonthlySummary);
+            Property(m => m.NotificationJobSystemName);
+
+            HasMany(m => m.Users)
+                .WithMany(u => u.MailNotifications)
+                .Map(m => m.ToTable("UserMailNotifications"));
         }
     }
 }

+ 126 - 0
GreenTree.Nachtragsmanagement.Services/Configuration/ConfigurationService.cs

@@ -6,11 +6,32 @@ using System.Text;
 using System.Threading.Tasks;
 using System.Web.Configuration;
 using GreenTree.Nachtragsmanagement.Core.Configuration;
+using GreenTree.Nachtragsmanagement.Core.Domain.Config;
+using GreenTree.Nachtragsmanagement.Core.Data;
 
 namespace GreenTree.Nachtragsmanagement.Services.Configuration
 {
     public class ConfigurationService : IConfigurationService
     {
+        #region Fields
+
+        private readonly IRepository<ConfigItem> _configItemRepository;
+
+        #endregion
+
+        #region Ctor
+
+        /// <summary>
+        /// Initializes a new instance of the ConfigurationService class
+        /// </summary>
+        public ConfigurationService(
+            IRepository<ConfigItem> configItemRepository)
+        {
+            _configItemRepository = configItemRepository;
+        }
+
+        #endregion
+
         /// <summary>
         /// Reads the current configuration from the global config
         /// </summary>
@@ -22,5 +43,110 @@ namespace GreenTree.Nachtragsmanagement.Services.Configuration
 
             return (AppendixConfigurationSection)section;
         }
+
+        #region ConfigItem
+
+        /// <summary>
+        /// Gets all configItems
+        /// </summary>
+        public IList<ConfigItem> GetAllConfigItems()
+        {
+            return _configItemRepository.Table.ToList();
+        }
+
+        /// <summary>
+        /// Gets a configItem by specified Id
+        /// </summary>
+        /// <param name="id">ConfigItem identifier.</param>
+        public ConfigItem GetConfigItemById(int id)
+        {
+            return _configItemRepository.GetById(id);
+        }
+
+        /// <summary>
+        /// Gets a configItem by specified name
+        /// </summary>
+        /// <param name="name">ConfigItem name.</param>
+        public ConfigItem GetConfigItemByName(string name)
+        {
+            return _configItemRepository.Table
+                .FirstOrDefault(r => r.Name == name);
+        }
+
+        /// <summary>
+        /// Gets all configItems to the specified ids
+        /// </summary>
+        public IList<ConfigItem> GetConfigItemsByIds(int[] ids)
+        {
+            return _configItemRepository.Table
+                .Where(r => ids.Contains(r.Id))
+                .ToList();
+        }
+
+        /// <summary>
+        /// Insert a configItem
+        /// </summary>
+        /// <param name="configItem">ConfigItem.</param>
+        public void InsertConfigItem(ConfigItem configItem)
+        {
+            _configItemRepository.Insert(configItem);
+        }
+
+        /// <summary>
+        /// Update a configItem
+        /// </summary>
+        /// <param name="configItem">ConfigItem.</param>
+        public void UpdateConfigItem(ConfigItem configItem)
+        {
+            _configItemRepository.Update(configItem);
+        }
+
+        /// <summary>
+        /// Delete a configItem
+        /// </summary>
+        /// <param name="configItem">ConfigItem.</param>
+        public void DeleteConfigItem(ConfigItem configItem)
+        {
+            _configItemRepository.Delete(configItem);
+        }
+
+        #endregion
+
+        #region Casting
+
+        /// <summary>
+        /// Trys to convert the config items value type and returns its actual value, otherwise NULL
+        /// </summary>
+        /// <typeparam name="T">The actual type.</typeparam>
+        /// <param name="configItem">ConfigItem.</param>
+        /// <param name="success">Determines if the conversion was possible.</param>
+        public T TryGetConfigItemValue<T>(ConfigItem configItem, out bool success)
+        {
+            success = false;
+
+            if (configItem == null)
+            {
+
+
+                return default(T);
+            }
+
+            object result = null;
+
+            try
+            {
+                result = Convert.ChangeType(configItem.Value, typeof(T));
+
+                success = true;
+            }
+            catch
+            {
+                success = false;
+            }
+
+            return (T)result;
+        }
+
+        #endregion
     }
 }

+ 57 - 0
GreenTree.Nachtragsmanagement.Services/Configuration/IConfigurationService.cs

@@ -4,6 +4,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using GreenTree.Nachtragsmanagement.Core.Configuration;
+using GreenTree.Nachtragsmanagement.Core.Domain.Config;
 
 namespace GreenTree.Nachtragsmanagement.Services.Configuration
 {
@@ -13,5 +14,61 @@ namespace GreenTree.Nachtragsmanagement.Services.Configuration
         /// Reads the current configuration from the global config
         /// </summary>
         AppendixConfigurationSection GetCurrentConfiguration();
+
+        #region ConfigItem
+
+        /// <summary>
+        /// Gets all configItems
+        /// </summary>
+        IList<ConfigItem> GetAllConfigItems();
+
+        /// <summary>
+        /// Gets a configItem by specified Id
+        /// </summary>
+        /// <param name="id">ConfigItem identifier.</param>
+        ConfigItem GetConfigItemById(int id);
+
+        /// <summary>
+        /// Gets a configItem by specified name
+        /// </summary>
+        /// <param name="name">ConfigItem name.</param>
+        ConfigItem GetConfigItemByName(string name);
+
+        /// <summary>
+        /// Gets all configItems to the specified ids
+        /// </summary>
+        IList<ConfigItem> GetConfigItemsByIds(int[] ids);
+
+        /// <summary>
+        /// Insert a configItem
+        /// </summary>
+        /// <param name="configItem">ConfigItem.</param>
+        void InsertConfigItem(ConfigItem configItem);
+
+        /// <summary>
+        /// Update a configItem
+        /// </summary>
+        /// <param name="configItem">ConfigItem.</param>
+        void UpdateConfigItem(ConfigItem configItem);
+
+        /// <summary>
+        /// Delete a configItem
+        /// </summary>
+        /// <param name="configItem">ConfigItem.</param>
+        void DeleteConfigItem(ConfigItem configItem);
+
+        #endregion
+
+        #region Casting
+
+        /// <summary>
+        /// Trys to convert the config items value type and returns its actual value, otherwise NULL
+        /// </summary>
+        /// <typeparam name="T">The actual type.</typeparam>
+        /// <param name="configItem">ConfigItem.</param>
+        /// <param name="success">Determines if the conversion was possible.</param>
+        T TryGetConfigItemValue<T>(ConfigItem configItem, out bool success);
+
+        #endregion
     }
 }

+ 9 - 4
GreenTree.Nachtragsmanagement.Services/Misc/NotificationService.cs

@@ -2,6 +2,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Reflection;
 using System.Text;
 using System.Threading.Tasks;
 
@@ -15,12 +16,16 @@ namespace GreenTree.Nachtragsmanagement.Services.Misc
         public IEnumerable<INotificationPlugin> GetNotificationPlugins()
         {
             var type = typeof(INotificationPlugin);
-            var types = AppDomain.CurrentDomain.GetAssemblies()
-                .SelectMany(s => s.GetTypes())
+            var assemblies = AppDomain.CurrentDomain.GetAssemblies();
+
+            var types = assemblies
+                .SelectMany(p => p.GetTypes())
                 .Where(p => type.IsAssignableFrom(p) && !p.IsInterface)
-                .OfType<INotificationPlugin>();
+                .Select(p => Activator.CreateInstance(p))
+                .OfType<INotificationPlugin>()
+                .ToList();
 
             return types;
         }
     }
-}
+}

+ 45 - 2
GreenTree.Nachtragsmanagement.Web/Global.asax.cs

@@ -26,6 +26,9 @@ using GreenTree.Nachtragsmanagement.Core.Domain.Appendix;
 using GreenTree.Nachtragsmanagement.Services.Appendix;
 using GreenTree.Nachtragsmanagement.Services.Site;
 using GreenTree.Nachtragsmanagement.Core.Plugins;
+using GreenTree.Nachtragsmanagement.Services.Configuration;
+using GreenTree.Nachtragsmanagement.Core.Domain.Config;
+using GreenTree.Nachtragsmanagement.Services.Misc;
 
 namespace GreenTree.Nachtragsmanagement.Web
 {
@@ -59,8 +62,6 @@ namespace GreenTree.Nachtragsmanagement.Web
             DevExpress.Web.ASPxWebControl.CallbackError += Application_Error;
 
             GenerateTestData();
-
-            var notificationPlugins = Singleton<IContainer>.Instance.Resolve<IEnumerable<INotificationPlugin>>();
         }
 
         protected void Application_Error(object sender, EventArgs e) 
@@ -79,6 +80,8 @@ namespace GreenTree.Nachtragsmanagement.Web
             var deviationService = Singleton<IContainer>.Instance.Resolve<IDeviationService>();
             var appendixService = Singleton<IContainer>.Instance.Resolve<IAppendixService>();
             var siteService = Singleton<IContainer>.Instance.Resolve<ISiteService>();
+            var configurationService = Singleton<IContainer>.Instance.Resolve<IConfigurationService>();
+            var miscService = Singleton<IContainer>.Instance.Resolve<IMiscService>();
             var dbContext = Singleton<IContainer>.Instance.Resolve<IDbContext>();
 
             try
@@ -614,6 +617,46 @@ namespace GreenTree.Nachtragsmanagement.Web
 
                 foreach (var deviation in deviations)
                     deviationService.InsertDeviation(deviation);
+
+                // Create config base data
+
+                var configItems = new[]
+                {
+                    new ConfigItem
+                    {
+                        Name = "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationDate.AgeDays",
+                        TypeFullName = "System.Int32",
+                        Value = "56"
+                    },
+                    new ConfigItem
+                    {
+                        Name = "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationDate.StateSet",
+                        TypeFullName = "System.Int32",
+                        Value = states[1].Id.ToString()
+                    }
+                };
+
+                foreach (var configItem in configItems)
+                    configurationService.InsertConfigItem(configItem);
+
+                // Create notification base data
+
+                var notifications = new[]
+                {
+                    new MailNotification
+                    {
+                        CronExpression = "0 0 6 ? * MON *",
+                        NotificationPluginSystemName = "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin",
+                        NotificationJobSystemName = "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationDate",
+                        Users =
+                        {
+                            u1
+                        }
+                    }
+                };
+
+                foreach (var notification in notifications)
+                    miscService.InsertMailNotification(notification);
             }
             finally
             {

+ 192 - 2
GreenTree.Nachtragsmanagement.Web/Scheduling/AppendixNotificationPlugin.cs

@@ -5,6 +5,11 @@ using System.Linq;
 using System.Web;
 using GreenTree.Nachtragsmanagement.Core.Domain.Misc;
 using GreenTree.Nachtragsmanagement.Services.User;
+using GreenTree.Nachtragsmanagement.Services.Configuration;
+using GreenTree.Nachtragsmanagement.Services.Appendix;
+using GreenTree.Nachtragsmanagement.Core.Domain.Appendix;
+using System.Net.Mail;
+using System.Net;
 
 namespace GreenTree.Nachtragsmanagement.Web.Scheduling
 {
@@ -13,6 +18,8 @@ namespace GreenTree.Nachtragsmanagement.Web.Scheduling
         #region Services
 
         private readonly IUserService _userService;
+        private readonly IConfigurationService _configurationService;
+        private readonly IAppendixService _appendixService;
 
         #endregion
 
@@ -40,6 +47,35 @@ namespace GreenTree.Nachtragsmanagement.Web.Scheduling
             }
         }
 
+        /// <summary>
+        /// List of available notification jobs
+        /// </summary>
+        public List<NotificationJob> AvailableNotificationJobs
+        {
+            get
+            {
+                return new List<NotificationJob>
+                {
+                    new NotificationJob
+                    (
+                        Guid.Parse("2F3642E0-259D-466D-8629-CB279F740313"),
+                        "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationDate",
+                        "Verhandlungstermine überprüfen",
+                        "Erstellt automatisch Benachrichtigungen für Nachträge, die nach 8 Wochen nach Einreichung noch keinen " +
+                        "Verhandlungstermin gesetzt haben und ändert den entsprechenden Status ab"
+                    ),
+                    new NotificationJob
+                    (
+                        Guid.Parse("2E46B32A-1912-47F9-951E-C8188AA9BA50"),
+                        "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationProtocol",
+                        "Verhandlungsprotokolle überprüfen",
+                        "Erstellt automatisch Benachrichtigungen für Nachträge, die nach 2 Wochen die zwar verhandelt sind, " +
+                        "jedoch noch kein Protokoll aufweisen."
+                    )
+                };
+            }
+        }
+
         /// <summary>
         /// Displayed name
         /// </summary>
@@ -63,25 +99,179 @@ namespace GreenTree.Nachtragsmanagement.Web.Scheduling
                     "Verhandlungstermin gesetzt haben und ändert den entsprechenden Status ab. Außerdem werden alle 2 Wochen " +
                     "Benachrichtigungen für Nachträge erstellt, die zwar verhandelt sind, jedoch noch kein Protokoll aufweisen.";
             }
-        } 
+        }
         #endregion
 
+        /// <summary>
+        /// Initializes a new instance of the AppendixNotificationPlugin class
+        /// </summary>
+        public AppendixNotificationPlugin() { }
+
         /// <summary>
         /// Initializes a new instance of the AppendixNotificationPlugin class
         /// </summary>
         public AppendixNotificationPlugin(
-            IUserService userService)
+            IUserService userService,
+            IConfigurationService configurationService,
+            IAppendixService appendixService)
         {
             _userService = userService;
+            _configurationService = configurationService;
+            _appendixService = appendixService;
         }
 
+        #region Processing
+
         /// <summary>
         /// Process all mail notifications registered for that plugin
         /// </summary>
         /// <param name="mailNotifications">The notifications which shall be generated.</param>
         public void ProcessNotifications(IEnumerable<MailNotification> mailNotifications)
+        {
+            if (mailNotifications == null || !mailNotifications.Any()) return;
+
+            foreach (var notification in mailNotifications)
+            {
+                switch (notification.NotificationJobSystemName)
+                {
+                    case "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationDate":
+                        {
+                            var conversionSuccess = false;
+
+                            var ageDaysConfigItem = _configurationService.GetConfigItemByName(
+                                "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationDate.AgeDays");
+
+                            var ageDays = ageDaysConfigItem == null
+                                ? 56
+                                : _configurationService.TryGetConfigItemValue<int>(ageDaysConfigItem, out conversionSuccess);
+
+                            if (!conversionSuccess)
+                                ageDays = 56;
+
+                            var stateSetConfigItem = _configurationService.GetConfigItemByName(
+                                "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationDate.StateSet");
+
+                            var stateId = stateSetConfigItem == null
+                                ? 2
+                                : _configurationService.TryGetConfigItemValue<int>(stateSetConfigItem, out conversionSuccess);
+
+                            if (!conversionSuccess)
+                                stateId = 2;
+
+                            var appendices = _appendixService.GetAllAppendices()
+                                .Where(a => a.OfferingDate <= DateTime.Now.AddDays(ageDays) &&
+                                            a.StateId != stateId &&
+                                            a.NegotiationDate == null)
+                                .ToList();
+
+                            var mailBody = GenerateNegotiationDateMailBody(appendices);
+
+                            foreach (var appendix in appendices)
+                            {
+                                appendix.StateId = stateId;
+
+                                _appendixService.UpdateAppendix(appendix);
+                            }
+
+                            SendNotification(notification, "Autom. Übersicht nicht verhandelte Nachträge", mailBody);
+                        }
+                        break;
+                    case "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationProtocol":
+
+                        break;
+                    default:
+                        continue;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Sets the corresponding status for appendices which offering date is older than 8 weeks and notifies the correspondig recipients
+        /// </summary>
+        private void ProcessNegotiationDateNotification()
         {
 
         }
+
+        #endregion
+
+        #region Mail body generation
+
+        /// <summary>
+        /// Generates a mail body with a list of all appendices with a offering date later than 8 weeks
+        /// </summary>
+        /// <param name="appendices">Appendices matching that criteria.</param>
+        public string GenerateNegotiationDateMailBody(IEnumerable<Appendix> appendices)
+        {
+            var template =
+                "<html>" +
+                "   <body>" +
+                "       <h3>Übersicht \"Nicht verhandelte Nachträge\"</h3>" +
+                "       <p>Folgende Nachträge haben ein Einreichdatum älter als 8 Wochen, jedoch noch kein Verhandlungstermin" +
+                "       gesetzt:</p>" +
+                "       <ul>" +
+                "           {0}" +
+                "       </ul>" +
+                "   </body>" +
+                "</html>";
+
+            if (!appendices.Any()) return String.Format(template, String.Empty);
+
+            var appendicesList = String.Empty;
+
+            foreach (var appendix in appendices)
+            {
+                appendicesList += String.Format(
+                    "<li>Nachtrag <b>\"{0}\"</b> in Baustelle <b>\"{1}\"</b> - Einreichdatum: <b>{2:dd.MM.yyyy}</b>",
+                    appendix.CustomNumber, appendix.Site.CustomNumber, appendix.OfferingDate);
+            }
+
+            return String.Format(template, appendicesList);
+        }
+
+        #endregion
+
+        #region Mail sending
+
+        /// <summary>
+        /// Sends a generated mail body to the specified recipients in the mail notification
+        /// </summary>
+        /// <param name="mailNotification">The mail notification.</param>
+        /// <param name="subject">The mail subject.</param>
+        /// <param name="body">The mail body.</param>
+        public void SendNotification(MailNotification mailNotification, string subject, string body)
+        {
+            if (mailNotification == null) return;
+
+            var mailServerConfig = _configurationService.GetCurrentConfiguration().MailServerElement;
+
+            var smptClient = new SmtpClient(mailServerConfig.SmtpServer, mailServerConfig.Port)
+            {
+                EnableSsl = mailServerConfig.UseSsl,
+                Credentials = new NetworkCredential(
+                    mailServerConfig.Username,
+                    mailServerConfig.Password,
+                    mailServerConfig.Domain)
+            };
+
+            var recipients =
+                mailNotification.Users
+                    .Select(u => u.MailAddress);
+
+            var mailMessage = new MailMessage
+            {
+                IsBodyHtml = true,
+                Subject = subject,
+                Body = body,
+                From = new MailAddress("Nachtragsbenachrichtigung@schweerbau.de")
+            };
+
+            foreach (var recipient in recipients)
+                mailMessage.To.Add(recipient);
+
+            smptClient.Send(mailMessage);
+        }
+
+        #endregion
     }
 }

+ 1 - 1
GreenTree.Nachtragsmanagement.Web/Web.config

@@ -29,7 +29,7 @@
   </appSettings>
   <appendixSectionGroup>
     <appendixConfigSection iisAnonymousUser="IUSR">
-      <mailServer smtpServer="localhost" port="25" username="" domain="" password="" useSsl="false">
+      <mailServer smtpServer="greentreestudios.de" port="25" username="a.diekmann@greentreestudios.de" domain="" password="14595809ad." useSsl="false">
       </mailServer>
       <ldapServer ldapServer="" administriveUser="" password="">
       </ldapServer>