Arne Diekmann 5 anos atrás
pai
commit
b391eae558

+ 22 - 0
GreenTree.Strohrmann.ERP.Core/Extension/TypeExtension.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace GreenTree.Strohrmann.ERP.Core.Extension
+{
+    public static class TypeExtension
+    {
+        /// <summary>
+        /// Gets the default value of the given type
+        /// </summary>
+        /// <param name="type">The type.</param>
+        /// <returns>Default value of value type or NULL for reference type.</returns>
+        public static object GetDefaultValue(this Type type)
+        {
+            if (type.IsValueType && Nullable.GetUnderlyingType(type) == null)
+                return Activator.CreateInstance(type);
+
+            return null;
+        }
+    }
+}

+ 0 - 4
GreenTree.Strohrmann.ERP.Web/Controllers/CraftController.cs

@@ -67,7 +67,6 @@ namespace GreenTree.Strohrmann.ERP.Web.Controllers
         public ActionResult Create()
         {
             AddEmployeeListForSelection();
-            AddMaterialListForSelection();
 
             return View();
         }
@@ -80,7 +79,6 @@ namespace GreenTree.Strohrmann.ERP.Web.Controllers
             if (!ModelState.IsValid)
             {
                 AddEmployeeListForSelection();
-                AddMaterialListForSelection();
 
                 foreach (var item in model.CraftMaterials)
                 {
@@ -136,7 +134,6 @@ namespace GreenTree.Strohrmann.ERP.Web.Controllers
         public ActionResult Edit(int id)
         {
             AddEmployeeListForSelection();
-            AddMaterialListForSelection();
 
             var craft = _eRPDbContext.Crafts
                 .FirstOrDefault(c => c.Id == id);
@@ -154,7 +151,6 @@ namespace GreenTree.Strohrmann.ERP.Web.Controllers
             if (!ModelState.IsValid)
             {
                 AddEmployeeListForSelection();
-                AddMaterialListForSelection();
 
                 foreach (var item in model.CraftMaterials)
                 {

+ 16 - 8
GreenTree.Strohrmann.ERP.Web/Controllers/CustomerController.cs

@@ -165,7 +165,6 @@ namespace GreenTree.Strohrmann.ERP.Web.Controllers
 
         #region Partials
 
-
         // POST: CustomerController/Search/*term*
         [HttpPost]
         public ActionResult Search(SearchModel search)
@@ -173,16 +172,25 @@ namespace GreenTree.Strohrmann.ERP.Web.Controllers
             if (search == null)
                 return new JsonResult(null);
 
+            var result = new List<CustomerModel>();
+
             var words = search.SearchTerm.Split(' ');
 
-            var result = _eRPDbContext.Customers
-                .Where(c => words.Contains(c.Firstname) ||
-                            words.Contains(c.Lastname) ||
-                            words.Contains(c.CompanyName))
-                .ToArray()
-                .Select(c => new CustomerModel(c));
+            foreach (var word in words)
+            {
+                result.AddRange(
+                    _eRPDbContext.Customers
+                        .Where(c => c.Firstname.Contains(word, StringComparison.OrdinalIgnoreCase) ||
+                                    c.Lastname.Contains(word, StringComparison.OrdinalIgnoreCase) ||
+                                    c.CompanyName.Contains(word, StringComparison.OrdinalIgnoreCase))
+                        .ToArray()
+                        .Select(c => new CustomerModel(c)));
+            }
 
-            return new JsonResult(result);
+            return new JsonResult(
+                result
+                    .GroupBy(c => c.Id)
+                    .Select(g => g.First()));
         }
 
         #endregion

+ 33 - 0
GreenTree.Strohrmann.ERP.Web/Controllers/MaterialController.cs

@@ -10,6 +10,7 @@ using GreenTree.Strohrmann.ERP.Services.Geolocator;
 using GreenTree.Strohrmann.ERP.Web.Extension;
 using GreenTree.Strohrmann.ERP.Web.Models.Business;
 using GreenTree.Strohrmann.ERP.Web.Models.Rights.User;
+using GreenTree.Strohrmann.ERP.Web.Models.Shared;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
 
@@ -183,5 +184,37 @@ namespace GreenTree.Strohrmann.ERP.Web.Controllers
         }
 
         #endregion
+
+        #region Partials
+
+        // POST: MaterialController/Search/*term*
+        [HttpPost]
+        public ActionResult Search(SearchModel search)
+        {
+            if (search == null)
+                return new JsonResult(null);
+
+            var result = new List<MaterialModel>();
+
+            var words = search.SearchTerm.Split(' ');
+
+            foreach (var word in words)
+            {
+                result.AddRange(
+                    _eRPDbContext.Materials
+                        .Where(c => c.Name.Contains(word, StringComparison.OrdinalIgnoreCase) ||
+                                    c.ItemNumber.Contains(word, StringComparison.OrdinalIgnoreCase) ||
+                                    c.Description.Contains(word, StringComparison.OrdinalIgnoreCase))
+                        .ToArray()
+                        .Select(m => new MaterialModel(m)));
+            }
+
+            return new JsonResult(
+                result
+                    .GroupBy(m => m.Id)
+                    .Select(g => g.First()));
+        }
+
+        #endregion
     }
 }

+ 195 - 0
GreenTree.Strohrmann.ERP.Web/Extension/HtmlContentExtension.cs

@@ -12,6 +12,7 @@ using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Linq.Expressions;
+using System.Reflection;
 using System.Text;
 using System.Threading.Tasks;
 
@@ -246,6 +247,200 @@ namespace GreenTree.Strohrmann.ERP.Web.Extension
             return new HtmlString(htmlString);
         }
 
