This commit is contained in:
Roland Drabesch
2023-05-08 14:43:59 +02:00
121 changed files with 10264 additions and 573 deletions
+5 -2
View File
@@ -1,4 +1,4 @@
## Ignore Visual Studio temporary files, build results, and
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
@@ -388,4 +388,7 @@ FodyWeavers.xsd
# JetBrains Rider
.idea/
*.sln.iml
© 2021 GitHub, Inc.
© 2021 GitHub, Inc.
*.config
+1 -1
View File
@@ -155,7 +155,7 @@
</site>
<site name="MeterProcessState" id="2">
<application path="/" applicationPool="Clr4IntegratedAppPool">
<virtualDirectory path="/" physicalPath="C:\Users\drabesch_ro\Repo\lab\la_operations\laa_production\Common\Service\MeterProcessState" />
<virtualDirectory path="/" physicalPath="C:\Users\SZLATEV\Source\Repos\laa_production\Common\Service\MeterProcessState" />
</application>
<bindings>
<binding protocol="http" bindingInformation="*:56011:localhost" />
@@ -1 +1 @@
5ed75d99ee45ff67821090f1b3dbb001dd9e4fe3
2060865c94a192d7a90bef853b7068b66f669cb3
@@ -1 +1 @@
0d85a5b3a7561b41163c20e5dc7713bcd0d65555
590ff54dbee38af4239a7555d24927a48da34616
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="nunit" Version="3.10.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LaaProductionWeb.API\LaaProductionWeb.API.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Mvc.Abstractions">
<HintPath>..\..\..\..\..\..\..\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.aspnetcore.mvc.abstractions\2.1.1\lib\netstandard2.0\Microsoft.AspNetCore.Mvc.Abstractions.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
@@ -0,0 +1,171 @@
namespace LaaProductionWeb.Tests.Models
{
using LaaProductionWeb.API.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using NUnit.Framework;
using System.Linq;
using System.Reflection;
public class ModelResultTests
{
[Test]
public void ModelResult_ShouldBeImplicitlyCreated_FromModelStateDictionary()
{
// Act
ModelResult<string> modelResult = default(ModelStateDictionary);
// Assert
Assert.IsNotNull(modelResult);
}
[Test]
public void ModelResult_ShouldInitializeModelErrors_WhenCreatedFrom_ModelStateDictionary()
{
// Act
ModelResult<string> modelResult = default(ModelStateDictionary);
// Assert
Assert.IsNotNull(modelResult.ModelErrors);
}
[Test]
public void ModelResult_ShouldNotSucceeded_WhenCreatedFrom_ModelStateDictionary()
{
// Act
ModelResult<string> modelResult = default(ModelStateDictionary);
// Assert
Assert.IsFalse(modelResult.Succeeded);
}
[Test]
public void ModelResult_ShouldBeImplicitlyCreated_FromOtherObjects()
{
// Act
ModelResult<dynamic> modelResult = default(object);
// Assert
Assert.IsNotNull(modelResult);
}
[Test]
public void ModelResult_ShouldSucceeded_WhenCreated_FromOtherObjects()
{
// Act
ModelResult<dynamic> modelResult = default(object);
// Assert
Assert.IsTrue(modelResult.Succeeded);
}
[Test]
public void ModelResult_ShouldHaveSameValue_WhenCreated_FromOtherObjects()
{
// Arrange
var value = nameof(ModelResult<dynamic>);
ModelResult<string> modelResult = value;
// Act
var fieldValue = typeof(ModelResult<string>)
.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
.FirstOrDefault(x => x.Name == nameof(value))
?.GetValue(modelResult);
// Assert
Assert.AreEqual(value, fieldValue);
}
[Test]
public void ModelResult_AddModelError_ShuldNotAddError_WhenKeyIsNull()
{
// Arrange
ModelResult<dynamic> modelResult = default(ModelStateDictionary);
var modelErrors = modelResult.ModelErrors;
// Act
var errorsCountBefore = modelErrors.Count;
modelResult.AddModelError(null, null);
var errorsCountAfter = modelErrors.Count;
// Assert
Assert.AreEqual(errorsCountBefore, errorsCountAfter);
}
[Test]
public void ModelResult_AddModelError_ShuldAddErrors_WhenKeyIsNotNull()
{
// Arrange
ModelResult<dynamic> modelResult = default(ModelStateDictionary);
var modelErrors = modelResult.ModelErrors;
// Act
var errorsCountBefore = modelErrors.Count;
modelResult.AddModelError(string.Empty, null);
var errorsCountAfter = modelErrors.Count;
// Assert
Assert.Greater(errorsCountAfter, errorsCountBefore);
}
[Test]
public void ModelResult_AddModelError_ShuldOverrideError_WhenKeyExists()
{
// Arrange
ModelResult<dynamic> modelResult = default(ModelStateDictionary);
var modelErrors = modelResult.ModelErrors;
// Act
modelResult.AddModelError(string.Empty, "1");
var valueBefore = modelErrors[string.Empty];
modelResult.AddModelError(string.Empty, "2");
var valueAfter = modelErrors[string.Empty];
// Assert
Assert.AreNotEqual(valueBefore, valueAfter);
}
[Test]
public void ModelResult_ReturnsNotFoundResult_WhenModelResult_IsNull()
{
// Arrange
ModelResult<dynamic> modelResult = null;
// Act
ActionResult actionResult = modelResult;
// Assert
Assert.AreEqual(actionResult.GetType(), typeof(NotFoundResult));
}
[Test]
public void ModelResult_ReturnsBadRequestObjectResult_WhenCreatedFrom_ModelStateDictionary()
{
// Arrange
ModelResult<dynamic> modelResult = default(ModelStateDictionary);
// Act
ActionResult actionResult = modelResult;
// Assert
Assert.AreEqual(actionResult.GetType(), typeof(BadRequestObjectResult));
}
[Test]
public void ModelResult_ReturnsOkObjectResult_WhenCreatedWhitValue()
{
// Arrange
ModelResult<dynamic> modelResult = default(string);
// Act
ActionResult actionResult = modelResult;
// Assert
Assert.AreEqual(actionResult.GetType(), typeof(OkObjectResult));
}
}
}
@@ -0,0 +1,145 @@
namespace LaaProductionWeb.Tests.Models
{
using LaaProductionWeb.API.Areas.Serach.Models;
using NUnit.Framework;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
public class WildcardInputModelTests
{
private readonly WildcardInputModel model;
private readonly PropertyInfo tokenPropertyInfo;
public WildcardInputModelTests()
{
this.model = new WildcardInputModel();
this.tokenPropertyInfo = typeof(WildcardInputModel)
.GetProperty(nameof(WildcardInputModel.Token), BindingFlags.Instance | BindingFlags.Public);
}
[Test]
public void ModelValidation_ShouldNotValidate_WhenTokenIsNull()
{
// Arrange
var errorMessage = this.ValidateModelWhitValue(default(string));
// Act
var attributeMessage = this.GetAttributeMessage<RequiredAttribute>();
// Assert
Assert.AreEqual(errorMessage, attributeMessage);
}
[Test]
public void ModelValidation_ShouldNotValidate_WhenTokenIsUnder_1CharLength()
{
// Arrange
var errorMessage = this.ValidateModelWhitValue(string.Empty);
// Act
var attributeMessage = this.GetAttributeMessage<StringLengthAttribute>();
// Assert
Assert.AreEqual(errorMessage, attributeMessage);
}
[Test]
public void ModelValidation_ShouldNotValidate_WhenTokenIsOver_50CharsLength()
{
// Arrange
var errorMessage = this.ValidateModelWhitValue(new string('0', 51));
// Act
var attributeMessage = this.GetAttributeMessage<StringLengthAttribute>();
// Assert
Assert.AreEqual(errorMessage, attributeMessage);
}
[Test]
[TestCase("^")]
[TestCase("°")]
[TestCase("\"")]
[TestCase("§")]
[TestCase("$")]
[TestCase("%")]
[TestCase("&")]
[TestCase("/")]
[TestCase("(")]
[TestCase(")")]
[TestCase("=")]
[TestCase("?")]
[TestCase("`")]
[TestCase("´")]
[TestCase("'")]
[TestCase("#")]
[TestCase("*")]
[TestCase("+")]
[TestCase("|")]
[TestCase("\t")]
[TestCase("\n")]
[TestCase("<")]
[TestCase(">")]
[TestCase(".")]
[TestCase(";")]
[TestCase(":")]
[TestCase("_")]
[TestCase("ä")]
[TestCase("ö")]
[TestCase("ü")]
[TestCase("Ä")]
[TestCase("Ö")]
[TestCase("Ü")]
[TestCase("ß")]
public void ModelValidation_ShouldNotValidate_WhenTokenContains_NonWordCharachters(string token)
{
// Arrange
var errorMessage = this.ValidateModelWhitValue(token);
// Act
var attributeMessage = this.GetAttributeMessage<RegularExpressionAttribute>();
// Assert
Assert.AreEqual(errorMessage, attributeMessage);
}
[Test]
[TestCase("0123245 5575689-ARWQGVVJALR fasdkfawe irfoelvm")]
public void ModelValidation_ShouldValidate_WhenTokenIsValidNr(string token)
{
// Arrange
var nullMessage = default(string);
// Act
var errorMessage = this.ValidateModelWhitValue(token);
// Assert
Assert.AreEqual(errorMessage, nullMessage);
}
private string ValidateModelWhitValue(string value)
{
var validationResults = new List<ValidationResult>();
var validationContext = new ValidationContext(this.model);
if (Validator.TryValidateObject(this.model, validationContext, validationResults))
{
return validationResults
.Where(x => x.MemberNames.Contains(nameof(WildcardInputModel.Token)))
.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x.ErrorMessage))
?.ErrorMessage ?? default(string);
}
return default(string);
}
private string GetAttributeMessage<T>() where T : ValidationAttribute
=> this.tokenPropertyInfo
.GetCustomAttribute<RequiredAttribute>()
.ErrorMessage;
}
}
@@ -0,0 +1,10 @@
namespace LaaProductionWeb.API.Areas
{
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("[area]/[controller]")]
public class AreaController : ControllerBase
{
}
}
@@ -0,0 +1,23 @@
namespace LaaProductionWeb.API.Areas.Serach.Controllers
{
using LaaProductionWeb.API.Areas.Serach.Models;
using LaaProductionWeb.API.Areas.Serach.Models.Interfaces;
using Microsoft.AspNetCore.Mvc;
public class WildcardController : SearchController
{
private readonly IWildcardModel wildcardModel;
public WildcardController(IWildcardModel wildcardModel)
=> this.wildcardModel = wildcardModel;
[HttpOptions]
public ActionResult Options()
=> this.wildcardModel.Options();
[HttpGet]
public ActionResult Get([FromQuery] WildcardInputModel model)
=> this.wildcardModel.Find(this.ModelState, model);
}
}
@@ -0,0 +1,14 @@
namespace LaaProductionWeb.API.Areas.Serach.Models.Interfaces
{
using LaaProductionWeb.API.Models;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System.Collections.Generic;
public interface IWildcardModel
{
ModelResult<IEnumerable<WildcardResult>> Find(ModelStateDictionary validationState, WildcardInputModel model);
ModelResult<IEnumerable<string>> Options();
}
}
@@ -0,0 +1,33 @@
namespace LaaProductionWeb.API.Areas.Serach.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
public class WildcardInputModel
{
private IEnumerable<string> fields = Array.Empty<string>();
[Required(ErrorMessageResourceType = typeof(ErrorMessages), ErrorMessageResourceName = nameof(ErrorMessages.Required))]
[StringLength(50, ErrorMessageResourceType = typeof(ErrorMessages), ErrorMessageResourceName = nameof(ErrorMessages.StringLength), MinimumLength = 1)]
[RegularExpression("^[0-9 a-z-A-Z]+$", ErrorMessageResourceType = typeof(ErrorMessages), ErrorMessageResourceName = nameof(ErrorMessages.RegularExpression))]
public string Token { get; set; }
public string Fields
{
get => string.Join(",", this.fields);
set => this.fields = $"{value}".Split(',', StringSplitOptions.RemoveEmptyEntries);
}
internal IEnumerable<string> GetRequiredFields(IEnumerable<string> allFields)
{
if (this.fields.Any())
{
return allFields.Where(x => this.fields.Contains(x, StringComparer.OrdinalIgnoreCase));
}
return allFields;
}
}
}
@@ -0,0 +1,136 @@
namespace LaaProductionWeb.API.Areas.Serach.Models
{
using LaaProductionWeb.Data.Interfaces;
using LaaProductionWeb.API.Areas.Serach.Models.Interfaces;
using LaaProductionWeb.API.Models;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System.Collections.Generic;
using System.Linq;
public class WildcardModel : IWildcardModel
{
public static readonly IEnumerable<string> fields = new[]
{
"Client",
"OrderNo",
"PosNo",
"SerialNo",
"ClientSerialNo",
"KurzBez",
"Typ",
"Nennweite",
"Baulaenge",
"Quantity",
"FabricNo",
"IdentNr",
"PcbId",
"ERAddress",
"GMAddress",
};
private readonly ISqlClient sqlClient;
public WildcardModel(ISqlClient sqlClient)
=> this.sqlClient = sqlClient;
public ModelResult<IEnumerable<WildcardResult>> Find(ModelStateDictionary validationState, WildcardInputModel model)
{
if (validationState.IsValid)
{
var requiredFields = model.GetRequiredFields(fields);
return sqlClient.ExecuteReader($@"
DECLARE @serienNr TABLE([Nr] VARCHAR(50));
-----------------------------------------------------------------------------
-- 1. LOOK FOR SERIAL NUMBER ------------------------------------------------
-----------------------------------------------------------------------------
INSERT INTO @serienNr
SELECT [APS].[SerienNr]
FROM [AuftragPositionSerienNr] AS [APS]
WHERE [APS].[SerienNr] LIKE @{nameof(model.Token)}
OR [APS].[KundeneigeneSerienNr] LIKE @{nameof(model.Token)};
-----------------------------------------------------------------------------
-- 2. LOOK FOR PCB NUMBER ---------------------------------------------------
-----------------------------------------------------------------------------
INSERT INTO @serienNr
SELECT [PCBS].[MapPcbIdToSerialNumber_SerialNumber]
FROM [MapPcbIdToSerialNumber] AS [PCBS]
WHERE [PCBS].[MapPcbIdToSerialNumber_PcbId] LIKE @{nameof(model.Token)};
-----------------------------------------------------------------------------
-- 3. LOOK FOR FUNK ADDRESS E-REGISTER --------------------------------------
-----------------------------------------------------------------------------
INSERT INTO @serienNr
SELECT [ER].[Seriennummer]
FROM [eRegister] AS [ER]
WHERE [ER].[Adresse] LIKE @{nameof(model.Token)};
-----------------------------------------------------------------------------
-- 4. LOOK FOR FUNK ADDRESS GENESIS -----------------------------------------
-----------------------------------------------------------------------------
INSERT INTO @serienNr
SELECT [GM].[Seriennummer]
FROM [Genesis_Meter] AS [GM]
WHERE [GM].[Adresse] LIKE @{nameof(model.Token)};
-----------------------------------------------------------------------------
-- 5. TAKE ORDERS INFO ------------------------------------------------------
-----------------------------------------------------------------------------
SELECT DISTINCT
-- KundenNr CustomerNr
[KN].[Name] AS [Client] -- Customer
-- FertigungNr - ProductionOrderNo
, [APS].[AuftragNr] AS [OrderNo] -- CustomerOrderNo
, [APS].[PositionNr] AS [PosNo]
, [APS].[SerienNr] AS [SerialNo]
, [APS].[KundeneigeneSerienNr] AS [ClientSerialNo] -- CustomerSerialNo
, [IDN].[KurzBez] AS [KurzBez]
, [IDN].[Typ] AS [Typ]
, [IDN].[Nennweite] AS [Nennweite]
, [IDN].[Baulaenge] AS [Baulaenge]
, [AP].[Menge] AS [Quantity]
, [APS].[FabNr] AS [FabricNo]
, [IDN].[IdentNr] AS [IdentNr]
, [PCBS].[MapPcbIdToSerialNumber_PcbId] AS [PcbId]
, [ER].[Adresse] AS [ERAddress] -- ErRadioAddress
, [GM].[Adresse] AS [GMAddress] -- GmRadioAddress
FROM [AuftragPositionSerienNr] AS [APS]
LEFT JOIN [AuftragPosition_Gesamt] AS [AP]
ON [AP].[AuftragNr] = [APS].[AuftragNr]
AND [AP].[PositionNr] = [APS].[PositionNr]
LEFT JOIN [Auftrag_Gesamt] AS [AG]
ON [AG].[AuftragNr] = [APS].[AuftragNr]
LEFT JOIN [Kunde] AS [KN]
ON [KN].[KundenNr] = [AG].[KundenNr]
LEFT JOIN [Identnr] AS [IDN]
ON [IDN].[IdentNr] = [AP].[Identnr]
LEFT JOIN [MapPcbIdToSerialNumber] AS [PCBS]
ON [PCBS].[MapPcbIdToSerialNumber_SerialNumber] = [APS].[SerienNr]
LEFT JOIN [eRegister] AS [ER]
ON [ER].[Seriennummer] = [APS].[SerienNr]
LEFT JOIN [Genesis_Meter] AS [GM]
ON [GM].[Seriennummer] = [APS].[SerienNr]
WHERE [APS].[SerienNr] IN (SELECT [Nr] FROM @serienNr)",
parameters => parameters.Add(nameof(model.Token), model.Token),
reader => this.ToWildcardResult(reader, requiredFields))
.ToList();
}
return validationState;
}
public ModelResult<IEnumerable<string>> Options()
=> fields.ToList();
internal WildcardResult ToWildcardResult(ISqlReader reader, IEnumerable<string> fields)
{
var wildcardResult = new WildcardResult();
foreach (var field in fields)
{
wildcardResult[field] = reader.GetValue(field);
}
return wildcardResult;
}
}
}
@@ -0,0 +1,8 @@
namespace LaaProductionWeb.API.Areas.Serach.Models
{
using System.Collections.Generic;
public class WildcardResult : Dictionary<string, object>
{
}
}
@@ -0,0 +1,9 @@
namespace LaaProductionWeb.API.Areas.Serach
{
using Microsoft.AspNetCore.Mvc;
[Area("Search")]
public class SearchController : AreaController
{
}
}
@@ -0,0 +1,20 @@
namespace LaaProductionWeb.API.Areas.Shipment.Controllers.Scanned
{
using LaaProductionWeb.API.Areas.Shipment.Models.Interfaces;
using Microsoft.AspNetCore.Mvc;
public class ScannedController : ShipmentController
{
private readonly IScanModel scanModel;
public ScannedController(IScanModel scanModel)
{
this.scanModel = scanModel;
}
[HttpGet("Serial/{serialNo}")]
public IActionResult Get([FromRoute] string serialNo)
=> this.Ok(serialNo);
}
}
@@ -0,0 +1,6 @@
namespace LaaProductionWeb.API.Areas.Shipment.Models.Interfaces
{
public interface IScanModel
{
}
}
@@ -0,0 +1,13 @@
namespace LaaProductionWeb.API.Areas.Shipment.Models
{
using LaaProductionWeb.API.Areas.Shipment.Models.Interfaces;
using LaaProductionWeb.Data.Interfaces;
public class ScanModel : IScanModel
{
private readonly ISqlClient sqlClient;
public ScanModel(ISqlClient sqlClient)
=> this.sqlClient = sqlClient;
}
}
@@ -0,0 +1,9 @@
namespace LaaProductionWeb.API.Areas.Shipment
{
using Microsoft.AspNetCore.Mvc;
[Area("Shipment")]
public class ShipmentController : AreaController
{
}
}
@@ -0,0 +1,90 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace LaaProductionWeb.API {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class ErrorMessages {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal ErrorMessages() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LaaProductionWeb.API.ErrorMessages", typeof(ErrorMessages).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Field {0} is not well formatted..
/// </summary>
public static string RegularExpression {
get {
return ResourceManager.GetString("RegularExpression", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Field {0} is required..
/// </summary>
public static string Required {
get {
return ResourceManager.GetString("Required", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Field {0} requires at liest {2} and up to {1} charachters..
/// </summary>
public static string StringLength {
get {
return ResourceManager.GetString("StringLength", resourceCulture);
}
}
}
}
@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="RegularExpression" xml:space="preserve">
<value>Field {0} is not well formatted.</value>
</data>
<data name="Required" xml:space="preserve">
<value>Field {0} is required.</value>
</data>
<data name="StringLength" xml:space="preserve">
<value>Field {0} requires at liest {2} and up to {1} charachters.</value>
</data>
</root>
@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<UserSecretsId>1fb61288-8dc8-4657-b44c-193c6aad5f65</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.1.2" PrivateAssets="All" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.1.9" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LaaProductionWeb.Data\LaaProductionWeb.Data.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="ErrorMessages.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>ErrorMessages.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="ErrorMessages.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>ErrorMessages.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Folder Include="Areas\Shipment\Data\" />
<Folder Include="Areas\Shipment\Views\" />
</ItemGroup>
</Project>
@@ -0,0 +1,21 @@
namespace LaaProductionWeb.API.Models
{
/// <summary>
/// C# class representation of the appsettings.json file.
/// </summary>
public class ApplicationSettings
{
/// <summary>
/// Default defined ConnectionStrings section for the appsettings.json
/// </summary>
public ConnectionStrings ConnectionStrings { get; set; }
}
/// <summary>
/// Default defined ConnectionStrings section for the appsettings.json
/// </summary>
public class ConnectionStrings
{
public string DefaultConnection { get; set; }
}
}
@@ -0,0 +1,77 @@
namespace LaaProductionWeb.API.Models
{
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System.Collections.Generic;
using System.Linq;
public class ModelResult<T>
{
private readonly T value;
private readonly Dictionary<string, string> errors;
private ModelResult(bool succeeded)
{
this.Succeeded = succeeded;
this.errors = new Dictionary<string, string>();
}
private ModelResult(T value)
{
this.Succeeded = true;
this.value = value;
}
public bool Succeeded { get; }
public IReadOnlyDictionary<string, string> ModelErrors
=> this.errors;
public void AddModelError(string key, string value)
{
if (key != null)
{
this.errors[key] = value;
}
}
public static implicit operator ModelResult<T>(T value)
=> new ModelResult<T>(value);
public static implicit operator ModelResult<T>(ModelStateDictionary modelState)
{
var validationResults = new ModelResult<T>(false);
if (modelState != null)
{
foreach (var entry in modelState)
{
var errorKey = entry.Key;
var errorValue = entry.Value;
if (errorValue.ValidationState == ModelValidationState.Invalid && errorValue.Errors.Any())
{
validationResults.AddModelError(errorKey, errorValue.Errors.First().ErrorMessage);
}
}
}
return validationResults;
}
public static implicit operator ActionResult(ModelResult<T> modelState)
{
if (modelState is null)
{
return new NotFoundResult();
}
else if (modelState.Succeeded)
{
return new OkObjectResult(modelState.value);
}
return new BadRequestObjectResult(modelState.errors);
}
}
}
@@ -0,0 +1,30 @@
namespace LaaProductionWeb.API
{
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
/// <summary>
/// Application startup class contining the <seealso cref="Program.Main(string[])"/> method.
/// </summary>
public class Program
{
/// <summary>
/// Default start point for the application.
/// </summary>
/// <param name="args"><see cref="string[]"/> arguments provided from server on application start.</param>
public static void Main(string[] args)
=> CreateWebHostBuilder(args)
.Build()
.Run();
/// <summary>
/// !Important do not refactor. This method is used from other frameworks, like EntityFramework.
/// </summary>
/// <param name="args"><see cref="string[]"/> arguments provided from for creating the <seealso cref="WebHost"/>.</param>
/// <returns>Created and configured <see cref="IWebHostBuilder"/>.</returns>
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
=> WebHost
.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
}
@@ -0,0 +1,30 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:59262",
"sslPort": 44346
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "api/values",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"LaaProductionWeb.API": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "api/values",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
@@ -0,0 +1,52 @@
namespace LaaProductionWeb.API
{
using LaaProductionWeb.Data;
using LaaProductionWeb.Data.Interfaces;
using LaaProductionWeb.API.Areas.Serach.Models;
using LaaProductionWeb.API.Areas.Serach.Models.Interfaces;
using LaaProductionWeb.API.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
public class Startup
{
private readonly IConfiguration configuration;
public Startup(IConfiguration configuration)
=> this.configuration = configuration;
public void ConfigureServices(IServiceCollection services)
{
var connectionString = this.configuration
.GetConnectionString(nameof(ConnectionStrings.DefaultConnection));
services.AddTransient<ISqlClient, SqlClient>(_ => new SqlClient(connectionString));
services.AddTransient<IWildcardModel, WildcardModel>();
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
});
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Latest);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseHsts();
app.UseDeveloperExceptionPage();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc(routes =>
{
routes.MapRoute(name: "default", template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(name: "areas", template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
});
}
}
}
@@ -0,0 +1,5 @@
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=SLASQL01.emea.sensus.net;Initial Catalog=Auftrag;Password=ServiceParingfile;Persist Security Info=True;User ID=ServiceParingfile;"
}
}
@@ -3,7 +3,8 @@
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Threading.Tasks;
public interface ISqlClient
{
int ExecuteNonQuery(string query, Action<SqlParameters> parameters, SqlInfoMessageEventHandler errorCallback = null);
@@ -7,5 +7,7 @@
string GetString(int index = -1);
T GetValue<T>(int index = -1);
object GetValue(string field);
}
}
@@ -12,6 +12,7 @@
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -48,6 +49,7 @@
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
@@ -56,8 +58,6 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Repositories\Interfaces\IRepository[T].cs" />
<Compile Include="Repositories\Repository[T].cs" />
<Compile Include="SqlClient.cs" />
<Compile Include="SqlClientExtensions.cs" />
<Compile Include="Interfaces\ISqlClient.cs" />
@@ -1,17 +0,0 @@
namespace LaaProductionWeb.Data.Repositories.Interfaces
{
using System.Collections.Generic;
public interface IRepository<T>
{
int Create(string query, object parameters);
int Delete(string query, object parameters);
IEnumerable<T> First(string query, object parameters);
T Read(string query, object parameters);
int Update(string query, object parameters);
}
}
@@ -1,40 +0,0 @@
namespace LaaProductionWeb.Data.Repositories
{
using System.Collections.Generic;
using LaaProductionWeb.Data.Interfaces;
using LaaProductionWeb.Data.Repositories.Interfaces;
public abstract class Repository<T> : IRepository<T>
{
private readonly ISqlClient sqlClient;
protected Repository(ISqlClient sqlClient)
=> this.sqlClient = sqlClient;
public int Create(string query, object parameters)
{
throw new System.NotImplementedException();
}
public int Delete(string query, object parameters)
{
throw new System.NotImplementedException();
}
public IEnumerable<T> First(string query, object parameters)
{
throw new System.NotImplementedException();
}
public T Read(string query, object parameters)
{
throw new System.NotImplementedException();
}
public int Update(string query, object parameters)
{
throw new System.NotImplementedException();
}
}
}
@@ -6,7 +6,7 @@
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
public class SqlClient : ISqlClient
{
private readonly string connectionString;
@@ -3,9 +3,13 @@
using LaaProductionWeb.Data.Interfaces;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Data.SqlClient;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Web;
public static class SqlClientExtensions
{
@@ -29,11 +33,31 @@
sqlConnection.Open();
}
internal static async Task OpenWithErrorHandlingAsync(this SqlConnection sqlConnection, SqlInfoMessageEventHandler messageHandler = null)
{
sqlConnection.FireInfoMessageEventOnUserErrors = true;
sqlConnection.InfoMessage += SqlConnectionInfoMessage;
if (messageHandler != null)
{
sqlConnection.InfoMessage += messageHandler;
}
await sqlConnection.OpenAsync();
}
internal static void SqlConnectionInfoMessage(object sender, SqlInfoMessageEventArgs e)
{
Debug.WriteLine(e);
Log($"[{DateTime.Now: hh:mm:ss}] Error: {e.Source} - {e.Message}{Environment.NewLine}{string.Join(Environment.NewLine, e.Errors)}");
// TODO: Handle errors
}
internal static void Log(string message)
{
// File.AppendAllText($"{Environment.CurrentDirectory}/App_Data/{DateTime.Now:yyyy_MM_dd}.log", $"{Environment.NewLine}{message}{Environment.NewLine}");
}
}
}
@@ -3,16 +3,32 @@
using LaaProductionWeb.Data.Interfaces;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
public class SqlReader : ISqlReader, IDisposable
{
private readonly SqlDataReader sqlDataReader;
private readonly IDictionary<string, int> columns;
private int column;
private SqlReader(SqlDataReader sqlDataReader)
=> this.sqlDataReader = sqlDataReader;
private SqlReader(SqlDataReader sqlDataReader)
{
this.sqlDataReader = sqlDataReader;
this.columns = new Dictionary<string, int>();
var columnsCount = this.sqlDataReader.FieldCount;
var columns = new string[columnsCount];
for (int i = 0; i < columnsCount; i++)
{
var columnName = this.sqlDataReader.GetName(i);
this.columns[columnName] = i;
}
}
public void Dispose()
{
@@ -38,17 +54,7 @@
}
internal string[] GetColumns()
{
var columnsCount = this.sqlDataReader.FieldCount;
var columns = new string[columnsCount];
for (int i = 0; i < columnsCount; i++)
{
columns[i] = this.sqlDataReader.GetName(i);
}
return columns;
}
=> this.columns.Keys.ToArray();
public string GetString(int index = -1)
{
@@ -90,6 +96,26 @@
return this.sqlDataReader.Read();
}
internal Task<bool> ReadAsync()
{
this.column = 0;
return this.sqlDataReader.ReadAsync();
}
public object GetValue(string field)
{
if (this.columns.TryGetValue($"{field}", out int index))
{
if (!this.sqlDataReader.IsDBNull(index))
{
return this.sqlDataReader.GetValue(index);
}
}
return default(object);
}
public static implicit operator SqlReader(SqlDataReader sqlDataReader)
=> new SqlReader(sqlDataReader);
}
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/></startup></configuration>
@@ -4,6 +4,8 @@
using LaaProductionWeb.Services.Interfaces;
using LaaProductionWeb.Services.Models;
using System.Collections.Generic;
public class AccountService : IAccountService
{
private readonly ISqlClient sqlClient;
@@ -11,22 +13,39 @@
public AccountService(ISqlClient sqlClient)
=> this.sqlClient = sqlClient;
public Employee FindEmployee(string firstName, string lastName)
public Employee FindEmployee(int userId)
{
var employeIdentity = this.sqlClient.FirstOrDefault($@"
SELECT TOP 1
[MitarbeiterNr]
, [Vorname] + ' ' + [Name]
FROM [Mitarbeiter]
WHERE [MitarbeiterNr] = @{nameof(userId)}",
parameters => parameters.Add(nameof(userId), userId),
reader => new KeyValuePair<int, string>(
key: reader.GetValue<short>(),
value: reader.GetString()));
var permissions = this.sqlClient.ExecuteReader($@"
SELECT [MitarbeiterRechte].[Recht]
FROM [Mitarbeiter]
JOIN [MitarbeiterRechte]
ON [MitarbeiterRechte].[MitarbeiterNr] = [Mitarbeiter].[MitarbeiterNr]
WHERE [Vorname] LIKE @{nameof(firstName)}
AND [Name] LIKE @{nameof(lastName)}
FROM [MitarbeiterRechte]
WHERE [MitarbeiterNr] = @{nameof(employeIdentity.Key)}
AND [MitarbeiterRechte].[Recht] LIKE 'WEB_%'",
parameters => parameters
.Add(nameof(firstName), firstName)
.Add(nameof(lastName), lastName),
parameters => parameters.Add(nameof(employeIdentity.Key), employeIdentity.Key),
reader => reader.GetString());
return new Employee(permissions);
return new Employee(employeIdentity.Value, permissions);
}
public int Login(string username, string password)
=> this.sqlClient.FirstOrDefault($@"
SELECT TOP 1 [MitarbeiterNr]
FROM [Mitarbeiter]
WHERE [Benutzername] LIKE @{nameof(username)}
AND [Kennwort] LIKE @{nameof(password)}",
parameters => parameters
.Add(nameof(username), username)
.Add(nameof(password), password),
reader => reader.GetValue<short>());
}
}
@@ -0,0 +1,80 @@
namespace LaaProductionWeb.Services
{
using LaaProductionWeb.Services.Interfaces;
using LaaProductionWeb.Services.Models;
using System;
using System.Net.Http;
using System.Threading.Tasks;
public class HttpService : IHttpService
{
private readonly HttpClient httpClient;
public HttpService(string url)
=> this.httpClient = new HttpClient
{
BaseAddress = new Uri(url, UriKind.Absolute)
};
public async Task<HttpResponseModel> GetAsync(string path)
{
var httpResponseModel = new HttpResponseModel();
try
{
using (var httpRequestMessage = new HttpRequestMessage())
{
httpRequestMessage.Method = HttpMethod.Get;
httpRequestMessage.RequestUri = new Uri(path, UriKind.Relative);
using (var httpResponseMessage = await this.httpClient.SendAsync(httpRequestMessage))
{
httpResponseModel.Succeeded = httpResponseMessage.IsSuccessStatusCode;
if (httpResponseMessage.Content != null)
{
httpResponseModel.Content = await httpResponseMessage.Content?.ReadAsStringAsync();
}
}
}
}
catch (Exception e)
{
httpResponseModel.Error = e.Message;
}
return httpResponseModel;
}
public async Task<HttpResponseModel> OptionsAsync(string path)
{
var httpResponseModel = new HttpResponseModel();
try
{
using (var httpRequestMessage = new HttpRequestMessage())
{
httpRequestMessage.Method = HttpMethod.Options;
httpRequestMessage.RequestUri = new Uri(path, UriKind.Relative);
using (var httpResponseMessage = await this.httpClient.SendAsync(httpRequestMessage))
{
httpResponseModel.Succeeded = httpResponseMessage.IsSuccessStatusCode;
if (httpResponseMessage.Content != null)
{
httpResponseModel.Content = await httpResponseMessage.Content?.ReadAsStringAsync();
}
}
}
}
catch (Exception e)
{
httpResponseModel.Error = e.Message;
}
return httpResponseModel;
}
}
}
@@ -4,6 +4,8 @@
public interface IAccountService : ITransient
{
Employee FindEmployee(string firstName, string lastName);
Employee FindEmployee(int userId);
int Login(string username, string password);
}
}
@@ -0,0 +1,13 @@
namespace LaaProductionWeb.Services.Interfaces
{
using LaaProductionWeb.Services.Models;
using System.Threading.Tasks;
public interface IHttpService
{
Task<HttpResponseModel> GetAsync(string path);
Task<HttpResponseModel> OptionsAsync(string path);
}
}
@@ -1,6 +1,7 @@
namespace LaaProductionWeb.Services.Interfaces
{
using LaaProductionWeb.Services.Models;
using LaaProductionWeb.Services.Models.Reports;
using System.Collections.Generic;
using System.IO;
@@ -1,6 +1,6 @@
namespace LaaProductionWeb.Services.Interfaces
{
using LaaProductionWeb.Services.Models;
using LaaProductionWeb.Services.Models.Reports;
using System.Collections.Generic;
@@ -11,5 +11,9 @@
IEnumerable<HeliumPressurePoint> LoadHeliumPressureResults(int testId);
byte[] LoadHeliumPressureReportAsCSV(HeliumReportModel model);
KottmannReportModel LoadKottmannPressureReport(KottmannReportModel model = null);
byte[] LoadKottmannPressureReportAsCSV(KottmannReportModel model);
}
}
@@ -0,0 +1,6 @@
namespace LaaProductionWeb.Services.Interfaces
{
public interface ISingleton
{
}
}
@@ -40,6 +40,10 @@
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.7.0.0\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\Program Files\dotnet\sdk\NuGetFallbackFolder\newtonsoft.json\11.0.2\lib\netstandard2.0\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" />
@@ -59,20 +63,28 @@
<ItemGroup>
<Compile Include="AccountService.cs" />
<Compile Include="ApprovalsService.cs" />
<Compile Include="HttpService.cs" />
<Compile Include="Interfaces\IAccountService.cs" />
<Compile Include="Interfaces\IApprovalsService.cs" />
<Compile Include="Interfaces\IHttpService.cs" />
<Compile Include="Interfaces\IOrdersService.cs" />
<Compile Include="Interfaces\IProtocolService.cs" />
<Compile Include="Interfaces\IReportService.cs" />
<Compile Include="Interfaces\IShipmentsService.cs" />
<Compile Include="Interfaces\ISingleton.cs" />
<Compile Include="Interfaces\ITransient.cs" />
<Compile Include="Models\Employee.cs" />
<Compile Include="Models\HeliumPressurePoint.cs" />
<Compile Include="Models\HeliumReportFilter.cs" />
<Compile Include="Models\HeliumReportFilterProperty.cs" />
<Compile Include="Models\HeliumReportItem.cs" />
<Compile Include="Models\HeliumReportModel.cs" />
<Compile Include="Models\HeliumReportModelExtensions.cs" />
<Compile Include="Models\HttpResponseModel.cs" />
<Compile Include="Models\Reports\HeliumPressurePoint.cs" />
<Compile Include="Models\Reports\HeliumReportFilter.cs" />
<Compile Include="Models\Reports\KottmannReportFilter.cs" />
<Compile Include="Models\Reports\KottmannReportItem.cs" />
<Compile Include="Models\Reports\PropertyType.cs" />
<Compile Include="Models\Reports\ReportFilterProperty.cs" />
<Compile Include="Models\Reports\HeliumReportItem.cs" />
<Compile Include="Models\Reports\HeliumReportModel.cs" />
<Compile Include="Models\Reports\ReportModelExtensions.cs" />
<Compile Include="Models\Reports\KottmannReportModel.cs" />
<Compile Include="Models\OrderIdentity.cs" />
<Compile Include="Models\PalletPosition.cs" />
<Compile Include="Models\ProductionApproval.cs" />
@@ -85,10 +97,13 @@
<Compile Include="Models\OrderScanModel.cs" />
<Compile Include="Models\OrdersFoundModel.cs" />
<Compile Include="Models\PalletsBatchModel.cs" />
<Compile Include="Models\Reports\ReportFilter.cs" />
<Compile Include="Models\Result.cs" />
<Compile Include="Models\Search\SearchWildcardModel.cs" />
<Compile Include="Models\SerialNr.cs" />
<Compile Include="Models\ShipmentModel.cs" />
<Compile Include="Models\TestStatus.cs" />
<Compile Include="Models\Reports\SqlExpression.cs" />
<Compile Include="Models\Reports\TestStatus.cs" />
<Compile Include="OrdersService.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ProtocolService.cs" />
@@ -5,8 +5,16 @@
public class Employee
{
public Employee(IEnumerable<string> permissions)
=> this.Permissions = permissions;
public Employee(string name, IEnumerable<string> permissions)
{
this.Name = name;
this.IsValid = !string.IsNullOrWhiteSpace(name);
this.Permissions = permissions ?? Array.Empty<string>();
}
public string Name { get; set; }
public bool IsValid { get; }
public IEnumerable<string> Permissions { get; }
= Array.Empty<string>();
@@ -1,38 +0,0 @@
namespace LaaProductionWeb.Services.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq.Expressions;
using System.Reflection;
public class HeliumReportModel
{
public HeliumReportFilter Filter { get; set; }
= new HeliumReportFilter();
public IEnumerable<HeliumReportItem> Items { get; set; }
= Array.Empty<HeliumReportItem>();
public static implicit operator HeliumReportModel(HeliumReportFilter filter)
=> new HeliumReportModel { Filter = filter };
public string Display(Expression<Func<HeliumReportItem, object>> expression)
{
var memberInfo = default(MemberInfo);
if (expression.Body is MemberExpression memberExpression)
{
memberInfo = memberExpression.Member;
}
else if (expression.Body is UnaryExpression unaryExpression && unaryExpression.Operand is MemberExpression operandExpression)
{
memberInfo = operandExpression.Member;
}
return memberInfo
?.GetCustomAttribute<DisplayAttribute>()
?.Name;
}
}
}
@@ -1,55 +0,0 @@
namespace LaaProductionWeb.Services.Models
{
using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq.Expressions;
using System.Reflection;
public enum PropertyType
{
Text,
Date,
Bool
}
public static class HeliumReportModelExtensions
{
public static HeliumReportFilterProperty Text(this HeliumReportItem model, Expression<Func<HeliumReportItem, object>> expression, int index, int orderBy = 0)
=> model.Property(expression, index, PropertyType.Text, orderBy);
public static HeliumReportFilterProperty DateTime(this HeliumReportItem model, Expression<Func<HeliumReportItem, object>> expression, int index, int orderBy = 0)
=> model.Property(expression, index, PropertyType.Date, orderBy);
public static HeliumReportFilterProperty Boolean(this HeliumReportItem model, Expression<Func<HeliumReportItem, object>> expression, int index, int orderBy = 0)
=> model.Property(expression, index, PropertyType.Bool, orderBy);
public static HeliumReportFilterProperty Property(
this HeliumReportItem model
, Expression<Func<HeliumReportItem, object>> expression
, int index
, PropertyType propertyType
, int orderBy)
{
var memberInfo = default(MemberInfo);
if (expression.Body is MemberExpression memberExpression)
{
memberInfo = memberExpression.Member;
}
else if (expression.Body is UnaryExpression unaryExpression && unaryExpression.Operand is MemberExpression operandExpression)
{
memberInfo = operandExpression.Member;
}
return new HeliumReportFilterProperty
{
Column = memberInfo?.GetCustomAttribute<ColumnAttribute>()?.Name,
Property = memberInfo?.Name,
OrderBy = orderBy,
Index = index,
Type = propertyType
};
}
}
}
@@ -0,0 +1,23 @@
namespace LaaProductionWeb.Services.Models
{
using Newtonsoft.Json;
public class HttpResponseModel
{
public bool Succeeded { get; internal set; }
public string Content { get; internal set; }
public string Error { get; internal set; }
public T Deserialize<T>() where T : new ()
{
if (!string.IsNullOrWhiteSpace(this.Content))
{
return JsonConvert.DeserializeObject<T>(this.Content);
}
return new T();
}
}
}
@@ -1,6 +1,4 @@
using System.Web;
namespace LaaProductionWeb.Services.Models
namespace LaaProductionWeb.Services.Models
{
public class OrderClientModel
{
@@ -19,10 +19,10 @@
= new Dictionary<int, bool> { { 1, false } };
public string PrintUrl
=> $"/Shipments/Print?ordernr={this.OrderNr}&positionnr={this.PositionNr}&palletnr={this.PalletNr}";
=> $"~/Shipments/Print?ordernr={this.OrderNr}&positionnr={this.PositionNr}&palletnr={this.PalletNr}";
public string DownloadUrl
=> $"/Shipments/Download?ordernr={this.OrderNr}&positionnr={this.PositionNr}&palletnr={this.PalletNr}";
=> $"~/Shipments/Download?ordernr={this.OrderNr}&positionnr={this.PositionNr}&palletnr={this.PalletNr}";
public void UpdatePalletsCount(int palletsCount)
{
@@ -1,4 +1,4 @@
namespace LaaProductionWeb.Services.Models
namespace LaaProductionWeb.Services.Models.Reports
{
public class HeliumPressurePoint
{
@@ -0,0 +1,30 @@
namespace LaaProductionWeb.Services.Models.Reports
{
public partial class HeliumReportFilter : ReportFilter
{
public HeliumReportFilter()
{
var model = default(HeliumReportItem);
this.Add(model.Text(x => x.FertigungNr, this.Count));
this.Add(model.Text(x => x.AuftragNr, this.Count));
this.Add(model.Text(x => x.PositionNr, this.Count));
this.Add(model.Text(x => x.SerialNr, this.Count));
this.Add(model.Text(x => x.IdentNr, this.Count));
this.Add(model.Text(x => x.HeId, this.Count));
this.Add(model.Text(x => x.HeTestId, this.Count));
this.Add(model.Text(x => x.PcbId, this.Count));
this.Add(model.Text(x => x.Nennweite, this.Count));
this.Add(model.Text(x => x.Baulaenge, this.Count));
this.Add(model.Text(x => x.Typ, this.Count));
this.Add(model.Text(x => x.Druck, this.Count));
this.Add(model.Text(x => x.Temperatur, this.Count));
this.Add(model.Text(x => x.HeLakeRate, this.Count));
this.Add(model.Text(x => x.HeTestPressure, this.Count));
this.Add(model.Boolean(x => x.IsValid, this.Count));
this.Add(model.Text(x => x.Text, this.Count));
this.Add(model.DateTime(x => x.TestDate, this.Count, -1));
this.Add(model.Text(x => x.Tester, this.Count));
}
}
}
@@ -1,4 +1,4 @@
namespace LaaProductionWeb.Services.Models
namespace LaaProductionWeb.Services.Models.Reports
{
using System;
using System.ComponentModel.DataAnnotations;
@@ -0,0 +1,21 @@
namespace LaaProductionWeb.Services.Models.Reports
{
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
public class HeliumReportModel
{
public HeliumReportFilter Filter { get; set; }
= new HeliumReportFilter();
public IEnumerable<HeliumReportItem> Items { get; set; }
= Array.Empty<HeliumReportItem>();
public static implicit operator HeliumReportModel(HeliumReportFilter filter)
=> new HeliumReportModel { Filter = filter };
public string Display(Expression<Func<HeliumReportItem, object>> expression)
=> ReportModelExtensions.Display(expression);
}
}
@@ -0,0 +1,23 @@
namespace LaaProductionWeb.Services.Models.Reports
{
public class KottmannReportFilter : ReportFilter
{
public KottmannReportFilter()
{
var model = default(KottmannReportItem);
this.Add(model.Text(x => x.FertigungsNr, this.Count));
this.Add(model.Text(x => x.SerialNr, this.Count));
this.Add(model.Text(x => x.PcbId, this.Count));
this.Add(model.Text(x => x.Nennweite, this.Count));
this.Add(model.Text(x => x.Baulaenge, this.Count));
this.Add(model.Text(x => x.Typ, this.Count));
this.Add(model.Text(x => x.StartDruck, this.Count));
this.Add(model.Text(x => x.EndDruck, this.Count));
this.Add(model.Text(x => x.DruckZeit, this.Count));
this.Add(model.Boolean(x => x.Dicht, this.Count, -1));
this.Add(model.DateTime(x => x.Datum, this.Count, -1));
this.Add(model.DateTime(x => x.Bemerkung, this.Count, -1));
}
}
}
@@ -0,0 +1,57 @@
namespace LaaProductionWeb.Services.Models.Reports
{
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
public class KottmannReportItem
{
[Display(Name = "Fertigung Nr.")]
[Column("[DP].[FertigungsauftragNr]")]
public int? FertigungsNr { get; set; }
[Display(Name = "Serial Nr.")]
[Column("[PCB2SN].[MapPcbIdToSerialNumber_SerialNumber]")]
public int SerialNr { get; set; }
[Display(Name = "Pcb Id")]
[Column("[PT].[CordonelPressureTest_PcbId]")]
public int PcbId { get; set; }
[Display(Name = "Nennweite")]
[Column("[ID].[Nennweite]")]
public int? Nennweite { get; set; }
[Display(Name = "Baulaenge")]
[Column("[ID].[Baulaenge]")]
public int? Baulaenge { get; set; }
[Display(Name = "Typ")]
[Column("[ID].[Typ]")]
public string Typ { get; set; }
[Display(Name = "Start Druck")]
[Column("[DP].[StartDruck]")]
public double StartDruck { get; set; }
[Display(Name = "End Druck")]
[Column("[DP].[EndDruck]")]
public double EndDruck { get; set; }
[Display(Name = "Zeit")]
[Column("[DP].[Pruefzeit]")]
public int DruckZeit { get; set; }
[Display(Name = "Dicht")]
[Column("[DP].[Dicht]")]
public bool Dicht { get; set; }
[Display(Name = "Datum")]
[Column("[DP].[Datum]")]
public DateTime Datum { get; set; }
[Display(Name = "Bemerkung")]
[Column("[DP].[Bemerkung]")]
public string Bemerkung { get; internal set; }
}
}
@@ -0,0 +1,21 @@
namespace LaaProductionWeb.Services.Models.Reports
{
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
public class KottmannReportModel
{
public KottmannReportFilter Filter { get; set; }
= new KottmannReportFilter();
public IEnumerable<KottmannReportItem> Items { get; set; }
= Array.Empty<KottmannReportItem>();
public static implicit operator KottmannReportModel(KottmannReportFilter filter)
=> new KottmannReportModel { Filter = filter };
public string Display(Expression<Func<KottmannReportItem, object>> expression)
=> ReportModelExtensions.Display(expression);
}
}
@@ -0,0 +1,9 @@
namespace LaaProductionWeb.Services.Models.Reports
{
public enum PropertyType
{
Text,
Date,
Bool
}
}
@@ -1,37 +1,12 @@
namespace LaaProductionWeb.Services.Models
namespace LaaProductionWeb.Services.Models.Reports
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
public class HeliumReportFilter : List<HeliumReportFilterProperty>
public class ReportFilter : List<ReportFilterProperty>
{
public HeliumReportFilter()
{
var model = default(HeliumReportItem);
this.Add(model.Text(x => x.FertigungNr, this.Count));
this.Add(model.Text(x => x.AuftragNr, this.Count));
this.Add(model.Text(x => x.PositionNr, this.Count));
this.Add(model.Text(x => x.SerialNr, this.Count));
this.Add(model.Text(x => x.IdentNr, this.Count));
this.Add(model.Text(x => x.HeId, this.Count));
this.Add(model.Text(x => x.HeTestId, this.Count));
this.Add(model.Text(x => x.PcbId, this.Count));
this.Add(model.Text(x => x.Nennweite, this.Count));
this.Add(model.Text(x => x.Baulaenge, this.Count));
this.Add(model.Text(x => x.Typ, this.Count));
this.Add(model.Text(x => x.Druck, this.Count));
this.Add(model.Text(x => x.Temperatur, this.Count));
this.Add(model.Text(x => x.HeLakeRate, this.Count));
this.Add(model.Text(x => x.HeTestPressure, this.Count));
this.Add(model.Boolean(x => x.IsValid, this.Count));
this.Add(model.Text(x => x.Text, this.Count));
this.Add(model.DateTime(x => x.TestDate, this.Count, -1));
this.Add(model.Text(x => x.Tester, this.Count));
}
internal SqlExpression OrderBy()
{
var sqlExpression = new SqlExpression();
@@ -69,8 +44,8 @@
var dateTokens = property
.Like
.Split(new[] { '-' })
.Select(x => DateTimeOffset.TryParse($"{x}".Trim(), new CultureInfo("de"), DateTimeStyles.None, out DateTimeOffset date)
? (DateTimeOffset?)date
.Select(x => DateTimeOffset.TryParse($"{x}".Trim(), new CultureInfo("de"), DateTimeStyles.None, out DateTimeOffset date)
? (DateTimeOffset?)date
: null)
.ToArray();
@@ -121,42 +96,5 @@
return sqlExpression.BuildWhere(" AND ");
}
public class SqlExpression
{
private readonly ICollection<string> expressions
= new List<string>();
public string Query { get; set; }
public IDictionary<string, object> Parameters { get; set; }
= new Dictionary<string, object>();
internal void Add(string expression)
=> this.expressions.Add(expression);
internal void Add(string expression, string propertyName, object propertyValue)
{
this.Parameters[propertyName] = propertyValue;
this.expressions.Add(expression);
}
internal SqlExpression BuildOrderBy(string delimiter)
{
this.Query = string.Join(delimiter, this.expressions);
return this;
}
internal SqlExpression BuildWhere(string delimiter)
{
this.Query = this.expressions.Any()
? string.Join(delimiter, this.expressions)
: "1 = 1";
return this;
}
}
}
}
@@ -1,6 +1,6 @@
namespace LaaProductionWeb.Services.Models
namespace LaaProductionWeb.Services.Models.Reports
{
public class HeliumReportFilterProperty
public class ReportFilterProperty
{
public int Index { get; set; }
@@ -0,0 +1,61 @@
namespace LaaProductionWeb.Services.Models.Reports
{
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq.Expressions;
using System.Reflection;
public static class ReportModelExtensions
{
public static ReportFilterProperty Text<T>(this T model, Expression<Func<T, object>> expression, int index, int orderBy = 0)
=> model.Property(expression, index, PropertyType.Text, orderBy);
public static ReportFilterProperty DateTime<T>(this T model, Expression<Func<T, object>> expression, int index, int orderBy = 0)
=> model.Property(expression, index, PropertyType.Date, orderBy);
public static ReportFilterProperty Boolean<T>(this T model, Expression<Func<T, object>> expression, int index, int orderBy = 0)
=> model.Property(expression, index, PropertyType.Bool, orderBy);
public static ReportFilterProperty Property<T>(this T model, Expression<Func<T, object>> expression, int index, PropertyType propertyType, int orderBy)
{
var memberInfo = default(MemberInfo);
if (expression.Body is MemberExpression memberExpression)
{
memberInfo = memberExpression.Member;
}
else if (expression.Body is UnaryExpression unaryExpression && unaryExpression.Operand is MemberExpression operandExpression)
{
memberInfo = operandExpression.Member;
}
return new ReportFilterProperty
{
Column = memberInfo?.GetCustomAttribute<ColumnAttribute>()?.Name,
Property = memberInfo?.Name,
OrderBy = orderBy,
Index = index,
Type = propertyType
};
}
public static string Display<T>(Expression<Func<T, object>> expression)
{
var memberInfo = default(MemberInfo);
if (expression.Body is MemberExpression memberExpression)
{
memberInfo = memberExpression.Member;
}
else if (expression.Body is UnaryExpression unaryExpression && unaryExpression.Operand is MemberExpression operandExpression)
{
memberInfo = operandExpression.Member;
}
return memberInfo
?.GetCustomAttribute<DisplayAttribute>()
?.Name;
}
}
}
@@ -0,0 +1,41 @@
namespace LaaProductionWeb.Services.Models.Reports
{
using System.Collections.Generic;
public class SqlExpression
{
private readonly ICollection<string> expressions
= new List<string>();
public string Query { get; set; }
public IDictionary<string, object> Parameters { get; set; }
= new Dictionary<string, object>();
internal void Add(string expression)
=> this.expressions.Add(expression);
internal void Add(string expression, string propertyName, object propertyValue)
{
this.Parameters[propertyName] = propertyValue;
this.expressions.Add(expression);
}
internal SqlExpression BuildOrderBy(string delimiter)
{
this.Query = string.Join(delimiter, this.expressions);
return this;
}
internal SqlExpression BuildWhere(string delimiter)
{
this.Query = this.expressions.Count > 0
? string.Join(delimiter, this.expressions)
: "1 = 1";
return this;
}
}
}
@@ -1,4 +1,4 @@
namespace LaaProductionWeb.Services.Models
namespace LaaProductionWeb.Services.Models.Reports
{
public class TestStatus
{
@@ -0,0 +1,16 @@
namespace LaaProductionWeb.Services.Models.Search
{
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public class SearchWildcardModel
{
[Display(Name = "Search text")]
public string Token { get; set; }
[Display(Name = "Select fields")]
public IDictionary<string, bool> Fields { get; set; }
public IEnumerable<IDictionary<string, object>> Results { get; set; }
}
}
@@ -3,6 +3,7 @@
using LaaProductionWeb.Data.Interfaces;
using LaaProductionWeb.Services.Interfaces;
using LaaProductionWeb.Services.Models;
using LaaProductionWeb.Services.Models.Reports;
using System;
using System.Collections.Generic;
@@ -2,14 +2,14 @@
{
using LaaProductionWeb.Data.Interfaces;
using LaaProductionWeb.Services.Interfaces;
using LaaProductionWeb.Services.Models;
using LaaProductionWeb.Services.Models.Reports;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class ReportService : IReportService
public partial class ReportService : IReportService
{
private readonly ISqlClient sqlClient;
@@ -27,32 +27,33 @@
var orderBy = model.Filter.OrderBy();
model.Items = this.sqlClient.ExecuteReader($@"
SELECT [PT].[CordonelPressureTest_FertigungsAuftragsNr] AS [FertigungNr] -- 0 Filter
, [POS].[AuftragNr] AS [AuftragNr] -- 1 Filter
, [POS].[PositionNr] AS [PositionNr] -- 2 Filter
, [PCB2SN].[MapPcbIdToSerialNumber_SerialNumber] AS [SerialNr] -- 3
, [ID].[IdentNr] AS [IdentNr] -- 4 Filter
, [PT_HE].[CordonelPressureTestHe_Id] AS [HeId] -- 5
, [PT_HE].[CordonelPressureTestHe_TestID] AS [HeTestId] -- 6
, [PT].[CordonelPressureTest_PcbId] AS [PcbId] -- 7 Filter
, [ID].[Nennweite] AS [Nennweite] -- 8 Filter
, [ID].[Baulaenge] AS [Baulaenge] -- 9 Filter
, [ID].[Typ] AS [Typ] -- 10 Filter
, [ID].[Druck] AS [Druck] -- 11 Filter
, [ID].[Temperatur] AS [Temperatur] -- 12 Filter
, [PT_HE].[CordonelPressureTestHe_LeakRate] AS [HeLakeRate] -- 13
, [PT_HE].[CordonelPressureTestHe_TestPressure] AS [HeTestPressure] -- 14
, [PT].[CordonelPressureTest_Valid] AS [IsValid] -- 15 Filter
, [PT].[CordonelPressureTest_Date] AS [TestDate] -- 16 Filter
, [PT].[CordonelPressureTest_Text] AS [Text] -- 17 Filter
, [PT_HE].[CordonelPressureTestHe_TesterName] AS [Tester] -- 18
SELECT DISTINCT
[PT].[CordonelPressureTest_FertigungsAuftragsNr] AS [FertigungNr] -- 0
, [POS].[AuftragNr] AS [AuftragNr] -- 1
, [POS].[PositionNr] AS [PositionNr] -- 2
, [PCB2SN].[MapPcbIdToSerialNumber_SerialNumber] AS [SerialNr] -- 3
, [ID].[IdentNr] AS [IdentNr] -- 4
, [PT_HE].[CordonelPressureTestHe_Id] AS [HeId] -- 5
, [PT_HE].[CordonelPressureTestHe_TestID] AS [HeTestId] -- 6
, [PT].[CordonelPressureTest_PcbId] AS [PcbId] -- 7
, [ID].[Nennweite] AS [Nennweite] -- 8
, [ID].[Baulaenge] AS [Baulaenge] -- 9
, [ID].[Typ] AS [Typ] -- 10
, [ID].[Druck] AS [Druck] -- 11
, [ID].[Temperatur] AS [Temperatur] -- 12
, [PT_HE].[CordonelPressureTestHe_LeakRate] AS [HeLakeRate] -- 13
, [PT_HE].[CordonelPressureTestHe_TestPressure] AS [HeTestPressure] -- 14
, [PT].[CordonelPressureTest_Valid] AS [IsValid] -- 15
, [PT].[CordonelPressureTest_Date] AS [TestDate] -- 16
, [PT].[CordonelPressureTest_Text] AS [Text] -- 17
, [PT_HE].[CordonelPressureTestHe_TesterName] AS [Tester] -- 18
FROM [Cordonel_PressureTest] AS [PT]
JOIN [Cordonel_PressureTest_He] AS [PT_HE] ON [PT_HE].[CordonelPressureTestHe_TestID] = [PT].[CordonelPressureTest_Id]
LEFT JOIN [Druckpruefung] AS [DP] ON [DP].[FabNr] = [PT].[CordonelPressureTest_PcbId]
JOIN [MapPcbIdToSerialNumber] AS [PCB2SN] ON [PCB2SN].[MapPcbIdToSerialNumber_PcbId] = [PT].[CordonelPressureTest_PcbId]
JOIN [AuftragPosition_Gesamt] AS [POS] ON [POS].[FertigungsauftragNr] = [PT].[CordonelPressureTest_FertigungsAuftragsNr]
JOIN [Identnr] AS [ID] ON [ID].[IdentNr] = [POS].[Identnr]
WHERE [CordonelPressureTest_IsDeleted] = 0
JOIN [Identnr] AS [ID] ON [ID].[IdentNr] = [POS].[Identnr]
WHERE [CordonelPressureTest_IsDeleted] = 0
AND {where.Query}
ORDER BY {orderBy.Query}",
parameters =>
@@ -105,35 +106,33 @@
var csvItems = new List<object[]>();
var headers = Array.Empty<string>();
var items = this.sqlClient.ExecuteReader($@"
SELECT [PT].[CordonelPressureTest_FertigungsAuftragsNr] AS [FertigungNr] -- 0 Filter
, [POS].[AuftragNr] AS [AuftragNr] -- 1 Filter
, [POS].[PositionNr] AS [PositionNr] -- 2 Filter
, [PCB2SN].[MapPcbIdToSerialNumber_SerialNumber] AS [SerialNr] -- 3
, [ID].[IdentNr] AS [IdentNr] -- 4 Filter
, [PT_HE].[CordonelPressureTestHe_Id] AS [HeId] -- 5
, [PT_HE].[CordonelPressureTestHe_TestID] AS [HeTestId] -- 6
, [PT].[CordonelPressureTest_PcbId] AS [PcbId] -- 7 Filter
, [ID].[Nennweite] AS [Nennweite] -- 8 Filter
, [ID].[Baulaenge] AS [Baulaenge] -- 9 Filter
, [ID].[Typ] AS [Typ] -- 10 Filter
, [ID].[Druck] AS [Druck] -- 11 Filter
, [ID].[Temperatur] AS [Temperatur] -- 12 Filter
, [PT_HE].[CordonelPressureTestHe_LeakRate] AS [HeLakeRate] -- 13
, [PT_HE].[CordonelPressureTestHe_TestPressure] AS [HeTestPressure] -- 14
, [PT].[CordonelPressureTest_Valid] AS [IsValid] -- 15 Filter
, [PT].[CordonelPressureTest_Date] AS [TestDate] -- 16 Filter
, [PT].[CordonelPressureTest_Text] AS [Text] -- 17 Filter
, [PT_HE].[CordonelPressureTestHe_TesterName] AS [Tester] -- 18
, [CPT].[CordonelPressureTest_TestPointNr] AS [PointNr] -- 19
, CAST([CPT].[CordonelPressureTest_TestPointResult] AS NUMERIC(9, 8)) AS [PointResult] -- 20
FROM [Cordonel_PressureTest] AS [PT]
JOIN [Cordonel_PressureTest_He] AS [PT_HE] ON [PT_HE].[CordonelPressureTestHe_TestID] = [PT].[CordonelPressureTest_Id]
LEFT JOIN [Druckpruefung] AS [DP] ON [DP].[FabNr] = [PT].[CordonelPressureTest_PcbId]
JOIN [MapPcbIdToSerialNumber] AS [PCB2SN] ON [PCB2SN].[MapPcbIdToSerialNumber_PcbId] = [PT].[CordonelPressureTest_PcbId]
JOIN [AuftragPosition_Gesamt] AS [POS] ON [POS].[FertigungsauftragNr] = [PT].[CordonelPressureTest_FertigungsAuftragsNr]
JOIN [Identnr] AS [ID] ON [ID].[IdentNr] = [POS].[Identnr]
LEFT JOIN [Cordonel_PressureTest_TestPoints] AS [CPT] ON [CPT].[CordonelPressureTest_Id] = [PT].[CordonelPressureTest_Id]
WHERE [CordonelPressureTest_IsDeleted] = 0
SELECT DISTINCT
[PT].[CordonelPressureTest_FertigungsAuftragsNr] AS [FertigungNr] -- 0 Filter
, [POS].[AuftragNr] AS [AuftragNr] -- 1 Filter
, [POS].[PositionNr] AS [PositionNr] -- 2 Filter
, [PCB2SN].[MapPcbIdToSerialNumber_SerialNumber] AS [SerialNr] -- 3
, [ID].[IdentNr] AS [IdentNr] -- 4 Filter
, [PT_HE].[CordonelPressureTestHe_Id] AS [HeId] -- 5
, [PT_HE].[CordonelPressureTestHe_TestID] AS [HeTestId] -- 6
, [PT].[CordonelPressureTest_PcbId] AS [PcbId] -- 7 Filter
, [ID].[Nennweite] AS [Nennweite] -- 8 Filter
, [ID].[Baulaenge] AS [Baulaenge] -- 9 Filter
, [ID].[Typ] AS [Typ] -- 10 Filter
, [ID].[Druck] AS [Druck] -- 11 Filter
, [ID].[Temperatur] AS [Temperatur] -- 12 Filter
, [PT_HE].[CordonelPressureTestHe_LeakRate] AS [HeLakeRate] -- 13
, [PT_HE].[CordonelPressureTestHe_TestPressure] AS [HeTestPressure] -- 14
, [PT].[CordonelPressureTest_Valid] AS [IsValid] -- 15 Filter
, [PT].[CordonelPressureTest_Date] AS [TestDate] -- 16 Filter
, [PT].[CordonelPressureTest_Text] AS [Text] -- 17 Filter
, [PT_HE].[CordonelPressureTestHe_TesterName] AS [Tester] -- 18
FROM [Cordonel_PressureTest] AS [PT]
JOIN [Cordonel_PressureTest_He] AS [PT_HE] ON [PT_HE].[CordonelPressureTestHe_TestID] = [PT].[CordonelPressureTest_Id]
LEFT JOIN [Druckpruefung] AS [DP] ON [DP].[FabNr] = [PT].[CordonelPressureTest_PcbId]
JOIN [MapPcbIdToSerialNumber] AS [PCB2SN] ON [PCB2SN].[MapPcbIdToSerialNumber_PcbId] = [PT].[CordonelPressureTest_PcbId]
JOIN [AuftragPosition_Gesamt] AS [POS] ON [POS].[FertigungsauftragNr] = [PT].[CordonelPressureTest_FertigungsAuftragsNr]
JOIN [Identnr] AS [ID] ON [ID].[IdentNr] = [POS].[Identnr]
WHERE [CordonelPressureTest_IsDeleted] = 0
AND {where.Query}
ORDER BY {orderBy.Query}",
parameters =>
@@ -168,10 +167,7 @@
reader.GetValue<bool>(15),
$"{reader.GetValue<DateTimeOffset>(16):yyyy-MM-dd}",
reader.GetString(17).Replace(',', ' '),
reader.GetString(18).Replace(',', ' '),
reader.GetValue<int>(19),
$"{reader.GetValue<decimal>(20):0.000000000}",
reader.GetString(18).Replace(',', ' ')
},
columns => csvItems.Add(columns))
.ToList();
@@ -198,5 +194,141 @@
PointNr = reader.GetValue<int>(1),
Result = reader.GetValue<decimal>(2),
});
public KottmannReportModel LoadKottmannPressureReport(KottmannReportModel model = null)
{
if (model is null)
{
model = new KottmannReportModel();
}
var where = model.Filter.Where();
var orderBy = model.Filter.OrderBy();
model.Items = this.sqlClient.ExecuteReader($@"
SELECT [DP].[FertigungsauftragNr] AS [FertigungNr]
, [PCB2SN].[MapPcbIdToSerialNumber_SerialNumber] AS [SerienNr]
, [DP].[FabNr] AS [PcbId]
, [ID].[Typ] AS [Typ]
, [ID].[Nennweite] AS [Nennweite]
, [ID].[Baulaenge] AS [Baulaenge]
, [DP].[StartDruck] AS [StartDruck]
, [DP].[EndDruck] AS [EndDruck]
, [DP].[Pruefzeit] AS [Pruefzeit]
, [DP].[Dicht] AS [Dicht]
, [DP].[Datum] AS [Datum]
, [DP].[Bemerkung] AS [Bemerkung]
FROM [Druckpruefung] AS [DP]
JOIN [AuftragPosition_Gesamt] AS [POS]
ON [POS].[FertigungsauftragNr] = [DP].[FertigungsauftragNr]
JOIN [Identnr] AS [ID]
ON [ID].[IdentNr] = [POS].[Identnr]
JOIN [MapPcbIdToSerialNumber] AS [PCB2SN]
ON [PCB2SN].[MapPcbIdToSerialNumber_PcbId] = [DP].[FabNr]
AND [PCB2SN].[MapPcbIdToSerialNumber_Deleted] = 0
AND {where.Query}
ORDER BY {orderBy.Query}",
parameters =>
{
foreach (var kvp in where.Parameters)
{
parameters.Add(kvp.Key, kvp.Value);
}
foreach (var kvp in orderBy.Parameters)
{
parameters.Add(kvp.Key, kvp.Value);
}
},
reader => new KottmannReportItem
{
FertigungsNr = reader.GetValue<int?>(),
SerialNr = reader.GetValue<int>(),
PcbId = reader.GetValue<int>(),
Typ = reader.GetString(),
Nennweite = reader.GetValue<int?>(),
Baulaenge = reader.GetValue<int?>(),
StartDruck = reader.GetValue<double>(),
EndDruck = reader.GetValue<double>(),
DruckZeit = reader.GetValue<int>(),
Dicht = reader.GetValue<bool>(),
Datum = reader.GetValue<DateTime>(),
Bemerkung = reader.GetValue<string>()
});
return model;
}
public byte[] LoadKottmannPressureReportAsCSV(KottmannReportModel model)
{
if (model is null)
{
model = new KottmannReportModel();
}
var where = model.Filter.Where();
var orderBy = model.Filter.OrderBy();
var csvItems = new List<object[]>();
var headers = Array.Empty<string>();
var items = this.sqlClient.ExecuteReader($@"
SELECT [DP].[FertigungsauftragNr] AS [FertigungNr]
, [PCB2SN].[MapPcbIdToSerialNumber_SerialNumber] AS [SerienNr]
, [DP].[FabNr] AS [PcbId]
, [ID].[Typ] AS [Typ]
, [ID].[Nennweite] AS [Nennweite]
, [ID].[Baulaenge] AS [Baulaenge]
, [DP].[StartDruck] AS [StartDruck]
, [DP].[EndDruck] AS [EndDruck]
, [DP].[Pruefzeit] AS [Pruefzeit]
, [DP].[Dicht] AS [Dicht]
, [DP].[Datum] AS [Datum]
, [DP].[Bemerkung] AS [Bemerkung]
FROM [Druckpruefung] AS [DP]
JOIN [AuftragPosition_Gesamt] AS [POS]
ON [POS].[FertigungsauftragNr] = [DP].[FertigungsauftragNr]
JOIN [Identnr] AS [ID]
ON [ID].[IdentNr] = [POS].[Identnr]
JOIN [MapPcbIdToSerialNumber] AS [PCB2SN]
ON [PCB2SN].[MapPcbIdToSerialNumber_PcbId] = [DP].[FabNr]
AND [PCB2SN].[MapPcbIdToSerialNumber_Deleted] = 0
AND {where.Query}
ORDER BY {orderBy.Query}",
parameters =>
{
foreach (var kvp in where.Parameters)
{
parameters.Add(kvp.Key, kvp.Value);
}
foreach (var kvp in orderBy.Parameters)
{
parameters.Add(kvp.Key, kvp.Value);
}
},
reader => new object[]
{
reader.GetValue<int?>(),
reader.GetValue<int>(),
reader.GetValue<int>(),
reader.GetString(),
reader.GetValue<int?>(),
reader.GetValue<int?>(),
reader.GetValue<double>(),
reader.GetValue<double>(),
reader.GetValue<int>(),
reader.GetValue<bool>(),
$"{reader.GetValue<DateTime>():yyyy-MM-dd}",
reader.GetValue<string>().Replace(',', ' ')
},
columns => csvItems.Add(columns))
.ToList();
csvItems.AddRange(items);
var csvLines = csvItems.Select(x => string.Join(",", x));
var csvContent = string.Join(Environment.NewLine, csvLines);
return Encoding.UTF8.GetBytes(csvContent);
}
}
}
@@ -38,5 +38,39 @@
return services;
}
public static IServiceCollection AddHttpClient(this IServiceCollection services, string baseAddress)
=> services.AddSingleton<IHttpService>(_ => new HttpService(baseAddress));
public static IServiceCollection AddSingletonServices(this IServiceCollection services)
{
var transient = typeof(ISingleton);
var assemblyServices = transient
.Assembly
.GetTypes()
.Where(x => !x.IsAbstract
&& transient.IsAssignableFrom(x));
foreach (var service in assemblyServices)
{
var interfaces = service
.GetInterfaces()
.Where(x => x != transient);
if (!interfaces.Any())
{
services.AddTransient(service);
continue;
}
foreach (var @interface in interfaces)
{
services.AddTransient(@interface, service);
}
}
return services;
}
}
}
@@ -9,6 +9,9 @@
using System.Linq;
using System.Text;
/// <summary>
///
/// </summary>
public class ShipmentsService : IShipmentsService
{
private readonly ISqlClient sqlClient;
+17
View File
@@ -9,6 +9,15 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LaaProductionWeb.Data", "La
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LaaProductionWeb.Services", "LaaProductionWeb.Services\LaaProductionWeb.Services.csproj", "{FF167011-431A-41A7-A7DB-560E08860388}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documents", "Documents", "{59875741-02AA-4D9A-9B3C-683CA6DDBE7B}"
ProjectSection(SolutionItems) = preProject
..\..\..\..\Downloads\SRS Wildcard Order Info Search.docx = ..\..\..\..\Downloads\SRS Wildcard Order Info Search.docx
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LaaProductionWeb.API", "LaaProductionWeb.API\LaaProductionWeb.API.csproj", "{66C33368-2ABC-4BB6-917C-FF949FAF043B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LaaProductionWeb.API.UnitTests", "LaaProductionWeb.API.UnitTests\LaaProductionWeb.API.UnitTests.csproj", "{EF371C00-4452-4506-A7B6-C2F94C170D2C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -27,6 +36,14 @@ Global
{FF167011-431A-41A7-A7DB-560E08860388}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FF167011-431A-41A7-A7DB-560E08860388}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FF167011-431A-41A7-A7DB-560E08860388}.Release|Any CPU.Build.0 = Release|Any CPU
{66C33368-2ABC-4BB6-917C-FF949FAF043B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{66C33368-2ABC-4BB6-917C-FF949FAF043B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{66C33368-2ABC-4BB6-917C-FF949FAF043B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{66C33368-2ABC-4BB6-917C-FF949FAF043B}.Release|Any CPU.Build.0 = Release|Any CPU
{EF371C00-4452-4506-A7B6-C2F94C170D2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EF371C00-4452-4506-A7B6-C2F94C170D2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EF371C00-4452-4506-A7B6-C2F94C170D2C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EF371C00-4452-4506-A7B6-C2F94C170D2C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -13,7 +13,7 @@
if (filterContext.HttpContext.User is ClaimsPrincipal claimsPrincipal)
{
filterContext.Controller.TempData["Unauthorized"] = "Dir fehlen berechtigungen.";
filterContext.Result = new RedirectResult($"/");
filterContext.Result = new RedirectResult("~/");
}
else
{
@@ -4,6 +4,10 @@
public class ApplicationSettings
{
public static string APIURL => ConfigurationManager.AppSettings.Get(nameof(APIURL));
public static string ConnectionString => ConfigurationManager.AppSettings.Get(nameof(ConnectionString));
public static int LoginTimeout => int.TryParse(ConfigurationManager.AppSettings.Get(nameof(LoginTimeout)), out int time) ? time : 0;
}
}
@@ -1,34 +1,41 @@
namespace LaaProductionWeb.App_Infrastructure
{
using LaaProductionWeb.Services.Interfaces;
using System.Collections.Generic;
using System.DirectoryServices.AccountManagement;
using System.Security.Claims;
using System.Security.Principal;
using System.Web.Mvc;
public class AuthorizationFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.User is WindowsPrincipal windowsPrincipal)
var httpContext = filterContext.HttpContext;
var userId = httpContext.UserId();
var accountService = ServiceProvider.Current.GetService<IAccountService>();
var employee = accountService.FindEmployee(userId);
var claims = new List<Claim>();
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity(new Claim[]
{
using (var principalContext = new PrincipalContext(ContextType.Domain))
new Claim(ClaimTypes.Name, string.Empty),
new Claim(ClaimTypes.Role, UserRoles.WEB_Anonymous),
}, nameof(Claim), ClaimTypes.Name, ClaimTypes.Role));
if (employee.IsValid)
{
claims.Add(new Claim(ClaimTypes.NameIdentifier, $"{userId}"));
claims.Add(new Claim(ClaimTypes.Name, employee.Name));
foreach (var permission in employee.Permissions)
{
var userPrincipal = UserPrincipal.FindByIdentity(principalContext, windowsPrincipal.Identity.Name);
var accountService = ServiceProvider.Current.GetService<IAccountService>();
var employee = accountService.FindEmployee(userPrincipal.GivenName, userPrincipal.Surname);
var permissionsClaims = new List<Claim> { new Claim(ClaimTypes.Name, $"{userPrincipal.GivenName} {userPrincipal.Surname}") };
foreach (var permission in employee.Permissions)
{
permissionsClaims.Add(new Claim(ClaimTypes.Role, permission));
}
windowsPrincipal.AddIdentity(new ClaimsIdentity(permissionsClaims, nameof(ClaimsPrincipal), ClaimTypes.Name, ClaimTypes.Role));
claims.Add(new Claim(ClaimTypes.Role, permission));
}
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity(claims, nameof(Claim), ClaimTypes.Name, ClaimTypes.Role));
}
httpContext.SetUrlReferer();
}
}
}
@@ -1,6 +1,12 @@
namespace LaaProductionWeb.App_Infrastructure
{
using LaaProductionWeb.Controllers;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
@@ -8,8 +14,8 @@
public class ControllerFactory : IControllerFactory
{
const string AREAS_PATTERN = "LaaProductionWeb.Areas.{0}.Controllers.{1}Controller";
const string CONTROLLERS_PATTERN = "LaaProductionWeb.Controllers.{0}Controller";
const string AREAS_PATTERN = "LaaProductionWeb.Areas.{0}.Controllers{1}.{2}Controller";
const string CONTROLLERS_PATTERN = "LaaProductionWeb.Controllers{0}.{1}Controller";
private readonly IServiceProvider serviceProvider;
@@ -18,18 +24,52 @@
public IController CreateController(RequestContext requestContext, string controllerName)
{
var controllerTypeName = requestContext.RouteData.Values.TryGetValue("area", out object areaName)
? string.Format(AREAS_PATTERN, areaName, controllerName)
: string.Format(CONTROLLERS_PATTERN, controllerName);
var controllerType = Type.GetType(controllerTypeName, throwOnError: false, ignoreCase: true);
var controllerType = default(Type);
var routeData = requestContext.RouteData;
var routeUrl = string.Empty;
this.RedirectToDefault(controllerType, requestContext.HttpContext.Response);
if (routeData.Route is Route route)
{
routeUrl = route.Url;
}
else if (routeData.Route is IEnumerable<RouteBase> routes && routes.FirstOrDefault() is Route _route)
{
routeUrl = _route.Url;
}
var controller = this.serviceProvider.GetService(controllerType) as IController;
var innerPath = string
.Join(".", routeUrl
.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries)
.Where(x => !x.StartsWith("{") && !x.EndsWith("}")));
this.RedirectToDefault(controller, requestContext.HttpContext.Response);
if (!string.IsNullOrWhiteSpace(innerPath))
{
innerPath = $".{innerPath}";
}
return controller;
var controllerTypeName = requestContext.RouteData.DataTokens.TryGetValue("area", out object areaName)
? string.Format(AREAS_PATTERN, areaName, innerPath, controllerName)
: string.Format(CONTROLLERS_PATTERN, innerPath, controllerName);
controllerType = Type.GetType(controllerTypeName, throwOnError: false, ignoreCase: true);
if (controllerType is null)
{
controllerTypeName = controllerTypeName.Replace(innerPath, string.Empty);
controllerType = Type.GetType(controllerTypeName, throwOnError: false, ignoreCase: true);
}
if (controllerType != null)
{
var controller = this.serviceProvider.GetService(controllerType) as IController;
if (controller != null)
{
return controller;
}
}
return this.serviceProvider.GetService<HomeController>() as IController;
}
public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
@@ -47,7 +87,7 @@
{
if (resource is null)
{
response.Redirect("/");
response.Redirect("~/", true);
response.End();
}
}
@@ -3,23 +3,42 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Security.Claims;
using System.Security.Principal;
using System.Web.Mvc;
/// <summary>
/// Static class with extension methods that run on controller contexts.
/// </summary>
public static class ControllersExtensions
{
/// <summary>
/// Adds controllers of type <see cref="IController"/> loaded by reflection from the current assembly to the <see cref="IServiceCollection"/>.
/// On this way all required services are loaded into the controller constructors.
/// </summary>
/// <param name="services"><see cref="IServiceCollection"/> provided from the extension method.</param>
/// <returns>The same <see cref="IServiceCollection"/> with added services.</returns>
public static IServiceCollection AddControllers(this IServiceCollection services)
{
var controllersMap = typeof(ControllersExtensions)
.Assembly
.GetTypes()
.Where(x => !x.IsAbstract && typeof(IController).IsAssignableFrom(x))
.Select(x => new
{
x.Name,
Path = x.FullName
.Replace("LaaProductionWeb.Controllers.", string.Empty)
.Replace(x.Name, string.Empty)
})
.GroupBy(x => x.Path)
.ToList();
var controllers = typeof(ControllersExtensions)
.Assembly
.GetTypes()
.Where(x => !x.IsAbstract
&& typeof(IController).IsAssignableFrom(x));
.Where(x => !x.IsAbstract && typeof(IController).IsAssignableFrom(x));
foreach (var controller in controllers)
{
@@ -38,7 +57,7 @@
return serviceProvider;
}
public static string Name(this IPrincipal principal)
public static string ClaimName(this IPrincipal principal)
{
if (principal is ClaimsPrincipal claimsPrincipal)
{
@@ -49,25 +68,5 @@
return "<unknown>";
}
public static string Display<T>(this T model, Expression<Func<T, object>> expression)
{
if (expression.Body is MemberExpression memberExpression)
{
return memberExpression
.Member
.GetCustomAttribute<DisplayAttribute>()
.GetName();
}
else if (expression.Body is UnaryExpression unaryExpression && unaryExpression.Operand is MemberExpression operandExpression)
{
return operandExpression
.Member
.GetCustomAttribute<DisplayAttribute>()
.GetName();
}
return default(string);
}
}
}
@@ -0,0 +1,30 @@
namespace LaaProductionWeb.App_Infrastructure
{
using System;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Web.Mvc;
public abstract class NestedController : Controller
{
protected ViewResult NestedView([CallerMemberName] string viewName = "", [CallerFilePath] string fileName = "")
{
var viewPath = fileName
?.Split(new[] { "Controllers" }, StringSplitOptions.RemoveEmptyEntries)
?.LastOrDefault()
?.Replace("Controller.cs", $"\\{viewName}");
return this.View($"~\\Views{viewPath}.cshtml");
}
protected ViewResult NestedView(object model, [CallerMemberName] string viewName = "", [CallerFilePath] string fileName = "")
{
var viewPath = fileName
?.Split(new[] { "Controllers" }, StringSplitOptions.RemoveEmptyEntries)
?.LastOrDefault()
?.Replace("Controller.cs", $"\\{viewName}");
return this.View($"~\\Views{viewPath}.cshtml", model);
}
}
}
@@ -26,14 +26,4 @@
internal static void SetServiceProvider(IServiceProvider serviceProvider)
=> Current = new ServiceProvider(serviceProvider);
}
public static class ServiceProviderExtensions
{
public static IServiceProvider RegisterServiceProvider(this IServiceProvider serviceProvider)
{
ServiceProvider.SetServiceProvider(serviceProvider);
return serviceProvider;
}
}
}
@@ -0,0 +1,14 @@
namespace LaaProductionWeb.App_Infrastructure
{
using System;
public static class ServiceProviderExtensions
{
public static IServiceProvider RegisterServiceProvider(this IServiceProvider serviceProvider)
{
ServiceProvider.SetServiceProvider(serviceProvider);
return serviceProvider;
}
}
}
@@ -0,0 +1,81 @@
namespace LaaProductionWeb.App_Infrastructure
{
using System;
using System.Web;
public class SessionUser
{
public SessionUser()
=> this.UserId = -1;
public SessionUser(int userId, DateTime expires)
{
this.UserId = userId;
this.Expires = expires;
}
public int UserId { get; }
public DateTime Expires { get; }
public bool IsExpired => this.Expires < DateTime.Now.AddMinutes(30) || this.Expires > DateTime.Now;
}
public static class SessionUserExtensions
{
public static int UserId(this HttpContextBase httpContext)
{
var cookieUserId = httpContext
.Request
.Cookies
.Get(nameof(SessionUser))
?.Value ?? string.Empty;
if (int.TryParse(cookieUserId, out int userId))
{
return userId;
}
return -1;
}
public static bool SignIn(this HttpContextBase httpContext, int userId)
{
if (userId > 0)
{
httpContext.Response.AppendCookie(new HttpCookie(nameof(SessionUser), $"{userId}")
{
Expires = DateTime.Now.AddSeconds(ApplicationSettings.LoginTimeout)
});
return true;
}
return false;
}
public static void SignOut(this HttpContextBase httpContext)
{
var cookieUserId = httpContext
.Request
.Cookies
.Get(nameof(SessionUser))
?.Value ?? string.Empty;
}
const string URL_REFERER = nameof(URL_REFERER);
public static void SetUrlReferer(this HttpContextBase httpContext)
=> httpContext
.Request
.Cookies
.Add(new HttpCookie(URL_REFERER, $"{httpContext.Request.Url}"));
public static string GetUrlReferer(this HttpContextBase httpContext)
=> httpContext
.Request
.Cookies
.Get(URL_REFERER)
?.Value ?? "/";
}
}
@@ -3,6 +3,8 @@
public class UserRoles
{
public const string WEB_Admin = nameof(WEB_Admin);
public const string WEB_Anonymous = nameof(WEB_Anonymous);
public const string WEB_API_Explorer = nameof(WEB_API_Explorer);
public const string WEB_HeReport = nameof(WEB_HeReport);
public const string WEB_PalettenScan = nameof(WEB_PalettenScan);
public const string WEB_ProdApproval = nameof(WEB_ProdApproval);
@@ -2,17 +2,25 @@
{
using System.Web.Optimization;
/// <summary>
/// A class that configures all static assets (.css, .js, fonts, etc.) for the web application.
/// </summary>
public class BundleConfig
{
/// <summary>
/// Registers all configured static assets for the web application.
/// </summary>
/// <param name="bundles"><see cref="BundleCollection"/> - passed by calling this method.</param>
public static void RegisterBundles(BundleCollection bundles)
{
bundles
.Add(new StyleBundle("~/bootstrap_css")
.Include("~/App_Content/css/bootstrap.min.css"));
bundles
.Add(new StyleBundle("~/bootstrap_icons")
.Include("~/App_Content/font/bootstrap-icons.min.css"));
// not loaded - try throught cdn
//bundles
// .Add(new StyleBundle("~/bootstrap_icons")
// .Include("~/App_Content/font/bootstrap-icons.min.css"));
bundles
.Add(new StyleBundle("~/styles_css")
@@ -4,8 +4,15 @@
using System.Web.Mvc;
/// <summary>
/// A class that configures custom filters for the application.
/// </summary>
public class FilterConfig
{
/// <summary>
/// Adds custom filters to the <see cref="GlobalFilterCollection"/>.
/// </summary>
/// <param name="filters"><see cref="GlobalFilterCollection"/> - passed by calling this method.</param>
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new ExceptionFilter());
@@ -3,12 +3,21 @@
using System.Web.Mvc;
using System.Web.Routing;
/// <summary>
/// A class that configures all view route templates for the web application.
/// </summary>
public class RouteConfig
{
/// <summary>
/// Registers all view route templates to the <see cref="RouteCollection"/>.
/// </summary>
/// <param name="bundles"><see cref="RouteCollection"/> - passed by calling this method.</param>
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "MVC_Default",
url: "{controller}/{action}/{id}",
@@ -6,8 +6,14 @@
using Microsoft.Extensions.DependencyInjection;
/// <summary>
/// A class for configuring and registering dependency injection.
/// </summary>
public static class ServicesConfig
{
/// <summary>
/// Builds and registers configured services to the service provider.
/// </summary>
public static void RegisterServices()
=> new ServiceCollection()
.ConfigureServices()
@@ -15,11 +21,17 @@
.BuildControllerFactory()
.RegisterServiceProvider();
/// <summary>
/// Allows developer to add services to the service collection.
/// </summary>
/// <param name="services"><see cref="IServiceCollection"/> provided from the extension method.</param>
/// <returns>The same <see cref="IServiceCollection"/> with added services.</returns>
static IServiceCollection ConfigureServices(this IServiceCollection services)
{
services
.AddControllers()
.AddTransientServices()
.AddHttpClient(ApplicationSettings.APIURL)
.AddDbContext(ApplicationSettings.ConnectionString);
return services;
@@ -1,17 +0,0 @@
namespace LaaProductionWeb
{
using System.Web.Http;
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
}
}
}
@@ -4,8 +4,7 @@
using LaaProductionWeb.Services.Interfaces;
using System.Web.Mvc;
[Authorize]
[AllowedRoles(UserRoles.WEB_Admin)]
public class AdminController : Controller
{
@@ -6,8 +6,7 @@
using System;
using System.Web.Mvc;
[Authorize]
[AllowedRoles(UserRoles.WEB_ProdApproval)]
public class ApprovalsController : Controller
{
@@ -20,18 +19,17 @@
public ActionResult Add()
=> this.View(new ProductionApproval
{
ApproverName = this.User.Name(),
ApproverName = this.User.ClaimName(),
ApprovalDate = DateTime.Now
});
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Add(ProductionApproval approval)
{
if (this.ModelState.IsValid)
{
approval.ApprovalDate = DateTime.Now;
approval.ApproverName = this.User.Name();
approval.ApproverName = this.User.ClaimName();
var addResult = this.approvals.Add(approval);
@@ -1,11 +1,52 @@
namespace LaaProductionWeb.Controllers
{
using LaaProductionWeb.App_Infrastructure;
using LaaProductionWeb.Services.Interfaces;
using System.Web.Mvc;
[Authorize]
[AllowAnonymous]
public class HomeController : Controller
{
private readonly IAccountService accountService;
public HomeController(IAccountService accountService)
=> this.accountService = accountService;
[HttpGet]
public ActionResult Index() => this.View();
public ActionResult Index()
=> this.View();
[HttpPost]
public ActionResult Index(string username, string password)
{
if (string.IsNullOrWhiteSpace(username))
{
this.ModelState.AddModelError(nameof(username), "Benutzername ist erforderlich.");
}
if (string.IsNullOrWhiteSpace(password))
{
this.ModelState.AddModelError(nameof(password), "Kennwort ist erforderlich.");
}
if (this.ModelState.IsValid)
{
var userId = this.accountService.Login(username, password);
if (this.HttpContext.SignIn(userId))
{
return this.Redirect(this.HttpContext.GetUrlReferer());
}
}
return this.View();
}
public ActionResult Logout()
{
this.HttpContext.SignOut();
return this.View(nameof(Index));
}
}
}
@@ -5,8 +5,7 @@
using LaaProductionWeb.Services.Models;
using System.Web.Mvc;
[Authorize]
[AllowedRoles(UserRoles.WEB_PuneProtokoll)]
public class ProtocolController : Controller
{
@@ -88,7 +87,6 @@
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult DeleteFile(int fileId, int orderId)
{
this.protocolService.DeleteFile(fileId);
@@ -110,6 +108,7 @@
return this.PartialView(clientsOrders);
}
[HttpGet]
public ActionResult Body(int orderId = 0)
{
var testStatus = this.protocolService.LoadTestStatus(orderId);
@@ -2,11 +2,11 @@
{
using LaaProductionWeb.App_Infrastructure;
using LaaProductionWeb.Services.Interfaces;
using LaaProductionWeb.Services.Models;
using LaaProductionWeb.Services.Models.Reports;
using System;
using System.Web.Mvc;
[Authorize]
[AllowedRoles(UserRoles.WEB_HeReport)]
public class ReportController : Controller
{
@@ -16,7 +16,7 @@
=> this.reportService = reportService;
[HttpGet]
public ActionResult Index()
public ActionResult Helium()
{
var model = this.reportService.LoadHeliumPressureReport();
@@ -24,7 +24,7 @@
}
[HttpPost]
public ActionResult Index(HeliumReportModel model)
public ActionResult Helium(HeliumReportModel model)
{
model = this.reportService.LoadHeliumPressureReport(model);
@@ -32,8 +32,7 @@
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CSV(HeliumReportModel model)
public ActionResult HeliumCSV(HeliumReportModel model)
{
var csvReport = this.reportService.LoadHeliumPressureReportAsCSV(model);
var fileName = $"HeReport_{DateTime.Now:yyyyMMddhhmmss}.csv";
@@ -43,11 +42,38 @@
return this.File(csvReport, "application/csv");
}
public ActionResult Results(int testId = 0)
public ActionResult HeliumResults(int testId = 0)
{
var results = this.reportService.LoadHeliumPressureResults(testId);
return this.PartialView(results);
}
[HttpGet]
public ActionResult Kottmann()
{
var model = this.reportService.LoadKottmannPressureReport();
return this.View(model);
}
[HttpPost]
public ActionResult Kottmann(KottmannReportModel model)
{
model = this.reportService.LoadKottmannPressureReport(model);
return this.View(model);
}
[HttpPost]
public ActionResult KottmannCSV(KottmannReportModel model)
{
var csvReport = this.reportService.LoadKottmannPressureReportAsCSV(model);
var fileName = $"KottmannReport_{DateTime.Now:yyyyMMddhhmmss}.csv";
this.Response.AddHeader("Content-Disposition", $"attachment;filename={fileName}");
return this.File(csvReport, "application/csv");
}
}
}
@@ -1,16 +1,26 @@
namespace LaaProductionWeb.Controllers
{
using LaaProductionWeb.App_Infrastructure;
using LaaProductionWeb.Services.Interfaces;
using LaaProductionWeb.Services.Models.Search;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web.Mvc;
[Authorize]
[AllowedRoles(UserRoles.WEB_API_Explorer)]
public class SearchController : Controller
{
private readonly IOrdersService ordersService;
private readonly IHttpService httpService;
public SearchController(IOrdersService ordersService)
=> this.ordersService = ordersService;
public SearchController(IOrdersService ordersService, IHttpService httpService)
{
this.ordersService = ordersService;
this.httpService = httpService;
}
[HttpGet]
public ActionResult Orders(string search)
@@ -35,5 +45,50 @@
return this.PartialView(model);
}
[HttpGet]
public async Task<ActionResult> Wildcard()
{
var httpOptions = await this.httpService.OptionsAsync("/Search/Wildcard");
var model = new SearchWildcardModel();
if (httpOptions.Succeeded)
{
model.Fields = httpOptions
.Deserialize<List<string>>()
.ToDictionary(x => x, x => false);
}
return this.View(model);
}
[HttpPost]
public async Task<ActionResult> Wildcard(SearchWildcardModel model)
{
var fields = string.Empty;
if (model.Fields.Any(x => x.Value))
{
fields = WebUtility.UrlEncode(string.Join(",", model.Fields.Where(x => x.Value).Select(x => x.Key)));
}
var httpResult = await this.httpService.GetAsync($"/Search/Wildcard?token={model.Token}&fields={fields}");
if (httpResult.Succeeded)
{
model.Results = httpResult.Deserialize<List<Dictionary<string, object>>>();
}
else
{
var errors = httpResult.Deserialize<Dictionary<string, string>>();
foreach (var errorKVP in errors)
{
this.ModelState.AddModelError(errorKVP.Key, errorKVP.Value.Replace("Token", "'Search text'"));
}
}
return this.View(model);
}
}
}
@@ -12,8 +12,7 @@
using System.Web.Mvc;
using SystemFile = System.IO.File;
[Authorize]
[AllowedRoles(UserRoles.WEB_PalettenScan)]
public class ShipmentsController : Controller
{
@@ -27,7 +26,6 @@
=> this.View(new ShipmentModel());
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(ShipmentModel model)
=> this.View(model);
@@ -51,7 +49,6 @@
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ScanPost(OrderScanModel model)
{
if (this.ModelState.IsValid)
@@ -64,7 +61,6 @@
if (result)
{
model.PositionNr = null;
model.SerialNr = default(string);
}
else
@@ -80,7 +76,6 @@
public ActionResult Delete(int id)
{
var orderScanModel = this.shipmentsService.DeletePalletEntry(id);
orderScanModel.PositionNr = null;
return this.View(nameof(this.Index), new ShipmentModel(orderScanModel));
}
@@ -3,18 +3,22 @@
using LaaProductionWeb.App_Start;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
/// <summary>
/// MVC base class for registering and configuring user-defined or customized applications.
/// </summary>
public class MvcApplication : HttpApplication
{
/// <summary>
/// Default launch method defined by the Asp.Net MVC framework.
/// </summary>
protected void Application_Start()
{
ServicesConfig.RegisterServices();
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
@@ -18,8 +18,8 @@
<UseIISExpress>true</UseIISExpress>
<Use64BitIISExpress />
<IISExpressSSLPort />
<IISExpressAnonymousAuthentication>disabled</IISExpressAnonymousAuthentication>
<IISExpressWindowsAuthentication>enabled</IISExpressWindowsAuthentication>
<IISExpressAnonymousAuthentication>enabled</IISExpressAnonymousAuthentication>
<IISExpressWindowsAuthentication>disabled</IISExpressWindowsAuthentication>
<IISExpressUseClassicPipelineMode />
<UseGlobalApplicationHostFile />
<NuGetPackageImportStamp>
@@ -60,25 +60,26 @@
<Reference Include="Microsoft.Web.Infrastructure, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Web.Infrastructure.2.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\Program Files\dotnet\sdk\NuGetFallbackFolder\newtonsoft.json\11.0.2\lib\netstandard2.0\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Data.OracleClient" />
<Reference Include="System.DirectoryServices" />
<Reference Include="System.DirectoryServices.AccountManagement" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http.Formatting, Version=5.2.9.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.2.9\lib\net45\System.Net.Http.Formatting.dll</HintPath>
</Reference>
<Reference Include="System.Net" />
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Security.Principal.Windows, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll</HintPath>
</Reference>
<Reference Include="System.Security" />
<Reference Include="System.ServiceProcess" />
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.Transactions" />
<Reference Include="System.Web.DynamicData" />
<Reference Include="System.Web.Entity" />
<Reference Include="System.Web.ApplicationServices" />
@@ -88,12 +89,6 @@
<Reference Include="System.Web.Helpers, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.9\lib\net45\System.Web.Helpers.dll</HintPath>
</Reference>
<Reference Include="System.Web.Http, Version=5.2.9.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.5.2.9\lib\net45\System.Web.Http.dll</HintPath>
</Reference>
<Reference Include="System.Web.Http.WebHost, Version=5.2.9.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.9\lib\net45\System.Web.Http.WebHost.dll</HintPath>
</Reference>
<Reference Include="System.Web.Mvc, Version=5.2.9.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.Mvc.5.2.9\lib\net45\System.Web.Mvc.dll</HintPath>
</Reference>
@@ -132,6 +127,7 @@
<Reference Include="WebGrease, Version=1.6.5135.21930, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\WebGrease.1.6.0\lib\WebGrease.dll</HintPath>
</Reference>
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="App_Infrastructure\ApplicationSettings.cs" />
@@ -140,13 +136,15 @@
<Compile Include="App_Infrastructure\ControllersExtensions.cs" />
<Compile Include="App_Infrastructure\ExceptionFilter.cs" />
<Compile Include="App_Infrastructure\AllowedRolesAttribute.cs" />
<Compile Include="App_Infrastructure\NestedController.cs" />
<Compile Include="App_Infrastructure\ServiceProvider.cs" />
<Compile Include="App_Infrastructure\ServiceProviderExtensions.cs" />
<Compile Include="App_Infrastructure\SessionUser.cs" />
<Compile Include="App_Infrastructure\UserRoles.cs" />
<Compile Include="App_Start\BundleConfig.cs" />
<Compile Include="App_Start\FilterConfig.cs" />
<Compile Include="App_Start\RouteConfig.cs" />
<Compile Include="App_Start\ServicesConfig.cs" />
<Compile Include="App_Start\WebApiConfig.cs" />
<Compile Include="Controllers\AdminController.cs" />
<Compile Include="Controllers\ApprovalsController.cs" />
<Compile Include="Controllers\HomeController.cs" />
@@ -167,29 +165,30 @@
<Content Include="App_Content\js\jsbarcode.all.min.js" />
<Content Include="App_Content\js\scripts.js" />
<Content Include="App_Content\svg\sensus_xylem_logo.svg" />
<Content Include="favicon.ico" />
<Content Include="favicon.svg" />
<Content Include="Global.asax" />
<Content Include="Views\Home\Index.cshtml" />
<Content Include="Web.config">
<SubType>Designer</SubType>
</Content>
<Content Include="Views\Web.config" />
<Content Include="Views\_ViewStart.cshtml" />
<Content Include="Views\Shared\_Layout.cshtml" />
<Content Include="Views\Home\Index.cshtml" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<Content Include="Views\Shared\_PrintLayout.cshtml" />
<Content Include="Views\Report\Index.cshtml" />
<Content Include="Views\Report\Helium.cshtml" />
<Content Include="Views\Report\Filter.cshtml" />
<Content Include="Views\Report\Results.cshtml" />
<Content Include="Views\Report\HeliumResults.cshtml" />
<Content Include="Views\Report\DateFilter.cshtml" />
<None Include="Properties\PublishProfiles\FolderProfile.pubxml" />
<Content Include="Views\Approvals\Add.cshtml" />
<Content Include="Views\Search\Production.cshtml" />
<Content Include="Views\Admin\Index.cshtml" />
<Content Include="Views\Report\HiddenFilter.cshtml" />
<Content Include="Views\Report\Kottmann.cshtml" />
<Content Include="Views\Search\Wildcard.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="App_Content\font\fonts\bootstrap-icons.woff" />
@@ -0,0 +1,30 @@
**LaaProductionWeb**
---
[&#129028; Go back to the parent directory](https://slm.sms-esaap.com/la_operations/laa_production/-/tree/main/LaaProductionWeb)
is a **ASP.Net MVC** **[local web application](http://sla12iis01.emea.sensus.net/LaaProductionWeb/)** written under **C# .Net Framework 6.4.2** that contains web services for digitization of the production processes.
- Authentication througth: username, password; initial session time: 10 min; configurable;
- Loading content according to current user permissions
**1. Pallets scan**
- Used to save pallets with their loads in a database. This will help later when creating a pallet load file to ship with the pallet.
**2. QAP test protocol**
- Used to create QAP test protocol automaticaly for particular customers. This protocol should be printet signed and uploaded to the particular order. Is unique for each order.
**3. Reports**
- [He] Report - Statistics for past helium tests, allows user to filter and sort all displayed data and then to download a csv file from that as well.
- Cotman - Statistics for past pressure tests, allows user to filter and sort all displayed data and then to download a csv file from that as well.
**4. Production approval**
- Used to approve particular production orders with different characteristics and notes.
**5. Wildcard search**
- is a none public web interface for searching orders info from one of following: Radio Addresse, PcbId, SerialNr, ClientSerialNr.
@@ -1,7 +1,6 @@
@model LaaProductionWeb.Services.Models.ProductionApproval
<form action="/Approvals/Add" method="post" autocomplete="off">
@this.Html.AntiForgeryToken()
<form action="~/Approvals/Add" method="post" autocomplete="off">
<div class="container my-3">
<div class="row">
<div class="col">
@@ -138,7 +137,6 @@
(function () {
let fertigungNrMenu = document.getElementById('fertigungNrMenu');
let orderNrInput = document.getElementById('OrderNr');
if (fertigungNrMenu && orderNrInput) {
function loadDropdownItems(html) {
fertigungNrMenu.innerHTML = html;
@@ -152,7 +150,7 @@
orderNrInput.oninput = (e) => {
let value = e.target.value;
if (value && value.length > 2) {
fetch(`/Search/Production?productionNr=${orderNrInput.value}`)
fetch(`@this.Url.Action("Production", "Search")?productionNr=${orderNrInput.value}`)
.then(response => response.text())
.then(html => loadDropdownItems(html))
.catch(error => console.log(error));
@@ -1,4 +1,6 @@
<div class="container my-5 py-5">
@using LaaProductionWeb.App_Infrastructure
<div class="container my-5 py-5">
<div class="row">
<div class="col-12">
<div class="jumbotron">
@@ -30,4 +32,27 @@
</div>
</div>
</div>
@if (this.User.IsInRole(UserRoles.WEB_Anonymous))
{
<div class="row">
<div class="col-5">
<form action="~/" method="post" autocomplete="off">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="username" name="username" placeholder="Benutzername" autocomplete="off">
<label for="username">Benutzername</label>
@this.Html.ValidationMessage("username", new { @class = "small text-danger" })
</div>
<div class="form-floating mb-3">
<input type="password" class="form-control" id="password" name="password" placeholder="Kennwort" autocomplete="off">
<label for="password">Kennwort</label>
@this.Html.ValidationMessage("password", new { @class = "small text-danger" })
</div>
<div class="d-flex mb-3">
<input class="btn btn-success py-3 px-5" value="Anmelden" type="submit" />
</div>
</form>
</div>
</div>
}
</div>
@@ -1,4 +1,4 @@
@model IEnumerable<LaaProductionWeb.Services.Models.TestStatus>
@model IEnumerable<LaaProductionWeb.Services.Models.Reports.TestStatus>
@{
Func<bool?, bool, MvcHtmlString> testPassed = (testStatus, nullable) =>
@@ -3,8 +3,7 @@
@using LaaProductionWeb.Services.Models
<div class="d-flex flex-column h-100">
<form action="/Protocol/DeleteFile" method="post" class="nav justify-content-end bg-whitesmoke">
@this.Html.AntiForgeryToken()
<form action="~/Protocol/DeleteFile" method="post" class="nav justify-content-end bg-whitesmoke">
@this.Html.HiddenFor(x => x.FileId)
@this.Html.HiddenFor(x => x.OrderId)
<button class="btn btn-link text-danger nav-link">

Some files were not shown because too many files have changed in this diff Show More