Prechádzať zdrojové kódy

Konfigurationsbearbeitung gestartet!

Arne Diekmann 8 rokov pred
rodič
commit
dea8556ad6
23 zmenil súbory, kde vykonal 591 pridanie a 11 odobranie
  1. 5 0
      GreenTree.Nachtragsmanagement.Core/Domain/Appendix/State.cs
  2. 5 0
      GreenTree.Nachtragsmanagement.Core/Domain/Config/ConfigItem.cs
  3. 1 0
      GreenTree.Nachtragsmanagement.Data/Mapping/Appendix/StateMap.cs
  4. 1 0
      GreenTree.Nachtragsmanagement.Data/Mapping/Config/ConfigItemMap.cs
  5. 9 0
      GreenTree.Nachtragsmanagement.Services/Appendix/AppendixService.cs
  6. 5 0
      GreenTree.Nachtragsmanagement.Services/Appendix/IAppendixService.cs
  7. 24 0
      GreenTree.Nachtragsmanagement.Web/App_Start/FunctionConfig.cs
  8. 14 0
      GreenTree.Nachtragsmanagement.Web/App_Start/RouteConfig.cs
  9. BIN
      GreenTree.Nachtragsmanagement.Web/Content/Images/function-Misc-ConfigItems-32-contrast.png
  10. BIN
      GreenTree.Nachtragsmanagement.Web/Content/Images/function-Misc-ConfigItems-32.png
  11. 11 0
      GreenTree.Nachtragsmanagement.Web/Controllers/AppendixController.cs
  12. 152 0
      GreenTree.Nachtragsmanagement.Web/Controllers/MiscController.cs
  13. 16 4
      GreenTree.Nachtragsmanagement.Web/Global.asax.cs
  14. 4 0
      GreenTree.Nachtragsmanagement.Web/GreenTree.Nachtragsmanagement.Web.csproj
  15. 6 2
      GreenTree.Nachtragsmanagement.Web/Implementations/AppendixNotificationPlugin.cs
  16. 5 2
      GreenTree.Nachtragsmanagement.Web/Models/Appendix/StateDataModel.cs
  17. 59 0
      GreenTree.Nachtragsmanagement.Web/Models/Config/ConfigItemDataModel.cs
  18. 3 0
      GreenTree.Nachtragsmanagement.Web/Validation/AppendixValidatorFactory.cs
  19. 24 0
      GreenTree.Nachtragsmanagement.Web/Validation/Config/ConfigItemDataModelValidator.cs
  20. 3 3
      GreenTree.Nachtragsmanagement.Web/Views/Admin/Plugins/_PluginsGridPartial.cshtml
  21. 35 0
      GreenTree.Nachtragsmanagement.Web/Views/Appendices/_StateEditPartial.cshtml
  22. 121 0
      GreenTree.Nachtragsmanagement.Web/Views/Config/View.cshtml
  23. 88 0
      GreenTree.Nachtragsmanagement.Web/Views/Config/_ConfigItemGridPartial.cshtml

+ 5 - 0
GreenTree.Nachtragsmanagement.Core/Domain/Appendix/State.cs

@@ -22,5 +22,10 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.Appendix
         /// Determines if this state is defaultly selected
         /// </summary>
         public bool IsDefault { get; set; }
+
+        /// <summary>
+        /// Determines if this state is the final state
+        /// </summary>
+        public bool IsFinish { get; set; }
     }
 }

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

@@ -22,5 +22,10 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.Config
         /// Value
         /// </summary>
         public string Value { get; set; }
+
+        /// <summary>
+        /// Description
+        /// </summary>
+        public string Description { get; set; }
     }
 }

+ 1 - 0
GreenTree.Nachtragsmanagement.Data/Mapping/Appendix/StateMap.cs

@@ -18,6 +18,7 @@ namespace GreenTree.Nachtragsmanagement.Data.Mapping.Deviation
             Property(f => f.Description);
             Property(f => f.HexColor);
             Property(f => f.IsDefault);
+            Property(f => f.IsFinish);
         }
     }
 }

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

@@ -18,6 +18,7 @@ namespace GreenTree.Nachtragsmanagement.Data.Mapping.Config
             Property(f => f.Name);
             Property(f => f.TypeFullName);
             Property(f => f.Value);
+            Property(f => f.Description);
         }
     }
 }

+ 9 - 0
GreenTree.Nachtragsmanagement.Services/Appendix/AppendixService.cs

@@ -293,6 +293,15 @@ namespace GreenTree.Nachtragsmanagement.Services.Appendix
                 .FirstOrDefault(s => s.IsDefault);
         }
 
