Bläddra i källkod

Vertragsabweichungen begonnen!

Arne Diekmann 8 år sedan
förälder
incheckning
1ce45684fc
34 ändrade filer med 1029 tillägg och 269 borttagningar
  1. 15 0
      GreenTree.Nachtragsmanagement.Core/Authentication/IUserHelper.cs
  2. 27 0
      GreenTree.Nachtragsmanagement.Core/Authentication/UserHelper.cs
  3. 22 3
      GreenTree.Nachtragsmanagement.Core/Domain/User/User.cs
  4. 9 83
      GreenTree.Nachtragsmanagement.Data/AppendixObjectContext.cs
  5. 1 0
      GreenTree.Nachtragsmanagement.Data/Mapping/User/UserMap.cs
  6. 6 0
      GreenTree.Nachtragsmanagement.Services/User/IUserService.cs
  7. 11 0
      GreenTree.Nachtragsmanagement.Services/User/UserService.cs
  8. 34 12
      GreenTree.Nachtragsmanagement.Web.Framework/Authorization/FunctionAuthorizeAttribute.cs
  9. 1 1
      GreenTree.Nachtragsmanagement.Web.Framework/GreenTree.Nachtragsmanagement.Web.Framework.csproj
  10. 171 0
      GreenTree.Nachtragsmanagement.Web/App_Start/FunctionConfig.cs
  11. 14 0
      GreenTree.Nachtragsmanagement.Web/App_Start/RouteConfig.cs
  12. BIN
      GreenTree.Nachtragsmanagement.Web/Content/Images/function-Deviation-Deviations-32-contrast.png
  13. BIN
      GreenTree.Nachtragsmanagement.Web/Content/Images/function-Deviation-Deviations-32.png
  14. 6 6
      GreenTree.Nachtragsmanagement.Web/Content/global.css
  15. 6 3
      GreenTree.Nachtragsmanagement.Web/Controllers/AdminController.cs
  16. 162 0
      GreenTree.Nachtragsmanagement.Web/Controllers/DeviationController.cs
  17. 12 7
      GreenTree.Nachtragsmanagement.Web/Controllers/GlobalController.cs
  18. 7 5
      GreenTree.Nachtragsmanagement.Web/Controllers/HomeController.cs
  19. 3 1
      GreenTree.Nachtragsmanagement.Web/Global.asax.cs
  20. 13 3
      GreenTree.Nachtragsmanagement.Web/GreenTree.Nachtragsmanagement.Web.csproj
  21. 3 3
      GreenTree.Nachtragsmanagement.Web/Models/Admin/User/UserDataModel.cs
  22. 77 0
      GreenTree.Nachtragsmanagement.Web/Models/Deviation/DeviationDataModel.cs
  23. 0 15
      GreenTree.Nachtragsmanagement.Web/Models/Test/DbRelationModel.cs
  24. 0 12
      GreenTree.Nachtragsmanagement.Web/Models/Test/PluginModel.cs
  25. 1 0
      GreenTree.Nachtragsmanagement.Web/Validation/Admin/User/UserDataModelValidator.cs
  26. 2 0
      GreenTree.Nachtragsmanagement.Web/Validation/AppendixValidatorFactory.cs
  27. 45 0
      GreenTree.Nachtragsmanagement.Web/Validation/Deviation/DeviationDataModelValidator.cs
  28. 110 111
      GreenTree.Nachtragsmanagement.Web/Views/Admin/Roles/_RoleEditPartial.cshtml
  29. 2 2
      GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/_UserEditPartial.cshtml
  30. 1 0
      GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/_UserGridPartial.cshtml
  31. 94 0
      GreenTree.Nachtragsmanagement.Web/Views/Deviations/View.cshtml
  32. 109 0
      GreenTree.Nachtragsmanagement.Web/Views/Deviations/_DeviationEditPartial.cshtml
  33. 57 0
      GreenTree.Nachtragsmanagement.Web/Views/Deviations/_DeviationGridPartial.cshtml
  34. 8 2
      GreenTree.Nachtragsmanagement.Web/Views/Home/Index.cshtml

+ 15 - 0
GreenTree.Nachtragsmanagement.Core/Authentication/IUserHelper.cs

@@ -26,6 +26,13 @@ namespace GreenTree.Nachtragsmanagement.Core.Authentication
         /// <param name="user">The current authenticated user.</param>
         void ToCookies(User user);
 
+        /// <summary>
+        /// Writes the current user to response cookies
+        /// </summary>
+        /// <param name="user">The current authenticated user.</param>
+        /// <param name="setRequestCookie">Set also the request cookie for further authorization.</param>
+        void ToCookies(User user, bool setRequestCookie);
+
         /// <summary>
         /// Writes the current user to response cookies
         /// </summary>
@@ -33,6 +40,14 @@ namespace GreenTree.Nachtragsmanagement.Core.Authentication
         /// <param name="expirationDate">The cookie expiration date.</param>
         void ToCookies(User user, DateTime expirationDate);
 
+        /// <summary>
+        /// Writes the current user to response cookies
+        /// </summary>
+        /// <param name="user">The current authenticated user.</param>
+        /// <param name="expirationDate">The cookie expiration date.</param>
+        /// <param name="setRequestCookie">Set also the request cookie for further authorization.</param>
+        void ToCookies(User user, DateTime expirationDate, bool setRequestCookie);
+
         /// <summary>
         /// Clears the authentication cookie
         /// </summary>

+ 27 - 0
GreenTree.Nachtragsmanagement.Core/Authentication/UserHelper.cs

@@ -51,6 +51,16 @@ namespace GreenTree.Nachtragsmanagement.Core.Authentication
         /// </summary>
         /// <param name="user">The current authenticated user.</param>
         public void ToCookies(User user)
+        {
+            ToCookies(user, false);
+        }
+
+        /// <summary>
+        /// Writes the current user to response cookies
+        /// </summary>
+        /// <param name="user">The current authenticated user.</param>
+        /// <param name="setRequestCookie">Set also the request cookie for further authorization.</param>
+        public void ToCookies(User user, bool setRequestCookie)
         {
             if (HttpContext.Current == null || HttpContext.Current.Session == null) return;
 
@@ -64,6 +74,9 @@ namespace GreenTree.Nachtragsmanagement.Core.Authentication
             };
 
             HttpContext.Current.Response.Cookies.Set(authCookie);
+
+            if (setRequestCookie)
+                HttpContext.Current.Request.Cookies.Set(authCookie);
         }
 
         /// <summary>
@@ -72,6 +85,17 @@ namespace GreenTree.Nachtragsmanagement.Core.Authentication
         /// <param name="user">The current authenticated user.</param>
         /// <param name="expirationDate">The cookie expiration date.</param>
         public void ToCookies(User user, DateTime expirationDate)
+        {
+            ToCookies(user, expirationDate, false);
+        }
+
+        /// <summary>
+        /// Writes the current user to response cookies
+        /// </summary>
+        /// <param name="user">The current authenticated user.</param>
+        /// <param name="expirationDate">The cookie expiration date.</param>
+        /// <param name="setRequestCookie">Set also the request cookie for further authorization.</param>
+        public void ToCookies(User user, DateTime expirationDate, bool setRequestCookie)
         {
             if (HttpContext.Current == null || HttpContext.Current.Session == null) return;
 
@@ -85,6 +109,9 @@ namespace GreenTree.Nachtragsmanagement.Core.Authentication
             };
 
             HttpContext.Current.Response.Cookies.Set(authCookie);
+
+            if (setRequestCookie)
+                HttpContext.Current.Request.Cookies.Set(authCookie);
         }
 
         /// <summary>

+ 22 - 3
GreenTree.Nachtragsmanagement.Core/Domain/User/User.cs

@@ -11,6 +11,11 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.User
     {
         #region Fields
 
+        /// <summary>
+        /// Current role
+        /// </summary>
+        private Role _currentRole;
+
         /// <summary>
         /// Rolelist
         /// </summary>
@@ -51,7 +56,22 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.User
         /// <summary>
         /// The role the current user is using
         /// </summary>
-        public Role CurrentRole { get; set; }
+        public Role CurrentRole
+        {
+            get
+            {
+                return _currentRole;
+            }
+            set
+            {
+                _currentRole = value;
+            }
+        }
+
+        /// <summary>
+        /// Determines if there are changes to the user settings
+        /// </summary>
+        public bool? IsChanged { get; set; }
 
         /// <summary>
         /// Roles the user have
@@ -92,8 +112,7 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.User
                 return false;
 
             return
-                Roles
-                    .SelectMany(s => s.Functions)
+                CurrentRole.Functions
                     .Any(f => f.Name == functionName);
         }
 

+ 9 - 83
GreenTree.Nachtragsmanagement.Data/AppendixObjectContext.cs

@@ -232,83 +232,6 @@ namespace GreenTree.Nachtragsmanagement.Data
             if (isTestDataGeneratedEntity != null && Convert.ToBoolean(isTestDataGeneratedEntity.Value))
                 return;
 
