Pārlūkot izejas kodu

Baustellenliste hinzugefügt.

Arne Diekmann 8 gadi atpakaļ
vecāks
revīzija
3cfb647329
49 mainītis faili ar 2425 papildinājumiem un 255 dzēšanām
  1. 54 8
      GreenTree.Nachtragsmanagement.Core/Domain/Deviation/Deviation.cs
  2. 5 0
      GreenTree.Nachtragsmanagement.Core/Domain/Deviation/Kind.cs
  3. 48 7
      GreenTree.Nachtragsmanagement.Core/Domain/Site/Site.cs
  4. 2 64
      GreenTree.Nachtragsmanagement.Data/AppendixObjectContext.cs
  5. 10 4
      GreenTree.Nachtragsmanagement.Data/Mapping/Deviation/DeviationMap.cs
  6. 1 0
      GreenTree.Nachtragsmanagement.Data/Mapping/Deviation/KindMap.cs
  7. 3 1
      GreenTree.Nachtragsmanagement.Data/Mapping/Site/SiteMap.cs
  8. 0 11
      GreenTree.Nachtragsmanagement.Services/Deviation/DeviationService.cs
  9. 0 6
      GreenTree.Nachtragsmanagement.Services/Deviation/IDeviationService.cs
  10. 86 1
      GreenTree.Nachtragsmanagement.Web/App_Start/FunctionConfig.cs
  11. 42 0
      GreenTree.Nachtragsmanagement.Web/App_Start/RouteConfig.cs
  12. BIN
      GreenTree.Nachtragsmanagement.Web/Content/Images/function-Appendix-Appendices-32-contrast.png
  13. BIN
      GreenTree.Nachtragsmanagement.Web/Content/Images/function-Appendix-Appendices-32.png
  14. BIN
      GreenTree.Nachtragsmanagement.Web/Content/Images/function-Appendix-Categories-32-contrast.png
  15. BIN
      GreenTree.Nachtragsmanagement.Web/Content/Images/function-Appendix-Categories-32.png
  16. BIN
      GreenTree.Nachtragsmanagement.Web/Content/Images/function-Site-Sites-32-contrast.png
  17. BIN
      GreenTree.Nachtragsmanagement.Web/Content/Images/function-Site-Sites-32.png
  18. 271 0
      GreenTree.Nachtragsmanagement.Web/Controllers/AppendixController.cs
  19. 14 18
      GreenTree.Nachtragsmanagement.Web/Controllers/DeviationController.cs
  20. 186 0
      GreenTree.Nachtragsmanagement.Web/Controllers/SiteController.cs
  21. 167 6
      GreenTree.Nachtragsmanagement.Web/Global.asax.cs
  22. 19 0
      GreenTree.Nachtragsmanagement.Web/GreenTree.Nachtragsmanagement.Web.csproj
  23. 10 0
      GreenTree.Nachtragsmanagement.Web/Models/Admin/User/RoleDataModel.cs
  24. 10 0
      GreenTree.Nachtragsmanagement.Web/Models/Admin/User/UserDataModel.cs
  25. 97 0
      GreenTree.Nachtragsmanagement.Web/Models/Appendix/AppendixDataModel.cs
  26. 40 0
      GreenTree.Nachtragsmanagement.Web/Models/Appendix/CategoryDataModel.cs
  27. 13 0
      GreenTree.Nachtragsmanagement.Web/Models/Appendix/IRequireCategoryDataModel.cs
  28. 47 9
      GreenTree.Nachtragsmanagement.Web/Models/Deviation/DeviationDataModel.cs
  29. 1 1
      GreenTree.Nachtragsmanagement.Web/Models/Deviation/IRequireDisturbanceDataModel.cs
  30. 5 2
      GreenTree.Nachtragsmanagement.Web/Models/Deviation/KindDataModel.cs
  31. 1 1
      GreenTree.Nachtragsmanagement.Web/Models/Global/EmptyIRequireDataModel.cs
  32. 127 0
      GreenTree.Nachtragsmanagement.Web/Models/Site/SiteDataModel.cs
  33. 2 0
      GreenTree.Nachtragsmanagement.Web/Validation/AppendixValidatorFactory.cs
  34. 16 8
      GreenTree.Nachtragsmanagement.Web/Validation/Deviation/DeviationDataModelValidator.cs
  35. 6 0
      GreenTree.Nachtragsmanagement.Web/Validation/Deviation/KindDataModelValidator.cs
  36. 34 0
      GreenTree.Nachtragsmanagement.Web/Validation/Site/SiteDataModelValidator.cs
  37. 3 14
      GreenTree.Nachtragsmanagement.Web/Views/Admin/Roles/_RoleGridPartial.cshtml
  38. 3 14
      GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/_UserGridPartial.cshtml
  39. 71 0
      GreenTree.Nachtragsmanagement.Web/Views/Appendices/Categories.cshtml
  40. 80 0
      GreenTree.Nachtragsmanagement.Web/Views/Appendices/_CategoryEditPartial.cshtml
  41. 29 0
      GreenTree.Nachtragsmanagement.Web/Views/Appendices/_CategoryListPartial.cshtml
  42. 4 9
      GreenTree.Nachtragsmanagement.Web/Views/Deviations/Claims.cshtml
  43. 244 66
      GreenTree.Nachtragsmanagement.Web/Views/Deviations/_DeviationEditPartial.cshtml
  44. 13 4
      GreenTree.Nachtragsmanagement.Web/Views/Deviations/_DeviationGridPartial.cshtml
  45. 1 1
      GreenTree.Nachtragsmanagement.Web/Views/Deviations/_DisturbanceEditPartial.cshtml
  46. 7 0
      GreenTree.Nachtragsmanagement.Web/Views/Deviations/_KindEditPartial.cshtml
  47. 94 0
      GreenTree.Nachtragsmanagement.Web/Views/Sites/View.cshtml
  48. 479 0
      GreenTree.Nachtragsmanagement.Web/Views/Sites/_SiteEditPartial.cshtml
  49. 80 0
      GreenTree.Nachtragsmanagement.Web/Views/Sites/_SiteGridPartial.cshtml

+ 54 - 8
GreenTree.Nachtragsmanagement.Core/Domain/Deviation/Deviation.cs

@@ -8,11 +8,25 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.Deviation
 {
     public class Deviation : BaseEntity
     {
+        #region Fields
+
+        /// <summary>
+        /// Disturbances related to the deviation
+        /// </summary>
+        private ICollection<Disturbance> _disturbances;
+
+        #endregion
+
         /// <summary>
         /// Customized number for identification
         /// </summary>
         public string CustomNumber { get; set; }
 
+        /// <summary>
+        /// Description
+        /// </summary>
+        public string Description { get; set; }
+
         /// <summary>
         /// Date when deviation were created
         /// </summary>
@@ -28,6 +42,11 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.Deviation
         /// </summary>
         public decimal? Value { get; set; }
 
+        /// <summary>
+        /// Montary percentage value
+        /// </summary>
+        public int? Percentage { get; set; }
+
         /// <summary>
         /// Id of corresponding appendix
         /// </summary>
@@ -39,24 +58,29 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.Deviation
         public Appendix.Appendix Appendix { get; set; }
 
         /// <summary>
-        /// Id of the current deviation status
+        /// Date when deviation was matched to a an appendix
         /// </summary>
-        public int? StatusId { get; set; }
+        public DateTime? AppendixDate { get; set; }
 
         /// <summary>
-        /// Current deviation status
+        /// Id of corresponding site
         /// </summary>
-        public Status Status { get; set; }
+        public int? SiteId { get; set; }
 
         /// <summary>
-        /// Id of the current deviation disturbance
+        /// Corresponding site
         /// </summary>
-        public int? DisturbanceId { get; set; }
+        public Site.Site Site { get; set; }
 
         /// <summary>
-        /// Current deviation disturbance
+        /// Id of the current deviation status
+        /// </summary>
+        public int? StatusId { get; set; }
+
+        /// <summary>
+        /// Current deviation status
         /// </summary>
-        public Disturbance Disturbance { get; set; }
+        public Status Status { get; set; }
 
         /// <summary>
         /// Id of the current deviation kind
@@ -67,5 +91,27 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.Deviation
         /// Current deviation kind
         /// </summary>
         public Kind Kind { get; set; }
+
+        /// <summary>
+        /// Disturbances related to the deviation
+        /// </summary>
+        public virtual ICollection<Disturbance> Disturbances
+        {
+            get { return _disturbances ?? (_disturbances = new List<Disturbance>()); }
+            protected set { _disturbances = value; }
+        }
+
+        #region Helper
+
+        /// <summary>
+        /// Adds missing disturbances and removes not selected disturbances
+        /// </summary>
+        /// <param name="disturbances">Deviation disturbances.</param>
+        public void SetDisturbances(ICollection<Disturbance> disturbances)
+        {
+            Disturbances = disturbances;
+        }
+
+        #endregion
     }
 }

+ 5 - 0
GreenTree.Nachtragsmanagement.Core/Domain/Deviation/Kind.cs

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

+ 48 - 7
GreenTree.Nachtragsmanagement.Core/Domain/Site/Site.cs

@@ -20,6 +20,11 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.Site
         /// </summary>
         private ICollection<Appendix.Appendix> _appendices;
 
+        /// <summary>
+        /// Deviations related to the site
+        /// </summary>
+        private ICollection<Deviation.Deviation> _deviations;
+
         #endregion
 
         /// <summary>
@@ -35,17 +40,12 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.Site
         /// <summary>
         /// Beginning of the site
         /// </summary>
-        public DateTime Start { get; set; }
+        public DateTime? Start { get; set; }
 
         /// <summary>
         /// Ending of the site
         /// </summary>
-        public DateTime End { get; set; }
-
-        /// <summary>
-        /// Corresponding department handling the site
-        /// </summary>
-        public string Department { get; set; }
+        public DateTime? End { get; set; }
 
         /// <summary>
         /// Editable comment
@@ -74,5 +74,46 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.Site
             get { return _appendices ?? (_appendices = new List<Appendix.Appendix>()); }
             protected set { _appendices = value; }
         }
+
+        /// <summary>
+        /// Deviations related to the site
+        /// </summary>
+        public virtual ICollection<Deviation.Deviation> Deviations
+        {
+            get { return _deviations ?? (_deviations = new List<Deviation.Deviation>()); }
+            protected set { _deviations = value; }
+        }
+
+        #region Helper
+
+        /// <summary>
+        /// Adds missing deviations and removes not selected deviations
+        /// </summary>
+        /// <param name="deviations">Site deviations.</param>
+        public void SetDeviations(ICollection<Deviation.Deviation> deviations)
+        {
+            Deviations = deviations;
+        }
+
+        /// <summary>
+        /// Adds missing appendices and removes not selected appendices
+        /// </summary>
+        /// <param name="deviations">Site appendices.</param>
+        public void SetAppendices(ICollection<Appendix.Appendix> appendices)
+        {
+            Appendices = appendices;
+        }
+
+
+        /// <summary>
+        /// Adds missing users and removes not selected users
+        /// </summary>
+        /// <param name="users">Site users.</param>
+        public void SetUsers(ICollection<User.User> users)
+        {
+            Users = users;
+        }
+
+        #endregion
     }
 }

+ 2 - 64
GreenTree.Nachtragsmanagement.Data/AppendixObjectContext.cs

@@ -79,13 +79,11 @@ namespace GreenTree.Nachtragsmanagement.Data
                 }
             );
 
-            Database.SetInitializer(new CreateDatabaseIfNotExists<AppendixObjectContext>());
-            //Database.SetInitializer(new DropCreateDatabaseIfModelChanges<AppendixObjectContext>());
+            //Database.SetInitializer(new CreateDatabaseIfNotExists<AppendixObjectContext>());
+            Database.SetInitializer(new DropCreateDatabaseIfModelChanges<AppendixObjectContext>());
 
             Database.CreateIfNotExists();
 
-            GenerateTestData();
-
             SaveChanges();
         }
 
@@ -219,65 +217,5 @@ namespace GreenTree.Nachtragsmanagement.Data
         }
 
         #endregion
-
-        #region Test
-
-        /// <summary>
-        /// Generates local test data
-        /// </summary>
-        public void GenerateTestData()
-        {
-            var isTestDataGeneratedEntity = Get<DbContextSpec>().FirstOrDefault(d => d.Name == "IsTestDataGenerated");
-
-            if (isTestDataGeneratedEntity != null && Convert.ToBoolean(isTestDataGeneratedEntity.Value))
-                return;
-
-            var r1 = Get<Role>().Add(new Role
-            {
-                Description = "Administrator",
-                Level = 100
-            });
-
-            var r2 = Get<Role>().Add(new Role
-            {
-                Description = "Kaufmann",
-                Level = 10
-            });
-
-            var r3 = Get<Role>().Add(new Role
-            {
-                Description = "Nachtragsmanager",
-                Level = 10
-            });
-
-            SaveChanges();
-
-            var u1 = Get<User>().Add(new User
-            {
-                Forename = "Arne",
-                Lastname = "Diekmann",
-                CustomNumber = "anw0486m",
-                MailAddress = "a.diekmann@porta.de",
-                Password = StaticHelper.GetMD5Hash("14595809")
-            });
-
-            SaveChanges();
-
-            u1.Roles.Add(r1);
-            u1.Roles.Add(r2);
-            u1.Roles.Add(r3);
-
-            SaveChanges();
-
-            var db1 = Get<DbContextSpec>().Add(new DbContextSpec
-            {
-                Name = "IsTestDataGenerated",
-                Value = "True"
-            });
-
-            SaveChanges();
-        }
-
-        #endregion
     }
 }

+ 10 - 4
GreenTree.Nachtragsmanagement.Data/Mapping/Deviation/DeviationMap.cs

@@ -15,11 +15,17 @@ namespace GreenTree.Nachtragsmanagement.Data.Mapping.Deviation
 
             HasKey(d => d.Id);
 
+            HasOptional(a => a.Site)
+                .WithMany(s => s.Deviations)
+                .HasForeignKey(a => a.SiteId);
+
             Property(d => d.Comment);
             Property(d => d.CustomNumber);
             Property(d => d.ReceiptDate);
+            Property(d => d.AppendixDate);
             Property(d => d.Comment);
             Property(d => d.Value);
+            Property(d => d.Percentage);
 
             HasOptional(d => d.Appendix)
                 .WithMany(s => s.Deviations)
@@ -29,13 +35,13 @@ namespace GreenTree.Nachtragsmanagement.Data.Mapping.Deviation
                 .WithMany()
                 .HasForeignKey(d => d.StatusId);
 
-            HasOptional(d => d.Disturbance)
-                .WithMany()
-                .HasForeignKey(d => d.DisturbanceId);
-
             HasOptional(d => d.Kind)
                 .WithMany()
                 .HasForeignKey(d => d.KindId);
+
+            HasMany(s => s.Disturbances)
+                .WithMany() 
+                .Map(a => a.ToTable("DeviationDisturbances"));
         }
     }
 }

+ 1 - 0
GreenTree.Nachtragsmanagement.Data/Mapping/Deviation/KindMap.cs

@@ -16,6 +16,7 @@ namespace GreenTree.Nachtragsmanagement.Data.Mapping.Deviation
             HasKey(f => f.Id);
 
             Property(f => f.Description);
+            Property(f => f.Shortance);
         }
     }
 }

+ 3 - 1
GreenTree.Nachtragsmanagement.Data/Mapping/Site/SiteMap.cs

@@ -17,7 +17,6 @@ namespace GreenTree.Nachtragsmanagement.Data.Mapping.Site
 
             Property(s => s.CustomNumber);
             Property(s => s.Description);