+        /// <summary>
+        /// Gets the state which is marked as finish
+        /// </summary>
+        public State GetFinishState()
+        {
+            return _stateRepository.Table
+                .FirstOrDefault(s => s.IsFinish);
+        }
+
         /// <summary>
         /// Insert a appendix
         /// </summary>

+ 5 - 0
GreenTree.Nachtragsmanagement.Services/Appendix/IAppendixService.cs

@@ -164,6 +164,11 @@ namespace GreenTree.Nachtragsmanagement.Services.Appendix
         /// </summary>
         State GetDefaultState();
 
+        /// <summary>
+        /// Gets the state which is marked as finish
+        /// </summary>
+        State GetFinishState();
+
         /// <summary>
         /// Insert a state
         /// </summary>

+ 24 - 0
GreenTree.Nachtragsmanagement.Web/App_Start/FunctionConfig.cs

@@ -348,6 +348,30 @@ namespace GreenTree.Nachtragsmanagement.Web.App_Start
                     IsMenuMember = false,
                     Plugin = "System"
                 },
+                new Function
+                {
+                    Name = "Misc-ConfigItems",
+                    Description = "Einstellungen",
+                    ImageUrl = "~/Content/Images/function-Misc-ConfigItems-32.png",
+                    GroupName = "Misc",
+                    RouteName = "GreenTree.Nachtragsmanagement.Web.Misc.ConfigItems",
+                    IsMenuMember = true,
+                    Plugin = "System",
+                    BaseWidth = 900,
+                    MinWidth = 700,
+                    BaseHeight = 550,
+                    MinHeight = 450,
+                    AllowMaximize = true,
+                    MaximizedOnStart = false
+                },
+                new Function
+                {
+                    Name = "Misc-ConfigItems-Edit",
+                    Description = "Einstellungen ändern",
+                    GroupName = "Misc-ConfigItems",
+                    IsMenuMember = false,
+                    Plugin = "System"
+                }
             };
         }
     }

+ 14 - 0
GreenTree.Nachtragsmanagement.Web/App_Start/RouteConfig.cs

@@ -159,6 +159,20 @@ namespace GreenTree.Nachtragsmanagement.Web
                    "GreenTree.Nachtragsmanagement.Web.Controllers"
                }
             );
+
+            routes.MapRoute(
+                "GreenTree.Nachtragsmanagement.Web.Misc.ConfigItems",
+                "misc/viewconfigitems",
+               new
+               {
+                   controller = "Misc",
+                   action = "ConfigItems"
+               },
+               new[]
+               {
+                   "GreenTree.Nachtragsmanagement.Web.Controllers"
+               }
+            );
         }
     }
 }

BIN
GreenTree.Nachtragsmanagement.Web/Content/Images/function-Misc-ConfigItems-32-contrast.png


BIN
GreenTree.Nachtragsmanagement.Web/Content/Images/function-Misc-ConfigItems-32.png


+ 11 - 0
GreenTree.Nachtragsmanagement.Web/Controllers/AppendixController.cs

@@ -535,6 +535,16 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
                     }
                 }
 
+                if (stateModel.IsFinish)
+                {
+                    foreach (var state in allStates)
+                    {
+                        state.IsFinish = false;
+
+                        _appendixService.UpdateState(state);
+                    }
+                }
+
                 if (stateModel.Id == -1)
                 {
                     var claim = stateModel.ToState();
@@ -550,6 +560,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
                     state.Description = stateModel.Description;
                     state.HexColor = stateModel.HexColor;
                     state.IsDefault = stateModel.IsDefault;
+                    state.IsFinish = stateModel.IsFinish;
 
                     _appendixService.UpdateState(state);
 

+ 152 - 0
GreenTree.Nachtragsmanagement.Web/Controllers/MiscController.cs

@@ -8,12 +8,14 @@ using GreenTree.Nachtragsmanagement.Core.Authentication;
 using GreenTree.Nachtragsmanagement.Core.Domain.Deviation;
 using GreenTree.Nachtragsmanagement.Core.Domain.User;
 using GreenTree.Nachtragsmanagement.Services.Appendix;
+using GreenTree.Nachtragsmanagement.Services.Configuration;
 using GreenTree.Nachtragsmanagement.Services.Deviation;
 using GreenTree.Nachtragsmanagement.Services.Logging;
 using GreenTree.Nachtragsmanagement.Services.Misc;
 using GreenTree.Nachtragsmanagement.Services.Scheduling;
 using GreenTree.Nachtragsmanagement.Services.User;
 using GreenTree.Nachtragsmanagement.Web.Framework.Authorization;
+using GreenTree.Nachtragsmanagement.Web.Models.Config;
 using GreenTree.Nachtragsmanagement.Web.Models.Global;
 using GreenTree.Nachtragsmanagement.Web.Models.Misc;
 using Newtonsoft.Json;
@@ -34,6 +36,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         private readonly IUserService _userService;
         private readonly INotificationService _notificationService;
         private readonly INotificationScheduler _notificationScheduler;
+        private readonly IConfigurationService _configurationService;
         private readonly IUserHelper _userHelper;
         private readonly ILogger _logger;
 
@@ -42,6 +45,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
             IUserService userService,
             INotificationService notificationService,
             INotificationScheduler notificationScheduler,
+            IConfigurationService configurationService,
             IUserHelper userHelper,
             ILogger logger)
         {
@@ -49,6 +53,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
             _userService = userService;
             _notificationService = notificationService;
             _notificationScheduler = notificationScheduler;
+            _configurationService = configurationService;
             _userHelper = userHelper;
             _logger = logger;
 
@@ -451,5 +456,152 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         }
 
         #endregion