+        /// <summary>
+        /// Return HTML markup for the <paramref name="valueExpression"/>, using a display template for a search selection field.
+        /// </summary>
+        /// <typeparam name="TModel">Model type.</typeparam>
+        /// <typeparam name="TValue">Model value type.</typeparam>
+        /// <param name="htmlHelper">HtmlHelper context.</param>
+        /// <param name="valueExpression">Expression for the requested model value.</param>
+        /// <param name="textExpression">Expression for the requested model text.</param>
+        /// <param name="searchActionUrl">POST action URL that returns a model enumeration as JSON result.</param>
+        /// <param name="htmlAttributesValue">Additional HTML attributes for the value input.</param>
+        /// <param name="htmlAttributesText">Additional HTML attributes for the text input.</param>
+        /// <returns></returns>
+        public static IHtmlContent SearchFor<TModel, TValue>(
+            this IHtmlHelper<TModel> htmlHelper, 
+            Expression<Func<TModel, TValue>> valueExpression,
+            Expression<Func<TModel, string>> textExpression,
+            string searchActionUrl,
+            object htmlAttributesValue = null,
+            object htmlAttributesText = null)
+        {
+            var attributesText = htmlAttributesText == null
+                ? new string[0]
+                : htmlAttributesText.GetType().GetProperties()
+                    .Where(p => p.Name != "name")
+                    .Select(p => String.Format("{0}='{1}'", p.Name.Replace("_", "-"), p.GetValue(htmlAttributesText)))
+                    .ToArray();
+
+            var attributesValue = htmlAttributesValue == null
+                ? new string[0]
+                : htmlAttributesValue.GetType().GetProperties()
+                    .Where(p => p.Name != "name")
+                    .Select(p => String.Format("{0}='{1}'", p.Name.Replace("_", "-"), p.GetValue(htmlAttributesValue)))
+                    .ToArray();
+
+            var id = RandomId();
+
+            var valFunc = valueExpression.Compile();
+
+            var funcVal = htmlHelper.ViewData.Model == null
+                ? default
+                : valFunc(htmlHelper.ViewData.Model);
+
+            var isDefaultValue = funcVal == null || (funcVal != null && funcVal.ToString() == default(TValue).ToString());
+
+            var textFunc = textExpression.Compile();
+
+            var funcText = htmlHelper.ViewData.Model == null
+                ? default
+                : textFunc(htmlHelper.ViewData.Model);
+
+            var memberInfoText = textExpression.Body.NodeType == ExpressionType.MemberAccess
+                ? ((MemberExpression)textExpression.Body).Member
+                : null;
+
+            var nameText = memberInfoText == null
+                ? Guid.NewGuid().ToShortString()
+                : memberInfoText.Name;
+
+            if (htmlAttributesText != null && htmlAttributesText.GetType().GetProperty("name") != null)
+                nameText = htmlAttributesText.GetType().GetProperty("name").GetValue(htmlAttributesText).ToString();
+
+            var memberInfoValue = valueExpression.Body.NodeType == ExpressionType.MemberAccess
+                ? ((MemberExpression)valueExpression.Body).Member
+                : null;
+
+            var nameValue = memberInfoValue == null
+                ? Guid.NewGuid().ToShortString()
+                : memberInfoValue.Name;
+
+            if (htmlAttributesValue != null && htmlAttributesValue.GetType().GetProperty("name") != null)
+                nameValue = htmlAttributesValue.GetType().GetProperty("name").GetValue(htmlAttributesValue).ToString();
+
+            var scriptBuilder = new StringBuilder();
+            scriptBuilder.AppendFormat(
+                "<script type='text/javascript'>\n" +
+                "$(document).ready(function() {{\n" +
+                "   $(\"#text_{0}\").on('keypress', function (e) {{\n" +
+                "       if (e.keyCode == 13)\n" +
+                "       {{\n" +
+                "           search_{0}();\n" +
+                "           e.preventDefault();\n" +
+                "       }};\n" +
+                "   }});\n" +
+                "   $(\"#text_{0}\").on('keydown', function (e) {{\n" +
+                "       if (e.keyCode == 8 && $(\"#value_{0}\").val() != '') {{\n" +
+                "           $(\"#text_{0}\").val('');\n" +
+                "           $(\"#value_{0}\").val('');\n" +
+                "           $(\"#text_{0}\").next().children('.fa-check-circle').fadeOut('fast', function() {{\n" +
+                "               $(\"#text_{0}\").next().children(\".fa-ellipsis-h\").fadeIn('fast');\n" +
+                "           }});\n" +
+                "           e.preventDefault();\n" +
+                "       }};\n" +
+                "   }});\n" +
+                "}});\n" +
+                "\n" +
+                "function search_{0}()\n" +
+                "{{\n" +
+                "   $.ajax({{\n" +
+                "       method: 'POST',\n" +
+                "       url: '{1}',\n" +
+                "       data:\n" +
+                "       {{\n" +
+                "           SearchTerm: $('#text_{0}').val()\n" +
+                "       }},\n" +
+                "       success: function(data) {{\n" +
+                "           $(\"#searchModal_{0}\").find('.list-group').empty();\n" +
+                "           if (data.length > 0)\n" +
+                "           {{\n" +
+                "               $(data).each(function(index, elem) {{\n" +
+                "                   $(\"#searchModal_{0}\").find('.list-group').append(\n" +
+                "                       \"<a href='#' class='list-group-item list-group-item-info list-group-item-action'" +
+                "                            data-toggle='list' data-val='{{0}}' data-text='{{1}}' ondblclick='select_{0}()'>{{1}}</a>\"\n" +
+                "                           .format(elem.searchId, elem.searchText));\n" +
+                "               }});\n" +
+                "           }}\n" +
+                "           else\n" +
+                "           {{\n" +
+                "           $(\"#searchModal_{0}\").find('.list-group').append(\n" +
+                "               \"'<button type='button' class='list-group-item'>Keine Treffer</button>'\");\n" +
+                "           }}\n" +
+                "           $(\"#searchModal_{0}\").modal('show');\n" +
+                "       }},\n" +
+                "       error: function(msg) {{\n" +
+                "           \n" +
+                "       }}\n" +
+                "   }});\n" +
+                "}}\n" +
+                "\n" +
+                "function select_{0}()\n" +
+                "{{\n" +
+                "   $(\"#searchModal_{0}\").modal('hide');\n" +
+                "   var val = $(\"#searchModal_{0}\").find('a.active').attr('data-val');\n" +
+                "   var text = $(\"#searchModal_{0}\").find('a.active').attr('data-text');\n" +
+                "   $(\"#value_{0}\").val(val);\n" +
+                "   $(\"#text_{0}\").val(text);\n" +
+                "   $(\"#text_{0}\").next().children('.fa-ellipsis-h').fadeOut('fast', function() {{\n" +
+                "       $(\"#text_{0}\").next().children('.fa-check-circle').fadeIn('fast');\n" +
+                "       $(\"#text_{0}\").focus();\n" +
+                "   }});\n" +
+                "}}" +
+                "</script>",
+                id, 
+                searchActionUrl
+            );
+
+            var contentBuilder = new StringBuilder();
+            contentBuilder.AppendFormat(
+                "<div class='input-group'>\n" +
+                    "<div class='input-group-prepend'>\n" +
+                        "<button class='btn btn-info fas fa-search' type='button' onclick='search_{0}()'></button>\n" +
+                    "</div>\n" +
+                    "<input id='text_{0}' type='text' name='{1}' value='{2}' class='form-control' placeholder='Suchbegriff'\n" +
+                    "       aria-label='Suchbegriff' aria-describedby='basic-addon1' {5} />\n" +
+                    "<div class='input-group-append'>\n" +
+                    "   <span class='input-group-text bg-success text-white rounded-right fas fa-cust-lh fa-check-circle' {7}></span>\n" +
+                    "   <span class='input-group-text rounded-right fas fa-cust-lh fa-ellipsis-h' {8}></span>\n" +
+                    "</div>\n" +
+                "</div>\n" +
+                "\n" +
+                "<input id='value_{0}' type='hidden' name='{3}' value='{4}' {6} />\n" +
+                "\n" +
+                "<div id='searchModal_{0}' class='modal fade'>\n" +
+                    "<div class='modal-dialog'>\n" +
+                        "<div class='modal-content'>\n" +
+                            "<div class='modal-header'>\n" +
+                                "<h4 class='modal-title'>Suchergebnisse</h4>\n" +
+                            "</div>\n" +
+                            "<div class='modal-body overflow-auto' style='max-height: 400px'>\n" +
+                                "<div class='list-group'>\n" +
+                                "</div>\n" +
+                            "</div>\n" +
+                            "<div class='modal-footer'>\n" +
+                                "<div class='btn btn-secondary' data-dismiss='modal'>Schließen</div>\n" +
+                                "<div class='btn btn-success' onclick='select_{0}()'>Auswählen</div>\n" +
+                            "</div>\n" +
+                        "</div>\n" +
+                    "</div>\n" +
+                "</div>",
+                id,
+                nameText,
+                funcText,
+                nameValue,
+                funcVal,
+                String.Join(" ", attributesText),
+                String.Join(" ", attributesValue),
+                (htmlHelper.ViewData.Model == null || isDefaultValue ? "style='display: none'" : String.Empty),
+                (htmlHelper.ViewData.Model == null || isDefaultValue ? String.Empty : "style='display: none'")
+            );
+
+            var htmlString = String.Format("{0}\n{1}", scriptBuilder.ToString(), contentBuilder.ToString());
+
+            return new HtmlString(htmlString);
+        }
+
         #endregion
 
         #region Helper