-            Property(s => s.Department);
             Property(s => s.Comment);
             Property(s => s.Start);
             Property(s => s.End);
@@ -29,6 +28,9 @@ namespace GreenTree.Nachtragsmanagement.Data.Mapping.Site
 
             HasMany(s => s.Appendices)
                 .WithOptional();
+
+            HasMany(s => s.Deviations)
+                .WithOptional();
         }
     }
 }

+ 0 - 11
GreenTree.Nachtragsmanagement.Services/Deviation/DeviationService.cs

@@ -135,17 +135,6 @@ namespace GreenTree.Nachtragsmanagement.Services.Deviation
                 .ToList();
         }
 
-        /// <summary>
-        /// Gets all deviations to the specified with the specified disturbance id
-        /// </summary>
-        /// <param name="id">Disturbance identifier.</param>
-        public IList<Core.Domain.Deviation.Deviation> GetDeviationsByDisturbance(int id)
-        {
-            return _deviationRepository.Table
-                .Where(d => d.DisturbanceId == id)
-                .ToList();
-        }
-
         /// <summary>
         /// Insert a deviation
         /// </summary>

+ 0 - 6
GreenTree.Nachtragsmanagement.Services/Deviation/IDeviationService.cs

@@ -71,12 +71,6 @@ namespace GreenTree.Nachtragsmanagement.Services.Deviation
         /// </summary>
         IList<Disturbance> GetDisturbancesByIds(int[] ids);
 
-        /// <summary>
-        /// Gets all deviations to the specified with the specified disturbance id
-        /// </summary>
-        /// <param name="id">Disturbance identifier.</param>
-        IList<Core.Domain.Deviation.Deviation> GetDeviationsByDisturbance(int id);
-
         /// <summary>
         /// Insert a disturbance
         /// </summary>

+ 86 - 1
GreenTree.Nachtragsmanagement.Web/App_Start/FunctionConfig.cs

@@ -145,7 +145,7 @@ namespace GreenTree.Nachtragsmanagement.Web.App_Start
                 new Function
                 {
                     Name = "Deviation-Deviations",
-                    Description = "Vertragsabweichungsverwaltung",
+                    Description = "Vertragsabweichungsliste",
                     ImageUrl = "~/Content/Images/function-Deviation-Deviations-32.png",
                     GroupName = "Deviation",
                     RouteName = "GreenTree.Nachtragsmanagement.Web.Deviation.Deviations",
@@ -188,6 +188,91 @@ namespace GreenTree.Nachtragsmanagement.Web.App_Start
                     IsMenuMember = false,
                     Plugin = "System"
                 },
+                new Function
+                {
+                    Name = "Appendix",
+                    Description = "Nachträge",
+                    ImageUrl = "~/Content/Images/function-Appendix-32.png",
+                    IsMenuMember = true,
+                    Plugin = "System"
+                },
+                new Function
+                {
+                    Name = "Appendix-Appendices",
+                    Description = "Nachtragsliste",
+                    ImageUrl = "~/Content/Images/function-Appendix-Appendices-32.png",
+                    GroupName = "Appendix",
+                    RouteName = "GreenTree.Nachtragsmanagement.Web.Appendix.Appendices",
+                    IsMenuMember = true,
+                    Plugin = "System",
+                    BaseWidth = 1100,
+                    MinWidth = 800,
+                    BaseHeight = 650,
+                    MinHeight = 500,
+                    AllowMaximize = true
+                },
+                new Function
+                {
+                    Name = "Appendix-Appendices-Edit",
+                    Description = "Nachträge editieren",
+                    GroupName = "Appendix-Appendices",
+                    IsMenuMember = false,
+                    Plugin = "System"
+                },
+                new Function
+                {
+                    Name = "Appendix-Categories",
+                    Description = "Kategorien",
+                    ImageUrl = "~/Content/Images/function-Appendix-Categories-32.png",
+                    GroupName = "Appendix",
+                    RouteName = "GreenTree.Nachtragsmanagement.Web.Appendix.Categories",
+                    IsMenuMember = true,
+                    Plugin = "System",
+                    BaseWidth = 500,
+                    MinWidth = 400,
+                    BaseHeight = 350,
+                    MinHeight = 300,
+                    AllowMaximize = true
+                },
+                new Function
+                {
+                    Name = "Appendix-Categories-Edit",
+                    Description = "Kategorien editieren",
+                    GroupName = "Appendix-Categories",
+                    IsMenuMember = false,
+                    Plugin = "System"
+                },
+                new Function
+                {
+                    Name = "Site",
+                    Description = "Baustellen",
+                    ImageUrl = "~/Content/Images/function-Site-32.png",
+                    IsMenuMember = true,
+                    Plugin = "System"
+                },
+                new Function
+                {
+                    Name = "Site-Sites",
+                    Description = "Baustellenliste",
+                    ImageUrl = "~/Content/Images/function-Site-Sites-32.png",
+                    GroupName = "Site",
+                    RouteName = "GreenTree.Nachtragsmanagement.Web.Site.Sites",
+                    IsMenuMember = true,
+                    Plugin = "System",
+                    BaseWidth = 1100,
+                    MinWidth = 800,
+                    BaseHeight = 650,
+                    MinHeight = 500,
+                    AllowMaximize = true
+                },
+                new Function
+                {
+                    Name = "Site-Sites-Edit",
+                    Description = "Baustellen editieren",
+                    GroupName = "Site-Sites",
+                    IsMenuMember = false,
+                    Plugin = "System"
+                },
             };
         }
     }

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

@@ -89,6 +89,48 @@ namespace GreenTree.Nachtragsmanagement.Web
                    "GreenTree.Nachtragsmanagement.Web.Controllers"
                }
             );
+
+            routes.MapRoute(
+                "GreenTree.Nachtragsmanagement.Web.Appendix.Appendices",
+                "appendix/viewappendices",
+               new
+               {
+                   controller = "Appendix",
+                   action = "ViewAppendices"
+               },
+               new[]
+               {
+                   "GreenTree.Nachtragsmanagement.Web.Controllers"
+               }
+            );
+
+            routes.MapRoute(
+                "GreenTree.Nachtragsmanagement.Web.Appendix.Categories",
+                "appendix/viewcategories",
+               new
+               {
+                   controller = "Appendix",
+                   action = "ViewCategories"
+               },
+               new[]
+               {
+                   "GreenTree.Nachtragsmanagement.Web.Controllers"
+               }
+            );
+
+            routes.MapRoute(
+                "GreenTree.Nachtragsmanagement.Web.Site.Sites",
+                "site/viewsites",
+               new
+               {
+                   controller = "Site",
+                   action = "ViewSites"
+               },
+               new[]
+               {
+                   "GreenTree.Nachtragsmanagement.Web.Controllers"
+               }
+            );
         }
     }
 }

BIN
GreenTree.Nachtragsmanagement.Web/Content/Images/function-Appendix-Appendices-32-contrast.png


BIN
GreenTree.Nachtragsmanagement.Web/Content/Images/function-Appendix-Appendices-32.png


BIN
GreenTree.Nachtragsmanagement.Web/Content/Images/function-Appendix-Categories-32-contrast.png


BIN
GreenTree.Nachtragsmanagement.Web/Content/Images/function-Appendix-Categories-32.png


BIN
GreenTree.Nachtragsmanagement.Web/Content/Images/function-Site-Sites-32-contrast.png


BIN
GreenTree.Nachtragsmanagement.Web/Content/Images/function-Site-Sites-32.png


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

@@ -0,0 +1,271 @@
+using GreenTree.Nachtragsmanagement.Services.Appendix;
+using GreenTree.Nachtragsmanagement.Services.Deviation;
+using GreenTree.Nachtragsmanagement.Web.Framework.Authorization;
+using GreenTree.Nachtragsmanagement.Web.Models.Appendix;
+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 AppendixController : Controller
+    {
+        private readonly IDeviationService _deviationSerivce;
+        private readonly IAppendixService _appendixService;
+
+        public AppendixController(
+            IDeviationService deviationService,
+            IAppendixService appendixService)
+        {
+            _deviationSerivce = deviationService;
+            _appendixService = appendixService;
+
+            ViewData["AllDeviations"] = _deviationSerivce.GetAllDeviations();
+            ViewData["AllCategories"] = _appendixService.GetAllCategories();
+        }
+
+        #region Appendices
+
+        /// <summary>
+        /// Basic appendix view function
+        /// </summary>
+        [FunctionAuthorize(true, "Appendix-Appendices")]
+        public ActionResult ViewAppendices()
+        {
+            var appendices = _appendixService.GetAllAppendices();
+            var appendixModels = appendices
+                .Select(u => AppendixDataModel.FromAppendix(u, false))
+                .ToList();
+
+            return View("~/Views/Appendices/View.cshtml", appendixModels);
+        }
+
+        /// <summary>
+        /// Get JSON data of specific appendix
+        /// </summary>
+        /// <param name="id">Appendix id.</param>
+        public ActionResult GetAppendix(int id = -1)
+        {
+            var appendix = _appendixService.GetAppendixById(id);
+            if (appendix == null)
+                return new JsonResult
+                {
+                    Data = "notFound",
+                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
+                };
+
+            var appendixModel = AppendixDataModel.FromAppendix(appendix, false);
+
+            return new JsonResult
+            {
+                Data = JsonConvert.SerializeObject(appendixModel),
+                JsonRequestBehavior = JsonRequestBehavior.AllowGet
+            };
+        }
+
+        /// <summary>
+        /// Callback result for appendix grid
+        /// </summary>
+        public ActionResult PartialAppendices()
+        {
+            var appendices = _appendixService.GetAllAppendices();
+            var appendixModels = appendices
+                .Select(u => AppendixDataModel.FromAppendix(u, false))
+                .ToList();
+
+            return PartialView("~/Views/Appendices/_AppendixGridPartial.cshtml", appendixModels);
+        }
+
+        /// <summary>
+        /// Partial edit for editing of existing or for new appendix
+        /// </summary>
+        /// <param name="id">Id for existing appendix, otherweise -1.</param>
+        public ActionResult EditAppendix(int id = -1)
+        {
+            var appendix = _appendixService.GetAppendixById(id);
+            var appendixModel = AppendixDataModel.FromAppendix(appendix, true);
+
+            return PartialView("~/Views/Appendices/_AppendixEditPartial.cshtml", appendixModel);
+        }
+
+        /// <summary>
+        /// Partial edit result if ModelState is valid, otherwise simple JSON result for success
+        /// </summary>
+        /// <param name="appendixModel">Appendix model to be saved.</param>
+        [HttpPost, ValidateInput(false)]
+        public ActionResult EditAppendix(AppendixDataModel appendixModel)
+        {
+            if (!ModelState.IsValid)
+                return PartialView("~/Views/Appendices/_AppendixEditPartial.cshtml", appendixModel);
+
+            if (appendixModel.Id == -1)
+            {
+                var appendix = appendixModel.ToAppendix();
+
+                _appendixService.InsertAppendix(appendix);
+            }
+            else
+            {
+                var appendix = _appendixService.GetAppendixById(appendixModel.Id);
+
+                appendix.CustomNumber = appendixModel.CustomNumber;
+                appendix.Lot = appendixModel.Lot;
+                appendix.Probability = appendixModel.Probability;
+                appendix.OfferingNumber = appendixModel.OfferingNumber;
+                appendix.OfferingDate = appendixModel.OfferingDate;
+                appendix.NegotiationDate = appendixModel.NegotiationDate;
+                appendix.NegotiationValue = appendixModel.NegotiationValue;
+                appendix.ProtocolExists = appendixModel.ProtocolExists;
+                appendix.OrderNumber = appendixModel.OrderNumber;
+                appendix.OrderDate = appendixModel.OrderDate;
+                appendix.Comment = appendixModel.Comment;
+                appendix.SiteId = appendixModel.SiteId;
+
+                _appendixService.UpdateAppendix(appendix);
+            }
+
+            return new JsonResult
+            {
+                Data = "success"
+            };
+        }
+
+        /// <summary>
+        /// Simple JSON result for deleting a specific appendix
+        /// </summary>
+        /// <param name="id">Appendix id.</param>
+        [HttpPost]
+        public ActionResult DeleteAppendix(int id)
+        {
+            var appendix = _appendixService.GetAppendixById(id);
+
+            if (appendix != null)
+                _appendixService.DeleteAppendix(appendix);
+
+            return new JsonResult
+            {
+                Data = "success"
+            };
+        }
+
+        #endregion
+
+        #region Categories
+
+        /// <summary>
+        /// Basic category view function
+        /// </summary>
+        [FunctionAuthorize(true, "Appendix-Categories")]
+        public ActionResult ViewCategories()
+        {
+            var categories = _appendixService.GetAllCategories();
+            var categoryModels = categories
+                .Select(u => CategoryDataModel.FromCategory(u, false))
+                .ToList();
+
+            return View("~/Views/Appendix/Categories.cshtml", categoryModels);
+        }
+
+        /// <summary>
+        /// Get JSON data of specific category
+        /// </summary>
+        /// <param name="id">Category id.</param>
+        public ActionResult GetCategory(int id = -1)
+        {
+            var category = _appendixService.GetCategoryById(id);
+            if (category == null)
+                return new JsonResult
+                {
+                    Data = "notFound",
+                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
+                };
+
+            var categoryModel = CategoryDataModel.FromCategory(category, false);
+
+            return new JsonResult
+            {
+                Data = JsonConvert.SerializeObject(categoryModel),
+                JsonRequestBehavior = JsonRequestBehavior.AllowGet
+            };
+        }
+
+        /// <summary>
+        /// Callback result for category grid
+        /// </summary>
+        public ActionResult PartialCategories()
+        {
+            var categories = _appendixService.GetAllCategories();
+            var categoryModels = categories
+                .Select(u => CategoryDataModel.FromCategory(u, false))
+                .ToList();
+
+            return PartialView("~/Views/Admin/Categories/_CategoryListPartial.cshtml", categoryModels);
+        }
+
+        /// <summary>
+        /// Partial edit for editing of existing or for new category
+        /// </summary>
+        /// <param name="id">Id for existing category, otherweise -1.</param>
+        public ActionResult EditCategory(int id = -1)
+        {
+            var category = _appendixService.GetCategoryById(id);
+            var categoryModel = CategoryDataModel.FromCategory(category, true);
+
+            return PartialView("~/Views/Admin/Categories/_CategoryEditPartial.cshtml", categoryModel);
+        }
+
+        /// <summary>
+        /// Partial edit result if ModelState is valid, otherwise simple JSON result for success
+        /// </summary>
+        /// <param name="categoryModel">Category model to be saved.</param>
+        [HttpPost, ValidateInput(false)]
+        public ActionResult EditCategory(CategoryDataModel categoryModel)
+        {
+            if (!ModelState.IsValid)
+                return PartialView("~/Views/Deviations/_CategoryEditPartial.cshtml", categoryModel);
+
+            if (categoryModel.Id == -1)
+            {
+                var claim = categoryModel.ToCategory();
+
+                _appendixService.InsertCategory(claim);
+            }
+            else
+            {
+                var disturbance = _appendixService.GetCategoryById(categoryModel.Id);
+
+                disturbance.Description = categoryModel.Description;
+
+                _appendixService.UpdateCategory(disturbance);
+            }
+
+            return new JsonResult
+            {
+                Data = "success"
+            };
+        }
+
+        /// <summary>
+        /// Simple JSON result for deleting a specific category
+        /// </summary>
+        /// <param name="id">Category id.</param>
+        [HttpPost]
+        public ActionResult DeleteCategory(int id)
+        {
+            var category = _appendixService.GetCategoryById(id);
+
+            if (category != null)
+                _appendixService.DeleteCategory(category);
+
+            return new JsonResult
+            {
+                Data = "success"
+            };
+        }
+
+        #endregion
+    }
+}