+
+        #region ConfigItems
+
+        /// <summary>
+        /// Basic configItem view function
+        /// </summary>
+        [FunctionAuthorize(true, "Misc-ConfigItems")]
+        public ActionResult ViewConfigItems()
+        {
+            var configItems = _configurationService.GetAllConfigItems();
+            var configItemModels = configItems
+                .Select(u => ConfigItemDataModel.FromConfigItem(u, false))
+                .ToList();
+
+            return View("~/Views/Config/View.cshtml", configItemModels);
+        }
+
+        /// <summary>
+        /// Get JSON data of specific configItem
+        /// </summary>
+        /// <param name="id">ConfigItem id.</param>
+        public ActionResult GetConfigItem(int id = -1)
+        {
+            var configItem = _configurationService.GetConfigItemById(id);
+            if (configItem == null)
+                return new JsonResult
+                {
+                    Data = "notFound",
+                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
+                };
+
+            var configItemModel = ConfigItemDataModel.FromConfigItem(configItem, false);
+
+            return new JsonResult
+            {
+                Data = JsonConvert.SerializeObject(configItemModel),
+                JsonRequestBehavior = JsonRequestBehavior.AllowGet
+            };
+        }
+
+        /// <summary>
+        /// Callback result for configItem grid
+        /// </summary>
+        /// <param name="scrollHeight">The height of the grid scrollable component.</param>
+        public ActionResult PartialConfigItems(int scrollHeight = -1)
+        {
+            var configItems = _configurationService.GetAllConfigItems();
+            var configItemModels = configItems
+                .Select(u => ConfigItemDataModel.FromConfigItem(u, false))
+                .ToList();
+
+            ViewData["ScrollHeight"] = scrollHeight;
+
+            return PartialView("~/Views/Config/_ConfigItemGridPartial.cshtml", configItemModels);
+        }
+
+        /// <summary>
+        /// Partial edit for editing of existing or for new configItem
+        /// </summary>
+        /// <param name="id">Id for existing configItem, otherweise -1.</param>
+        public ActionResult EditConfigItem(int id = -1)
+        {
+            var configItem = _configurationService.GetConfigItemById(id);
+            var configItemModel = ConfigItemDataModel.FromConfigItem(configItem, true);
+
+            return PartialView("~/Views/Config/_ConfigItemEditPartial.cshtml", configItemModel);
+        }
+
+        /// <summary>
+        /// Partial edit result if ModelState is valid, otherwise simple JSON result for success
+        /// </summary>
+        /// <param name="configItemModel">ConfigItem model to be saved.</param>
+        [HttpPost, ValidateInput(false)]
+        public ActionResult EditConfigItem(ConfigItemDataModel configItemModel)
+        {
+            try
+            {
+                if (!ModelState.IsValid)
+                    return PartialView("~/Views/Config/_ConfigItemEditPartial.cshtml", configItemModel);
+
+                if (configItemModel.Id == -1)
+                {
+                    var configItem = configItemModel.ToConfigItem();
+
+                    _configurationService.InsertConfigItem(configItem);
+
+                    _logger.Entity(configItem, Core.Domain.Logging.LogEntityActivity.Insert, _userHelper.FromCookies());
+                }
+                else
+                {
+                    var configItem = _configurationService.GetConfigItemById(configItemModel.Id);
+
+                    configItem.Name = configItemModel.Name;
+                    configItem.TypeFullName = configItemModel.TypeFullName;
+                    configItem.Value = configItemModel.Value;
+                    configItem.Description = configItem.Description;
+
+                    _configurationService.UpdateConfigItem(configItem);
+
+                    _logger.Entity(configItem, Core.Domain.Logging.LogEntityActivity.Update, _userHelper.FromCookies());
+                }
+
+                _notificationScheduler.Start();
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
+            {
+                _logger.Error("Fehler bei Speicherung einer Einstellung.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
+        }
+
+        /// <summary>
+        /// Simple JSON result for deleting a specific configItem
+        /// </summary>
+        /// <param name="id">ConfigItem id.</param>
+        [HttpPost]
+        public ActionResult DeleteConfigItem(int id)
+        {
+            try
+            {
+                var configItem = _configurationService.GetConfigItemById(id);
+
+                if (configItem != null)
+                    _configurationService.DeleteConfigItem(configItem);
+
+                _logger.Entity(configItem, Core.Domain.Logging.LogEntityActivity.Delete, _userHelper.FromCookies());
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
+            {
+                _logger.Error("Fehler bei Löschung einer Einstellung.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
+        }
+
+        #endregion
     }
 }

+ 16 - 4
GreenTree.Nachtragsmanagement.Web/Global.asax.cs

@@ -659,25 +659,37 @@ namespace GreenTree.Nachtragsmanagement.Web
                     {
                         Name = "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationDate.AgeDays",
                         TypeFullName = "System.Int32",
-                        Value = "56"
+                        Value = "56",
+                        Description =
+                            "Die Anzahl der Tage ab dem Verhandlungstermin eines Nachtrags, damit er bei der Überprüfung des " +
+                            "Verhandlungsdatums mit berücksichtigt werden soll"
                     },
                     new ConfigItem
                     {
                         Name = "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationDate.StateCondition",
                         TypeFullName = "System.Int32",
-                        Value = states[0].Id.ToString()
+                        Value = states[0].Id.ToString(),
+                        Description =
+                            "Die ID des Nachtragsstatus, den ein Nachtrag haben muss, damit er bei der Überprüfung des " +
+                            "Verhandlungsdatums mit berücksichtigt werden soll"
                     },
                     new ConfigItem
                     {
                         Name = "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationDate.StateSet",
                         TypeFullName = "System.Int32",
-                        Value = states[1].Id.ToString()
+                        Value = states[1].Id.ToString(),
+                        Description =
+                            "Die ID des Nachtragsstatus, auf den der Nachtrag gesetzt wird, nachdem er bei der Überprüfung des " +
+                            "Verhandlungsdatums geprüft wurde"
                     },
                     new ConfigItem
                     {
                         Name = "GreenTree.Nachtragsmanagement.AppendixNotificationPlugin.ProcessNegotiationDate.Interval",
                         TypeFullName = "System.Int32",
-                        Value = "2"
+                        Value = "2",
+                        Description =
+                            "Der Wocheninterval, in dem weitere Benachrichtigungen über nicht verhandelte Nachträge gesendet werden " +
+                            "soll"
                     },
                     new ConfigItem
                     {

+ 4 - 0
GreenTree.Nachtragsmanagement.Web/GreenTree.Nachtragsmanagement.Web.csproj

@@ -325,6 +325,8 @@
     <Content Include="Views\Misc\_LogViewPartial.cshtml" />
     <Content Include="Views\Shared\_EditCommentPartial.cshtml" />
     <Content Include="Views\Admin\Plugins\_PluginsGridPartial.cshtml" />
+    <Content Include="Views\Config\View.cshtml" />
+    <Content Include="Views\Config\_ConfigItemGridPartial.cshtml" />
     <None Include="Web.Debug.config">
       <DependentUpon>Web.config</DependentUpon>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -343,6 +345,7 @@
     <Compile Include="Controllers\MiscController.cs" />
     <Compile Include="Extensions\ControlHelper.cs" />
     <Compile Include="Models\Admin\Plugins\PluginDataModel.cs" />
+    <Compile Include="Models\Config\ConfigItemDataModel.cs" />
     <Compile Include="Models\Global\EditEntityCommentModel.cs" />
     <Compile Include="Models\Misc\LogDataModel.cs" />
     <Compile Include="Models\Misc\MailNotificationDataModel.cs" />
@@ -398,6 +401,7 @@
     <Compile Include="Validation\Appendix\CategoryDataModelValidator.cs" />
     <Compile Include="Validation\Appendix\InvoiceDataModelValidator.cs" />
     <Compile Include="Validation\Appendix\StateDataModelValidator.cs" />
+    <Compile Include="Validation\Config\ConfigItemDataModelValidator.cs" />
     <Compile Include="Validation\Deviation\KindDataModelValidator.cs" />
     <Compile Include="Validation\Deviation\DisturbanceDataModelValidator.cs" />
     <Compile Include="Validation\Deviation\StatusDataModelValidator.cs" />

+ 6 - 2
GreenTree.Nachtragsmanagement.Web/Implementations/AppendixNotificationPlugin.cs

@@ -195,10 +195,12 @@ namespace GreenTree.Nachtragsmanagement.Web.Implementations
                 ? 1
                 : interval;
 
+            var finishStateId = _appendixService.GetFinishState().Id;
+
             var appendices = _appendixService.GetAllAppendices()
                 .Where(a => a.OfferingDate.HasValue &&
                             (DateTime.Now - a.OfferingDate).Value.Days >= ageDays &&
-                            a.StateId == stateConditionId &&
+                            a.StateId != stateSetId && a.StateId != finishStateId &&
                             a.NegotiationDate == null)
                 .ToList();
 
@@ -243,10 +245,12 @@ namespace GreenTree.Nachtragsmanagement.Web.Implementations
                 ? 1 
                 : interval;
 
+            var finishStateId = _appendixService.GetFinishState().Id;
+
             var appendices = _appendixService.GetAllAppendices()
                 .Where(a => a.NegotiationDate.HasValue &&
                             (DateTime.Now - a.NegotiationDate).Value.Days >= ageDays &&
-                            a.StateId == stateConditionId)
+                            !a.ProtocolExists && a.StateId != finishStateId)
                 .ToList();
 
             var currentCalendarWeek = GetCalendarWeek(DateTime.Now);

+ 5 - 2
GreenTree.Nachtragsmanagement.Web/Models/Appendix/StateDataModel.cs

@@ -11,6 +11,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Appendix
         public string Description { get; set; }
         public string HexColor { get; set; }
         public bool IsDefault { get; set; }
+        public bool IsFinish { get; set; }
 
         public static StateDataModel FromState(Core.Domain.Appendix.State stateEntity, bool newWhenIsNull)
         {
@@ -28,7 +29,8 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Appendix
                 Id = stateEntity.Id,
                 Description = stateEntity.Description,
                 HexColor = stateEntity.HexColor,
-                IsDefault = stateEntity.IsDefault
+                IsDefault = stateEntity.IsDefault,
+                IsFinish = stateEntity.IsFinish
             };
         }
 
@@ -39,7 +41,8 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Appendix
                 Id = this.Id,
                 Description = this.Description,
                 HexColor = this.HexColor,
-                IsDefault = this.IsDefault
+                IsDefault = this.IsDefault,
+                IsFinish = this.IsFinish
             };
         }
     }