+ 7 - 0
GreenTree.Strohrmann.ERP.Web/Models/Business/CraftMaterialModel.cs

@@ -46,6 +46,12 @@ namespace GreenTree.Strohrmann.ERP.Web.Models.Business
         [Display(Name = "Material")]
         public MaterialModel Material { get; set; }
 
+        /// <summary>
+        /// Material
+        /// </summary>
+        [Display(Name = "Material")]
+        public string MaterialText { get; set; }
+
         /// <summary>
         /// The amount of the material
         /// </summary>
@@ -86,6 +92,7 @@ namespace GreenTree.Strohrmann.ERP.Web.Models.Business
             CraftId = craftMaterial.CraftId;
             MaterialId = craftMaterial.MaterialId;
             Amount = craftMaterial.Amount;
+            MaterialText = craftMaterial.Material.Name;
             CalculationFactor = craftMaterial.CalculationFactor;
             Value = Convert.ToSingle(craftMaterial.Value);
         }

+ 14 - 2
GreenTree.Strohrmann.ERP.Web/Models/Business/CraftModel.cs

@@ -1,6 +1,5 @@
 using GreenTree.Strohrmann.ERP.Core.Domain.Business;
 using GreenTree.Strohrmann.ERP.Web.Extension;
-using GreenTree.Strohrmann.ERP.Web.Models.Search;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Mvc.ViewFeatures;
 using Microsoft.EntityFrameworkCore.Internal;