+ 14 - 18
GreenTree.Nachtragsmanagement.Web/Controllers/DeviationController.cs

@@ -103,12 +103,23 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         public ActionResult EditDeviation(DeviationDataModel deviationModel)
         {
             if (!ModelState.IsValid)
+            {
+                foreach (var disturbance in deviationModel.DisturbanceValues)
+                    deviationModel.DisturbanceDescriptions.Add(
+                        ((IList<Disturbance>)ViewData["AllDisturbances"])
+                            .First(r => r.Id == disturbance).Description);
+
                 return PartialView("~/Views/Deviations/_DeviationEditPartial.cshtml", deviationModel);
+            }
+
+            var selectedDisturbances = _deviationService.GetDisturbancesByIds(deviationModel.DisturbanceValues.ToArray());
 
             if (deviationModel.Id == -1)
             {
                 var deviation = deviationModel.ToDeviation();
 
+                deviation.SetDisturbances(selectedDisturbances);
+
                 _deviationService.InsertDeviation(deviation);
             }
             else
@@ -117,13 +128,15 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
 
                 deviation.CustomNumber = deviationModel.CustomNumber;
                 deviation.ReceiptDate = deviationModel.ReceiptDate;
+                deviation.AppendixDate = deviationModel.AppendixDate;
                 deviation.Value = deviationModel.Value;
                 deviation.AppendixId = deviationModel.AppendixId;
                 deviation.StatusId = deviationModel.StatusId;
-                deviation.DisturbanceId = deviationModel.DisturbanceId;
                 deviation.KindId = deviationModel.KindId;
                 deviation.Comment = deviationModel.Comment;
 
+                deviation.SetDisturbances(selectedDisturbances);
+
                 _deviationService.UpdateDeviation(deviation);
             }
 
@@ -376,23 +389,6 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
                     if (status != null)
                         _deviationService.DeleteStatus(status);
                     break;
-                case "disturbance":
-                    var disturbance = _deviationService.GetDisturbanceById(id);
-                    var replaceDisturbance = _deviationService.GetDisturbanceById(replaceId);
-
-                    var disturbanceDeviations = _deviationService.GetDeviationsByDisturbance(id);
-
-                    foreach (var deviation in disturbanceDeviations)
-                    {
-                        deviation.DisturbanceId = replaceId;
-                        deviation.Disturbance = replaceDisturbance;
-
-                        _deviationService.UpdateDeviation(deviation);
-                    }
-
-                    if (disturbance != null)
-                        _deviationService.DeleteDisturbance(disturbance);
-                    break;
                 case "kind":
                     var kind = _deviationService.GetKindById(id);
                     var replaceKind = _deviationService.GetKindById(replaceId);

+ 186 - 0
GreenTree.Nachtragsmanagement.Web/Controllers/SiteController.cs

@@ -0,0 +1,186 @@
+using GreenTree.Nachtragsmanagement.Core.Domain.Deviation;
+using GreenTree.Nachtragsmanagement.Services.Appendix;
+using GreenTree.Nachtragsmanagement.Services.Deviation;
+using GreenTree.Nachtragsmanagement.Services.Site;
+using GreenTree.Nachtragsmanagement.Services.User;
+using GreenTree.Nachtragsmanagement.Web.Framework.Authorization;
+using GreenTree.Nachtragsmanagement.Web.Models.Site;
+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 SiteController : Controller
+    {
+        private readonly ISiteService _siteService;
+        private readonly IDeviationService _deviationService;
+        private readonly IAppendixService _appendixService;
+        private readonly IUserService _userService;
+
+        public SiteController(
+            ISiteService siteService,
+            IDeviationService deviationService,
+            IAppendixService appendixService,
+            IUserService userService)
+        {
+            _siteService = siteService;
+            _deviationService = deviationService;
+            _appendixService = appendixService;
+            _userService = userService;
+
+            ViewData["AllSite"] = _siteService.GetAllSites();
+        }
+
+        #region Sites
+
+        /// <summary>
+        /// Basic site view function
+        /// </summary>
+        [FunctionAuthorize(true, "Site-Sites")]
+        public ActionResult ViewSites()
+        {
+            var sites = _siteService.GetAllSites();
+            var siteModels = sites
+                .Select(u => SiteDataModel.FromSite(u, false))
+                .ToList();
+
+            return View("~/Views/Sites/View.cshtml", siteModels);
+        }
+
+        /// <summary>
+        /// Get JSON data of specific site
+        /// </summary>
+        /// <param name="id">Site id.</param>
+        public ActionResult GetSite(int id = -1)
+        {
+            var site = _siteService.GetSiteById(id);
+            if (site == null)
+                return new JsonResult
+                {
+                    Data = "notFound",
+                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
+                };
+
+            var siteModel = SiteDataModel.FromSite(site, false);
+
+            return new JsonResult
+            {
+                Data = JsonConvert.SerializeObject(siteModel),
+                JsonRequestBehavior = JsonRequestBehavior.AllowGet
+            };
+        }
+
+        /// <summary>
+        /// Callback result for site grid
+        /// </summary>
+        public ActionResult PartialSites()
+        {
+            var sites = _siteService.GetAllSites();
+            var siteModels = sites
+                .Select(u => SiteDataModel.FromSite(u, false))
+                .ToList();
+
+            return PartialView("~/Views/Sites/_SiteGridPartial.cshtml", siteModels);
+        }
+
+        /// <summary>
+        /// Partial edit for editing of existing or for new site
+        /// </summary>
+        /// <param name="id">Id for existing site, otherweise -1.</param>
+        public ActionResult EditSite(int id = -1)
+        {
+            var site = _siteService.GetSiteById(id);
+            var siteModel = SiteDataModel.FromSite(site, true);
+
+            return PartialView("~/Views/Sites/_SiteEditPartial.cshtml", siteModel);
+        }
+
+        /// <summary>
+        /// Partial edit result if ModelState is valid, otherwise simple JSON result for success
+        /// </summary>
+        /// <param name="siteModel">Site model to be saved.</param>
+        [HttpPost, ValidateInput(false)]
+        public ActionResult EditSite(SiteDataModel siteModel)
+        {
+            if (!ModelState.IsValid)
+            {
+                //foreach (var deviation in siteModel.DeviationValues)
+                //    siteModel.DeviationDescriptions.Add(
+                //        ((IList<Deviation>)ViewData["AllDeviations"])
+                //            .First(r => r.Id == deviation).Description);
+
+                //foreach (var deviation in siteModel.DeviationValues)
+                //    siteModel.DeviationDescriptions.Add(
+                //        ((IList<Deviation>)ViewData["AllDeviations"])
+                //            .First(r => r.Id == deviation).Description);
+
+                //foreach (var deviation in siteModel.DeviationValues)
+                //    siteModel.DeviationDescriptions.Add(
+                //        ((IList<Deviation>)ViewData["AllDeviations"])
+                //            .First(r => r.Id == deviation).Description);
+
+                return PartialView("~/Views/Sites/_SiteEditPartial.cshtml", siteModel);
+            }
+
+            var selectedDeviations = _deviationService.GetDeviationsByIds(siteModel.DeviationValues.ToArray());
+            var selectedAppendices = _appendixService.GetAppendicesByIds(siteModel.AppendixValues.ToArray());
+            var selectedUsers = _userService.GetUsersByIds(siteModel.UserValues.ToArray());
+
+            if (siteModel.Id == -1)
+            {
+                var site = siteModel.ToSite();
+
+                site.SetDeviations(selectedDeviations);
+                site.SetAppendices(selectedAppendices);
+                site.SetUsers(selectedUsers);
+
+                _siteService.InsertSite(site);
+            }
+            else
+            {
+                var site = _siteService.GetSiteById(siteModel.Id);
+
+                site.CustomNumber = siteModel.CustomNumber;
+                site.Description = siteModel.Description;
+                site.Start = siteModel.Start;
+                site.End = siteModel.End;
+                site.Comment = siteModel.Comment;
+
+                site.SetDeviations(selectedDeviations);
+                site.SetAppendices(selectedAppendices);
+                site.SetUsers(selectedUsers);
+
+                _siteService.UpdateSite(site);
+            }
+
+            return new JsonResult
+            {
+                Data = "success"
+            };
+        }
+
+        /// <summary>
+        /// Simple JSON result for deleting a specific site
+        /// </summary>
+        /// <param name="id">Site id.</param>
+        [HttpPost]
+        public ActionResult DeleteSite(int id)
+        {
+            var site = _siteService.GetSiteById(id);
+
+            if (site != null)
+                _siteService.DeleteSite(site);
+
+            return new JsonResult
+            {
+                Data = "success"
+            };
+        }
+
+        #endregion
+    }
+}

+ 167 - 6
GreenTree.Nachtragsmanagement.Web/Global.asax.cs

@@ -16,6 +16,11 @@ using System.Web.Optimization;
 using FluentValidation.Mvc;
 using GreenTree.Nachtragsmanagement.Web.Validation;
 using GreenTree.Nachtragsmanagement.Services.User;
+using GreenTree.Nachtragsmanagement.Data;
+using GreenTree.Nachtragsmanagement.Core.Domain.Misc;
+using GreenTree.Nachtragsmanagement.Core.Domain.User;
+using GreenTree.Nachtragsmanagement.Core.Domain.Deviation;
+using GreenTree.Nachtragsmanagement.Services.Deviation;
 
 namespace GreenTree.Nachtragsmanagement.Web
 {
@@ -46,13 +51,9 @@ namespace GreenTree.Nachtragsmanagement.Web
                 provider.ValidatorFactory = new AppendixValidatorFactory();
             });
 
-            var userService = Singleton<IContainer>.Instance.Resolve<IUserService>();
-            var allFunctions = userService.GetAllFunctions();
-            var adminRole = userService.GetRoleById(1);
-            adminRole.SetFunctions(allFunctions);
-            userService.UpdateRole(adminRole);
+            DevExpress.Web.ASPxWebControl.CallbackError += Application_Error;
 
-            DevExpress.Web.ASPxWebControl.CallbackError += Application_Error;   
+            GenerateTestData();
         }
 
         protected void Application_Error(object sender, EventArgs e) 
@@ -60,5 +61,165 @@ namespace GreenTree.Nachtragsmanagement.Web
             Exception exception = System.Web.HttpContext.Current.Server.GetLastError();
             //TODO: Handle Exception
         }
+
+        #region Test
+
+        private void GenerateTestData()
+        {
+            // Get services
+
+            var dbContext = Singleton<IContainer>.Instance.Resolve<IDbContext>();
+            var userService = Singleton<IContainer>.Instance.Resolve<IUserService>();
+            var deviationService = Singleton<IContainer>.Instance.Resolve<IDeviationService>();
+
+            // Check if test data is already created
+
+            var isTestDataGeneratedEntity = dbContext.Get<DbContextSpec>().FirstOrDefault(d => d.Name == "IsTestDataGenerated");
+
+            if (isTestDataGeneratedEntity != null && Convert.ToBoolean(isTestDataGeneratedEntity.Value))
+                return;
+
+            // Create base data
+
+            var kinds = new []
+            {
+                new Kind { Shortance = "MK", Description = "Mehrkosten" },
+                new Kind { Shortance = "BH", Description = "Behinderung" },
+                new Kind { Shortance = "BD", Description = "Bedenken" }
+            };
+
+            foreach (var kind in kinds)
+                deviationService.InsertKind(kind);
+
+            var disturbances = new[]
+            {
+                new Disturbance { Description = "RPM" },
+                new Disturbance { Description = "RM" },
+                new Disturbance { Description = "Stopftechnik" },
+                new Disturbance { Description = "Umbauzug" },
+                new Disturbance { Description = "Logistik" },
+                new Disturbance { Description = "Oberbau" },
+                new Disturbance { Description = "Erdbau" },
+                new Disturbance { Description = "Kabeltiefbau" },
+                new Disturbance { Description = "Entwässerung" },
+                new Disturbance { Description = "Sonstiges" }
+            };
+
+            foreach (var disturbance in disturbances)
+                deviationService.InsertDisturbance(disturbance);
+
+            var statuses = new []
+            {
+                new Status { Description = "Standard" },
+                new Status { Description = "Entfällt" },
+                new Status { Description = "Strittig" },
+                new Status { Description = "Abr. über HLV" }
+            };
+
+            foreach (var status in statuses)
+                deviationService.InsertStatus(status);
+
+            // Create roles
+
+            var r1 = new Role
+            {
+                Description = "Administrator",
+                Level = 100
+            };
+            userService.InsertRole(r1);
+
+            var r2 = new Role
+            {
+                Description = "Kaufmann",
+                Level = 10
+            };
+            userService.InsertRole(r2);
+
+            var r3 = new Role
+            {
+                Description = "Nachtragsmanager",
+                Level = 10
+            };
+            userService.InsertRole(r3);
+
+            // Create users
+
+            var u1 = new User
+            {
+                Forename = "Arne",
+                Lastname = "Diekmann",
+                CustomNumber = "a.diekmann",
+                MailAddress = "a.diekmann@greentreestudios.de",
+                Password = StaticHelper.GetMD5Hash("14595809")
+            };
+            userService.InsertUser(u1);
+
+            var u2 = new User
+            {
+                Forename = "Rocco",
+                Lastname = "Lavella",
+                CustomNumber = "r.lavella",
+                MailAddress = "lavella@schweerbau.de",
+                Password = StaticHelper.GetMD5Hash("lavella")
+            };
+            userService.InsertUser(u2);
+
+            var u3 = new User
+            {
+                Forename = "Kletus",
+                Lastname = "Lingemann",
+                CustomNumber = "k.lingemann",
+                MailAddress = "k.lingemann@schweerbau.de",
+                Password = StaticHelper.GetMD5Hash("lingemann")
+            };
+            userService.InsertUser(u3);
+
+            // Add users to roles
+
+            u1.Roles.Add(r1);
+            u2.Roles.Add(r1);
+            u3.Roles.Add(r3);
+
+            // Get all functions and add them to the admin role
+
+            var allFunctions = userService.GetAllFunctions();
+
+            r1.SetFunctions(allFunctions);
+            userService.UpdateRole(r1);
+
+            // Get all appendix manager function and add them to the apendix manager / merchant role
+
+            var deviationFunctions = allFunctions
+                .Where(f => f.Name.StartsWith("Deviation"));
+
+            foreach (var function in deviationFunctions)
+            {
+                r2.Functions.Add(function);
+                r3.Functions.Add(function);
+            }
+
+            var appendixFunctions = allFunctions
+                .Where(f => f.Name.StartsWith("Appendix"));
+
+            foreach (var function in appendixFunctions)
+            {
+                r2.Functions.Add(function);
+                r3.Functions.Add(function);
+            }
+
+            userService.UpdateRole(r2);
+            userService.UpdateRole(r3);
+
+            // Create DbContecSpecification object
+
+            var db1 = new DbContextSpec
+            {
+                Name = "IsTestDataGenerated",
+                Value = "True"
+            };
+            dbContext.Get<DbContextSpec>().Add(db1);
+        }
+
+        #endregion
     }
 }

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

@@ -158,6 +158,9 @@
     <Content Include="Content\Images\add-32-contrast.png" />
     <Content Include="Content\Images\add-32.png" />
     <Content Include="Content\Images\close-16.png" />
+    <Content Include="Content\Images\function-Appendix-Appendices-32-contrast.png" />
+    <Content Include="Content\Images\function-Appendix-Appendices-32.png" />
+    <Content Include="Content\Images\function-Appendix-Categories-32-contrast.png" />
     <Content Include="Content\Images\function-Deviation-Claims-32-contrast.png" />
     <Content Include="Content\Images\function-Deviation-Claims-32.png" />
     <Content Include="Content\Images\function-Deviation-Deviations-32-contrast.png" />