+ 59 - 0
GreenTree.Nachtragsmanagement.Web/Models/Config/ConfigItemDataModel.cs

@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace GreenTree.Nachtragsmanagement.Web.Models.Config
+{
+    public class ConfigItemDataModel
+    {
+        public static Dictionary<string, string> FullTypeTranslations = new Dictionary<string, string>
+        {
+            { typeof(System.String).FullName, "Text" },
+            { typeof(System.Int32).FullName, "Ganze Zahl" },
+            { typeof(System.Int64).FullName, "Große ganze Zahl" },
+            { typeof(System.Boolean).FullName, "Wahrheitswert" },
+            { typeof(System.Double).FullName, "Kommazahl" },
+            { typeof(System.DateTime).FullName, "Datum" }
+        };
+
+        public int Id { get; set; }
+        public string Name { get; set; }
+        public string TypeFullName { get; set; }
+        public string TypeDescription { get; set; }
+        public string Value { get; set; }
+        public string Description { get; set; }
+
+        public static ConfigItemDataModel FromConfigItem(Core.Domain.Config.ConfigItem configItemEntity, bool newWhenIsNull)
+        {
+            if (configItemEntity == null && newWhenIsNull)
+                return new ConfigItemDataModel
+                {
+                    Id = -1
+                };
+
+            if (configItemEntity == null && !newWhenIsNull)
+                throw new ArgumentNullException("configItemEntity", "Cannot create ConfigItemDataModel from NULL configItem entity.");
+
+            return new ConfigItemDataModel
+            {
+                Id = configItemEntity.Id,
+                TypeFullName = configItemEntity.TypeFullName,
+                TypeDescription = FullTypeTranslations[configItemEntity.TypeFullName],
+                Value = configItemEntity.Value,
+                Description = configItemEntity.Description
+            };
+        }
+
+        public Core.Domain.Config.ConfigItem ToConfigItem()
+        {
+            return new Core.Domain.Config.ConfigItem
+            {
+                Id = this.Id,
+                TypeFullName = this.TypeFullName,
+                Value = this.Value,
+                Description = this.Description
+            };
+        }
+    }
+}