-            var f1 = Get<Function>().Add(new Function
-            {
-                Name = "Administration",
-                Description = "Administration",
-                ImageUrl = "~/Content/Images/function-Administration-32.png",
-                IsMenuMember = true,
-                Plugin = "System"
-            });
-
-            var f2 = Get<Function>().Add(new Function
-            {
-                Name = "Administration-Users",
-                Description = "Benutzerverwaltung",
-                ImageUrl = "~/Content/Images/function-Administration-Users-32.png",
-                GroupName = "Administration",
-                RouteName = "GreenTree.Nachtragsmanagement.Web.Administration.Users",
-                IsMenuMember = true,
-                Plugin = "System",
-                BaseWidth = 900,
-                MinWidth = 600,
-                BaseHeight = 600,
-                MinHeight = 400,
-                AllowMaximize = true
-            });
-
-            var f2_1 = Get<Function>().Add(new Function
-            {
-                Name = "Administration-Users-Edit",
-                Description = "Benutzer editieren",
-                GroupName = "Administration-Users",
-                IsMenuMember = false,
-                Plugin = "System"
-            });
-
-            var f3 = Get<Function>().Add(new Function
-            {
-                Name = "Administration-Roles",
-                Description = "Rollenverwaltung",
-                ImageUrl = "~/Content/Images/function-Administration-Roles-32.png",
-                GroupName = "Administration",
-                RouteName = "GreenTree.Nachtragsmanagement.Web.Administration.Roles",
-                IsMenuMember = true,
-                Plugin = "System",
-                BaseWidth = 800,
-                MinWidth = 600,
-                BaseHeight = 500,
-                MinHeight = 400,
-                AllowMaximize = true
-            });
-
-            var f3_1 = Get<Function>().Add(new Function
-            {
-                Name = "Administration-Roles-Edit",
-                Description = "Rollen editieren",
-                GroupName = "Administration-Roles",
-                IsMenuMember = false,
-                Plugin = "System"
-            });
-
-            var f4 = Get<Function>().Add(new Function
-            {
-                Name = "Administration-Plugins",
-                Description = "Pluginverwaltung",
-                ImageUrl = "~/Content/Images/function-Administration-Plugins-32.png",
-                GroupName = "Administration",
-                RouteName = "GreenTree.Nachtragsmanagement.Web.Administration.Plugins",
-                IsMenuMember = true,
-                Plugin = "System",
-                BaseWidth = 800,
-                MinWidth = 500,
-                BaseHeight = 450,
-                MinHeight = 300,
-                AllowMaximize = true
-            });
-
-            SaveChanges();
-
             var r1 = Get<Role>().Add(new Role
             {
                 Description = "Administrator",
@@ -329,12 +252,15 @@ namespace GreenTree.Nachtragsmanagement.Data
 
             SaveChanges();
 
-            r1.Functions.Add(f1);
-            r1.Functions.Add(f2);
-            r1.Functions.Add(f2_1);
-            r1.Functions.Add(f3);
-            r1.Functions.Add(f3_1);
-            r1.Functions.Add(f4);
+            r1.Functions.Add(Get<Function>().FirstOrDefault(f => f.Name == "Administration"));
+            r1.Functions.Add(Get<Function>().FirstOrDefault(f => f.Name == "Administration-Users"));
+            r1.Functions.Add(Get<Function>().FirstOrDefault(f => f.Name == "Administration-Users-Edit"));
+            r1.Functions.Add(Get<Function>().FirstOrDefault(f => f.Name == "Administration-Roles"));
+            r1.Functions.Add(Get<Function>().FirstOrDefault(f => f.Name == "Administration-Roles-Edit"));
+            r1.Functions.Add(Get<Function>().FirstOrDefault(f => f.Name == "Administration-Plugins"));
+            r1.Functions.Add(Get<Function>().FirstOrDefault(f => f.Name == "Deviation"));
+            r1.Functions.Add(Get<Function>().FirstOrDefault(f => f.Name == "Deviation-Deviaitons"));
+            r1.Functions.Add(Get<Function>().FirstOrDefault(f => f.Name == "Deviation-Deviaitons-Edit"));
 
             var u1 = Get<User>().Add(new User
             {

+ 1 - 0
GreenTree.Nachtragsmanagement.Data/Mapping/User/UserMap.cs

@@ -20,6 +20,7 @@ namespace GreenTree.Nachtragsmanagement.Data.Mapping.User
             Property(u => u.Lastname);
             Property(u => u.MailAddress);
             Property(u => u.Password);
+            Property(u => u.IsChanged);
 
             Ignore(u => u.CurrentRole);
 

+ 6 - 0
GreenTree.Nachtragsmanagement.Services/User/IUserService.cs

@@ -114,6 +114,12 @@ namespace GreenTree.Nachtragsmanagement.Services.User
         /// <param name="id">Function identifier.</param>
         Function GetFunctionById(int id);
 
+        /// <summary>
+        /// Gets a function by specified name
+        /// </summary>
+        /// <param name="name">Function name.</param>
+        Function GetFunctionByName(string name);
+
         /// <summary>
         /// Gets all functions to the specified ids
         /// </summary>

+ 11 - 0
GreenTree.Nachtragsmanagement.Services/User/UserService.cs

@@ -196,6 +196,17 @@ namespace GreenTree.Nachtragsmanagement.Services.User
             return _functionRepository.GetById(id);
         }
 
+        /// <summary>
+        /// Gets a function by specified name
+        /// </summary>
+        /// <param name="name">Function name.</param>
+        public Function GetFunctionByName(string name)
+        {
+            return
+                _functionRepository.Table
+                    .FirstOrDefault(f => f.Name == name);
+        }
+
         /// <summary>
         /// Gets all functions to the specified ids
         /// </summary>

+ 34 - 12
GreenTree.Nachtragsmanagement.Web.Framework/Authorization/RoleAuthorizeAttribute.cs → GreenTree.Nachtragsmanagement.Web.Framework/Authorization/FunctionAuthorizeAttribute.cs

@@ -12,55 +12,77 @@ using System.Web.Mvc;
 
 namespace GreenTree.Nachtragsmanagement.Web.Framework.Authorization
 {
-    public class RoleAuthorizeAttribute : AuthorizeAttribute
+    public class FunctionAuthorizeAttribute : AuthorizeAttribute
     {
         #region Fields
 
         private readonly IAuthenticationService _authenticationService;
         private readonly IUserHelper _userHelper;
+        private readonly IUserService _userService;
         private readonly string[] _allowedFunctions;
         private readonly bool _showNotAuthorized;
 
         #endregion
 
         /// <summary>
-        /// Initializes a new instance of the RoleAuthorizeAttribute class
+        /// Initializes a new instance of the FunctionAuthorizeAttribute class
         /// </summary>
         /// <param name="showNotAuthorized">Determines if a NotAuthorized message or a login redirection is made.</param>
         /// <param name="functions">The functions needed.</param>
-        public RoleAuthorizeAttribute(bool showNotAuthorized, params string[] functions)
+        public FunctionAuthorizeAttribute(bool showNotAuthorized, params string[] functions)
         {
             _showNotAuthorized = showNotAuthorized;
             _allowedFunctions = functions;
 
             _authenticationService = Singleton<IContainer>.Instance.Resolve<IAuthenticationService>();
             _userHelper = Singleton<IContainer>.Instance.Resolve<IUserHelper>();
+            _userService = Singleton<IContainer>.Instance.Resolve<IUserService>();
         }
 
+        /// <summary>
+        /// Core authorization
+        /// </summary>
+        /// <param name="httpContext">Current HttpContext.</param>
+        /// <returns>Valid access.</returns>
         protected override bool AuthorizeCore(HttpContextBase httpContext)
         {
-            var user = _userHelper.FromCookies();
+            var cookieUser = _userHelper.FromCookies();
 
-            if (user == null)
+            if (cookieUser == null)
                 return false;
 
             if (!_allowedFunctions.Any())
                 return true;
 
-            foreach (var role in user.Roles)
+            var dbUser = _userService.GetUserById(cookieUser.Id);
+
+            var role = (cookieUser != null && cookieUser.CurrentRole != null)
+                ? _userService.GetRoleById(cookieUser.CurrentRole.Id)
+                : null;
+
+            if (role == null)
+                dbUser.CurrentRole = dbUser.Roles
+                    .First(r1 => r1.Level == dbUser.Roles.Max(r2 => r2.Level));
+            else
+                dbUser.CurrentRole = role;
+
+            _userHelper.ToCookies(dbUser, DateTime.Now.AddHours(2), true);
+
+            foreach (var function in dbUser.CurrentRole.Functions)
             {
-                foreach (var function in role.Functions)
-                {
-                    var allowed = _allowedFunctions.Contains(function.Description);
+                var allowed = _allowedFunctions.Contains(function.Name);
 
-                    if (allowed)
-                        return true;
-                }
+                if (allowed)
+                    return true;
             }
 
             return false;
         }
 
+        /// <summary>
+        /// Handle not authorized access
+        /// </summary>
+        /// <param name="filterContext">Current filterContext.</param>
         protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
         {
             if (!_showNotAuthorized)

+ 1 - 1
GreenTree.Nachtragsmanagement.Web.Framework/GreenTree.Nachtragsmanagement.Web.Framework.csproj

@@ -131,7 +131,7 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="ApplicationContext.cs" />
-    <Compile Include="Authorization\RoleAuthorizeAttribute.cs" />
+    <Compile Include="Authorization\FunctionAuthorizeAttribute.cs" />
     <Compile Include="Extension\DevExpress\TreeViewSettingsExtension.cs" />
     <Compile Include="Mvc\Routes\GuidConstraint.cs" />
     <Compile Include="Mvc\Routes\IRouteProvider.cs" />

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

@@ -0,0 +1,171 @@
+using Autofac;
+using GreenTree.Nachtragsmanagement.Core;
+using GreenTree.Nachtragsmanagement.Core.Domain.User;
+using GreenTree.Nachtragsmanagement.Services.User;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace GreenTree.Nachtragsmanagement.Web.App_Start
+{
+    public class FunctionConfig
+    {
+        /// <summary>
+        /// Registers all system functions provided by the base system
+        /// </summary>
+        public static void RegisterFunctions()
+        {
+            var userService = Singleton<IContainer>.Instance.Resolve<IUserService>();
+
+            var systemFunctions = AllSystemFunctions();
+
+            foreach (var function in systemFunctions)
+            {
+                var existingFunction = userService.GetFunctionByName(function.Name);
+
+                if (existingFunction != null)
+                {
+                    var equals =
+                        existingFunction.Description == function.Description &&
+                        existingFunction.ImageUrl == function.ImageUrl &&
+                        existingFunction.GroupName == function.GroupName &&
+                        existingFunction.RouteName == function.RouteName &&
+                        existingFunction.IsMenuMember == function.IsMenuMember &&
+                        existingFunction.Plugin == function.Plugin &&
+                        existingFunction.BaseWidth == function.BaseWidth &&
+                        existingFunction.MinWidth == function.MinWidth &&
+                        existingFunction.BaseHeight == function.BaseHeight &&
+                        existingFunction.MinHeight == function.MinHeight &&
+                        existingFunction.AllowMaximize == function.AllowMaximize;
+
+                    if (!equals)
+                    {
+                        existingFunction.Description = function.Description;
+                        existingFunction.ImageUrl = function.ImageUrl;
+                        existingFunction.GroupName = function.GroupName;
+                        existingFunction.RouteName = function.RouteName;
+                        existingFunction.IsMenuMember = function.IsMenuMember;
+                        existingFunction.Plugin = function.Plugin;
+                        existingFunction.BaseWidth = function.BaseWidth;
+                        existingFunction.MinWidth = function.MinWidth;
+                        existingFunction.BaseHeight = function.BaseHeight;
+                        existingFunction.MinHeight = function.MinHeight;
+                        existingFunction.AllowMaximize = function.AllowMaximize;
+
+                        userService.UpdateFunction(existingFunction);
+                    }
+                }
+                else
+                    userService.InsertFunction(function);
+            }
+        }
+
+        private static Function[] AllSystemFunctions()
+        {
+            return new[]
+            {
+                new Function
+                {
+                    Name = "Administration",
+                    Description = "Administration",
+                    ImageUrl = "~/Content/Images/function-Administration-32.png",
+                    IsMenuMember = true,
+                    Plugin = "System"
+                },
+                new Function
+                {
+                    Name = "Administration-Users",
+                    Description = "Benutzerverwaltung",
+                    ImageUrl = "~/Content/Images/function-Administration-Users-32.png",
+                    GroupName = "Administration",
+                    RouteName = "GreenTree.Nachtragsmanagement.Web.Administration.Users",
+                    IsMenuMember = true,
+                    Plugin = "System",
+                    BaseWidth = 900,
+                    MinWidth = 600,
+                    BaseHeight = 600,
+                    MinHeight = 400,
+                    AllowMaximize = true
+                },
+                new Function
+                {
+                    Name = "Administration-Users-Edit",
+                    Description = "Benutzer editieren",
+                    GroupName = "Administration-Users",
+                    IsMenuMember = false,
+                    Plugin = "System"
+                },
+                new Function
+                {
+                    Name = "Administration-Roles",
+                    Description = "Rollenverwaltung",
+                    ImageUrl = "~/Content/Images/function-Administration-Roles-32.png",
+                    GroupName = "Administration",
+                    RouteName = "GreenTree.Nachtragsmanagement.Web.Administration.Roles",
+                    IsMenuMember = true,
+                    Plugin = "System",
+                    BaseWidth = 800,
+                    MinWidth = 600,
+                    BaseHeight = 500,
+                    MinHeight = 400,
+                    AllowMaximize = true
+                },
+                new Function
+                {
+                    Name = "Administration-Roles-Edit",
+                    Description = "Rollen editieren",
+                    GroupName = "Administration-Roles",
+                    IsMenuMember = false,
+                    Plugin = "System"
+                },
+                new Function
+                {
+                    Name = "Administration-Plugins",
+                    Description = "Pluginverwaltung",
+                    ImageUrl = "~/Content/Images/function-Administration-Plugins-32.png",
+                    GroupName = "Administration",
+                    RouteName = "GreenTree.Nachtragsmanagement.Web.Administration.Plugins",
+                    IsMenuMember = true,
+                    Plugin = "System",
+                    BaseWidth = 800,
+                    MinWidth = 500,
+                    BaseHeight = 450,
+                    MinHeight = 300,
+                    AllowMaximize = true
+                },
+                new Function
+                {
+                    Name = "Deviation",
+                    Description = "Vertragsabweichungen",
+                    ImageUrl = "~/Content/Images/function-Deviation-32.png",
+                    IsMenuMember = true,
+                    Plugin = "System"
+                },
+                new Function
+                {
+                    Name = "Deviation-Deviations",
+                    Description = "Vertragsabweichungsverwaltung",
+                    ImageUrl = "~/Content/Images/function-Deviation-Deviations-32.png",
+                    GroupName = "Deviation",
+                    RouteName = "GreenTree.Nachtragsmanagement.Web.Deviation.Deviations",
+                    IsMenuMember = true,
+                    Plugin = "System",
+                    BaseWidth = 1000,
+                    MinWidth = 800,
+                    BaseHeight = 650,
+                    MinHeight = 500,
+                    AllowMaximize = true
+                },
+                new Function
+                {
+                    Name = "Deviation-Deviations-Edit",
+                    Description = "Vertragsabweichungen editieren",
+                    GroupName = "Deviation-Deviations",
+                    IsMenuMember = false,
+                    Plugin = "System"
+                },
+            };
+        }
+    }
+}

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

@@ -61,6 +61,20 @@ namespace GreenTree.Nachtragsmanagement.Web
                    "GreenTree.Nachtragsmanagement.Web.Controllers"
                }
             );
+
+            routes.MapRoute(
+                "GreenTree.Nachtragsmanagement.Web.Deviation.Deviations",
+                "admin/viewdeviations",
+               new
+               {
+                   controller = "Deviation",
+                   action = "ViewDeviations"
+               },
+               new[]
+               {
+                   "GreenTree.Nachtragsmanagement.Web.Controllers"
+               }
+            );
         }
     }
 }

BIN
GreenTree.Nachtragsmanagement.Web/Content/Images/function-Deviation-Deviations-32-contrast.png


BIN
GreenTree.Nachtragsmanagement.Web/Content/Images/function-Deviation-Deviations-32.png


+ 6 - 6
GreenTree.Nachtragsmanagement.Web/Content/global.css

@@ -123,7 +123,7 @@ h1, h2, h3, h4, h5, h6 {
     width: auto;
     background: white;
     border-radius: 6px 6px 6px 6px;
-    line-height: 20px;
+    line-height: 18px;
     cursor: pointer;
     color: #808080;
     font-size: 13px;
@@ -143,18 +143,18 @@ h1, h2, h3, h4, h5, h6 {
 }
 
 .popupPanelItem > img:first-of-type {
-    width: 24px;
-    height: 24px;
-    margin: 4px 8px 0 12px;
+    width: 20px;
+    height: 20px;
+    margin: 0 8px 0 12px;
 }
 
 .popupPanelItem > img:last-of-type {
     width: 16px;
     height: 16px;
-    margin: 4px 0 4px 8px;
+    margin: 6px 0 4px 8px;
 }
 
 .popupPanelItem > span {
     vertical-align: top;
-    line-height: 34px;
+    line-height: 30px;
 }

+ 6 - 3
GreenTree.Nachtragsmanagement.Web/Controllers/AdminController.cs

@@ -3,13 +3,14 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Web;
 using System.Web.Mvc;
+using Newtonsoft.Json;
 using GreenTree.Nachtragsmanagement.Core.Authentication;
 using GreenTree.Nachtragsmanagement.Services.User;
 using GreenTree.Nachtragsmanagement.Web.Models.Admin.User;
 using GreenTree.Nachtragsmanagement.Core.Domain.User;
-using Newtonsoft.Json;
 using GreenTree.Nachtragsmanagement.Core;
 using GreenTree.Nachtragsmanagement.Core.Plugins;
+using GreenTree.Nachtragsmanagement.Web.Framework.Authorization;
 
 namespace GreenTree.Nachtragsmanagement.Web.Controllers
 {
@@ -37,6 +38,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         /// <summary>
         /// Basic user view function
         /// </summary>
+        [FunctionAuthorize(true, "Administration-Users")]
         public ActionResult ViewUsers()
         {
             var users = _userService.GetAllUsers();
@@ -127,7 +129,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
             {
                 var user = _userService.GetUserById(userModel.Id);
 
-                user.CustomNumber = userModel.CustomerNumber;
+                user.CustomNumber = userModel.CustomNumber;
                 user.Forename = userModel.Forename;
                 user.Lastname = userModel.Lastname;
                 user.MailAddress = userModel.MailAddress;
@@ -171,6 +173,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         /// <summary>
         /// Basic role view function
         /// </summary>
+        [FunctionAuthorize(true, "Administration-Roles")]
         public ActionResult ViewRoles()
         {
             var roles = _userService.GetAllRoles();
@@ -240,7 +243,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
             {
                 foreach (var role in roleModel.FunctionValues)
                     roleModel.FunctionDescriptions.Add(
-                        ((IList<Role>)ViewData["AllRoles"])
+                        ((IList<Function>)ViewData["AllFunctions"])
                             .First(r => r.Id == role).Description);
 
                 return PartialView("~/Views/Admin/Roles/_RoleEditPartial.cshtml", roleModel);

+ 162 - 0
GreenTree.Nachtragsmanagement.Web/Controllers/DeviationController.cs

@@ -0,0 +1,162 @@
+using GreenTree.Nachtragsmanagement.Core.Domain.Appendix;
+using GreenTree.Nachtragsmanagement.Core.Domain.Deviation;
+using GreenTree.Nachtragsmanagement.Services.Appendix;
+using GreenTree.Nachtragsmanagement.Services.Deviation;
+using GreenTree.Nachtragsmanagement.Web.Framework.Authorization;
+using GreenTree.Nachtragsmanagement.Web.Models.Deviation;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using System.Web.Mvc;
+
+namespace GreenTree.Nachtragsmanagement.Web.Controllers
+{
+    public class DeviationController : Controller
+    {
+        private readonly IDeviationService _deviationService;
+        private readonly IAppendixService _appendixService;
+
+        public DeviationController(
+            IDeviationService deviationService,
+            IAppendixService appendixService)
+        {
+            _deviationService = deviationService;
+
+            ViewData["AllAppendices"] = _appendixService.GetAllAppendices();
+            ViewData["AllStatuses"] = _deviationService.GetAllStatuses();
+            ViewData["AllDisturbances"] = _deviationService.GetAllDisturbances();
+            ViewData["AllKinds"] = _deviationService.GetAllKinds();
+        }
+
+        /// <summary>
+        /// Basic deviation view function
+        /// </summary>
+        [FunctionAuthorize(true, "Deviation-Deviations")]
+        public ActionResult ViewDeviations()
+        {
+            var deviations = _deviationService.GetAllDeviations();
+            var deviationModels = deviations
+                .Select(u => DeviationDataModel.FromDeviation(u, false))
+                .ToList();
+
+            return View("~/Views/Admin/Deviations/View.cshtml", deviationModels);
+        }
+
+        /// <summary>
+        /// Get JSON data of specific deviation
+        /// </summary>
+        /// <param name="id">Deviation id.</param>
+        public ActionResult GetDeviation(int id = -1)
+        {
+            var deviation = _deviationService.GetDeviationById(id);
+            if (deviation == null)
+                return new JsonResult
+                {
+                    Data = "notFound",
+                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
+                };
+
+            var deviationModel = DeviationDataModel.FromDeviation(deviation, false);
+
+            return new JsonResult
+            {
+                Data = JsonConvert.SerializeObject(deviationModel),
+                JsonRequestBehavior = JsonRequestBehavior.AllowGet
+            };
+        }
+
+        /// <summary>
+        /// Callback result for deviation grid
+        /// </summary>
+        public ActionResult PartialDeviations()
+        {
+            var deviations = _deviationService.GetAllDeviations();
+            var deviationModels = deviations
+                .Select(u => DeviationDataModel.FromDeviation(u, false))
+                .ToList();
+
+            return PartialView("~/Views/Admin/Deviations/_DeviationGridPartial.cshtml", deviationModels);
+        }
+
+        /// <summary>
+        /// Partial edit for editing of existing or for new deviation
+        /// </summary>
+        /// <param name="id">Id for existing deviation, otherweise -1.</param>
+        public ActionResult EditDeviation(int id = -1)
+        {
+            var deviation = _deviationService.GetDeviationById(id);
+            var deviationModel = DeviationDataModel.FromDeviation(deviation, true);
+
+            return PartialView("~/Views/Admin/Deviations/_DeviationEditPartial.cshtml", deviationModel);
+        }
+
+        /// <summary>
+        /// Partial edit result if ModelState is valid, otherwise simple JSON result for success
+        /// </summary>
+        /// <param name="deviationModel">Deviation model to be saved.</param>
+        [HttpPost, ValidateInput(false)]
+        public ActionResult EditDeviation(DeviationDataModel deviationModel)
+        {
+            if (!ModelState.IsValid)
+            {
+                //deviationModel.AppendixDescription = ((IList<Appendix>)ViewData["AllAppendices"])
+                //    .First(d => d.Id == deviationModel.AppendixId).CustomNumber;
+                //deviationModel.StatusDescription = ((IList<Status>)ViewData["AllStatuses"])
+                //    .First(d => d.Id == deviationModel.StatusId).Description;
+                //deviationModel.DisturbanceDescription = ((IList<Disturbance>)ViewData["AllDisturbances"])
+                //    .First(d => d.Id == deviationModel.DisturbanceId).Description;
+                //deviationModel.KindDescription = ((IList<Kind>)ViewData["AllKinds"])
+                //    .First(d => d.Id == deviationModel.KindId).Description;
+
+                return PartialView("~/Views/Admin/Deviations/_DeviationEditPartial.cshtml", deviationModel);
+            }
+
+            if (deviationModel.Id == -1)
+            {
+                var deviation = deviationModel.ToDeviation();
+
+                _deviationService.InsertDeviation(deviation);
+            }
+            else
+            {
+                var deviation = _deviationService.GetDeviationById(deviationModel.Id);
+
+                deviation.CustomNumber = deviationModel.CustomNumber;
+                deviation.ReceiptDate = deviationModel.ReceiptDate;
+                deviation.Value = deviationModel.Value;
+                deviation.AppendixId = deviationModel.AppendixId;
+                deviation.StatusId = deviationModel.StatusId;
+                deviation.DisturbanceId = deviationModel.DisturbanceId;
+                deviation.KindId = deviationModel.KindId;
+                deviation.Comment = deviationModel.Comment;
+
+                _deviationService.UpdateDeviation(deviation);
+            }
+
+            return new JsonResult
+            {
+                Data = "success"
+            };
+        }
+
+        /// <summary>
+        /// Simple JSON result for deleting a specific deviation
+        /// </summary>
+        /// <param name="id">Deviation id.</param>
+        [HttpPost]
+        public ActionResult DeleteDeviation(int id)
+        {
+            var deviation = _deviationService.GetDeviationById(id);
+
+            if (deviation != null)
+                _deviationService.DeleteDeviation(deviation);
+
+            return new JsonResult
+            {
+                Data = "success"
+            };
+        }
+    }
+}

+ 12 - 7
GreenTree.Nachtragsmanagement.Web/Controllers/GlobalController.cs

@@ -27,21 +27,26 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         /// </summary>
         public ActionResult Footer()
         {
-            var currentUser = _userHelper.FromCookies();
+            var cookieUser = _userHelper.FromCookies();
 
-            if (currentUser == null)
+            if (cookieUser == null)
+                return View("~/Views/Shared/_Footer.cshtml", null);
+
+            var dbUser = _userService.GetUserById(cookieUser.Id);
+
+            if (dbUser == null)
                 return View("~/Views/Shared/_Footer.cshtml", null);
 
             var footerModel = new FooterModel
             {
-                CustomNumber = currentUser.CustomNumber,
-                Forename = currentUser.Forename,
-                Lastname = currentUser.Lastname,
-                RoleDescription = currentUser.CurrentRole.Description
+                CustomNumber = dbUser.CustomNumber,
+                Forename = dbUser.Forename,
+                Lastname = dbUser.Lastname,
+                RoleDescription = dbUser.CurrentRole.Description
             };
 
             ViewData["Roles"] =
-                currentUser.Roles
+                dbUser.Roles
                     .Select(r => new
                     {
                         r.Id,

+ 7 - 5
GreenTree.Nachtragsmanagement.Web/Controllers/HomeController.cs

@@ -7,7 +7,6 @@ using GreenTree.Nachtragsmanagement.Services.Configuration;
 using GreenTree.Nachtragsmanagement.Services.Test;
 using GreenTree.Nachtragsmanagement.Services.User;
 using GreenTree.Nachtragsmanagement.Web.Framework;
-using GreenTree.Nachtragsmanagement.Web.Models.Test;
 using Newtonsoft.Json;
 using GreenTree.Nachtragsmanagement.Core.Plugins;
 using GreenTree.Nachtragsmanagement.Core;
@@ -18,7 +17,7 @@ using GreenTree.Nachtragsmanagement.Core.Domain.User;
 
 namespace GreenTree.Nachtragsmanagement.Web.Controllers
 {
-    [RoleAuthorize(false)]
+    [FunctionAuthorize(false)]
     public class HomeController : Controller
     {
         private readonly IDbRelationService _dbRelationService;
@@ -44,10 +43,13 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         // GET: Home
         public ActionResult Index()
         {
-            var user = _userHelper.FromCookies();
+            var cookieUser = _userHelper.FromCookies();
+            var dbUser = _userService.GetUserById(cookieUser.Id);
 
-            var userFunctions = user.Roles
-                .SelectMany(s => s.Functions)
+            if (dbUser.CurrentRole == null || dbUser.CurrentRole.Id != cookieUser.CurrentRole.Id)
+                dbUser.CurrentRole = cookieUser.CurrentRole;
+
+            var userFunctions = dbUser.CurrentRole.Functions
                 .Where(f => f.IsMenuMember.HasValue && f.IsMenuMember.Value)
                 .Distinct();
 

+ 3 - 1
GreenTree.Nachtragsmanagement.Web/Global.asax.cs

@@ -38,12 +38,14 @@ namespace GreenTree.Nachtragsmanagement.Web
             ApplicationContext.InitApplication();
             ApplicationContext.InitPluginRoutes(RouteTable.Routes);
 
+            FunctionConfig.RegisterFunctions();
+
             FluentValidationModelValidatorProvider.Configure(provider =>
             {
                 provider.ValidatorFactory = new AppendixValidatorFactory();
             });
 
-            DevExpress.Web.ASPxWebControl.CallbackError += Application_Error;
+            DevExpress.Web.ASPxWebControl.CallbackError += Application_Error;   
         }
 
         protected void Application_Error(object sender, EventArgs e) 

+ 13 - 3
GreenTree.Nachtragsmanagement.Web/GreenTree.Nachtragsmanagement.Web.csproj

@@ -152,6 +152,10 @@
     <Content Include="Content\function.css" />
     <Content Include="Content\global.css" />
     <Content Include="Content\Images\close-16.png" />
+    <Content Include="Content\Images\function-Deviation-Deviations-32-contrast.png" />
+    <Content Include="Content\Images\function-Deviation-Deviations-32.png" />
+    <Content Include="Content\Images\function-Administration-Plugins-32-contrast.png" />
+    <Content Include="Content\Images\function-Administration-Roles-32-contrast.png" />
     <Content Include="Content\Images\function-Administration-Roles-32.png" />
     <Content Include="Content\Images\background-1-medium.jpg" />
     <Content Include="Content\Images\background-1-small.jpg" />
@@ -159,6 +163,7 @@
     <Content Include="Content\Images\background-3-full.jpg" />
     <Content Include="Content\Images\background-4-full.jpg" />
     <Content Include="Content\Images\footer-1-full.jpg" />
+    <Content Include="Content\Images\function-Administration-Users-32-contrast.png" />
     <Content Include="Content\Images\function-Administration-Users-32.png" />
     <Content Include="Content\Images\function-Administration-Plugins-32.png" />
     <Content Include="Content\Images\maximize-16.png" />
@@ -231,6 +236,9 @@
     <Content Include="Views\Admin\Roles\View.cshtml" />
     <Content Include="Views\Shared\_PopupButtonPanelOption.cshtml" />
     <Content Include="Views\Shared\_PopupDialogOption.cshtml" />
+    <Content Include="Views\Deviations\View.cshtml" />
+    <Content Include="Views\Deviations\_DeviationGridPartial.cshtml" />
+    <Content Include="Views\Deviations\_DeviationEditPartial.cshtml" />
     <None Include="Web.Debug.config">
       <DependentUpon>Web.config</DependentUpon>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -243,10 +251,12 @@
   <ItemGroup>
     <Compile Include="App_Start\BundleConfig.cs" />
     <Compile Include="App_Start\FilterConfig.cs" />
+    <Compile Include="App_Start\FunctionConfig.cs" />
     <Compile Include="App_Start\RouteConfig.cs" />
     <Compile Include="App_Start\WebApiConfig.cs" />
     <Compile Include="Controllers\AdminController.cs" />
     <Compile Include="Controllers\AuthController.cs" />
+    <Compile Include="Controllers\DeviationController.cs" />
     <Compile Include="Controllers\GlobalController.cs" />
     <Compile Include="Controllers\HomeController.cs" />
     <Compile Include="Controllers\LoginController.cs" />
@@ -256,6 +266,7 @@
     </Compile>
     <Compile Include="Models\Admin\User\RoleDataModel.cs" />
     <Compile Include="Models\Admin\User\UserDataModel.cs" />
+    <Compile Include="Models\Deviation\DeviationDataModel.cs" />
     <Compile Include="Models\Global\OptionDialogItemModel.cs" />
     <Compile Include="Models\Global\YesNoDialogModel.cs" />
     <Compile Include="Models\Global\OptionDialogModel.cs" />
@@ -265,15 +276,14 @@
     <Compile Include="Models\Global\PopupModel.cs" />
     <Compile Include="Models\Home\HomeModel.cs" />
     <Compile Include="Models\Login\LoginModel.cs" />
-    <Compile Include="Models\Test\DbRelationModel.cs" />
-    <Compile Include="Models\Test\PluginModel.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Validation\AppendixValidatorFactory.cs" />
+    <Compile Include="Validation\Deviation\DeviationDataModelValidator.cs" />
   </ItemGroup>
   <ItemGroup>
     <Folder Include="App_Data\" />
     <Folder Include="Fonts\" />
-    <Folder Include="Views\Auth\" />
+    <Folder Include="Views\Deviation\" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\GreenTree.Nachtragsmanagement.Core\GreenTree.Nachtragsmanagement.Core.csproj">

+ 3 - 3
GreenTree.Nachtragsmanagement.Web/Models/Admin/User/UserDataModel.cs

@@ -12,7 +12,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Admin.User
     public class UserDataModel
     {
         public int Id { get; set; }
-        public string CustomerNumber { get; set; }
+        public string CustomNumber { get; set; }
         public string Forename { get; set; }
         public string Lastname { get; set; }
         public string MailAddress { get; set; }
@@ -40,7 +40,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Admin.User
             return new UserDataModel
             {
                 Id = userEntity.Id,
-                CustomerNumber = userEntity.CustomNumber,
+                CustomNumber = userEntity.CustomNumber,
                 Forename = userEntity.Forename,
                 Lastname = userEntity.Lastname,
                 MailAddress = userEntity.MailAddress,
@@ -60,7 +60,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Admin.User
             return new Core.Domain.User.User
             {
                 Id = this.Id,
-                CustomNumber = this.CustomerNumber,
+                CustomNumber = this.CustomNumber,
                 Forename = this.Forename,
                 Lastname = this.Lastname,
                 MailAddress = this.MailAddress

+ 77 - 0
GreenTree.Nachtragsmanagement.Web/Models/Deviation/DeviationDataModel.cs

@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace GreenTree.Nachtragsmanagement.Web.Models.Deviation
+{
+    public class DeviationDataModel
+    {
+        public int Id { get; set; }
+        public string CustomNumber { get; set; }
+        public DateTime ReceiptDate { get; set; }
+        public decimal Value { get; set; }
+        public int StatusId { get; set; }
+        public string StatusDescription { get; set; }
+        public int DisturbanceId { get; set; }
+        public string DisturbanceDescription { get; set; }
+        public int KindId { get; set; }
+        public string KindDescription { get; set; }
+        public string Comment { get; set; }
+        public string AppendixDescription { get; set; }
+        public int? AppendixId { get; set; }
+
+        public static DeviationDataModel FromDeviation(Core.Domain.Deviation.Deviation deviationEntity, bool newWhenIsNull)
+        {
+            if (deviationEntity == null && newWhenIsNull)
+                return new DeviationDataModel
+                {
+                    Id = -1
+                };
+
+            if (deviationEntity == null && !newWhenIsNull)
+                throw new ArgumentNullException("deviationEntity", "Cannot create DeviationDataModel from NULL deviation entity.");
+
+            return new DeviationDataModel
+            {
+                Id = deviationEntity.Id,
+                CustomNumber = deviationEntity.CustomNumber,
+                ReceiptDate = deviationEntity.ReceiptDate,
+                Value = deviationEntity.Value.Value,
+                AppendixId = deviationEntity.AppendixId,
+                AppendixDescription = deviationEntity.Appendix == null
+                    ? null
+                    : deviationEntity.Appendix.CustomNumber,
+                StatusId = deviationEntity.StatusId.Value,
+                StatusDescription = deviationEntity.Status == null
+                    ? null
+                    : deviationEntity.Status.Description,
+                DisturbanceId = deviationEntity.DisturbanceId.Value,
+                DisturbanceDescription = deviationEntity.Disturbance == null
+                    ? null
+                    : deviationEntity.Disturbance.Description,
+                KindId = deviationEntity.KindId.Value,
+                KindDescription = deviationEntity.Kind == null
+                    ? null
+                    : deviationEntity.Kind.Description,
+                Comment = deviationEntity.Comment
+            };
+        }
+
+        public Core.Domain.Deviation.Deviation ToDeviation()
+        {
+            return new Core.Domain.Deviation.Deviation
+            {
+                Id = this.Id,
+                CustomNumber = this.CustomNumber,
+                ReceiptDate = this.ReceiptDate,
+                Value = this.Value,
+                AppendixId = this.AppendixId,
+                StatusId = this.StatusId,
+                DisturbanceId = this.DisturbanceId,
+                KindId = this.KindId,
+                Comment = this.Comment
+            };
+        }
+    }
+}

+ 0 - 15
GreenTree.Nachtragsmanagement.Web/Models/Test/DbRelationModel.cs

@@ -1,15 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Web;
-
-namespace GreenTree.Nachtragsmanagement.Web.Models.Test
-{
-    public class DbRelationModel
-    {
-        public string UserJson { get; set; }
-        public string DeviationJson { get; set; }
-        public string SiteJson { get; set; }
-        public string AppendixJson { get; set; }
-    }
-}

+ 0 - 12
GreenTree.Nachtragsmanagement.Web/Models/Test/PluginModel.cs

@@ -1,12 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Web;
-
-namespace GreenTree.Nachtragsmanagement.Web.Models.Test
-{
-    public class PluginModel
-    {
-        public List<string[]> PluginNames { get; set; }
-    }
-}

+ 1 - 0
GreenTree.Nachtragsmanagement.Web/Validation/Admin/User/UserDataModelValidator.cs

@@ -35,6 +35,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Validation.Admin.User
 
             RuleFor(m => m.Password)
                 .NotEmpty()
+                .When(p => p.Id == -1)
                     .WithMessage("Passwort wird benötigt");
         }
     }

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

@@ -1,5 +1,6 @@
 using FluentValidation;
 using GreenTree.Nachtragsmanagement.Web.Models.Admin.User;
+using GreenTree.Nachtragsmanagement.Web.Models.Deviation;
 using GreenTree.Nachtragsmanagement.Web.Validation.Admin.User;
 using System;
 using System.Collections.Generic;
@@ -16,6 +17,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Validation
         {
             validators.Add(typeof(IValidator<UserDataModel>), new UserDataModelValidator());
             validators.Add(typeof(IValidator<RoleDataModel>), new RoleDataModelValidator());
+            validators.Add(typeof(IValidator<DeviationDataModel>), new DeviationDataModelValidator());
         }
 
         public override IValidator CreateInstance(Type validatorType)

+ 45 - 0
GreenTree.Nachtragsmanagement.Web/Validation/Deviation/DeviationDataModelValidator.cs

@@ -0,0 +1,45 @@
+using FluentValidation;
+using GreenTree.Nachtragsmanagement.Web.Models.Deviation;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace GreenTree.Nachtragsmanagement.Web.Validation.Admin.User
+{
+    public class DeviationDataModelValidator : AbstractValidator<DeviationDataModel>
+    {
+        public DeviationDataModelValidator()
+        {
+            RuleFor(m => m.CustomNumber)
+                .NotEmpty()
+                    .WithMessage("Eigene ID wird benötigt")
+                .MaximumLength(10)
+                    .WithMessage("Muss unter 10 Zeichen sein");
+
+            RuleFor(m => m.ReceiptDate)
+                .NotEmpty()
+                    .WithMessage("Eingangsdatum wird benötigt");
+
+            RuleFor(m => m.Value)
+                .NotEmpty()
+                    .WithMessage("Ein Wert wird benötigt");
+
+            RuleFor(m => m.StatusId)
+                .NotEmpty()
+                    .WithMessage("Ein Status muss gewählt werden");
+
+            RuleFor(m => m.DisturbanceId)
+                .NotEmpty()
+                    .WithMessage("Ein Status muss gewählt werden");
+
+            RuleFor(m => m.KindId)
+                .NotEmpty()
+                    .WithMessage("Ein Status muss gewählt werden");
+
+            RuleFor(m => m.Comment)
+                .NotEmpty()
+                    .WithMessage("Ein Kommentar wird benötigt");
+        }
+    }
+}

+ 110 - 111
GreenTree.Nachtragsmanagement.Web/Views/Admin/Roles/_RoleEditPartial.cshtml

@@ -5,136 +5,135 @@
 @model GreenTree.Nachtragsmanagement.Web.Models.Admin.User.RoleDataModel
 
 <div class="roleEditContainer">
-
-<script>
-	var textSeparator = ", ";
-	var checkedNodesText = "";
-	var checkedNodesCounter = 0;
-
-	function synchronizeTreeViewValues(s, e) {
-		var treeView = MVCxClientTreeView.Cast(s);
-		var node = treeView.GetNodeByName(e.node.name);
-		if (node.GetChecked()) {
-			while (node) {
-				node.SetChecked(true);
-				if (node.parent) {
-					node = treeView.GetNodeByName(node.parent.name);
-				} else {
-					node = null;
+	<script>
+		var textSeparator = ", ";
+		var checkedNodesText = "";
+		var checkedNodesCounter = 0;
+
+		function synchronizeTreeViewValues(s, e) {
+			var treeView = MVCxClientTreeView.Cast(s);
+			var node = treeView.GetNodeByName(e.node.name);
+			if (node.GetChecked()) {
+				while (node) {
+					node.SetChecked(true);
+					if (node.parent) {
+						node = treeView.GetNodeByName(node.parent.name);
+					} else {
+						node = null;
+					}
 				}
 			}
+			checkedNodesText = "";
+			checkedNodesCounter = 0;
+
+			$("#roleEditForm").children(".hiddenRoleVal").remove();
+			getCheckedNodesText(treeView.GetRootNode());
+			devDropDownListFunctionValues.SetText(checkedNodesText.substr(0, checkedNodesText.length - textSeparator.length));
 		}
-		checkedNodesText = "";
-		checkedNodesCounter = 0;
-
-		$("#roleEditForm").children(".hiddenRoleVal").remove();
-		getCheckedNodesText(treeView.GetRootNode());
-		devDropDownListFunctionValues.SetText(checkedNodesText.substr(0, checkedNodesText.length - textSeparator.length));
-	}
-
-	function getCheckedNodesText(parent) {
-		for (var i = 0; i < parent.GetNodeCount(); i++) {
-			if (parent.GetNode(i).GetChecked()) {
-				checkedNodesText = checkedNodesText + parent.GetNode(i).GetText() + textSeparator;
-				$("#roleEditForm").append('<input class="hiddenRoleVal" type="hidden" name="FunctionValues[' + checkedNodesCounter + ']" value="' + parent.GetNode(i).name + '" />');
-				checkedNodesCounter++;
-			}
-			if (parent.GetNode(i).GetNodeCount() != 0) {
-				getCheckedNodesText(parent.GetNode(i));
+
+		function getCheckedNodesText(parent) {
+			for (var i = 0; i < parent.GetNodeCount(); i++) {
+				if (parent.GetNode(i).GetChecked()) {
+					checkedNodesText = checkedNodesText + parent.GetNode(i).GetText() + textSeparator;
+					$("#roleEditForm").append('<input class="hiddenRoleVal" type="hidden" name="FunctionValues[' + checkedNodesCounter + ']" value="' + parent.GetNode(i).name + '" />');
+					checkedNodesCounter++;
+				}
+				if (parent.GetNode(i).GetNodeCount() != 0) {
+					getCheckedNodesText(parent.GetNode(i));
+				}
 			}
 		}
-	}
-</script>
+	</script>
 
 	@Html.DevExpress().PopupControl(s =>
-{
-	s.Name = "devPopupControlEditRole";
-
-	if (Model.Id == -1)
-		s.HeaderText = "Neue Rolle erstellen";
-	else
-		s.HeaderText = "\"" + Model.Description + "\" bearbeiten";
-
-	s.Modal = true;
-	s.Width = new Unit(400, UnitType.Pixel);
-	s.CloseAction = CloseAction.CloseButton;
-	s.PopupHorizontalAlign = PopupHorizontalAlign.WindowCenter;
-	s.PopupVerticalAlign = PopupVerticalAlign.TopSides;
-	s.PopupVerticalOffset = 10;
-	s.AllowDragging = false;
-	s.AllowResize = false;
-	s.ShowFooter = false;
-	s.ShowOnPageLoad = true;
-	s.SetContent(() =>
 	{
-		using (Html.BeginForm("EditRole", "Admin", FormMethod.Post, new { id = "roleEditForm" }))
+		s.Name = "devPopupControlEditRole";
+
+		if (Model.Id == -1)
+			s.HeaderText = "Neue Rolle erstellen";
+		else
+			s.HeaderText = "\"" + Model.Description + "\" bearbeiten";
+
+		s.Modal = true;
+		s.Width = new Unit(400, UnitType.Pixel);
+		s.CloseAction = CloseAction.CloseButton;
+		s.PopupHorizontalAlign = PopupHorizontalAlign.WindowCenter;
+		s.PopupVerticalAlign = PopupVerticalAlign.TopSides;
+		s.PopupVerticalOffset = 10;
+		s.AllowDragging = false;
+		s.AllowResize = false;
+		s.ShowFooter = false;
+		s.ShowOnPageLoad = true;
+		s.SetContent(() =>
 		{
-			ViewContext.Writer.Write("<div class='editFormWrapper'>");
-
-			ViewContext.Writer.Write("<input type=\"hidden\" value=\"" + Model.Id + "\" id=\"Id\" name=\"Id\" />");
-
-			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.FunctionValues, "Funktionen:"));
-			ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.FunctionValues).ToHtmlString());
-			Html.DevExpress().DropDownEdit(t =>
+			using (Html.BeginForm("EditRole", "Admin", FormMethod.Post, new { id = "roleEditForm" }))
 			{
-				t.Name = "devDropDownListFunctionValues";
-				t.Width = new Unit(100, UnitType.Percentage);
+				ViewContext.Writer.Write("<div class='editFormWrapper'>");
 
-				if (Model.FunctionDescriptions != null && Model.FunctionDescriptions.Any())
-					t.Text = String.Join(", ", Model.FunctionDescriptions);
+				ViewContext.Writer.Write("<input type=\"hidden\" value=\"" + Model.Id + "\" id=\"Id\" name=\"Id\" />");
 
-				t.SetDropDownWindowTemplateContent(l =>
+				ViewContext.Writer.Write(Html.CustomLabelFor(m => m.FunctionValues, "Funktionen:"));
+				ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.FunctionValues).ToHtmlString());
+				Html.DevExpress().DropDownEdit(t =>
 				{
-					ViewContext.Writer.Write("<div style='width: auto; overflow: auto; max-height: 200px'>");
-					Html.DevExpress().TreeView(tv =>
+					t.Name = "devDropDownListFunctionValues";
+					t.Width = new Unit(100, UnitType.Percentage);
+
+					if (Model.FunctionDescriptions != null && Model.FunctionDescriptions.Any())
+						t.Text = String.Join(", ", Model.FunctionDescriptions);
+
+					t.SetDropDownWindowTemplateContent(l =>
 					{
-						var allFunctions = ViewData["AllFunctions"] as IList<Function>;
+						ViewContext.Writer.Write("<div style='width: auto; overflow: auto; max-height: 200px'>");
+						Html.DevExpress().TreeView(tv =>
+						{
+							var allFunctions = ViewData["AllFunctions"] as IList<Function>;
 
-						tv.Name = "devTreeViewFunctionValues";
-						tv.AllowCheckNodes = true;
+							tv.Name = "devTreeViewFunctionValues";
+							tv.AllowCheckNodes = true;
 
-						tv.CreateGroupedFunctionList(allFunctions);
+							tv.CreateGroupedFunctionList(allFunctions);
 
-						foreach (var checkedFunction in Model.FunctionValues)
-						{
-							tv.Nodes.FindRecursive(n => n.Name == checkedFunction.ToString()).Checked = true;
-						}
-
-						tv.ClientSideEvents.CheckedChanged = "function (s, e) { synchronizeTreeViewValues(s, e); }";
-					}).Render();
-					ViewContext.Writer.Write("</div>");
-				});
-			}).Render();
-
-			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Description, "Beschreibung:"));
-			ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Description).ToHtmlString());
-			Html.DevExpress().TextBoxFor(m => m.Description, t =>
-			{
-				t.Width = new Unit(100, UnitType.Percentage);
-			}).Render();
+							foreach (var checkedFunction in Model.FunctionValues)
+							{
+								tv.Nodes.FindRecursive(n => n.Name == checkedFunction.ToString()).Checked = true;
+							}
 
-			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Level, "Stufe:"));
-			ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Level).ToHtmlString());
-			Html.DevExpress().SpinEditFor(m => m.Level, t =>
-			{
-				t.Width = new Unit(60, UnitType.Percentage);
-				t.Properties.NumberType = SpinEditNumberType.Integer;
-				t.Properties.MinValue = 0;
-			}).Render();
+							tv.ClientSideEvents.CheckedChanged = "function (s, e) { synchronizeTreeViewValues(s, e); }";
+						}).Render();
+						ViewContext.Writer.Write("</div>");
+					});
+				}).Render();
 
-			ViewContext.Writer.Write("</div>");
+				ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Description, "Beschreibung:"));
+				ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Description).ToHtmlString());
+				Html.DevExpress().TextBoxFor(m => m.Description, t =>
+				{
+					t.Width = new Unit(100, UnitType.Percentage);
+				}).Render();
 
-			Html.RenderPartial(
-				"~/Views/Shared/_PopupButtonPanel.cshtml",
-				new GreenTree.Nachtragsmanagement.Web.Models.Global.PopupModel
+				ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Level, "Stufe:"));
+				ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Level).ToHtmlString());
+				Html.DevExpress().SpinEditFor(m => m.Level, t =>
 				{
-					PopupName = "devPopupControlEditRole",
-					AcceptFunction = "function (s, e) { saveRole(); }"
-				}
-			);
-		}
-	});
-	s.Styles.Content.Paddings.Padding = new Unit(0);
-	s.Styles.ModalBackground.Opacity = 0;
-}).GetHtml()
+					t.Width = new Unit(60, UnitType.Percentage);
+					t.Properties.NumberType = SpinEditNumberType.Integer;
+					t.Properties.MinValue = 0;
+				}).Render();
+
+				ViewContext.Writer.Write("</div>");
+
+				Html.RenderPartial(
+					"~/Views/Shared/_PopupButtonPanel.cshtml",
+					new GreenTree.Nachtragsmanagement.Web.Models.Global.PopupModel
+					{
+						PopupName = "devPopupControlEditRole",
+						AcceptFunction = "function (s, e) { saveRole(); }"
+					}
+				);
+			}
+		});
+		s.Styles.Content.Paddings.Padding = new Unit(0);
+		s.Styles.ModalBackground.Opacity = 0;
+	}).GetHtml()
 </div>