@@ -174,8 +177,11 @@
     <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\function-Site-Sites-32-contrast.png" />
+    <Content Include="Content\Images\function-Site-Sites-32.png" />
     <Content Include="Content\Images\maximize-16.png" />
     <Content Include="Content\Images\minimize-16.png" />
+    <Content Include="Content\Images\function-Appendix-Categories-32.png" />
     <Content Include="Content\Images\user-32.png" />
     <Content Include="Content\Images\password-32.png" />
     <Content Include="Content\Images\password-24.png" />
@@ -260,6 +266,12 @@
     <Content Include="Views\Shared\DataEditorTemplates\_KindsComboBox.cshtml" />
     <Content Include="Views\Shared\DataEditorTemplates\_DisturbancesComboBox.cshtml" />
     <Content Include="Views\Shared\DataEditorTemplates\_RolesComboBox.cshtml" />
+    <Content Include="Views\Appendices\Categories.cshtml" />
+    <Content Include="Views\Appendices\_CategoryListPartial.cshtml" />
+    <Content Include="Views\Appendices\_CategoryEditPartial.cshtml" />
+    <Content Include="Views\Sites\_SiteEditPartial.cshtml" />
+    <Content Include="Views\Sites\_SiteGridPartial.cshtml" />
+    <Content Include="Views\Sites\View.cshtml" />
     <None Include="Web.Debug.config">
       <DependentUpon>Web.config</DependentUpon>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -276,12 +288,14 @@
     <Compile Include="App_Start\RouteConfig.cs" />
     <Compile Include="App_Start\WebApiConfig.cs" />
     <Compile Include="Controllers\AdminController.cs" />
+    <Compile Include="Controllers\AppendixController.cs" />
     <Compile Include="Controllers\AuthController.cs" />
     <Compile Include="Controllers\DataCallbackController.cs" />
     <Compile Include="Controllers\DeviationController.cs" />
     <Compile Include="Controllers\GlobalController.cs" />
     <Compile Include="Controllers\HomeController.cs" />
     <Compile Include="Controllers\LoginController.cs" />
+    <Compile Include="Controllers\SiteController.cs" />
     <Compile Include="Extensions\HtmlHelper.cs" />
     <Compile Include="Global.asax.cs">
       <DependentUpon>Global.asax</DependentUpon>
@@ -289,7 +303,10 @@
     <Compile Include="Models\Admin\User\IRequireRoleDataModel.cs" />
     <Compile Include="Models\Admin\User\RoleDataModel.cs" />
     <Compile Include="Models\Admin\User\UserDataModel.cs" />
+    <Compile Include="Models\Appendix\AppendixDataModel.cs" />
     <Compile Include="Models\Appendix\IRequireAppendixDataModel.cs" />
+    <Compile Include="Models\Appendix\CategoryDataModel.cs" />
+    <Compile Include="Models\Appendix\IRequireCategoryDataModel.cs" />
     <Compile Include="Models\Deviation\DeviationDataModel.cs" />
     <Compile Include="Models\Deviation\DisturbanceDataModel.cs" />
     <Compile Include="Models\Deviation\IRequireKindDataModel.cs" />
@@ -301,6 +318,7 @@
     <Compile Include="Models\Global\OptionDialogItemModel.cs" />
     <Compile Include="Models\Global\YesNoDialogModel.cs" />
     <Compile Include="Models\Global\OptionDialogModel.cs" />
+    <Compile Include="Models\Site\SiteDataModel.cs" />
     <Compile Include="Validation\Admin\User\RoleDataModelValidator.cs" />
     <Compile Include="Validation\Admin\User\UserDataModelValidator.cs" />
     <Compile Include="Models\Global\FooterModel.cs" />
@@ -313,6 +331,7 @@
     <Compile Include="Validation\Deviation\DisturbanceDataModelValidator.cs" />
     <Compile Include="Validation\Deviation\StatusDataModelValidator.cs" />
     <Compile Include="Validation\Deviation\DeviationDataModelValidator.cs" />
+    <Compile Include="Validation\Site\SiteDataModelValidator.cs" />
   </ItemGroup>
   <ItemGroup>
     <Folder Include="App_Data\" />

+ 10 - 0
GreenTree.Nachtragsmanagement.Web/Models/Admin/User/RoleDataModel.cs

@@ -12,6 +12,16 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Admin.User
         public int Level { get; set; }
         public ICollection<int> FunctionValues { get; set; }
         public ICollection<string> FunctionDescriptions { get; set; }