+ 3 - 0
GreenTree.Nachtragsmanagement.Web/Validation/AppendixValidatorFactory.cs

@@ -1,11 +1,13 @@
 using FluentValidation;
 using GreenTree.Nachtragsmanagement.Web.Models.Admin.User;
 using GreenTree.Nachtragsmanagement.Web.Models.Appendix;
+using GreenTree.Nachtragsmanagement.Web.Models.Config;
 using GreenTree.Nachtragsmanagement.Web.Models.Deviation;
 using GreenTree.Nachtragsmanagement.Web.Models.Misc;
 using GreenTree.Nachtragsmanagement.Web.Models.Site;
 using GreenTree.Nachtragsmanagement.Web.Validation.Admin.User;
 using GreenTree.Nachtragsmanagement.Web.Validation.Appendix;
+using GreenTree.Nachtragsmanagement.Web.Validation.Config;
 using GreenTree.Nachtragsmanagement.Web.Validation.Deviation;
 using GreenTree.Nachtragsmanagement.Web.Validation.Misc;
 using GreenTree.Nachtragsmanagement.Web.Validation.Site;
@@ -34,6 +36,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Validation
             validators.Add(typeof(IValidator<CategoryDataModel>), new CategoryDataModelValidator());
             validators.Add(typeof(IValidator<SiteDataModel>), new SiteDataModelValidator());
             validators.Add(typeof(IValidator<MailNotificationDataModel>), new MailNotificationDataModelValidator());