@@ -12,7 +11,7 @@ using System.Threading.Tasks;
 
 namespace GreenTree.Strohrmann.ERP.Web.Models.Business
 {
-    public class CraftModel : ICustomerSearchable
+    public class CraftModel
     {
         #region Properties
 
@@ -97,6 +96,19 @@ namespace GreenTree.Strohrmann.ERP.Web.Models.Business
             }
         }
 
+        /// <summary>
+        /// Craft value sum
+        /// </summary>
+        [Display(Name = "Wert (Gesamt)")]
+        [DisplayFormat(DataFormatString = "{0:c2}")]
+        public float CraftValue
+        {
+            get
+            {
+                return CraftEmployeeValue + CraftMaterialValue;
+            }
+        }
+
         #endregion
 
         #region Ctor

+ 27 - 1
GreenTree.Strohrmann.ERP.Web/Models/Business/CustomerModel.cs

@@ -8,7 +8,7 @@ using System.Threading.Tasks;
 
 namespace GreenTree.Strohrmann.ERP.Web.Models.Business
 {
-    public class CustomerModel : TrackingModel
+    public class CustomerModel : TrackingModel, ISearchResult
     {
         #region Properties
 
@@ -110,6 +110,32 @@ namespace GreenTree.Strohrmann.ERP.Web.Models.Business
 
         #endregion
 
+        #region Search result
+
+        /// <summary>
+        /// Search result id
+        /// </summary>
+        public object SearchId 
+        { 
+            get
+            {
+                return Id;
+            }
+        }
+
+        /// <summary>
+        /// Search result text
+        /// </summary>
+        public string SearchText
+        { 
+            get
+            {
+                return String.Format("{0} ({1})", Fullname, LocalAddress);
+            }
+        }
+
+        #endregion
+
         #region Ctor
 
         /// <summary>

+ 27 - 1
GreenTree.Strohrmann.ERP.Web/Models/Business/MaterialModel.cs

@@ -8,7 +8,7 @@ using System.Threading.Tasks;
 
 namespace GreenTree.Strohrmann.ERP.Web.Models.Business
 {
-    public class MaterialModel : TrackingModel
+    public class MaterialModel : TrackingModel, ISearchResult
     {
         #region Properties
 
@@ -93,6 +93,32 @@ namespace GreenTree.Strohrmann.ERP.Web.Models.Business
 
         #endregion
 
+        #region Search result
+
+        /// <summary>
+        /// Search result id
+        /// </summary>
+        public object SearchId 
+        { 
+            get
+            {
+                return Id;
+            }
+        }
+
+        /// <summary>
+        /// Search result text
+        /// </summary>
+        public string SearchText
+        { 
+            get
+            {
+                return String.Format("{0} ({1})", Name, ItemNumber);
+            }
+        }
+
+        #endregion
+
         #region Ctor
 
         /// <summary>

+ 0 - 23
GreenTree.Strohrmann.ERP.Web/Models/Search/ICustomerSearchable.cs

@@ -1,23 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.Linq;
-using System.Threading.Tasks;
-
-namespace GreenTree.Strohrmann.ERP.Web.Models.Search
-{
-    public interface ICustomerSearchable
-    {
-        /// <summary>
-        /// Model class searchable for customer id
-        /// </summary>
-        [Display(Name = "Kunde")]
-        public int CustomerId { get; set; }
-
-        /// <summary>
-        /// Model class searchable for customer text
-        /// </summary>
-        [Display(Name = "Kundenname")]
-        public string CustomerText { get; set; }
-    }
-}

+ 20 - 0
GreenTree.Strohrmann.ERP.Web/Models/Shared/ISearchResult.cs

@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace GreenTree.Strohrmann.ERP.Web.Models.Shared
+{
+    public interface ISearchResult
+    {
+        /// <summary>
+        /// Search result id
+        /// </summary>
+        public object SearchId { get; }
+
+        /// <summary>
+        /// Search result text
+        /// </summary>
+        public string SearchText { get; }
+    }
+}

+ 5 - 1
GreenTree.Strohrmann.ERP.Web/Views/Craft/Create.cshtml

@@ -35,7 +35,11 @@
                     </div>
                     <div class="form-group">
                         <label asp-for="CustomerId" class="control-label"></label>
-                        @await Html.PartialAsync("~/Views/Customer/_SearchCustomerPartial.cshtml")
+                        @Html.SearchFor(
+                            m => m.CustomerId, 
+                            m => m.CustomerText, 
+                            Url.Action("Search", "Customer")
+                        )
                         <span asp-validation-for="CustomerId" class="text-danger"></span>
                     </div>
                     <div class="form-group">

+ 5 - 1
GreenTree.Strohrmann.ERP.Web/Views/Craft/Edit.cshtml

@@ -37,7 +37,11 @@
                     </div>
                     <div class="form-group">
                         <label asp-for="CustomerId" class="control-label"></label>
-                        @await Html.PartialAsync("~/Views/Customer/_SearchCustomerPartial.cshtml")
+                        @Html.SearchFor(
+                            m => m.CustomerId, 
+                            m => m.CustomerText, 
+                            Url.Action("Search", "Customer")
+                        )
                         <span asp-validation-for="CustomerId" class="text-danger"></span>
                     </div>
                     <div class="form-group">

+ 6 - 0
GreenTree.Strohrmann.ERP.Web/Views/Craft/Index.cshtml

@@ -88,6 +88,9 @@
             <th class="text-right">
                 @Html.DisplayNameFor(model => model.CraftMaterialValue)
             </th>
+            <th class="text-right">
+                @Html.DisplayNameFor(model => model.CraftValue)
+            </th>
             <th data-priority="1">
                 Aktionen
             </th>
@@ -117,6 +120,9 @@
             <td class="text-right">
                 @Html.DisplayFor(modelItem => item.CraftMaterialValue)
             </td>
+            <td class="text-right">
+                @Html.DisplayFor(modelItem => item.CraftValue)
+            </td>
             <td>
                 @Html.ActionLink("Bearbeiten", "Edit", new { id = item.Id }) |
                 @Html.ActionLink("Anzeigen", "Details", new { id = item.Id }) |

+ 2 - 2
GreenTree.Strohrmann.ERP.Web/Views/Craft/_CraftEmployeePartial.cshtml

@@ -40,7 +40,7 @@
             <label asp-for="EmployeeId" class="control-label"></label>
 		}
         <select asp-items='(IEnumerable<SelectListItem>)ViewData["AvailableEmployees"]' 
-                name="CraftEmployees[@id].EmployeeId" class="form-control" 
+                asp-for="EmployeeId" name="CraftEmployees[@id].EmployeeId" class="form-control" 
                 onchange="calculateEmployeeAggregation(); recalculateEmployeeValue_@id ();">
         </select>
         @Html.ValidationMessage(String.Format("CraftEmployees[{0}].EmployeeId", id), new { @class = "text-danger" })
@@ -56,7 +56,7 @@
             </div>
             <input name="CraftEmployees[@id].Amount" type="number" min="0.00" step="0.50" class="form-control"
                    id="CraftEmployees[@id].Amount" data-aggregation="employeeAmount"
-                   value="@Model.Amount.ToString("G29")"
+                   value="@(Model == null ? "0,00" : Model.Amount.ToString("G29"))"
                    onchange="calculateEmployeeAggregation(); recalculateEmployeeValue_@id ();" />
         </div>
         @Html.ValidationMessage(String.Format("CraftEmployees[{0}].Amount", id), new { @class = "text-danger" })

+ 14 - 7
GreenTree.Strohrmann.ERP.Web/Views/Craft/_CraftMaterialPartial.cshtml

@@ -14,7 +14,7 @@
                 method: "POST",
                 url: "@Url.Action("CalculateMaterialCalculationFactor", "Craft")",
                 data: {
-                    MaterialId: $("select[name='CraftMaterials[@id].MaterialId']").val(),
+                    MaterialId: $("input[name='CraftMaterials[@id].MaterialId']").val(),
                     Amount: $("input[name='CraftMaterials[@id].Amount']").val().replace('.', ','),
                     Value: $("input[name='CraftMaterials[@id].Value']").val()
                 },
@@ -31,7 +31,7 @@
                 method: "POST",
                 url: "@Url.Action("CalculateMaterialValue", "Craft")",
                 data: {
-                    MaterialId: $("select[name='CraftMaterials[@id].MaterialId']").val(),
+                    MaterialId: $("input[name='CraftMaterials[@id].MaterialId']").val(),
                     Amount: $("input[name='CraftMaterials[@id].Amount']").val().replace('.', ','),
                     CalculationFactor: $("input[name='CraftMaterials[@id].CalculationFactor']").val()
                 },
@@ -59,10 +59,17 @@
 		{
             <label asp-for="MaterialId" class="control-label"></label>
 		}
-        <select asp-items='(IEnumerable<SelectListItem>)ViewData["AvailableMaterials"]' 
-                name="CraftMaterials[@id].MaterialId" class="form-control" 
-                onchange="calculateMaterialAggregation(); recalculateMaterialValue_@id (this);">
-        </select>
+        @Html.SearchFor(
+            m => m.MaterialId, 
+            m => m.MaterialText, 
+            Url.Action("Search", "Material"), 
+            new { name = String.Format("CraftMaterials[{0}].MaterialId", id) }, 
+            new 
+            { 
+                name = String.Format("CraftMaterials[{0}].MaterialText", id), 
+                onblur = String.Format("calculateMaterialAggregation(); recalculateMaterialValue_{0}(this);", id) 
+            }
+        )
         @Html.ValidationMessage(String.Format("CraftMaterials[{0}].MaterialId", id), new { @class = "text-danger" })
     </div>
     <div class="form-group col-md-2">
@@ -76,7 +83,7 @@
             </div>
             <input name="CraftMaterials[@id].Amount" type="number" min="0.00" step="0.50" class="form-control"
                    id="CraftMaterials[@id].Amount"
-                   value="@Model.Amount.ToString("G29")"
+                   value="@(Model == null ? "0.00" : Model.Amount.ToString("G29"))"
                    onchange="calculateMaterialAggregation(); recalculateMaterialValue_@id (this);" />
         </div>
         @Html.ValidationMessage(String.Format("CraftMaterials[{0}].Amount", id), new { @class = "text-danger" })

+ 2 - 2
GreenTree.Strohrmann.ERP.Web/Views/Craft/_CraftScriptPartial.cshtml

@@ -13,7 +13,7 @@
         }).get().sum();
 
         $("#aggEmployeeCount").text(count);
-        $("#aggEmployeeAmountAvg").text(isNaN(avgAmount) ? " -" : avgAmount.toFixed(2) + " Std.");
+        $("#aggEmployeeAmountAvg").text(isNaN(avgAmount) ? " -" : avgAmount.toUntrailingFixed(2) + " Std.");
         $("#aggEmployeeValueSum").text(isNaN(sumValues) ? " -" : sumValues + " €");
     }
 