+        public string FunctionDescription
+        {
+            get
+            {
+                if (FunctionDescriptions == null)
+                    return String.Empty;
+                else
+                    return String.Join(", ", FunctionDescriptions);
+            }
+        }
 
         public RoleDataModel()
         {

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

@@ -19,6 +19,16 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Admin.User
         public string Password { get; set; }
         public ICollection<int> RoleValues { get; set; }
         public ICollection<string> RoleDescriptions { get; set; }
+        public string RoleDescription
+        {
+            get
+            {
+                if (RoleDescriptions == null)
+                    return String.Empty;
+                else
+                    return String.Join(", ", RoleDescriptions);
+            }
+        }
 
         public UserDataModel()
         {

+ 97 - 0
GreenTree.Nachtragsmanagement.Web/Models/Appendix/AppendixDataModel.cs

@@ -0,0 +1,97 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace GreenTree.Nachtragsmanagement.Web.Models.Appendix
+{
+    public class AppendixDataModel
+    {
+        public int Id { get; set; }
+        public string CustomNumber { get; set; }
+        public string Lot { get; set; }
+        public decimal? Probability { get; set; }
+        public int? OfferingNumber { get; set; }
+        public DateTime OfferingDate { get; set; }
+        public DateTime NegotiationDate { get; set; }
+        public decimal? NegotiationValue { get; set; }
+        public bool ProtocolExists { get; set; }
+        public int? OrderNumber { get; set; }
+        public DateTime OrderDate { get; set; }
+        public string Comment { get; set; }
+        public int? SiteId { get; set; }
+        public string SiteDescription { get; set; }
+        public ICollection<int> CategoryValues { get; set; }
+        public ICollection<string> CategoryDescriptions { get; set; }
+        public ICollection<int> DeviationValues { get; set; }
+        public ICollection<string> DeviationDescriptions { get; set; }
+
+        public static AppendixDataModel FromAppendix(Core.Domain.Appendix.Appendix appendixEntity, bool newWhenIsNull)
+        {
+            if (appendixEntity == null && newWhenIsNull)
+                return new AppendixDataModel
+                {
+                    Id = -1
+                };
+
+            if (appendixEntity == null && !newWhenIsNull)
+                throw new ArgumentNullException("appendixEntity", "Cannot create AppendixDataModel from NULL appendix entity.");
+
+            return new AppendixDataModel
+            {
+                Id = appendixEntity.Id,
+                CustomNumber = appendixEntity.CustomNumber,
+                Lot = appendixEntity.Lot,
+                Probability = appendixEntity.Probability,
+                OfferingNumber = appendixEntity.OfferingNumber,
+                OfferingDate = appendixEntity.OfferingDate,
+                NegotiationDate = appendixEntity.NegotiationDate,
+                NegotiationValue = appendixEntity.NegotiationValue,
+                ProtocolExists = appendixEntity.ProtocolExists,
+                OrderNumber = appendixEntity.OrderNumber,
+                OrderDate = appendixEntity.OrderDate,
+                Comment = appendixEntity.Comment,
+                SiteId = appendixEntity.SiteId,
+                SiteDescription = appendixEntity.Site == null
+                    ? null
+                    : appendixEntity.Site.Description,
+                CategoryValues =
+                    appendixEntity.Categories
+                        .Select(r => r.Id)
+                        .ToList(),
+                CategoryDescriptions =
+                    appendixEntity.Categories
+                        .Select(r => r.Description)
+                        .ToList(),
+                DeviationValues =
+                    appendixEntity.Deviations
+                        .Select(r => r.Id)
+                        .ToList(),
+                DeviationDescriptions =
+                    appendixEntity.Deviations
+                        .Select(r => r.CustomNumber)
+                        .ToList()
+            };
+        }
+
+        public Core.Domain.Appendix.Appendix ToAppendix()
+        {
+            return new Core.Domain.Appendix.Appendix
+            {
+                Id = this.Id,
+                CustomNumber = this.CustomNumber,
+                Lot = this.Lot,
+                Probability = this.Probability,
+                OfferingNumber = this.OfferingNumber,
+                OfferingDate = this.OfferingDate,
+                NegotiationDate = this.NegotiationDate,
+                NegotiationValue = this.NegotiationValue,
+                ProtocolExists = this.ProtocolExists,
+                OrderNumber = this.OrderNumber,
+                OrderDate = this.OrderDate,
+                Comment = this.Comment,
+                SiteId = this.SiteId
+            };
+        }
+    }
+}

+ 40 - 0
GreenTree.Nachtragsmanagement.Web/Models/Appendix/CategoryDataModel.cs

@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace GreenTree.Nachtragsmanagement.Web.Models.Appendix
+{
+    public class CategoryDataModel
+    {
+        public int Id { get; set; }
+        public string Description { get; set; }
+
+        public static CategoryDataModel FromCategory(Core.Domain.Appendix.Category categoryEntity, bool newWhenIsNull)
+        {
+            if (categoryEntity == null && newWhenIsNull)
+                return new CategoryDataModel
+                {
+                    Id = -1
+                };
+
+            if (categoryEntity == null && !newWhenIsNull)
+                throw new ArgumentNullException("categoryEntity", "Cannot create CategoryDataModel from NULL category entity.");
+
+            return new CategoryDataModel
+            {
+                Id = categoryEntity.Id,
+                Description = categoryEntity.Description
+            };
+        }
+
+        public Core.Domain.Appendix.Category ToCategory()
+        {
+            return new Core.Domain.Appendix.Category
+            {
+                Id = this.Id,
+                Description = this.Description
+            };
+        }
+    }
+}

+ 13 - 0
GreenTree.Nachtragsmanagement.Web/Models/Appendix/IRequireCategoryDataModel.cs

@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GreenTree.Nachtragsmanagement.Web.Models.Appendix
+{
+    public interface IRequireCategoryDataModel
+    {
+        int? CategoryId { get; set; }
+    }
+}

+ 47 - 9
GreenTree.Nachtragsmanagement.Web/Models/Deviation/DeviationDataModel.cs

@@ -11,24 +11,47 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Deviation
     {
         public int Id { get; set; }
         public string CustomNumber { get; set; }
+        public string Description { get; set; }
         public DateTime ReceiptDate { get; set; }
+        public DateTime? AppendixDate { get; set; }
         public decimal Value { get; set; }
+        public int Percentage { get; set; }
+        public decimal PercentageValue { 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 SiteDescription { get; set; }
+        public int? SiteId { get; set; }
         public string AppendixDescription { get; set; }
         public int? AppendixId { get; set; }
+        public ICollection<int> DisturbanceValues { get; set; }
+        public ICollection<string> DisturbanceDescriptions { get; set; }
+        public string DisturbanceDescription
+        {
+            get
+            {
+                if (DisturbanceDescriptions == null)
+                    return String.Empty;
+                else
+                    return String.Join(", ", DisturbanceDescriptions);
+            }
+        }
+
+        public DeviationDataModel()
+        {
+            DisturbanceValues = new List<int>();
+            DisturbanceDescriptions = new List<string>();
+        }
 
         public static DeviationDataModel FromDeviation(Core.Domain.Deviation.Deviation deviationEntity, bool newWhenIsNull)
         {
             if (deviationEntity == null && newWhenIsNull)
                 return new DeviationDataModel
                 {
-                    Id = -1
+                    Id = -1,
+                    Percentage = 100
                 };
 
             if (deviationEntity == null && !newWhenIsNull)
@@ -38,8 +61,16 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Deviation
             {
                 Id = deviationEntity.Id,
                 CustomNumber = deviationEntity.CustomNumber,
+                Description = deviationEntity.Description,
                 ReceiptDate = deviationEntity.ReceiptDate,
+                AppendixDate = deviationEntity.AppendixDate,
                 Value = deviationEntity.Value.Value,
+                Percentage = deviationEntity.Percentage.Value,
+                PercentageValue = deviationEntity.Value.Value * (deviationEntity.Percentage.Value / 100),
+                SiteId = deviationEntity.SiteId,
+                SiteDescription = deviationEntity.Site == null
+                    ? null
+                    : deviationEntity.Site.CustomNumber,
                 AppendixId = deviationEntity.AppendixId,
                 AppendixDescription = deviationEntity.Appendix == null
                     ? null
@@ -48,15 +79,19 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Deviation
                 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
+                Comment = deviationEntity.Comment,
+                DisturbanceValues =
+                    deviationEntity.Disturbances
+                        .Select(r => r.Id)
+                        .ToList(),
+                DisturbanceDescriptions =
+                    deviationEntity.Disturbances
+                        .Select(r => r.Description)
+                        .ToList()
             };
         }
 
@@ -66,11 +101,14 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Deviation
             {
                 Id = this.Id,
                 CustomNumber = this.CustomNumber,
+                Description = Description,
                 ReceiptDate = this.ReceiptDate,
+                AppendixDate = this.AppendixDate,
                 Value = this.Value,
+                Percentage = this.Percentage,
+                SiteId = this.SiteId,
                 AppendixId = this.AppendixId,
                 StatusId = this.StatusId,
-                DisturbanceId = this.DisturbanceId,
                 KindId = this.KindId,
                 Comment = this.Comment
             };

+ 1 - 1
GreenTree.Nachtragsmanagement.Web/Models/Deviation/IRequireDisturbanceDataModel.cs

@@ -8,6 +8,6 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Deviation
 {
     public interface IRequireDisturbanceDataModel
     {
-        int? DisturbanceId { get; set; }
+        ICollection<int> DisturbanceValues { get; set; }
     }
 }

+ 5 - 2
GreenTree.Nachtragsmanagement.Web/Models/Deviation/KindDataModel.cs

@@ -9,6 +9,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Deviation
     {
         public int Id { get; set; }
         public string Description { get; set; }
+        public string Shortance { get; set; }
 
         public static KindDataModel FromKind(Core.Domain.Deviation.Kind kindEntity, bool newWhenIsNull)
         {
@@ -24,7 +25,8 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Deviation
             return new KindDataModel
             {
                 Id = kindEntity.Id,
-                Description = kindEntity.Description
+                Description = kindEntity.Description,
+                Shortance = kindEntity.Shortance
             };
         }
 
@@ -33,7 +35,8 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Deviation
             return new Core.Domain.Deviation.Kind
             {
                 Id = this.Id,
-                Description = this.Description
+                Description = this.Description,
+                Shortance = this.Shortance
             };
         }
     }

+ 1 - 1
GreenTree.Nachtragsmanagement.Web/Models/Global/EmptyIRequireDataModel.cs

@@ -13,7 +13,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Global
     {
         public int? RoleId { get; set; }
         public int? StatusId { get; set; }
-        public int? DisturbanceId { get; set; }
+        public ICollection<int> DisturbanceValues { get; set; }
         public int? KindId { get; set; }
         public int? AppendixId { get; set; }
 

+ 127 - 0
GreenTree.Nachtragsmanagement.Web/Models/Site/SiteDataModel.cs

@@ -0,0 +1,127 @@
+using GreenTree.Nachtragsmanagement.Web.Models.Appendix;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace GreenTree.Nachtragsmanagement.Web.Models.Site
+{
+    public class SiteDataModel
+    {
+        public int Id { get; set; }
+        public string CustomNumber { get; set; }
+        public string Description { get; set; }
+        public DateTime? Start { get; set; }
+        public DateTime? End { get; set; }
+        public string Comment { get; set; }
+        public ICollection<int> DeviationValues { get; set; }
+        public ICollection<string> DeviationDescriptions { get; set; }
+        public string DeviationDescription
+        {
+            get
+            {
+                if (DeviationDescriptions == null)
+                    return String.Empty;
+                else
+                    return String.Join(", ", DeviationDescriptions);
+            }
+        }
+        public decimal? DeviationValue { get; set; }
+
+        public ICollection<int> AppendixValues { get; set; }
+        public ICollection<string> AppendixDescriptions { get; set; }
+        public string AppendixDescription
+        {
+            get
+            {
+                if (AppendixDescriptions == null)
+                    return String.Empty;
+                else
+                    return String.Join(", ", AppendixDescriptions);
+            }
+        }
+        public ICollection<int> UserValues { get; set; }
+        public ICollection<string> UserDescriptions { get; set; }
+        public string UserDescription
+        {
+            get
+            {
+                if (UserDescriptions == null)
+                    return String.Empty;
+                else
+                    return String.Join(", ", UserDescriptions);
+            }
+        }
+
+        public SiteDataModel()
+        {
+            DeviationValues = new List<int>();
+            DeviationDescriptions = new List<string>();
+
+            AppendixValues = new List<int>();
+            AppendixDescriptions = new List<string>();
+
+            UserValues = new List<int>();
+            UserDescriptions = new List<string>();
+        }
+
+        public static SiteDataModel FromSite(Core.Domain.Site.Site siteEntity, bool newWhenIsNull)
+        {
+            if (siteEntity == null && newWhenIsNull)
+                return new SiteDataModel
+                {
+                    Id = -1,
+                };
+
+            if (siteEntity == null && !newWhenIsNull)
+                throw new ArgumentNullException("siteEntity", "Cannot create SiteDataModel from NULL site entity.");
+
+            return new SiteDataModel
+            {
+                Id = siteEntity.Id,
+                CustomNumber = siteEntity.CustomNumber,
+                Description = siteEntity.Description,
+                Start = siteEntity.Start,
+                End = siteEntity.End,
+                DeviationValues =
+                    siteEntity.Deviations
+                        .Select(r => r.Id)
+                        .ToList(),
+                DeviationDescriptions =
+                    siteEntity.Deviations
+                        .Select(r => r.Description)
+                        .ToList(),
+                DeviationValue =
+                    siteEntity.Deviations
+                        .Sum(r => r.Value * (r.Percentage.Value / 100)),
+                AppendixValues =
+                    siteEntity.Appendices
+                        .Select(r => r.Id)
+                        .ToList(),
+                AppendixDescriptions =
+                    siteEntity.Appendices
+                        .Select(r => r.CustomNumber)
+                        .ToList(),
+                UserValues =
+                    siteEntity.Appendices
+                        .Select(r => r.Id)
+                        .ToList(),
+                UserDescriptions =
+                    siteEntity.Users
+                        .Select(r => r.Lastname)
+                        .ToList(),
+            };
+        }
+
+        public Core.Domain.Site.Site ToSite()
+        {
+            return new Core.Domain.Site.Site
+            {
+                Id = this.Id,
+                CustomNumber = this.CustomNumber,
+                Description = Description,
+                Comment = this.Comment
+            };
+        }
+    }
+}

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

@@ -1,6 +1,7 @@
 using FluentValidation;
 using GreenTree.Nachtragsmanagement.Web.Models.Admin.User;
 using GreenTree.Nachtragsmanagement.Web.Models.Deviation;
+using GreenTree.Nachtragsmanagement.Web.Models.Site;
 using GreenTree.Nachtragsmanagement.Web.Validation.Admin.User;
 using System;
 using System.Collections.Generic;
@@ -21,6 +22,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Validation
             validators.Add(typeof(IValidator<StatusDataModel>), new StatusDataModelValidator());
             validators.Add(typeof(IValidator<DisturbanceDataModel>), new DisturbanceDataModelValidator());
             validators.Add(typeof(IValidator<KindDataModel>), new KindDataModelValidator());
+            validators.Add(typeof(IValidator<SiteDataModel>), new SiteDataModelValidator());
         }
 
         public override IValidator CreateInstance(Type validatorType)

+ 16 - 8
GreenTree.Nachtragsmanagement.Web/Validation/Deviation/DeviationDataModelValidator.cs

@@ -13,29 +13,37 @@ namespace GreenTree.Nachtragsmanagement.Web.Validation.Admin.User
         {
             RuleFor(m => m.CustomNumber)
                 .NotEmpty()
-                    .WithMessage("Eigene ID wird benötigt")
+                    .WithMessage("Nummer wird benötigt")
                 .MaximumLength(10)
                     .WithMessage("Muss unter 10 Zeichen sein");
 
-            RuleFor(m => m.ReceiptDate)
+            RuleFor(m => m.Description)
                 .NotEmpty()
-                    .WithMessage("Eingangsdatum wird benötigt");
+                    .WithMessage("Beschreibung wird benötigt");
+
+            //RuleFor(m => m.ReceiptDate)
+            //    .NotEmpty()
+            //        .WithMessage("Einreichdatum wird benötigt");
 
             RuleFor(m => m.Value)
                 .NotEmpty()
-                    .WithMessage("Ein Wert wird benötigt");
+                    .WithMessage("Eine Schätzung wird benötigt");
 
-            RuleFor(m => m.StatusId)
+            RuleFor(m => m.Percentage)
                 .NotEmpty()
-                    .WithMessage("Ein Status muss gewählt werden");
+                    .WithMessage("Eine Bewertung wird benötigt");
 
-            RuleFor(m => m.DisturbanceId)
+            RuleFor(m => m.StatusId)
                 .NotEmpty()
                     .WithMessage("Ein Status muss gewählt werden");
 
+            RuleFor(m => m.DisturbanceValues)
+                .Must(r => r.Count > 0)
+                    .WithMessage("Eine Kategorie muss ausgewählt werden");
+
             RuleFor(m => m.KindId)
                 .NotEmpty()
-                    .WithMessage("Ein Status muss gewählt werden");
+                    .WithMessage("Eine Art muss gewählt werden");
 
             RuleFor(m => m.Comment)
                 .NotEmpty()

+ 6 - 0
GreenTree.Nachtragsmanagement.Web/Validation/Deviation/KindDataModelValidator.cs

@@ -16,6 +16,12 @@ namespace GreenTree.Nachtragsmanagement.Web.Validation.Admin.User
                     .WithMessage("Beschreibung wird benötigt")
                 .MaximumLength(50)
                     .WithMessage("Muss unter 50 Zeichen sein");
+
+            RuleFor(m => m.Shortance)
+                .NotEmpty()
+                    .WithMessage("Abkürzung wird benötigt")
+                .MaximumLength(50)
+                    .WithMessage("Muss unter 8 Zeichen sein");
         }
     }
 }

+ 34 - 0
GreenTree.Nachtragsmanagement.Web/Validation/Site/SiteDataModelValidator.cs

@@ -0,0 +1,34 @@
+using FluentValidation;
+using GreenTree.Nachtragsmanagement.Web.Models.Deviation;
+using GreenTree.Nachtragsmanagement.Web.Models.Site;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace GreenTree.Nachtragsmanagement.Web.Validation.Admin.User
+{
+    public class SiteDataModelValidator : AbstractValidator<SiteDataModel>
+    {
+        public SiteDataModelValidator()
+        {
+            RuleFor(m => m.CustomNumber)
+                .NotEmpty()
+                    .WithMessage("Kostenstelle wird benötigt")
+                .Length(7)
+                    .WithMessage("Muss 7 Zeichen lang sein");
+
+            RuleFor(m => m.Description)
+                .NotEmpty()
+                    .WithMessage("Beschreibung wird benötigt");
+
+            RuleFor(m => m.UserValues)
+                .Must(r => r.Count > 0)
+                    .WithMessage("Mind. ein Bearbeiter muss ausgewählt werden");
+
+            RuleFor(m => m.Comment)
+                .NotEmpty()
+                    .WithMessage("Ein Kommentar wird benötigt");
+        }
+    }
+}

+ 3 - 14
GreenTree.Nachtragsmanagement.Web/Views/Admin/Roles/_RoleGridPartial.cshtml

@@ -10,6 +10,8 @@
 	s.KeyFieldName = "Id";
 	s.CallbackRouteValues = new { Controller = "Admin", Action = "PartialRoles" };
 	s.Width = Unit.Percentage(100);
+	s.Settings.ShowFilterRow = true;
+	s.Settings.ShowFilterRowMenu = true;
 
 	if (userContext.CurrentUser.HasFunction("Administration-Roles-Edit"))
 	{
@@ -35,20 +37,7 @@
 	}
 	s.Columns.Add("Description", "Beschreibung");
 	s.Columns.Add("Level", "Stufe");
-	s.Columns.Add(column =>
-	{
-		column.Caption = "Funktionen";
-
-		column.SetDataItemTemplateContent(r =>
-		{
-			var modelItem = Model
-				.ElementAt(r.ItemIndex).FunctionDescriptions;
-
-			ViewContext.Writer.Write(
-				String.Join(", ", String.Join(", ", modelItem))
-			);
-		});
-	});
+	s.Columns.Add("FunctionDescription", "Funktionen");
 
 	s.ClientLayout = (sender, e) =>
 	{

+ 3 - 14
GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/_UserGridPartial.cshtml

@@ -10,6 +10,8 @@
 	s.KeyFieldName = "Id";
 	s.CallbackRouteValues = new { Controller = "Admin", Action = "PartialUsers" };
 	s.Width = Unit.Percentage(100);
+	s.Settings.ShowFilterRow = true;
+	s.Settings.ShowFilterRowMenu = true;
 
 	if (userContext.CurrentUser.HasFunction("Administration-Users-Edit"))
 	{
@@ -37,20 +39,7 @@
 	s.Columns.Add("Forename", "Vorname");
 	s.Columns.Add("Lastname", "Nachname");
 	s.Columns.Add("MailAddress", "Mail-Adresse");
-	s.Columns.Add(column =>
-	{
-		column.Caption = "Rollen";
-
-		column.SetDataItemTemplateContent(r =>
-		{
-			var modelItem = Model
-				.ElementAt(r.ItemIndex).RoleDescriptions;
-
-			ViewContext.Writer.Write(
-				String.Join(", ", String.Join(", ", modelItem))
-			);
-		});
-	});
+	s.Columns.Add("RoleDescription", "Rollen");
 
 	s.ClientLayout = (sender, e) =>
 	{

+ 71 - 0
GreenTree.Nachtragsmanagement.Web/Views/Appendices/Categories.cshtml

@@ -0,0 +1,71 @@
+@{
+	Layout = "~/Views/Shared/_FunctionLayout.cshtml";
+}
+
+@model IEnumerable<GreenTree.Nachtragsmanagement.Web.Models.Appendix.CategoryDataModel>
+
+<script>
+	var deleteId;
+
+	function editCategory(id) {
+		if (!id) return;
+		$.ajax({
+			url: '@Url.Action("EditCategory", "Appendix")',
+			data: { Id: id },
+			success: function (response) {
+				setTimeout(function () {
+					$(".categoryEditContainer").remove();
+					$("body").append(response);
+				}, 200);
+			},
+			error: function () {
+				 alert("error occured");
+			}
+		});
+	}
+
+	function confirmDelete(id) {
+		if (!id) return;
+		deleteId = id;
+		$.ajax({
+			type: "GET",
+			url: '@Url.Action("GetCategory", "Appendix")',
+			data: { Id: id },
+			success: function (response) {
+				if (response == "notFound") return;
+				var category = JSON.parse(response);
+				var popupControl = MVCxClientPopupControl.Cast(devPopupControlDeleteCategory);
+				popupControl.SetHeaderText(popupControl.GetHeaderText().replace("{category}", category.Description));
+				$(".dialogText").text($(".dialogText").text().replace("{category}", category.Description));
+				popupControl.Show();
+			}
+		});
+	}
+
+	function deleteCategory() {
+		$.ajax({
+			type: "POST",
+			url: '@Url.Action("DeleteCategory", "Appendix")',
+			data: { Id: deleteId },
+			success: function (response) {
+				var popupControl = MVCxClientPopupControl.Cast(devPopupControlDeleteCategory);
+				popupControl.Hide();
+				setTimeout(function () {
+					devGridViewCategory.PerformCallback();
+				}, 200);
+			},
+			error: function () {
+				 alert("error occured");
+			}
+		});
+	}
+</script>
+
+@Html.Partial("~/Views/Appendix/_CategoryListPartial.cshtml", Model)
+@Html.Partial("~/Views/Shared/_PopupDialogYesNo.cshtml", new GreenTree.Nachtragsmanagement.Web.Models.Global.YesNoDialogModel
+{
+	PopupName = "devPopupControlDeleteCategory",
+	Content = "<div class='dialogText' style='padding: 12px'>Sind Sie sicher, dass Sie die Kategorie \"{category}\" löschen möchten?</div>",
+	HeaderText = "\"{category}\" löschen",
+	YesFunction = "function (s, e) { deleteCategory(); }"
+})

+ 80 - 0
GreenTree.Nachtragsmanagement.Web/Views/Appendices/_CategoryEditPartial.cshtml

@@ -0,0 +1,80 @@
+@using GreenTree.Nachtragsmanagement.Web.Extensions
+
+@model GreenTree.Nachtragsmanagement.Web.Models.Appendix.CategoryDataModel
+
+<div class="categoryEditContainer">
+
+	<script>
+		function saveCategory() {
+			var form = $("#categoryEditForm");
+			$(form).submit(function (e) {
+				$.ajax({
+					type: "POST",
+					url: '@Url.Action("EditCategory", "Deviation")',
+					data: form.serialize(),
+					success: function (response) {
+						setTimeout(function () {
+							$(".claimEditContainer").remove();
+							if (response == "success") {
+								parent.callCustomEventListener('CategoryDataCallbackEventReceiver');
+							} else {
+								$("body").append(response);
+							}
+						}, 200);
+					}
+				});
+				e.preventDefault();
+			});
+			form.submit();
+		}
+	</script>
+
+	@Html.DevExpress().PopupControl(s =>
+{
+	s.Name = "devPopupControlEditCategory";
+
+	if (Model.Id == -1)
+		s.HeaderText = "Neue Kategorie erstellen";
+	else
+		s.HeaderText = "\"" + Model.Description + "\" bearbeiten";
+
+	s.Modal = true;
+	s.Width = new Unit(250, 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("EditCategory", "Appendix", FormMethod.Post, new { id = "categoryEditForm" }))
+		{
+			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.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();
+
+			ViewContext.Writer.Write("</div>");
+
+			Html.RenderPartial(
+				"~/Views/Shared/_PopupButtonPanel.cshtml",
+				new GreenTree.Nachtragsmanagement.Web.Models.Global.PopupModel
+				{
+					PopupName = "devPopupControlEditCategory",
+					AcceptFunction = "function (s, e) { saveCategory(); }"
+				}
+			);
+		}
+	});
+	s.Styles.Content.Paddings.Padding = new Unit(0);
+	s.Styles.ModalBackground.Opacity = 0;
+}).GetHtml()
+</div>

+ 29 - 0
GreenTree.Nachtragsmanagement.Web/Views/Appendices/_CategoryListPartial.cshtml

@@ -0,0 +1,29 @@
+@model IEnumerable<GreenTree.Nachtragsmanagement.Core.Domain.Appendix.Category>
+
+@{ 
+	var userContext = GreenTree.Nachtragsmanagement.Core.CommonHelper.UserContext();
+}
+
+@Html.DevExpress().ListBox(s =>
+{
+	s.Name = "devListBoxCategories";
+	s.Properties.ValueType = typeof(int);
+	s.Properties.ValueField = "Id";
+	s.Properties.TextField = "Description";
+	if (userContext.CurrentUser.HasFunction("Appendix-Category-Edit"))
+	{
+		s.SetItemTemplateContent(c =>
+		{
+			ViewContext.Writer.Write(DataBinder.Eval(c.DataItem, "Description"));
+			ViewContext.Writer.Write(
+				"<div class=\"devExListItemControlContainer\">" +
+					"<a href=\"#\" onclick='editCategory(\"disturbance\", " + DataBinder.Eval(c.DataItem, "Id") + ")'>Bearbeiten</a>&nbsp;" +
+					"<a href=\"#\" onclick='confirmDelete(\"disturbance\", " + DataBinder.Eval(c.DataItem, "Id") + ")'>Löschen</a>" +
+				"</div>"
+			);
+		});
+	}
+	s.CallbackRouteValues = new { Controller = "Appendix", Action = "PartialCategories" };
+	s.Width = Unit.Percentage(100);
+	s.Height = Unit.Pixel(370);
+}).BindList(Model).GetHtml()

+ 4 - 9
GreenTree.Nachtragsmanagement.Web/Views/Deviations/Claims.cshtml

@@ -10,8 +10,8 @@
 	var deleteClaimType;
 
 	var claimTypeTranslation = {
-		status: "Status",
-		disturbance: "Verzögerung",
+		status: "VA-Status",
+		disturbance: "Kategorie",
 		kind: "Art"
 	};
 
@@ -74,9 +74,6 @@
 				if (deleteClaimType == "status") {
 					devComboBoxClaimDeleteReplaceStatus.SetVisible(true);
 					comboBox = MVCxClientComboBox.Cast(devComboBoxClaimDeleteReplaceStatus);
-				} else if (deleteClaimType == "disturbance") {
-					devComboBoxClaimDeleteReplaceDisturbance.SetVisible(true);
-					comboBox = MVCxClientComboBox.Cast(devComboBoxClaimDeleteReplaceStatus);
 				} else if (deleteClaimType == "kind") {
 					devComboBoxClaimDeleteReplaceKind.SetVisible(true);
 					comboBox = MVCxClientComboBox.Cast(devComboBoxClaimDeleteReplaceStatus);
@@ -92,8 +89,6 @@
 	function deleteClaim() {
 		if (deleteClaimType == "status") {
 			deleteReplaceId = devComboBoxClaimDeleteReplaceStatus.GetValue();
-		} else if (deleteClaimType == "disturbance") {
-			deleteReplaceId = devComboBoxClaimDeleteReplaceDisturbance.GetValue();
 		} else if (deleteClaimType == "kind") {
 			deleteReplaceId = devComboBoxClaimDeleteReplaceKind.GetValue();
 		}
@@ -145,8 +140,8 @@
 			</td>
 			<td style="width: 33%; padding: 0 6px">
 				<div class="listHeader">
-					<span>Verzögerungen</span>
-					<img src='@Url.Content("~/Content/Images/add-24-contrast.png")' onclick='editClaim("disturbance", -1)' title="Neue Verzögerung" />
+					<span>Kategorien</span>
+					<img src='@Url.Content("~/Content/Images/add-24-contrast.png")' onclick='editClaim("disturbance", -1)' title="Neue VA-Kategorie" />
 				</div>
 				@Html.Partial("~/Views/Deviations/_DisturbanceListPartial.cshtml", ViewData["AllDisturbances"])
 			</td>

+ 244 - 66
GreenTree.Nachtragsmanagement.Web/Views/Deviations/_DeviationEditPartial.cshtml

@@ -9,6 +9,8 @@
 <div class="deviationEditContainer">
 
 	<script>
+		var textSeparator = ", ";
+
 		function addStatus() {
 			$.ajax({
 				url: '@Url.Action("EditClaim", "Deviation")',
@@ -65,6 +67,55 @@
 				}
 			});
 		}
+
+		function calculateValue() {
+			Sum.SetValue(Value.GetValue() * (Percentage.GetValue() / 100));
+		}
+
+		function onListBoxSelectionChanged(s, e) {
+			if (e.index == 0)
+				e.isSelected ? s.SelectAll() : s.UnselectAll();
+			updateSelectAllItemState();
+			updateText();
+		}
+		function updateSelectAllItemState() {
+			isAllSelected() ? DisturbanceValues.SelectIndices([0]) : DisturbanceValues.UnselectIndices([0]);
+		}
+		function isAllSelected() {
+			for (var i = 1; i < DisturbanceValues.GetItemCount(); i++)
+				if (!DisturbanceValues.GetItem(i).selected)
+					return false;
+			return true;
+		}
+		function updateText() {
+			var selectedItems = DisturbanceValues.GetSelectedItems();
+			devDropDownListDisturbanceValues.SetText(getSelectedItemsText(selectedItems));
+		}
+		function synchronizeListBoxValues(s, e) {
+			DisturbanceValues.UnselectAll();
+			var texts = s.GetText().split(textSeparator);
+			var values = getValuesByTexts(texts);
+			DisturbanceValues.SelectValues(values);
+			updateSelectAllItemState();
+			updateText();
+		}
+		function getSelectedItemsText(items) {
+			var texts = [];
+			for (var i = 0; i < items.length; i++)
+				if (items[i].index != 0)
+					texts.push(items[i].text);
+			return texts.join(textSeparator);
+		}
+		function getValuesByTexts(texts) {
+			var actualValues = [];
+			var item;
+			for (var i = 0; i < texts.length; i++) {
+				item = DisturbanceValues.FindItemByText(texts[i]);
+				if (item != null)
+					actualValues.push(item.value);
+			}
+			return actualValues;
+		}
 	</script>
 	@Html.DevExpress().PopupControl(s =>
 {
@@ -76,7 +127,7 @@
 		s.HeaderText = "\"" + Model.CustomNumber + "\" bearbeiten";
 
 	s.Modal = true;
-	s.Width = new Unit(500, UnitType.Pixel);
+	s.Width = new Unit(700, UnitType.Pixel);
 	s.CloseAction = CloseAction.CloseButton;
 	s.PopupHorizontalAlign = PopupHorizontalAlign.WindowCenter;
 	s.PopupVerticalAlign = PopupVerticalAlign.TopSides;
@@ -93,31 +144,31 @@
 
 			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(47.5, UnitType.Percentage);
-			}).Render();
-
 			ViewContext.Writer.Write("<div class='inlineModelPropertyContainer'>");
 			{
-				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 50%'>");
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 15%'>");
 				{
-					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 =>
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.CustomNumber, "Nummer:"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.CustomNumber).ToHtmlString());
+					Html.DevExpress().TextBoxFor(m => m.CustomNumber, t =>
 					{
-						t.Width = new Unit(95, UnitType.Percentage);
-						t.Properties.DecimalPlaces = 2;
-						t.Properties.NumberType = SpinEditNumberType.Float;
-						t.Properties.DisplayFormatString = "c2";
+						t.Width = new Unit(80, UnitType.Percentage);
 					}).Render();
 				}
 				ViewContext.Writer.Write("</div>");
-				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 50%'>");
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 55%'>");
 				{
-					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.ReceiptDate, "Eingangsdatum:"));
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Description, "Bezeichnung:"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Description).ToHtmlString());
+					Html.DevExpress().TextBoxFor(m => m.Description, t =>
+					{
+						t.Width = new Unit(92, UnitType.Percentage);
+					}).Render();
+				}
+				ViewContext.Writer.Write("</div>");
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 30%'>");
+				{
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.ReceiptDate, "Einreichdatum VA:"));
 					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.ReceiptDate).ToHtmlString());
 					Html.DevExpress().DateEditFor(m => m.ReceiptDate, t =>
 					{
@@ -128,37 +179,121 @@
 			}
 			ViewContext.Writer.Write("</div>");
 
-			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.AppendixId, "Nachtrag:"));
-
-			Session.Add("DeviationAppendicesComboBoxSettings", new Action<ComboBoxSettings>(a =>
+			ViewContext.Writer.Write("<div class='inlineModelPropertyContainer'>");
 			{
-				a.Width = new Unit(47.5, UnitType.Percentage);
-				a.Properties.ValueType = typeof(int);
-				a.Properties.ValueField = "Id";
-				a.Properties.TextField = "Description";
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 40%'>");
+				{
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.DisturbanceValues, "Kategorien:"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.DisturbanceValues).ToHtmlString());
+					Html.DevExpress().DropDownEdit(t =>
+					{
+						t.Name = "devDropDownListDisturbanceValues";
+						t.Width = new Unit(95, UnitType.Percentage);
+
+						if (Model.DisturbanceDescriptions != null && Model.DisturbanceDescriptions.Any())
+							t.Text = String.Join(", ", Model.DisturbanceDescriptions);
+
+						t.SetDropDownWindowTemplateContent(l =>
+						{
+							Html.DevExpress().ListBox(lb =>
+							{
+								lb.Name = "DisturbanceValues";
+								lb.Width = new Unit(100, UnitType.Percentage);
+								lb.Properties.TextField = "Description";
+								lb.Properties.ValueField = "Id";
+								lb.Properties.ValueType = typeof(int);
+								lb.Properties.SelectionMode = ListEditSelectionMode.CheckColumn;
+								lb.ControlStyle.Border.BorderStyle = BorderStyle.None;
+								lb.PreRender = (sender, e) =>
+								{
+									var listBox = sender as MVCxListBox;
+
+									foreach (ListEditItem listItem in listBox.Items)
+									{
+										if (Model.DisturbanceValues == null || !Model.DisturbanceValues.Any(m => m == (int)listItem.Value)) continue;
+
+										listItem.Selected = true;
+									}
+
+									listBox.Items.Insert(0, new ListEditItem("(Alle auswählen)", -1));
+								};
+								lb.Properties.ClientSideEvents.SelectedIndexChanged = "function (s, e) { onListBoxSelectionChanged(s, e); }";
+							}).BindList(ViewData["AllDisturbances"]).Render();
+
+							t.Properties.ClientSideEvents.TextChanged = "function (s, e) { synchronizeListBoxValues(s, e); }";
+							t.Properties.ClientSideEvents.DropDown = "function (s, e) { synchronizeListBoxValues(s, e); }";
+						});
+					}).Render();
 
-				var serializedModel = Newtonsoft.Json.JsonConvert.SerializeObject(Model);
+					if (userContext.CurrentUser.HasFunction("Deviation-Claims-Edit"))
+					{
+						Html.DevExpress().HyperLink(t =>
+						{
+							t.Name = "devHyperLinkDisturbanceEventReceiver";
+							t.Properties.Text = "Hinzufügen";
+							t.NavigateUrl = "#";
+							t.Properties.ClientSideEvents.Click = "function (s, e) { addDisturbance(); }";
+							t.Style.Add("line-height", "24px");
+						}).Render();
+					}
+				}
+				ViewContext.Writer.Write("</div>");
 
-				a.CallbackRouteValues = new
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 22%'>");
 				{
-					Controller = "DataCallback",
-					Action = "AppendicesComboBox",
-					SettingsKey = "DeviationAppendicesComboBoxSettings",
-					Model = serializedModel,
-					Type = Model.GetType().FullName
-				};
-			}));
-			ViewData.Add("AppendicesComboBoxSettings", "DeviationAppendicesComboBoxSettings");
-			Html.RenderPartial("~/Views/Shared/DataEditorTemplates/_AppendicesComboBox.cshtml", Model, ViewData);
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Value, "Schätzung VA:"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Value).ToHtmlString());
+					Html.DevExpress().SpinEditFor(m => m.Value, t =>
+					{
+						t.Width = new Unit(90, UnitType.Percentage);
+						t.Properties.DecimalPlaces = 2;
+						t.Properties.NumberType = SpinEditNumberType.Float;
+						t.Properties.DisplayFormatString = "c2";
+						t.Properties.ClientSideEvents.ValueChanged = "function (s, e) { calculateValue(); }";
+					}).Render();
+				}
+				ViewContext.Writer.Write("</div>");
+
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 16%'>");
+				{
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Value, "Bewertung (%):"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Value).ToHtmlString());
+					Html.DevExpress().SpinEditFor(m => m.Percentage, t =>
+					{
+						t.Width = new Unit(88, UnitType.Percentage);
+						t.Properties.DecimalPlaces = 0;
+						t.Properties.MaxValue = 100;
+						t.Properties.MinValue = 0;
+						t.Properties.NumberType = SpinEditNumberType.Integer;
+						t.Properties.DisplayFormatString = "{0:n0} %";
+						t.Properties.ClientSideEvents.ValueChanged = "function (s, e) { calculateValue(); }";
+					}).Render();
+				}
+				ViewContext.Writer.Write("</div>");
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 22%'>");
+				{
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Value, "Wert:"));
+					Html.DevExpress().SpinEdit(t =>
+					{
+						t.Name = "Sum";
+						t.Width = new Unit(100, UnitType.Percentage);
+						t.ClientEnabled = false;
+						t.Properties.DecimalPlaces = 2;
+						t.Properties.NumberType = SpinEditNumberType.Float;
+						t.Properties.DisplayFormatString = "c2";
+					}).Render();
+				}
+				ViewContext.Writer.Write("</div>");
+			}
+			ViewContext.Writer.Write("</div>");
 
 			ViewContext.Writer.Write("<div class='inlineModelPropertyContainer'>");
 			{
-				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 33%'>");
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 50%'>");
 				{
-					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.StatusId, "Status:"));
-					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.StatusId).ToHtmlString());
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.AppendixId, "Nachtrag:"));
 