+            validators.Add(typeof(IValidator<ConfigItemDataModel>), new ConfigItemDataModelValidator());
         }
 
         public override IValidator CreateInstance(Type validatorType)

+ 24 - 0
GreenTree.Nachtragsmanagement.Web/Validation/Config/ConfigItemDataModelValidator.cs

@@ -0,0 +1,24 @@
+using FluentValidation;
+using GreenTree.Nachtragsmanagement.Web.Models.Config;
+using GreenTree.Nachtragsmanagement.Web.Models.Deviation;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace GreenTree.Nachtragsmanagement.Web.Validation.Config
+{
+    public class ConfigItemDataModelValidator : AbstractValidator<ConfigItemDataModel>
+    {
+        public ConfigItemDataModelValidator()
+        {
+            RuleFor(m => m.Name)
+                .NotEmpty()
+                    .WithMessage("Ein Name wird benötigt");
+
+            RuleFor(m => m.TypeFullName)
+                .NotEmpty()
+                    .WithMessage("Der Wertetyp wird benötigt");
+        }
+    }
+}

+ 3 - 3
GreenTree.Nachtragsmanagement.Web/Views/Admin/Plugins/_PluginsGridPartial.cshtml

@@ -95,11 +95,11 @@
 	{
 		if (e.LayoutMode == ClientLayoutMode.Loading)
 		{
-			if (Session["UserGridState"] != null)
-				e.LayoutData = (string)Session["UserGridState"];
+			if (Session["PluginGridState"] != null)
+				e.LayoutData = (string)Session["PluginGridState"];
 		}
 		else
-			Session["UserGridState"] = e.LayoutData;
+			Session["PluginGridState"] = e.LayoutData;
 	};
 	s.ClientSideEvents.BeginCallback = "function (s, e) { e.customArgs['scrollHeight'] = [ gridScrollHeight ]; }";
 

+ 35 - 0
GreenTree.Nachtragsmanagement.Web/Views/Appendices/_StateEditPartial.cshtml

@@ -104,6 +104,41 @@
 			}
 			ViewContext.Writer.Write("</div>");
 
