namespace LaaProduction.Services { using LaaPackages.SqlClient; using LaaProduction.Services.Interfaces; using LaaProduction.Services.Models; using LaaProduction.Services.Models.Reports; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; public class ProtocolService : IProtocolService { private const String PROTOCOL = "PROTOCOL"; private readonly SqlConnection sqlConnection; public ProtocolService(SqlConnection sqlConnection) => this.sqlConnection = sqlConnection; public Boolean DeleteFile(Int32 fileId) => this.sqlConnection .CreateCommand($@" DELETE FROM [File] WHERE [FileId] = @{nameof(fileId)}; DELETE FROM [FilesOrders] OUTPUT [Deleted].[FileId] WHERE [FileId] = @{nameof(fileId)};") .SetParameter(nameof(fileId), fileId) .ExecuteScalar(x => x.GetValue(0)) > 0; public String FileContent(Int32? fileId) { var fileContent = this.sqlConnection .CreateCommand($@" SELECT [dbo].[File].[FileContent] FROM [dbo].[File] WHERE [dbo].[File].[FileId] = @{nameof(fileId)}") .SetParameter(nameof(fileId), fileId) .FirstOrDefault(reader => reader.GetValue(0)) ?? Array.Empty(); return String.Format("data:application/pdf;base64,{0}", Convert.ToBase64String(fileContent)); } public OrderClientModel FindOrder(Int32 orderId) { /// Für ungültige auftrag nummer einfach leere objekt zurück geben. if (orderId <= 0) { return new OrderClientModel(); } /// Abfragen der algemeinen auftrag daten var model = this.sqlConnection .CreateCommand($@" SELECT [APG].[AuftragNr] , [K].[KundenNr] , [K].[Name] FROM [AuftragPosition_Gesamt] AS [APG] JOIN [Auftrag_Gesamt] AS [AG] ON [AG].[AuftragNr] = [APG].[AuftragNr] JOIN [IdentNr] AS [IN] ON [IN].[IdentNr] = [APG].[Identnr] AND [IN].[Typ] = 'GNS' JOIN [Kunde] AS [K] ON [K].[KundenNr] = [AG].[KundenNr] WHERE [APG].[AuftragNr] = @{nameof(orderId)}") .SetParameter(nameof(orderId), orderId) .FirstOrDefault(reader => new OrderClientModel { OrderId = reader.GetValue(0), ClientId = reader.GetValue(1), ClientName = reader.GetValue(2) }); /// Wenn auftrag nicht gefunden ist einfach leere objekt zurück geben. if (model is null) { return new OrderClientModel(); } model.Results = this.LoadTestResults(orderId); model.Positions = model .Results .GroupBy(x => x.PosNr) .Where(x => x.Key > 0) .OrderBy(x => x.Key) .ToDictionary(x => x.Key, x => x.All(r => r.Succeeded)); /// Abfragen bestähende protokolle. model.Files = this.sqlConnection .CreateCommand($@" SELECT COUNT([FileId]) FROM [FilesOrders] WHERE [OrderId] = @{nameof(orderId)} AND [Usage] = '{PROTOCOL}'") .SetParameter(nameof(orderId), orderId) .FirstOrDefault(x => x.GetValue(0)); return model; } public IEnumerable> FindOrdersById(Int32 orderno) => this.sqlConnection .CreateCommand($@" SELECT DISTINCT [APG].[AuftragNr] , [K].[Name] FROM [AuftragPosition_Gesamt] AS [APG] JOIN [Auftrag_Gesamt] AS [AG] ON [AG].[AuftragNr] = [APG].[AuftragNr] JOIN [IdentNr] AS [IN] ON [IN].[IdentNr] = [APG].[Identnr] AND [IN].[Typ] = 'GNS' JOIN [Kunde] AS [K] ON [K].[KundenNr] = [AG].[KundenNr] WHERE [APG].[AuftragNr] LIKE @{nameof(orderno)}") .SetParameter(nameof(orderno), $"%{orderno}%") .ExecuteReader(reader => new KeyValuePair( key: reader.GetValue(0), value: reader.GetValue(1))); public IEnumerable> FindOrdersByClient(String clientName) => this.sqlConnection .CreateCommand($@" SELECT DISTINCT [APG].[AuftragNr] , [K].[Name] FROM [AuftragPosition_Gesamt] AS [APG] JOIN [Auftrag_Gesamt] AS [AG] ON [AG].[AuftragNr] = [APG].[AuftragNr] JOIN [IdentNr] AS [IN] ON [IN].[IdentNr] = [APG].[Identnr] AND [IN].[Typ] = 'GNS' JOIN [Kunde] AS [K] ON [K].[KundenNr] = [AG].[KundenNr] WHERE [K].[Name] LIKE @{nameof(clientName)}") .SetParameter(nameof(clientName), $"%{clientName}%") .ExecuteReader(reader => new KeyValuePair( key: reader.GetValue(0), value: reader.GetValue(1))); public IDictionary GetFiles(Int32 orderId) { var files = this.sqlConnection .CreateCommand($@" SELECT [FileId] , [FileContent] FROM [File] WHERE [FileId] IN (SELECT [FileId] FROM [FilesOrders] WHERE [OrderId] = @{nameof(orderId)}) ORDER BY [FileId] ASC") .SetParameter(nameof(orderId), orderId) .ExecuteReader(x => new { Key = x.GetValue(0), Value = String.Format("data:application/pdf;base64,{0}", Convert.ToBase64String(x.GetValue(1))) }) ?.ToDictionary(x => x.Key, x => x.Value) ?? new Dictionary(); return files; } public IEnumerable LoadTestResults(Int32 orderId) => this.sqlConnection .CreateCommand($@" SELECT DISTINCT [APS].[PositionNr] AS [PosNr] -- 0 , ISNULL([APS].[KundeneigeneSerienNr], [APS].[SerienNr]) AS [SerialNr] -- 1 , [CPT].[CordonelPressureTest_Valid] AS [3TightnessTest] -- 2 , [DP].[Dicht] AS [4PressureAndTightness] -- 3 , MAX([APS].[StatusFertigung]) AS [5Metrologicaltesting] -- 4 , [CRRH].[Result] AS [6ConfigComparisonAndRadiotest] -- 5 FROM [AuftragPositionSerienNr] AS [APS] LEFT JOIN [Genesis_Meter] AS [GM] ON [GM].[Seriennummer] = [APS].[SerienNr] LEFT JOIN [MapPcbIdToSerialNumber] AS [PCB] ON [PCB].[MapPcbIdToSerialNumber_SerialNumber] = [GM].[Seriennummer] LEFT JOIN [Cordonel_Radio_RSSI_History] AS [CRRH] ON [CRRH].[RadioAdress] = [GM].[Adresse] - 10000000000 AND [CRRH].[IsLast] = 1 AND [CRRH].[Result] = 1 LEFT JOIN [Druckpruefung] AS [DP] ON [DP].[FabNr] = [PCB].[MapPcbIdToSerialNumber_PcbId] AND [DP].[Dicht] = 1 LEFT JOIN [Cordonel_PressureTest] AS [CPT] ON [CPT].[CordonelPressureTest_PcbId] = [PCB].[MapPcbIdToSerialNumber_PcbId] AND [CPT].[CordonelPressureTest_Valid] = 1 WHERE [APS].[AuftragNr] = @{nameof(orderId)} GROUP BY [APS].[PositionNr] , [APS].[SerienNr] , [APS].[KundeneigeneSerienNr] , [CPT].[CordonelPressureTest_Valid] , [DP].[Dicht] , [CRRH].[Result] ORDER BY [APS].[PositionNr] , ISNULL([APS].[KundeneigeneSerienNr], [APS].[SerienNr])") .SetParameter(nameof(orderId), orderId) .ExecuteReader(reader => new TestStatus { PosNr = reader.GetValue(0), SerialNr = reader.GetValue(1), HeliumTest = reader.GetValue(2), PressureTest = reader.GetValue(3), MetroTest = this.MetrologicalValue(reader.GetValue(4)), RadioTest = reader.GetValue(5), }) .OrderBy(x => !x.Succeeded) .ThenBy(x => x.PosNr); public Boolean OrderCompleted(Int32 orderId) { var sumResults = this.sqlConnection .CreateCommand($@" SELECT CAST(CASE WHEN ISNULL([CPT].[CordonelPressureTest_Valid], 1) = 1 THEN 0 ELSE 1 END AS INT) + CAST(CASE WHEN ISNULL([DP].[Dicht], 1) = 1 THEN 0 ELSE 1 END AS INT) + ISNULL(MAX([APS].[StatusFertigung]) - 30, 0) + CAST(CASE WHEN ISNULL([CRRH].[Result], 1) = 1 THEN 0 ELSE 1 END AS INT) FROM [AuftragPositionSerienNr] AS [APS] LEFT JOIN [Genesis_Meter] AS [GM] ON [GM].[Seriennummer] = [APS].[SerienNr] LEFT JOIN [MapPcbIdToSerialNumber] AS [PCB] ON [PCB].[MapPcbIdToSerialNumber_SerialNumber] = [GM].[Seriennummer] LEFT JOIN [Cordonel_Radio_RSSI_History] AS [CRRH] ON [CRRH].[RadioAdress] = [GM].[Adresse] - 10000000000 AND [CRRH].[IsLast] = 1 AND [CRRH].[Result] = 1 LEFT JOIN [Druckpruefung] AS [DP] ON [DP].[FabNr] = [PCB].[MapPcbIdToSerialNumber_PcbId] AND [DP].[Dicht] = 1 LEFT JOIN [Cordonel_PressureTest] AS [CPT] ON [CPT].[CordonelPressureTest_PcbId] = [PCB].[MapPcbIdToSerialNumber_PcbId] AND [CPT].[CordonelPressureTest_Valid] = 1 WHERE [APS].[AuftragNr] = @{nameof(orderId)} GROUP BY [APS].[PositionNr] , [APS].[SerienNr] , [APS].[KundeneigeneSerienNr] , [CPT].[CordonelPressureTest_Valid] , [DP].[Dicht] , [CRRH].[Result]") .SetParameter(nameof(orderId), orderId) .ExecuteReader(reader => reader.GetValue(0)) .Sum(x => x); return sumResults == 0; } public Int32 SaveFile(Stream fileStream, Int32 clientId, Int32 orderId) { var fileName = $"{clientId}_{orderId}.pdf"; var fileContent = this.GetBuffer(fileStream); var fileMD5Buffer = MD5.Create().ComputeHash(fileContent); var fileMD5 = BitConverter .ToString(fileMD5Buffer) .Replace("-", String.Empty) .ToLower(); var length = fileMD5?.Length; var exists = this.sqlConnection .CreateCommand($@" SELECT COUNT(1) FROM [FilesOrders] WHERE [Hash] = @{nameof(fileMD5)}") .SetParameter(nameof(fileMD5), fileMD5) .FirstOrDefault(x => x.GetValue(0)) > 0; if (exists) { return -1; } return this.sqlConnection .CreateCommand($@" INSERT INTO [dbo].[File] ( [FileName] , [FileContent]) OUTPUT @{nameof(orderId)}, [Inserted].[FileId], '{PROTOCOL}', @{nameof(fileMD5)} INTO [FilesOrders]([OrderId], [FileId], [Usage], [Hash]) OUTPUT [Inserted].[FileId] VALUES (@{nameof(fileName)} , @{nameof(fileContent)})") .SetParameter(nameof(orderId), orderId) .SetParameter(nameof(fileName), fileName) .SetParameter(nameof(fileContent), fileContent) .SetParameter(nameof(fileMD5), fileMD5) .ExecuteScalar(x => x.GetValue(0)); } private Byte[] GetBuffer(Stream fileStream) { var fileContent = new Byte[fileStream.Length]; using (var memoryStream = new MemoryStream()) { fileStream.CopyTo(memoryStream); fileContent = memoryStream.ToArray(); } return fileContent; } private Boolean? MetrologicalValue(Byte? value) { if (value is null) { return null; } return value == 30; } } }