-					Session.Add("DeviationStatusesComboBoxSettings", new Action<ComboBoxSettings>(a =>
+					Session.Add("DeviationAppendicesComboBoxSettings", new Action<ComboBoxSettings>(a =>
 					{
 						a.Width = new Unit(95, UnitType.Percentage);
 						a.Properties.ValueType = typeof(int);
@@ -170,35 +305,38 @@
 						a.CallbackRouteValues = new
 						{
 							Controller = "DataCallback",
-							Action = "StatusesComboBox",
-							SettingsKey = "DeviationStatusesComboBoxSettings",
+							Action = "AppendicesComboBox",
+							SettingsKey = "DeviationAppendicesComboBoxSettings",
 							Model = serializedModel,
 							Type = Model.GetType().FullName
 						};
 					}));
-					ViewData.Add("StatusesComboBoxSettings", "DeviationStatusesComboBoxSettings");
-					Html.RenderPartial("~/Views/Shared/DataEditorTemplates/_StatusesComboBox.cshtml", Model, ViewData);
+					ViewData.Add("AppendicesComboBoxSettings", "DeviationAppendicesComboBoxSettings");
+					Html.RenderPartial("~/Views/Shared/DataEditorTemplates/_AppendicesComboBox.cshtml", Model, ViewData);
+				}
+				ViewContext.Writer.Write("</div>");
 
-					if (userContext.CurrentUser.HasFunction("Deviation-Claims-Edit"))
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 50%'>");
+				{
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.AppendixDate, "Einreichdatum NT:"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.AppendixDate).ToHtmlString());
+					Html.DevExpress().DateEditFor(m => m.AppendixDate, t =>
 					{
-						Html.DevExpress().HyperLink(t =>
-						{
-							t.Name = "devHyperLinkStatusEventReceiver";
-							t.Properties.Text = "Hinzufügen";
-							t.NavigateUrl = "#";
-							t.Properties.ClientSideEvents.Click = "function (s, e) { addStatus(); }";
-							t.Style.Add("line-height", "24px");
-						}).Render();
-					}
+						t.Width = new Unit(100, UnitType.Percentage);
+					}).Render();
 				}
 				ViewContext.Writer.Write("</div>");