+ 2 - 2
GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/_UserEditPartial.cshtml

@@ -78,8 +78,8 @@
 
 			ViewContext.Writer.Write("<input type=\"hidden\" value=\"" + Model.Id + "\" id=\"Id\" name=\"Id\" />");
 
-			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Forename, "Personalnummer:"));
-			Html.DevExpress().TextBoxFor(m => m.CustomerNumber, t =>
+			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.CustomNumber, "Personalnummer:"));
+			Html.DevExpress().TextBoxFor(m => m.CustomNumber, t =>
 			{
 				t.Width = new Unit(60, UnitType.Percentage);
 			}).Render();

+ 1 - 0
GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/_UserGridPartial.cshtml

@@ -33,6 +33,7 @@
 			column.Width = 70;
 		});
 	}
+	s.Columns.Add("CustomNumber", "Personal-Nr.");
 	s.Columns.Add("Forename", "Vorname");
 	s.Columns.Add("Lastname", "Nachname");
 	s.Columns.Add("MailAddress", "Mail-Adresse");

+ 94 - 0
GreenTree.Nachtragsmanagement.Web/Views/Deviations/View.cshtml

@@ -0,0 +1,94 @@
+@{
+	Layout = "~/Views/Shared/_FunctionLayout.cshtml";
+}
+
+@model IEnumerable<GreenTree.Nachtragsmanagement.Web.Models.Deviation.DeviationDataModel>
+
+<script>
+	var deleteId;
+
+	function saveDeviation() {
+		var form = $("#deviationEditForm");
+		$(form).submit(function (e) {
+			$.ajax({
+				type: "POST",
+				url: '@Url.Action("EditDeviation", "Admin")',
+				data: form.serialize(),
+				success: function (response) {
+					setTimeout(function () {
+						$(".deviationEditContainer").remove();
+						if (response == "success") {
+							devGridViewDeviation.PerformCallback();
+						} else {
+							$("body").append(response);
+						}
+					}, 200);
+				}
+			});
+			e.preventDefault();
+		});
+		form.submit();
+	}
+
+	function editDeviation(id) {
+		if (!id) return;
+		$.ajax({
+			url: '@Url.Action("EditDeviation", "Admin")',
+			data: { Id: id },
+			success: function (response) {
+				setTimeout(function () {
+					$(".deviationEditContainer").remove();
+					$("body").append(response);
+				}, 200);
+			},
+			error: function () {
+				 alert("error occured");
+			}
+		});
+	}
+
+	function confirmDelete(id) {
+		if (!id) return;
+		deleteId = id;
+		$.ajax({
+			type: "GET",
+			url: '@Url.Action("GetDeviation", "Deviation")',
+			data: { Id: id },
+			success: function (response) {
+				if (response == "notFound") return;
+				var deviation = JSON.parse(response);
+				var popupControl = MVCxClientPopupControl.Cast(devPopupControlDeleteDeviation);
+				popupControl.SetHeaderText(popupControl.GetHeaderText().replace("{deviation}", deviation.CustomNumber));
+				$(".dialogText").text($(".dialogText").text().replace("{deviation}", deviation.CustomNumber);
+				popupControl.Show();
+			}
+		});
+	}
+
+	function deleteDeviation() {
+		$.ajax({
+			type: "POST",
+			url: '@Url.Action("DeleteDeviation", "Deviation")',
+			data: { Id: deleteId },
+			success: function (response) {
+				var popupControl = MVCxClientPopupControl.Cast(devPopupControlDeleteDeviation);
+				popupControl.Hide();
+				setTimeout(function () {
+					devGridViewDeviation.PerformCallback();
+				}, 200);
+			},
+			error: function () {
+				 alert("error occured");
+			}
+		});
+	}
+</script>
+
+@Html.Partial("~/Views/Deviations/_DeviationGridPartial.cshtml", Model)
+@Html.Partial("~/Views/Shared/_PopupDialogYesNo.cshtml", new GreenTree.Nachtragsmanagement.Web.Models.Global.YesNoDialogModel
+{
+	PopupName = "devPopupControlDeleteDeviation",
+	Content = "<div class='dialogText' style='padding: 12px'>Sind Sie sicher, dass Sie die Vertragsabweichung \"{deviation}\" löschen möchten?</div>",
+	HeaderText = "\"{deviation}\" löschen",
+	YesFunction = "function (s, e) { deleteDeviation(); }"
+})

+ 109 - 0
GreenTree.Nachtragsmanagement.Web/Views/Deviations/_DeviationEditPartial.cshtml

@@ -0,0 +1,109 @@
+@using GreenTree.Nachtragsmanagement.Web.Extensions
+
+@model GreenTree.Nachtragsmanagement.Web.Models.Deviation.DeviationDataModel
+
+<div class="deviationEditContainer">
+
+	@Html.DevExpress().PopupControl(s =>
+{
+	s.Name = "devPopupControlEditDeviation";
+
+	if (Model.Id == -1)
+		s.HeaderText = "Neue Vertragsabweichung erstellen";
+	else
+		s.HeaderText = "\"" + Model.CustomNumber + "\" bearbeiten";
+
+	s.Modal = true;
+	s.Width = new Unit(500, UnitType.Pixel);
+	s.CloseAction = CloseAction.CloseButton;
+	s.PopupHorizontalAlign = PopupHorizontalAlign.WindowCenter;
+	s.PopupVerticalAlign = PopupVerticalAlign.WindowCenter;
+	s.AllowDragging = false;
+	s.AllowResize = false;
+	s.ShowFooter = false;
+	s.ShowOnPageLoad = true;
+	s.SetContent(() =>
+	{
+		using (Html.BeginForm("EditDeviation", "Admin", FormMethod.Post, new { id = "deviationEditForm" }))
+		{
+			ViewContext.Writer.Write("<div class='editFormWrapper'>");
+
+			ViewContext.Writer.Write("<input type=\"hidden\" value=\"" + Model.Id + "\" id=\"Id\" name=\"Id\" />");
+
+			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.CustomNumber, "Eigene ID:"));
+			ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.CustomNumber).ToHtmlString());
+			Html.DevExpress().TextBoxFor(m => m.CustomNumber, t =>
+			{
+				t.Width = new Unit(60, UnitType.Percentage);
+			}).Render();
+
+			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Value, "Wert:"));
+			ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Value).ToHtmlString());
+			Html.DevExpress().SpinEditFor(m => m.Value, t =>
+			{
+				t.Width = new Unit(50, UnitType.Percentage);
+			}).Render();
+
+			ViewContext.Writer.Write("<div class='inlineModelPropertyContainer'>");
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 30%'>");
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.StatusId, "Status:"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.StatusId).ToHtmlString());
+					Html.DevExpress().ComboBoxFor(m => m.StatusId, t =>
+					{
+						t.Width = new Unit(100, UnitType.Percentage);
+						t.Properties.ValueType = typeof(int);
+						t.Properties.ValueField = "Id";
+						t.Properties.TextField = "Description";
+					}).BindList(ViewData["AllStatuses"]).Render();
+				ViewContext.Writer.Write("</div>");
+
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 30%'>");
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.DisturbanceId, "Verzögerung:"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.DisturbanceId).ToHtmlString());
+					Html.DevExpress().ComboBoxFor(m => m.DisturbanceId, t =>
+					{
+						t.Width = new Unit(100, UnitType.Percentage);
+						t.Properties.ValueType = typeof(int);
+						t.Properties.ValueField = "Id";
+						t.Properties.TextField = "Description";
+					}).BindList(ViewData["AllDisturbances"]).Render();
+				ViewContext.Writer.Write("</div>");
+
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 40%'>");
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.KindId, "Art:"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.KindId).ToHtmlString());
+					Html.DevExpress().ComboBoxFor(m => m.KindId, t =>
+					{
+						t.Width = new Unit(100, UnitType.Percentage);
+						t.Properties.ValueType = typeof(int);
+						t.Properties.ValueField = "Id";
+						t.Properties.TextField = "Description";
+					}).BindList(ViewData["AllKinds"]).Render();
+				ViewContext.Writer.Write("</div>");
+			ViewContext.Writer.Write("</div>");
+
+			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.AppendixId, "Nachtrag:"));
+			Html.DevExpress().ComboBoxFor(m => m.AppendixId, t =>
+			{
+				t.Width = new Unit(100, UnitType.Percentage);
+				t.Properties.ValueType = typeof(int);
+				t.Properties.ValueField = "Id";
+				t.Properties.TextField = "Description";
+			}).BindList(ViewData["AllAppendices"]).Render();
+
+			ViewContext.Writer.Write("</div>");
+
+			Html.RenderPartial(
+				"~/Views/Shared/_PopupButtonPanel.cshtml",
+				new GreenTree.Nachtragsmanagement.Web.Models.Global.PopupModel
+				{
+					PopupName = "devPopupControlEditDeviation",
+					AcceptFunction = "function (s, e) { saveDeviation(); }"
+				}
+			);
+		}
+	});
+	s.Styles.Content.Paddings.Padding = new Unit(0);
+	s.Styles.ModalBackground.Opacity = 0;
+}).GetHtml()
+</div>