+			ViewContext.Writer.Write("<div class='inlineModelPropertyContainer'>");
+			{
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 100%'>");
+				{
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.IsFinish, "Ist Abschluss:"));
+					ViewContext.Writer.Write("<div style=\"overflow: hidden\">");
+					{
+						ViewContext.Writer.Write("<div style=\"float: left\">");
+						{
+							Html.DevExpress().RadioButtonFor(m => m.IsFinish, t =>
+							{
+								t.Text = "Ja";
+								t.GroupName = "isFinish";
+							}).Render();
+						}
+						ViewContext.Writer.Write("</div>");
+
+						ViewContext.Writer.Write("<div style=\"float: left; margin-left: 8px\">");
+						{
+							Html.DevExpress().RadioButton(t =>
+							{
+								t.Name = "isFinishFalse";
+								t.Text = "Nein";
+								t.GroupName = "isFinish";
+								t.Checked = !Model.IsFinish;
+							}).Render();
+						}
+						ViewContext.Writer.Write("</div>");
+					}
+					ViewContext.Writer.Write("</div>");
+				}
+				ViewContext.Writer.Write("</div>");
+			}
+			ViewContext.Writer.Write("</div>");
+
 			ViewContext.Writer.Write("</div>");
 
 			Html.RenderPartial(

+ 121 - 0
GreenTree.Nachtragsmanagement.Web/Views/Config/View.cshtml

@@ -0,0 +1,121 @@
+@{
+	Layout = "~/Views/Shared/_FunctionLayout.cshtml";
+}
+
+@model IEnumerable<GreenTree.Nachtragsmanagement.Web.Models.Config.ConfigItemDataModel>
+
+<script>
+	var deleteId;
+	var gridScrollHeight;
+	var gridScrollOffset = 240;
+	var resizeFinished;
+
+	$(document).ready(function () {
+		gridScrollHeight = calculateGridScrollHeight();
+		setTimeout(function () {
+			devGridViewConfigItem.PerformCallback();
+		}, 500);
+	});
+
+	$(window).resize(function () {
+		clearTimeout(window.resizedFinished);
+		window.resizedFinished = setTimeout(function () {
+			setGridScrollHeight();
+		}, 250);
+	});
+
+	function calculateGridScrollHeight() {
+		var windowHeight = $(window).height();
+		var gridHeaderHeight = $("#devGridViewConfigItem_DXHeadersRow0").height();
+		var gridFooterHeight = $("#devGridViewConfigItem_DXFooterRow").height();
+		return windowHeight - gridHeaderHeight - gridFooterHeight - gridScrollOffset;
+	}
+
+	function setGridScrollHeight() {
+		gridScrollHeight = calculateGridScrollHeight();
+		devGridViewConfigItem.PerformCallback();
+	}
+
+	function onToolbarItemClick(s, e) {
+		if (!s || !e) return;
+		if (IsExportToolbarCommand(e.item.name)) {
+			$("#Format").val(e.item.name);
+			$("#configItemExportForm").submit();
+		} else if (e.item.name == "ToggleColumnChooser") {
+			if (devGridViewConfigItem.IsCustomizationWindowVisible())
+				devGridViewConfigItem.HideCustomizationWindow();
+			else
+				devGridViewConfigItem.ShowCustomizationWindow();
+		}
+	}
+
+	function IsExportToolbarCommand(command) {
+		return command == "Pdf" || command == "Xlsx" || command == "Xls";
+	}
+
+	function editConfigItem(id) {
+		if (!id) return;
+		$.ajax({
+			url: '@Url.Action("EditConfigItem", "Misc")',
+			data: { Id: id },
+			success: function (response) {
+				setTimeout(function () {
+					$(".configItemEditContainer").remove();
+					$("body").append(response);
+				}, 200);
+			},
+			error: function () {
+				 alert("error occured");
+			}
+		});
+	}
+
+	function confirmDelete(id) {
+		if (!id) return;
+		deleteId = id;
+		$.ajax({
+			type: "GET",
+			url: '@Url.Action("GetConfigItem", "Misc")',
+			data: { Id: id },
+			success: function (response) {
+				if (response == "notFound") return;
+				var configItem = JSON.parse(response);
+				var popupControl = MVCxClientPopupControl.Cast(devPopupControlDeleteConfigItem);
+				popupControl.SetHeaderText(popupControl.GetHeaderText().replace("{configItem}", configItem.NotificationPluginSystemNameDescription));
+				$(".dialogTextConfigItem").text($(".dialogTextConfigItem").text().replace("{configItem}", configItem.NotificationPluginSystemNameDescription));
+				popupControl.Show();
+			}
+		});
+	}
+
+	function deleteConfigItem() {
+		$.ajax({
+			type: "POST",
+			url: '@Url.Action("DeleteConfigItem", "Misc")',
+			data: { Id: deleteId },
+			success: function (response) {
+				var popupControl = MVCxClientPopupControl.Cast(devPopupControlDeleteConfigItem);
+				popupControl.Hide();
+				setTimeout(function () {
+					devGridViewConfigItem.PerformCallback();
+				}, 200);
+			},
+			error: function () {
+				 alert("error occured");
+			}
+		});
+	}
+</script>
+
+@Html.Partial("~/Views/Config/_ConfigItemGridPartial.cshtml", Model)
+
+@Html.Partial("~/Views/Shared/_PopupDialogYesNo.cshtml", new GreenTree.Nachtragsmanagement.Web.Models.Global.YesNoDialogModel
+{
+	PopupName = "devPopupControlDeleteConfigItem",
+	Content = "<div class='dialogTextConfigItem' style='padding: 12px'>Sind Sie sicher, dass Sie die Benachrichtigung " +
+			  "\"{configItem}\" löschen möchten?</div>",
+	HeaderText = "\"{configItem}\" löschen",
+	YesFunction = "function (s, e) { deleteConfigItem(); }",
+	YesButtonName = "devButtonDeleteConfigItemYes",
+	NoButtonName = "devButtonDeleteConfigItemNo"
+})

+ 88 - 0
GreenTree.Nachtragsmanagement.Web/Views/Config/_ConfigItemGridPartial.cshtml

@@ -0,0 +1,88 @@
+@model IEnumerable<GreenTree.Nachtragsmanagement.Web.Models.Config.ConfigItemDataModel>
+
+@using GreenTree.Nachtragsmanagement.Web.Extensions
+
+@{ 
+	var userContext = GreenTree.Nachtragsmanagement.Core.CommonHelper.UserContext();
+}
+
+@Html.DevExpress().GridView(s =>
+{
+	s.Name = "devGridViewConfigItem";
+	s.KeyFieldName = "Id";
+	s.CallbackRouteValues = new { Controller = "Misc", Action = "PartialConfigItems" };
+	s.Width = Unit.Percentage(99);
+	s.Settings.ShowFilterRow = true;
+	s.Settings.ShowFilterRowMenu = true;
+	s.Settings.VerticalScrollBarMode = ScrollBarMode.Auto;
+	s.Settings.VerticalScrollableHeight =
+		(ViewData["ScrollHeight"] == null || (int)ViewData["ScrollHeight"] == -1)
+			? 400
+			: (int)ViewData["ScrollHeight"];
+	s.SettingsPager.AlwaysShowPager = true;
+
+	if (userContext.CurrentUser.HasFunction("Misc-ConfigItems-Edit"))
+	{
+		s.Columns.Add(column =>
+		{
+			column.Caption = "#";
+			column.SetDataItemTemplateContent(c =>
+			{
+				ViewContext.Writer.Write(
+					"<a href=\"#\" onclick=\"editConfigItem(" + DataBinder.Eval(c.DataItem, "Id") + ")\">Bearbeiten</a>&nbsp;" +
+					"<a href=\"#\" onclick=\"confirmDelete(" + DataBinder.Eval(c.DataItem, "Id") + ")\">Löschen</a>"
+				);
+			});
+			column.SetHeaderTemplateContent(c =>
+			{
+				ViewContext.Writer.Write(
+					"<a href=\"#\" onclick=\"editConfigItem(-1)\">Neu</a>&nbsp;");
+			});
+			column.Settings.AllowDragDrop = DefaultBoolean.False;
+			column.Settings.AllowSort = DefaultBoolean.False;
+			column.Width = new Unit(120, UnitType.Pixel);
+		});
+	}
+	s.Columns.Add(column =>
+	{
+		column.Caption = "Name";
+		column.FieldName = "Name";
+		column.MinWidth = 120;
+		column.Width = new Unit(10, UnitType.Percentage);
+	});
+	s.Columns.Add(column =>
+	{
+		column.Caption = "Datentyp";
+		column.FieldName = "TypeDescription";
+		column.MinWidth = 80;
+		column.Width = new Unit(15, UnitType.Percentage);
+	});
+	s.Columns.Add(column =>
+	{
+		column.Caption = "Wert";
+		column.FieldName = "Value";
+		column.MinWidth = 150;
+		column.Width = new Unit(30, UnitType.Percentage);
+	});
+	s.Columns.Add(column =>
+	{
+		column.Caption = "Beschreibung";
+		column.FieldName = "Description";
+		column.MinWidth = 300;
+		column.Width = new Unit(45, UnitType.Percentage);
+	});
+
+	s.ClientLayout = (sender, e) =>
+	{
+		if (e.LayoutMode == ClientLayoutMode.Loading)
+		{
+			if (Session["ConfigItemGridState"] != null)
+				e.LayoutData = (string)Session["ConfigItemGridState"];
+		}
+		else
+			Session["ConfigItemGridState"] = e.LayoutData;
+	};
+	s.ClientSideEvents.BeginCallback = "function (s, e) { e.customArgs['scrollHeight'] = [ gridScrollHeight ]; }";
+
+	s.Styles.AlternatingRow.BackColor = System.Drawing.Color.FromArgb(247, 247, 247);
+}).Bind(Model).GetHtml()