+			}
+			ViewContext.Writer.Write("</div>");
 
-				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 33%'>");
+			ViewContext.Writer.Write("<div class='inlineModelPropertyContainer'>");
+			{
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 50%'>");
 				{
-					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.DisturbanceId, "Verzögerung:"));
-					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.DisturbanceId).ToHtmlString());
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.StatusId, "VA-Status:"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.StatusId).ToHtmlString());
 
-					Session.Add("DeviationDisturbancesComboBoxSettings", new Action<ComboBoxSettings>(a =>
+					Session.Add("DeviationStatusesComboBoxSettings", new Action<ComboBoxSettings>(a =>
 					{
 						a.Width = new Unit(95, UnitType.Percentage);
 						a.Properties.ValueType = typeof(int);
@@ -210,37 +348,77 @@
 						a.CallbackRouteValues = new
 						{
 							Controller = "DataCallback",
-							Action = "DisturbancesComboBox",
-							SettingsKey = "DeviationDisturbancesComboBoxSettings",
+							Action = "StatusesComboBox",
+							SettingsKey = "DeviationStatusesComboBoxSettings",
 							Model = serializedModel,
 							Type = Model.GetType().FullName
 						};
 					}));
-					ViewData.Add("DisturbancesComboBoxSettings", "DeviationDisturbancesComboBoxSettings");
-					Html.RenderPartial("~/Views/Shared/DataEditorTemplates/_DisturbancesComboBox.cshtml", Model, ViewData);
+					ViewData.Add("StatusesComboBoxSettings", "DeviationStatusesComboBoxSettings");
+					Html.RenderPartial("~/Views/Shared/DataEditorTemplates/_StatusesComboBox.cshtml", Model, ViewData);
 
 					if (userContext.CurrentUser.HasFunction("Deviation-Claims-Edit"))
 					{
 						Html.DevExpress().HyperLink(t =>
 						{
-							t.Name = "devHyperLinkDisturbanceEventReceiver";
+							t.Name = "devHyperLinkStatusEventReceiver";
 							t.Properties.Text = "Hinzufügen";
 							t.NavigateUrl = "#";
-							t.Properties.ClientSideEvents.Click = "function (s, e) { addDisturbance(); }";
+							t.Properties.ClientSideEvents.Click = "function (s, e) { addStatus(); }";
 							t.Style.Add("line-height", "24px");
 						}).Render();
 					}
 				}
 				ViewContext.Writer.Write("</div>");
 
-				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 34%'>");
+				//ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 33%'>");
+				//{
+				//	ViewContext.Writer.Write(Html.CustomLabelFor(m => m.DisturbanceId, "Verzögerung:"));
+				//	ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.DisturbanceId).ToHtmlString());
+
+				//	Session.Add("DeviationDisturbancesComboBoxSettings", new Action<ComboBoxSettings>(a =>
+				//	{
+				//		a.Width = new Unit(95, UnitType.Percentage);
+				//		a.Properties.ValueType = typeof(int);
+				//		a.Properties.ValueField = "Id";
+				//		a.Properties.TextField = "Description";
+
+				//		var serializedModel = Newtonsoft.Json.JsonConvert.SerializeObject(Model);
+
+				//		a.CallbackRouteValues = new
+				//		{
+				//			Controller = "DataCallback",
+				//			Action = "DisturbancesComboBox",
+				//			SettingsKey = "DeviationDisturbancesComboBoxSettings",
+				//			Model = serializedModel,
+				//			Type = Model.GetType().FullName
+				//		};
+				//	}));
+				//	ViewData.Add("DisturbancesComboBoxSettings", "DeviationDisturbancesComboBoxSettings");
+				//	Html.RenderPartial("~/Views/Shared/DataEditorTemplates/_DisturbancesComboBox.cshtml", Model, ViewData);
+
+				//	if (userContext.CurrentUser.HasFunction("Deviation-Claims-Edit"))
+				//	{
+				//		Html.DevExpress().HyperLink(t =>
+				//		{
+				//			t.Name = "devHyperLinkDisturbanceEventReceiver";
+				//			t.Properties.Text = "Hinzufügen";
+				//			t.NavigateUrl = "#";
+				//			t.Properties.ClientSideEvents.Click = "function (s, e) { addDisturbance(); }";
+				//			t.Style.Add("line-height", "24px");
+				//		}).Render();
+				//	}
+				//}
+				//ViewContext.Writer.Write("</div>");
+
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 50%'>");
 				{
 					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.KindId, "Art:"));
 					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.KindId).ToHtmlString());
 
 					Session.Add("DeviationKindsComboBoxSettings", new Action<ComboBoxSettings>(a =>
 					{
-						a.Width = new Unit(95, UnitType.Percentage);
+						a.Width = new Unit(100, UnitType.Percentage);
 						a.Properties.ValueType = typeof(int);
 						a.Properties.ValueField = "Id";
 						a.Properties.TextField = "Description";

+ 13 - 4
GreenTree.Nachtragsmanagement.Web/Views/Deviations/_DeviationGridPartial.cshtml

@@ -10,6 +10,10 @@
 	s.KeyFieldName = "Id";
 	s.CallbackRouteValues = new { Controller = "Deviation", Action = "PartialDeviations" };
 	s.Width = Unit.Percentage(100);
+	s.Settings.ShowFilterRow = true;
+	s.Settings.ShowFilterRowMenu = true;
+	s.Settings.ShowFooter = true;
+	s.Settings.ShowGroupPanel = true;
 
 	if (userContext.CurrentUser.HasFunction("Deviation-Deviations-Edit"))
 	{
@@ -33,7 +37,9 @@
 			column.Width = 70;
 		});
 	}
-	s.Columns.Add("CustomNumber", "Eigene ID");
+	s.Columns.Add("CustomNumber", "Nummer");
+	s.Columns.Add("SiteDescription", "Baustelle");
+	s.Columns.Add("Description", "Bezeichnung");
 	s.Columns.Add(column =>
 	{
 		column.Caption = "Eingang";
@@ -42,16 +48,19 @@
 	});
 	s.Columns.Add(column =>
 	{
-		column.Caption = "Wert";
-		column.FieldName = "Value";
+		column.Caption = "Schätzung bew.";
+		column.FieldName = "PercentageValue";
 		column.PropertiesEdit.DisplayFormatString = "c2";
 	});
 	s.Columns.Add("StatusDescription", "Status");
-	s.Columns.Add("DisturbanceDescription", "Verzögerung");
+	s.Columns.Add("DisturbanceDescription", "Kategorien");
 	s.Columns.Add("KindDescription", "Art");
 	s.Columns.Add("AppendixDescription", "Nachtrag");
 	s.Columns.Add("Comment", "Kommentar");
 
+	s.TotalSummary.Add(DevExpress.Data.SummaryItemType.Sum, "Value");
+	s.GroupSummary.Add(DevExpress.Data.SummaryItemType.Sum, "Value");
+
 	s.ClientLayout = (sender, e) =>
 	{
 		if (e.LayoutMode == ClientLayoutMode.Loading)

+ 1 - 1
GreenTree.Nachtragsmanagement.Web/Views/Deviations/_DisturbanceEditPartial.cshtml

@@ -34,7 +34,7 @@
 	s.Name = "devPopupControlEditDisturbance";
 
 	if (Model.Id == -1)
-		s.HeaderText = "Neue Verzögerung erstellen";
+		s.HeaderText = "Neue VA-Kategorie erstellen";
 	else
 		s.HeaderText = "\"" + Model.Description + "\" bearbeiten";
 

+ 7 - 0
GreenTree.Nachtragsmanagement.Web/Views/Deviations/_KindEditPartial.cshtml

@@ -62,6 +62,13 @@
 				t.Width = new Unit(100, UnitType.Percentage);
 			}).Render();
 