+ 57 - 0
GreenTree.Nachtragsmanagement.Web/Views/Deviations/_DeviationGridPartial.cshtml

@@ -0,0 +1,57 @@
+@model IEnumerable<GreenTree.Nachtragsmanagement.Web.Models.Deviation.DeviationDataModel>
+
+@{ 
+	var userContext = GreenTree.Nachtragsmanagement.Core.CommonHelper.UserContext();
+}
+
+@Html.DevExpress().GridView(s =>
+{
+	s.Name = "devGridViewDeviation";
+	s.KeyFieldName = "Id";
+	s.CallbackRouteValues = new { Controller = "Admin", Action = "PartialDeviations" };
+	s.Width = Unit.Percentage(100);
+
+	if (userContext.CurrentUser.HasFunction("Deviation-Deviations-Edit"))
+	{
+		s.Columns.Add(column =>
+		{
+			column.Caption = "#";
+			column.SetDataItemTemplateContent(c =>
+			{
+				ViewContext.Writer.Write(
+					"<a href=\"#\" onclick=\"editDeviation(" + 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=\"editDeviation(-1)\">Neu</a>&nbsp;");
+			});
+			column.Settings.AllowDragDrop = DefaultBoolean.False;
+			column.Settings.AllowSort = DefaultBoolean.False;
+			column.Width = 70;
+		});
+	}
+	s.Columns.Add("CustomNumber", "Eigene ID");
+	s.Columns.Add("ReceiptDate", "Eingang");
+	s.Columns.Add("Value", "Wert");
+	s.Columns.Add("StatusDescription", "Status");
+	s.Columns.Add("DisturbanceDescription", "Verzögerung");
+	s.Columns.Add("KindDescription", "Art");
+	s.Columns.Add("AppendixDescription", "Nachtrag");
+	s.Columns.Add("Comment", "Kommentar");
+
+	s.ClientLayout = (sender, e) =>
+	{
+		if (e.LayoutMode == ClientLayoutMode.Loading)
+		{
+			if (Session["DeviationGridState"] != null)
+				e.LayoutData = (string)Session["DeviationGridState"];
+		}
+		else
+			Session["DeviationGridState"] = e.LayoutData;
+	};
+
+	s.Styles.AlternatingRow.BackColor = System.Drawing.Color.FromArgb(247, 247, 247);
+}).Bind(Model).GetHtml()