@@ -29,7 +29,7 @@
         }).get().sum();
 
         $("#aggMaterialCount").text(count);
-        $("#aggMaterialCalculationFactorAvg").text(isNaN(avgCalculationFactor) ? " -" : avgCalculationFactor.toFixed(2) + " %");
+        $("#aggMaterialCalculationFactorAvg").text(isNaN(avgCalculationFactor) ? " -" : avgCalculationFactor.toUntrailingFixed(2) + " %");
         $("#aggMaterialValueSum").text(isNaN(sumValues) ? " -" : sumValues + " €");
     }
 

+ 0 - 112
GreenTree.Strohrmann.ERP.Web/Views/Customer/_SearchCustomerPartial.cshtml

@@ -1,112 +0,0 @@
-@model GreenTree.Strohrmann.ERP.Web.Models.Search.ICustomerSearchable
-
-@{ 
-	var partialId = Html.RandomId();
-}
-
-<script type="text/javascript">
-
-@{ 
-    <text>
-    $(document).ready(function () {
-        $("#text_@partialId").on('keypress', function (e) {
-            if (e.keyCode == 13) {
-                search_@partialId ();
-                e.preventDefault();
-            };
-        });
-        $("#text_@partialId").on('keydown', function (e) {
-            if (e.keyCode == 8 && $("#value_@partialId").val() != "") {
-                $("#text_@partialId").val("");
-                $("#value_@partialId").val("");
-                $("#text_@partialId").next().children(".fa-check-circle").fadeOut("fast", function () {
-                    $("#text_@partialId").next().children(".fa-ellipsis-h").fadeIn("fast");
-                });
-                e.preventDefault();
-            };
-        });
-    });
-
-    function search_@partialId () {
-        $.ajax({
-            method: "POST",
-            url: "@Url.Action("Search", "Customer")",
-            data: {
-                SearchTerm: $("#text_@partialId").val()
-            },
-            success: function (data) {
-                $("#searchCustomerModal_@partialId").find(".list-group").empty();
-                if (data.length > 0) {
-                    $(data).each(function (index, elem) {
-                        $("#searchCustomerModal_@partialId").find(".list-group").append(
-                            '<a href="#" class="list-group-item list-group-item-info list-group-item-action" ' +
-                            'data-toggle="list" data-val="{0}" data-text="{1}" ondblclick="select_@partialId ()">{1} ({2})</a>'
-                                .format(elem.id, elem.fullname, elem.localAddress));
-                    });
-                } else {
-                    $("#searchCustomerModal_@partialId").find(".list-group").append(
-                        '<button type="button" class="list-group-item">Keine Treffer</button>');
-				}
-                $("#searchCustomerModal_@partialId").modal("show");
-            },
-            error: function (msg) {
-
-            }
-        });
-    }
-
-    function select_@partialId () {
-        $("#searchCustomerModal_@partialId").modal("hide");
-        var val = $("#searchCustomerModal_@partialId").find("a.active").attr("data-val");
-        var text = $("#searchCustomerModal_@partialId").find("a.active").attr("data-text");
-        $("#value_@partialId").val(val);
-        $("#text_@partialId").val(text);
-        $("#text_@partialId").next().children(".fa-ellipsis-h").fadeOut("fast", function () {
-            $("#text_@partialId").next().children(".fa-check-circle").fadeIn("fast");
-        });
-	}
-            
-    </text>
-}
-
-</script>
-
-<div class="input-group">
-    <div class="input-group-prepend">
-        <button class="btn btn-info fas fa-search" type="button" onclick="search_@partialId ()"></button>
-    </div>
-    <input id="text_@partialId" type="text" asp-for="CustomerText" class="form-control" placeholder="Suchbegriff" 
-           aria-label="Suchbegriff" aria-describedby="basic-addon1">
-    <div class="input-group-append">
-        @if (Model.CustomerId > 0)
-		{
-            @Html.Span("", new { @class = "input-group-text bg-success text-white rounded-right fas fa-cust-lh fa-check-circle" })
-            @Html.Span("", new { @class = "input-group-text rounded-right fas fa-cust-lh fa-ellipsis-h", style = "display: none" })
-		}
-		else
-		{
-            @Html.Span("", new { @class = "input-group-text bg-success text-white rounded-right fas fa-cust-lh fa-check-circle", style = "display: none" })
-            @Html.Span("", new { @class = "input-group-text rounded-right fas fa-cust-lh fa-ellipsis-h" })
-		}
-    </div>
-</div>
-
-<input id="value_@partialId" type="hidden" asp-for="CustomerId" />
-
-<div id="searchCustomerModal_@partialId" class="modal fade">
-    <div class="modal-dialog">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title">Suchergebnisse</h4>
-            </div>
-            <div class="modal-body overflow-auto" style="max-height: 400px">
-                <div class="list-group">
-                </div>
-            </div>
-            <div class="modal-footer">
-                <div class="btn btn-secondary" data-dismiss="modal">Schließen</div>
-                <div class="btn btn-success" onclick="select_@partialId ()">Auswählen</div>
-            </div>
-        </div>
-    </div>
-</div>

+ 7 - 1
GreenTree.Strohrmann.ERP.Web/wwwroot/js/extension.js

@@ -36,4 +36,10 @@ Array.prototype.avg = function () {
         r += this[i];
     }
     return r / this.length;
-};
+};
+
+//************** Number extension *************//
+
+Number.prototype.toUntrailingFixed = function (decimalPlaces) {
+    return (this).toFixed(decimalPlaces).replace(/([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/, '$1');
+}