+			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Shortance, "Abkürzung:"));
+			ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Shortance).ToHtmlString());
+			Html.DevExpress().TextBoxFor(m => m.Shortance, t =>
+			{
+				t.Width = new Unit(100, UnitType.Percentage);
+			}).Render();
+
 			ViewContext.Writer.Write("</div>");
 
 			Html.RenderPartial(

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

@@ -0,0 +1,94 @@
+@{
+	Layout = "~/Views/Shared/_FunctionLayout.cshtml";
+}
+
+@model IEnumerable<GreenTree.Nachtragsmanagement.Web.Models.Site.SiteDataModel>
+
+<script>
+	var deleteId;
+
+	function saveSite() {
+		var form = $("#siteEditForm");
+		$(form).submit(function (e) {
+			$.ajax({
+				type: "POST",
+				url: '@Url.Action("EditSite", "Site")',
+				data: form.serialize(),
+				success: function (response) {
+					setTimeout(function () {
+						$(".siteEditContainer").remove();
+						if (response == "success") {
+							devGridViewSite.PerformCallback();
+						} else {
+							$("body").append(response);
+						}
+					}, 200);
+				}
+			});
+			e.preventDefault();
+		});
+		form.submit();
+	}
+
+	function editSite(id) {
+		if (!id) return;
+		$.ajax({
+			url: '@Url.Action("EditSite", "Site")',
+			data: { Id: id },
+			success: function (response) {
+				setTimeout(function () {
+					$(".siteEditContainer").remove();
+					$("body").append(response);
+				}, 200);
+			},
+			error: function () {
+				 alert("error occured");
+			}
+		});
+	}
+
+	function confirmDelete(id) {
+		if (!id) return;
+		deleteId = id;
+		$.ajax({
+			type: "GET",
+			url: '@Url.Action("GetSite", "Site")',
+			data: { Id: id },
+			success: function (response) {
+				if (response == "notFound") return;
+				var site = JSON.parse(response);
+				var popupControl = MVCxClientPopupControl.Cast(devPopupControlDeleteSite);
+				popupControl.SetHeaderText(popupControl.GetHeaderText().replace("{site}", site.CustomNumber));
+				$(".dialogText").text($(".dialogText").text().replace("{site}", site.CustomNumber));
+				popupControl.Show();
+			}
+		});
+	}
+
+	function deleteSite() {
+		$.ajax({
+			type: "POST",
+			url: '@Url.Action("DeleteSite", "Site")',
+			data: { Id: deleteId },
+			success: function (response) {
+				var popupControl = MVCxClientPopupControl.Cast(devPopupControlDeleteSite);
+				popupControl.Hide();
+				setTimeout(function () {
+					devGridViewSite.PerformCallback();
+				}, 200);
+			},
+			error: function () {
+				 alert("error occured");
+			}
+		});
+	}
+</script>
+
+@Html.Partial("~/Views/Sites/_SiteGridPartial.cshtml", Model)
+@Html.Partial("~/Views/Shared/_PopupDialogYesNo.cshtml", new GreenTree.Nachtragsmanagement.Web.Models.Global.YesNoDialogModel
+{
+	PopupName = "devPopupControlDeleteSite",
+	Content = "<div class='dialogText' style='padding: 12px'>Sind Sie sicher, dass Sie die Baustelle \"{site}\" löschen möchten?</div>",
+	HeaderText = "\"{site}\" löschen",
+	YesFunction = "function (s, e) { deleteSite(); }"
+})

+ 479 - 0
GreenTree.Nachtragsmanagement.Web/Views/Sites/_SiteEditPartial.cshtml

@@ -0,0 +1,479 @@
+@using GreenTree.Nachtragsmanagement.Web.Extensions
+
+@{ 
+	var userContext = GreenTree.Nachtragsmanagement.Core.CommonHelper.UserContext();
+}
+
+@model GreenTree.Nachtragsmanagement.Web.Models.Deviation.DeviationDataModel
+
+<div class="deviationEditContainer">
+
+	<script>
+		var textSeparator = ", ";
+
+		function addStatus() {
+			$.ajax({
+				url: '@Url.Action("EditClaim", "Deviation")',
+				data: { claimType: "status", Id: -1 },
+				success: function (response) {
+					setTimeout(function () {
+						$(".claimEditContainer").remove();
+						$("body").append(response);
+						parent.addCustomEventListener('StatusDataCallbackEventReceiver', function () {
+							StatusId.PerformCallback();
+						});
+					}, 200);
+				},
+				error: function () {
+					 alert("error occured");
+				}
+			});
+		}
+
+		function addDisturbance() {
+			$.ajax({
+				url: '@Url.Action("EditClaim", "Deviation")',
+				data: { claimType: "disturbance", Id: -1 },
+				success: function (response) {
+					setTimeout(function () {
+						$(".claimEditContainer").remove();
+						$("body").append(response);
+						parent.addCustomEventListener('DisturbanceDataCallbackEventReceiver', function () {
+							DisturbanceId.PerformCallback();
+						});
+					}, 200);
+				},
+				error: function () {
+					 alert("error occured");
+				}
+			});
+		}
+
+		function addKind() {
+			$.ajax({
+				url: '@Url.Action("EditClaim", "Deviation")',
+				data: { claimType: "kind", Id: -1 },
+				success: function (response) {
+					setTimeout(function () {
+						$(".claimEditContainer").remove();
+						$("body").append(response);
+						parent.addCustomEventListener('KindDataCallbackEventReceiver', function () {
+							KindId.PerformCallback();
+						});
+					}, 200);
+				},
+				error: function () {
+					 alert("error occured");
+				}
+			});
+		}
+
+		function calculateValue() {
+			Sum.SetValue(Value.GetValue() * (Percentage.GetValue() / 100));
+		}
+
+		function onListBoxSelectionChanged(s, e) {
+			if (e.index == 0)
+				e.isSelected ? s.SelectAll() : s.UnselectAll();
+			updateSelectAllItemState();
+			updateText();
+		}
+		function updateSelectAllItemState() {
+			isAllSelected() ? DisturbanceValues.SelectIndices([0]) : DisturbanceValues.UnselectIndices([0]);
+		}
+		function isAllSelected() {
+			for (var i = 1; i < DisturbanceValues.GetItemCount(); i++)
+				if (!DisturbanceValues.GetItem(i).selected)
+					return false;
+			return true;
+		}
+		function updateText() {
+			var selectedItems = DisturbanceValues.GetSelectedItems();
+			devDropDownListDisturbanceValues.SetText(getSelectedItemsText(selectedItems));
+		}
+		function synchronizeListBoxValues(s, e) {
+			DisturbanceValues.UnselectAll();
+			var texts = s.GetText().split(textSeparator);
+			var values = getValuesByTexts(texts);
+			DisturbanceValues.SelectValues(values);
+			updateSelectAllItemState();
+			updateText();
+		}
+		function getSelectedItemsText(items) {
+			var texts = [];
+			for (var i = 0; i < items.length; i++)
+				if (items[i].index != 0)
+					texts.push(items[i].text);
+			return texts.join(textSeparator);
+		}
+		function getValuesByTexts(texts) {
+			var actualValues = [];
+			var item;
+			for (var i = 0; i < texts.length; i++) {
+				item = DisturbanceValues.FindItemByText(texts[i]);
+				if (item != null)
+					actualValues.push(item.value);
+			}
+			return actualValues;
+		}
+	</script>
+	@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(700, 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("EditDeviation", "Deviation", 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("<div class='inlineModelPropertyContainer'>");
+			{
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 15%'>");
+				{
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.CustomNumber, "Nummer:"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.CustomNumber).ToHtmlString());
+					Html.DevExpress().TextBoxFor(m => m.CustomNumber, t =>
+					{
+						t.Width = new Unit(80, UnitType.Percentage);
+					}).Render();
+				}
+				ViewContext.Writer.Write("</div>");
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 55%'>");
+				{
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Description, "Bezeichnung:"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Description).ToHtmlString());
+					Html.DevExpress().TextBoxFor(m => m.Description, t =>
+					{
+						t.Width = new Unit(92, UnitType.Percentage);
+					}).Render();
+				}
+				ViewContext.Writer.Write("</div>");
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 30%'>");
+				{
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.ReceiptDate, "Einreichdatum VA:"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.ReceiptDate).ToHtmlString());
+					Html.DevExpress().DateEditFor(m => m.ReceiptDate, t =>
+					{
+						t.Width = new Unit(100, UnitType.Percentage);
+					}).Render();
+				}
+				ViewContext.Writer.Write("</div>");
+			}
+			ViewContext.Writer.Write("</div>");
+
+			ViewContext.Writer.Write("<div class='inlineModelPropertyContainer'>");
+			{
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 40%'>");
+				{
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.DisturbanceValues, "Kategorien:"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.DisturbanceValues).ToHtmlString());
+					Html.DevExpress().DropDownEdit(t =>
+					{
+						t.Name = "devDropDownListDisturbanceValues";
+						t.Width = new Unit(95, UnitType.Percentage);
+
+						if (Model.DisturbanceDescriptions != null && Model.DisturbanceDescriptions.Any())
+							t.Text = String.Join(", ", Model.DisturbanceDescriptions);
+
+						t.SetDropDownWindowTemplateContent(l =>
+						{
+							Html.DevExpress().ListBox(lb =>
+							{
+								lb.Name = "DisturbanceValues";
+								lb.Width = new Unit(100, UnitType.Percentage);
+								lb.Properties.TextField = "Description";
+								lb.Properties.ValueField = "Id";
+								lb.Properties.ValueType = typeof(int);
+								lb.Properties.SelectionMode = ListEditSelectionMode.CheckColumn;
+								lb.ControlStyle.Border.BorderStyle = BorderStyle.None;
+								lb.PreRender = (sender, e) =>
+								{
+									var listBox = sender as MVCxListBox;
+
+									foreach (ListEditItem listItem in listBox.Items)
+									{
+										if (Model.DisturbanceValues == null || !Model.DisturbanceValues.Any(m => m == (int)listItem.Value)) continue;
+
+										listItem.Selected = true;
+									}
+
+									listBox.Items.Insert(0, new ListEditItem("(Alle auswählen)", -1));
+								};
+								lb.Properties.ClientSideEvents.SelectedIndexChanged = "function (s, e) { onListBoxSelectionChanged(s, e); }";
+							}).BindList(ViewData["AllDisturbances"]).Render();
+
+							t.Properties.ClientSideEvents.TextChanged = "function (s, e) { synchronizeListBoxValues(s, e); }";
+							t.Properties.ClientSideEvents.DropDown = "function (s, e) { synchronizeListBoxValues(s, e); }";
+						});
+					}).Render();
+
+					if (userContext.CurrentUser.HasFunction("Deviation-Claims-Edit"))
+					{
+						Html.DevExpress().HyperLink(t =>
+						{
+							t.Name = "devHyperLinkDisturbanceEventReceiver";
+							t.Properties.Text = "Hinzufügen";
+							t.NavigateUrl = "#";
+							t.Properties.ClientSideEvents.Click = "function (s, e) { addDisturbance(); }";
+							t.Style.Add("line-height", "24px");
+						}).Render();
+					}
+				}
+				ViewContext.Writer.Write("</div>");
+
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 22%'>");
+				{
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Value, "Schätzung VA:"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Value).ToHtmlString());
+					Html.DevExpress().SpinEditFor(m => m.Value, t =>
+					{
+						t.Width = new Unit(90, UnitType.Percentage);
+						t.Properties.DecimalPlaces = 2;
+						t.Properties.NumberType = SpinEditNumberType.Float;
+						t.Properties.DisplayFormatString = "c2";
+						t.Properties.ClientSideEvents.ValueChanged = "function (s, e) { calculateValue(); }";
+					}).Render();
+				}
+				ViewContext.Writer.Write("</div>");
+
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 16%'>");
+				{
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Value, "Bewertung (%):"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Value).ToHtmlString());
+					Html.DevExpress().SpinEditFor(m => m.Percentage, t =>
+					{
+						t.Width = new Unit(88, UnitType.Percentage);
+						t.Properties.DecimalPlaces = 0;
+						t.Properties.MaxValue = 100;
+						t.Properties.MinValue = 0;
+						t.Properties.NumberType = SpinEditNumberType.Integer;
+						t.Properties.DisplayFormatString = "{0:n0} %";
+						t.Properties.ClientSideEvents.ValueChanged = "function (s, e) { calculateValue(); }";
+					}).Render();
+				}
+				ViewContext.Writer.Write("</div>");
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 22%'>");
+				{
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Value, "Wert:"));
+					Html.DevExpress().SpinEdit(t =>
+					{
+						t.Name = "Sum";
+						t.Width = new Unit(100, UnitType.Percentage);
+						t.ClientEnabled = false;
+						t.Properties.DecimalPlaces = 2;
+						t.Properties.NumberType = SpinEditNumberType.Float;
+						t.Properties.DisplayFormatString = "c2";
+					}).Render();
+				}
+				ViewContext.Writer.Write("</div>");
+			}
+			ViewContext.Writer.Write("</div>");
+
+			ViewContext.Writer.Write("<div class='inlineModelPropertyContainer'>");
+			{
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 50%'>");
+				{
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.AppendixId, "Nachtrag:"));
+
+					Session.Add("DeviationAppendicesComboBoxSettings", new Action<ComboBoxSettings>(a =>
+					{
+						a.Width = new Unit(95, UnitType.Percentage);
+						a.Properties.ValueType = typeof(int);
+						a.Properties.ValueField = "Id";
+						a.Properties.TextField = "Description";
+
+						var serializedModel = Newtonsoft.Json.JsonConvert.SerializeObject(Model);
+
+						a.CallbackRouteValues = new
+						{
+							Controller = "DataCallback",
+							Action = "AppendicesComboBox",
+							SettingsKey = "DeviationAppendicesComboBoxSettings",
+							Model = serializedModel,
+							Type = Model.GetType().FullName
+						};
+					}));
+					ViewData.Add("AppendicesComboBoxSettings", "DeviationAppendicesComboBoxSettings");
+					Html.RenderPartial("~/Views/Shared/DataEditorTemplates/_AppendicesComboBox.cshtml", Model, ViewData);
+				}
+				ViewContext.Writer.Write("</div>");
+
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 50%'>");
+				{
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.AppendixDate, "Einreichdatum NT:"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.AppendixDate).ToHtmlString());
+					Html.DevExpress().DateEditFor(m => m.AppendixDate, t =>
+					{
+						t.Width = new Unit(100, UnitType.Percentage);
+					}).Render();
+				}
+				ViewContext.Writer.Write("</div>");
+			}
+			ViewContext.Writer.Write("</div>");
+
+			ViewContext.Writer.Write("<div class='inlineModelPropertyContainer'>");
+			{
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 50%'>");
+				{
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.StatusId, "VA-Status:"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.StatusId).ToHtmlString());
+
+					Session.Add("DeviationStatusesComboBoxSettings", new Action<ComboBoxSettings>(a =>
+					{
+						a.Width = new Unit(95, UnitType.Percentage);
+						a.Properties.ValueType = typeof(int);
+						a.Properties.ValueField = "Id";
+						a.Properties.TextField = "Description";
+
+						var serializedModel = Newtonsoft.Json.JsonConvert.SerializeObject(Model);
+
+						a.CallbackRouteValues = new
+						{
+							Controller = "DataCallback",
+							Action = "StatusesComboBox",
+							SettingsKey = "DeviationStatusesComboBoxSettings",
+							Model = serializedModel,
+							Type = Model.GetType().FullName
+						};
+					}));
+					ViewData.Add("StatusesComboBoxSettings", "DeviationStatusesComboBoxSettings");
+					Html.RenderPartial("~/Views/Shared/DataEditorTemplates/_StatusesComboBox.cshtml", Model, ViewData);
+
+					if (userContext.CurrentUser.HasFunction("Deviation-Claims-Edit"))
+					{
+						Html.DevExpress().HyperLink(t =>
+						{
+							t.Name = "devHyperLinkStatusEventReceiver";
+							t.Properties.Text = "Hinzufügen";
+							t.NavigateUrl = "#";
+							t.Properties.ClientSideEvents.Click = "function (s, e) { addStatus(); }";
+							t.Style.Add("line-height", "24px");
+						}).Render();
+					}
+				}
+				ViewContext.Writer.Write("</div>");
+
+				//ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 33%'>");
+				//{
+				//	ViewContext.Writer.Write(Html.CustomLabelFor(m => m.DisturbanceId, "Verzögerung:"));
+				//	ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.DisturbanceId).ToHtmlString());
+
+				//	Session.Add("DeviationDisturbancesComboBoxSettings", new Action<ComboBoxSettings>(a =>
+				//	{
+				//		a.Width = new Unit(95, UnitType.Percentage);
+				//		a.Properties.ValueType = typeof(int);
+				//		a.Properties.ValueField = "Id";
+				//		a.Properties.TextField = "Description";
+
+				//		var serializedModel = Newtonsoft.Json.JsonConvert.SerializeObject(Model);
+
+				//		a.CallbackRouteValues = new
+				//		{
+				//			Controller = "DataCallback",
+				//			Action = "DisturbancesComboBox",
+				//			SettingsKey = "DeviationDisturbancesComboBoxSettings",
+				//			Model = serializedModel,
+				//			Type = Model.GetType().FullName
+				//		};
+				//	}));
+				//	ViewData.Add("DisturbancesComboBoxSettings", "DeviationDisturbancesComboBoxSettings");
+				//	Html.RenderPartial("~/Views/Shared/DataEditorTemplates/_DisturbancesComboBox.cshtml", Model, ViewData);
+
+				//	if (userContext.CurrentUser.HasFunction("Deviation-Claims-Edit"))
+				//	{
+				//		Html.DevExpress().HyperLink(t =>
+				//		{
+				//			t.Name = "devHyperLinkDisturbanceEventReceiver";
+				//			t.Properties.Text = "Hinzufügen";
+				//			t.NavigateUrl = "#";
+				//			t.Properties.ClientSideEvents.Click = "function (s, e) { addDisturbance(); }";
+				//			t.Style.Add("line-height", "24px");
+				//		}).Render();
+				//	}
+				//}
+				//ViewContext.Writer.Write("</div>");
+
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 50%'>");
+				{
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.KindId, "Art:"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.KindId).ToHtmlString());
+
+					Session.Add("DeviationKindsComboBoxSettings", new Action<ComboBoxSettings>(a =>
+					{
+						a.Width = new Unit(100, UnitType.Percentage);
+						a.Properties.ValueType = typeof(int);
+						a.Properties.ValueField = "Id";
+						a.Properties.TextField = "Description";
+
+						var serializedModel = Newtonsoft.Json.JsonConvert.SerializeObject(Model);
+
+						a.CallbackRouteValues = new
+						{
+							Controller = "DataCallback",
+							Action = "KindsComboBox",
+							SettingsKey = "DeviationKindsComboBoxSettings",
+							Model = serializedModel,
+							Type = Model.GetType().FullName
+						};
+					}));
+					ViewData.Add("KindsComboBoxSettings", "DeviationKindsComboBoxSettings");
+					Html.RenderPartial("~/Views/Shared/DataEditorTemplates/_KindsComboBox.cshtml", Model, ViewData);
+
+					if (userContext.CurrentUser.HasFunction("Deviation-Claims-Edit"))
+					{
+						Html.DevExpress().HyperLink(t =>
+						{
+							t.Name = "devHyperLinkKindEventReceiver";
+							t.Properties.Text = "Hinzufügen";
+							t.NavigateUrl = "#";
+							t.Properties.ClientSideEvents.Click = "function (s, e) { addKind(); }";
+							t.Style.Add("line-height", "24px");
+						}).Render();
+					}
+				}
+				ViewContext.Writer.Write("</div>");
+			}
+			ViewContext.Writer.Write("</div>");
+
+			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Comment, "Kommentar:"));
+			ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Comment).ToHtmlString());
+			Html.DevExpress().MemoFor(m => m.Comment, t =>
+			{
+				t.Width = new Unit(100, UnitType.Percentage);
+				t.Height = new Unit(90, UnitType.Pixel);
+			}).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>

+ 80 - 0
GreenTree.Nachtragsmanagement.Web/Views/Sites/_SiteGridPartial.cshtml

@@ -0,0 +1,80 @@
+@model IEnumerable<GreenTree.Nachtragsmanagement.Web.Models.Site.SiteDataModel>
+
+@{ 
+	var userContext = GreenTree.Nachtragsmanagement.Core.CommonHelper.UserContext();
+}
+
+@Html.DevExpress().GridView(s =>
+{
+	s.Name = "devGridViewSite";
+	s.KeyFieldName = "Id";
+	s.CallbackRouteValues = new { Controller = "Site", Action = "PartialSites" };
+	s.Width = Unit.Percentage(100);
+	s.Settings.ShowFilterRow = true;
+	s.Settings.ShowFilterRowMenu = true;
+	s.Settings.ShowFooter = true;
+	s.Settings.ShowGroupPanel = true;
+
+	if (userContext.CurrentUser.HasFunction("Site-Sites-Edit"))
+	{
+		s.Columns.Add(column =>
+		{
+			column.Caption = "#";
+			column.SetDataItemTemplateContent(c =>
+			{
+				ViewContext.Writer.Write(
+					"<a href=\"#\" onclick=\"editSite(" + 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=\"editSite(-1)\">Neu</a>&nbsp;");
+			});
+			column.Settings.AllowDragDrop = DefaultBoolean.False;
+			column.Settings.AllowSort = DefaultBoolean.False;
+			column.Width = 70;
+		});
+	}
+	s.Columns.Add("CustomNumber", "Nummer");
+	s.Columns.Add("Description", "Bauvorhaben");
+	s.Columns.Add(column =>
+	{
+		column.Caption = "Start";
+		column.FieldName = "Start";
+		column.PropertiesEdit.DisplayFormatString = "dd.MM.yyyy";
+	});
+	s.Columns.Add(column =>
+	{
+		column.Caption = "Ende";
+		column.FieldName = "End";
+		column.PropertiesEdit.DisplayFormatString = "dd.MM.yyyy";
+	});
+	s.Columns.Add("DeviationDescription", "Vertragsabweichungen");
+	s.Columns.Add(column =>
+	{
+		column.Caption = "Wert";
+		column.FieldName = "DeviationValue";
+		column.PropertiesEdit.DisplayFormatString = "c2";
+	});
+	s.Columns.Add("AppendixDescription", "Nachträge");
+	s.Columns.Add("UserDescription", "Mitarbeiter");
+	s.Columns.Add("Comment", "Kommentar");
+
+	s.TotalSummary.Add(DevExpress.Data.SummaryItemType.Sum, "DeviationValue");
+	s.GroupSummary.Add(DevExpress.Data.SummaryItemType.Sum, "DeviationValue");
+
+	s.ClientLayout = (sender, e) =>
+	{
+		if (e.LayoutMode == ClientLayoutMode.Loading)
+		{
+			if (Session["SiteGridState"] != null)
+				e.LayoutData = (string)Session["SiteGridState"];
+		}
+		else
+			Session["SiteGridState"] = e.LayoutData;
+	};
+
+	s.Styles.AlternatingRow.BackColor = System.Drawing.Color.FromArgb(247, 247, 247);
+}).Bind(Model).GetHtml()