+ 8 - 2
GreenTree.Nachtragsmanagement.Web/Views/Home/Index.cshtml

@@ -20,6 +20,7 @@
 	var popupLocationsX = {};
 	var popupLocationsY = {};
 	var popupMinimized = {};
+	var popupUrls = {};
 
 	var contentX = 0;
 	var contentY = 0;
@@ -35,6 +36,7 @@
 			ViewContext.Writer.WriteLine("popupElements[\"" + i.Name + "\"] = \"" + i.Name + "_PW-1\";");
 			ViewContext.Writer.WriteLine("popupImages[\"" + i.Name + "\"] = \"" + Url.Content(i.ImageUrl) + "\";");
 			ViewContext.Writer.WriteLine("popupDescriptions[\"" + i.Name + "\"] = \"" + i.Description + "\";");
+			ViewContext.Writer.WriteLine("popupUrls[\"" + i.Name + "\"] = \"" + Url.RouteUrl(i.RouteName) + "\";");
 		}
 	}
 
@@ -84,7 +86,7 @@
 		}
 	})
 
-	function showFunction(e) {
+	function showFunction(e, parameters) {
 		if (!e) return;
 		var controls = ASPxClientControl.GetControlCollection();
 		var popupName = e.item.name + "-Popup";
@@ -99,7 +101,11 @@
 				popupControl.SetWidth(popupWidths[e.item.name]);
 				popupControl.SetHeight(popupHeights[e.item.name]);
 			}
-			popupControl.RefreshContentUrl();
+			if (!parameters) {
+				popupControl.SetContentUrl(popupUrls[e.item.name]);
+			} else {
+				popupControl.SetContentUrl(popupUrls[e.item.name] + parameters);
+			}
 			popupControl.ShowAtPos(contentX + openOffsetX, contentY + openOffsetY);
 			popupLocationsX[e.item.name] = contentX + openOffsetX;
 			popupLocationsY[e.item.name] = contentY + openOffsetY;