From f5d85d4c5692c8741c0a823623f45e044ff80987 Mon Sep 17 00:00:00 2001 From: Rene Evertz Date: Fri, 19 Jul 2024 14:41:49 +0200 Subject: [PATCH] =?UTF-8?q?KApp=20->=20App8:=20GetIKNummern=20Abrechnungsc?= =?UTF-8?q?ode=20+=20Tarifkennzeichen=20werden=20=C3=BCbergeben=20Meldung,?= =?UTF-8?q?=20falls=20IK=20nicht=20hinterlegt=20ist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dakota/DakotaValidator.cs | 30 ++-- Dakota/Logic/DakotaKTDReader.cs | 10 +- Dakota/Logic/DakotaUtils.cs | 37 +++++ DakotaUnitTest/DakotaReaderUnitTest.cs | 14 ++ Data/Entities/GkvAbrechnung.cs | 7 +- Host/GkvAbrechnungView.aspx.cs | 17 ++- .../GkvAbrechnungDC_GkvAbrechnung.cs | 2 +- .../AccountingServiceImp.cs | 138 +++++++++++------- .../GkvAbrechnung/GkvApp8GetIKRequest.cs | 8 +- .../GkvAbrechnung/GkvApp8GetIKResponse.cs | 7 +- 10 files changed, 186 insertions(+), 84 deletions(-) diff --git a/Dakota/DakotaValidator.cs b/Dakota/DakotaValidator.cs index 2edfd956e..622ba6b0b 100644 --- a/Dakota/DakotaValidator.cs +++ b/Dakota/DakotaValidator.cs @@ -1,4 +1,5 @@ -using BS.Shared.DataContracts; +using BeWo.Data.Entities; +using BS.Shared.DataContracts; using BS.Shared.DataContracts.GkvAbrechnung; using Dakota.Models; using System; @@ -47,34 +48,23 @@ namespace Dakota throw new DakotaException(error); } - public static void ValidateOrganisation(OrganisationDC orgadc) + public static void ValidateOrganisation(Organisation orga) { string error = null; - if (orgadc == null) + if (orga == null) error = "Organisation ist null"; - - if (!ValidateIKNumbers(orgadc)) - { - - } - - - //if (!ValidateIKNumber(orgadc.IKDatenannahmestelle)) - // error = $"Die Organisation \"{orgadc.Name}\" hat keine gültige IK Nummer für die Datenannahmestelle."; - //else if (!ValidateIKNumber(orgadc.IKKostentrager)) - // error = $"Die Organisation \"{orgadc.Name}\" hat keine gültige IK Nummer für die Kostenträger."; - //else if (!ValidateIKNumber(orgadc.IKKrankenkasse)) - // error = $"Die Organisation \"{orgadc.Name}\" hat keine gültige IK Nummer für die Krankenkasse."; - if (!ValidateLeistungserbringergruppe(orgadc.Leistungserbringergruppe)) - error = $"Die Organisation \"{orgadc.Name}\" hat keine gültige Leistungserbringergruppe: \"{orgadc.Leistungserbringergruppe}\". Erwarten werden 5 Stellen für das Tarifkennzeichen alleine oder 7 Stellen für den Abrechnungscode und für das Tarifkennzeichen zusammen."; + else if (!ValidateLeistungserbringergruppe(orga.Leistungserbringergruppe)) + error = $"Die Organisation \"{orga.Name}\" hat keine gültige Leistungserbringergruppe: \"{orga.Leistungserbringergruppe}\". Erwarten werden 5 Stellen für das Tarifkennzeichen alleine oder 7 Stellen für den Abrechnungscode und für das Tarifkennzeichen zusammen."; + else if (!ValidateIKNumber(orga.IKKrankenkasse)) + error = $"Die Organisation \"{orga.Name}\" hat kein gültiges IK Kennzeichen: \"{orga.IKKrankenkasse}\"."; if (error != null) throw new DakotaException(error); } - private static bool ValidateIKNumbers(OrganisationDC dc) - => ValidateIKNumbers(dc.IKDatenannahmestelle, dc.IKKostentrager, dc.IKKrankenkasse); + public static bool ValidateIKNumbers(Organisation orga) + => ValidateIKNumbers(orga.IKDatenannahmestelle, orga.IKKostentrager, orga.IKKrankenkasse); private static bool ValidateIKNumbers(params string[] iks) => iks?.All(ValidateIKNumber) ?? false; diff --git a/Dakota/Logic/DakotaKTDReader.cs b/Dakota/Logic/DakotaKTDReader.cs index 76ae81e19..310866b2f 100644 --- a/Dakota/Logic/DakotaKTDReader.cs +++ b/Dakota/Logic/DakotaKTDReader.cs @@ -33,9 +33,17 @@ namespace Dakota.Logic } public int GetFileCount() => FileDictionary.Count(); - public int GetAllFileEntryCount() => FileDictionary.Sum(x => x.Value.Count); + /// + /// + /// + /// + /// + /// + /// Item1:Datenannahmestelle - + /// Item2:Kostenträger - + /// Item3:Krankenkasse public Tuple Resolve(string ikkrankenkasse, string abrechnungscode, string tarifkennzeichen) { foreach (var kv in FileDictionary) diff --git a/Dakota/Logic/DakotaUtils.cs b/Dakota/Logic/DakotaUtils.cs index 98704fc5b..e8fe2b5df 100644 --- a/Dakota/Logic/DakotaUtils.cs +++ b/Dakota/Logic/DakotaUtils.cs @@ -43,5 +43,42 @@ namespace Dakota.Logic return sb.ToString(); } + + public static string GetAbrechnungscode(string leistungserbringergruppe) + { + var len = leistungserbringergruppe.Length; + + if(len == 5) + { + return "69"; + } + else if(len == 7) + { + return leistungserbringergruppe.Substring(0, 2); + } + else + { + throw new ArgumentException($"Leistungserbringergruppe \"{leistungserbringergruppe}\" hat ungültige Anzahl Stellen"); + } + } + + public static string GetTarifkennzeichen(string leistungserbringergruppe) + { + var len = leistungserbringergruppe.Length; + + if (len == 5) + { + return leistungserbringergruppe; + } + else if (len == 7) + { + return leistungserbringergruppe.Substring(2); + // return leistungserbringergruppe.Substring(2, 5); + } + else + { + throw new ArgumentException($"Leistungserbringergruppe \"{leistungserbringergruppe}\" hat ungültige Anzahl Stellen"); + } + } } } diff --git a/DakotaUnitTest/DakotaReaderUnitTest.cs b/DakotaUnitTest/DakotaReaderUnitTest.cs index 0fd8f548f..450fcf68d 100644 --- a/DakotaUnitTest/DakotaReaderUnitTest.cs +++ b/DakotaUnitTest/DakotaReaderUnitTest.cs @@ -54,5 +54,19 @@ namespace DakotaUnitTest Assert.IsTrue(test.Item3 == "105328415"); } + + [TestMethod] + public void ValidateAOKRheinlandHamburgSoz() + { + var test = Reader.Resolve("104212505", "69", "00000"); + + Assert.IsNotNull(test); + + Assert.IsTrue(test.Item1 == "104212516"); + + Assert.IsTrue(test.Item2 == "104212505"); + + Assert.IsTrue(test.Item3 == "104212505"); + } } } diff --git a/Data/Entities/GkvAbrechnung.cs b/Data/Entities/GkvAbrechnung.cs index 72cae06a4..bcfff7ee6 100644 --- a/Data/Entities/GkvAbrechnung.cs +++ b/Data/Entities/GkvAbrechnung.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; - +using System.Linq; using BS.Shared; using BS.Shared.Extensions; @@ -31,5 +31,10 @@ namespace BeWo.Data.Entities (x) => x.Fehlerdatum, (x) => x.Fehlerlevel, (x) => x.ErstelltAm.Value); + + public virtual Organisation GetOrganisation() + { + return InvoiceBases.First().CostBearer2SupportConcept.CostBearer.Organisation; + } } } \ No newline at end of file diff --git a/Host/GkvAbrechnungView.aspx.cs b/Host/GkvAbrechnungView.aspx.cs index 29840075b..4067700aa 100644 --- a/Host/GkvAbrechnungView.aspx.cs +++ b/Host/GkvAbrechnungView.aspx.cs @@ -32,6 +32,7 @@ using System.Xml; using System.Xml.Linq; using System.Diagnostics; using System.Xml.Serialization; +using Dakota.Logic; namespace Host { @@ -44,6 +45,7 @@ namespace Host private string ResultFolderPath { get; set; } = ConfigurationManager.AppSettings.Get("GkvAbrechnungResultFolderPath"); private string QueueFolderPath { get; set; } = ConfigurationManager.AppSettings.Get("GkvAbrechnungQueueFolderPath"); + private string KostentragerdateiFolderPath { get; set; } = ConfigurationManager.AppSettings.Get("GkvKostentragerdateiFolderPath"); public GkvApp8Request GkvRequest { get; set; } @@ -253,14 +255,21 @@ namespace Host } private void GetIK(GkvApp8GetIKRequest req) { - var input = req.KrankenkassenIK; + var ktd_reader = new DakotaKTDReader(KostentragerdateiFolderPath); + + var resolved = ktd_reader.Resolve(req.IKKrankenkasse, req.Abrechnungscode, req.Tarifkennzeichen); var response = new GkvApp8GetIKResponse(); response.Success = true; - response.IKVersichertenKarte = input; - response.IKDatenannahmestelle = "111222333"; - response.IKKostenträger = "333444555"; + response.Gefunden = resolved is object; + + if (response.Gefunden) + { + response.IKDatenannahmestelle = resolved.Item1; + response.IKKostentrager = resolved.Item2; + response.IKKrankenkasse = resolved.Item3; + } SendResponse(response); } diff --git a/Service/DCEntityMapper/GkvAbrechnungDC_GkvAbrechnung.cs b/Service/DCEntityMapper/GkvAbrechnungDC_GkvAbrechnung.cs index 19b75f4cc..ded8c603f 100644 --- a/Service/DCEntityMapper/GkvAbrechnungDC_GkvAbrechnung.cs +++ b/Service/DCEntityMapper/GkvAbrechnungDC_GkvAbrechnung.cs @@ -17,7 +17,7 @@ namespace BeWo.Service.DCEntityMapper { } - var orga = pEntity.InvoiceBases.First().CostBearer2SupportConcept.CostBearer.Organisation; + var orga = pEntity.GetOrganisation(); pDataContract.GkvAbrechnungOid = pEntity.Oid; pDataContract.GkvAbrechnungVersion = pEntity.Version; diff --git a/Service/ServiceImplementations/AccountingServiceImp.cs b/Service/ServiceImplementations/AccountingServiceImp.cs index d7ea5557c..26c5d0749 100644 --- a/Service/ServiceImplementations/AccountingServiceImp.cs +++ b/Service/ServiceImplementations/AccountingServiceImp.cs @@ -566,7 +566,7 @@ namespace BeWo.Service.ServiceImplementations { var dc = invoices.Where(i => i.InvoiceBaseOid == entity.Oid).FirstOrDefault(); - + if (dc.IsPaid != entity.IsPaid || dc.IsExported != entity.IsExported || dc.IsReviewed != entity.IsReviewed || @@ -718,7 +718,6 @@ namespace BeWo.Service.ServiceImplementations throw Utils.CreateBeWoFaultException(e); } } - public List GetFilteredGkvAbrechnungen(GkvAbrechnungViewFilterDC gkvAbrechnungViewFilter) { try @@ -739,7 +738,6 @@ namespace BeWo.Service.ServiceImplementations throw Utils.CreateBeWoFaultException(e); } } - public List GetAllGkvAbrechnungen() { try @@ -755,7 +753,7 @@ namespace BeWo.Service.ServiceImplementations if (!updateables.Any()) return dcs; - var isUpToDate = SendApp8GetStatusRequest(updateables, user.LoginName, user.BCryptPassword); + var isUpToDate = SendApp8GetStatusRequest(updateables); if (isUpToDate) return dcs; @@ -829,7 +827,6 @@ namespace BeWo.Service.ServiceImplementations return response; } - public GkvAbrechnungDC UpdateGkvAbrechnung(GkvAbrechnungDC abrechnung) { try @@ -847,7 +844,6 @@ namespace BeWo.Service.ServiceImplementations throw Utils.CreateBeWoFaultException(e); } } - public void DeleteGkvAbrechnung(GkvAbrechnungDC abrechnung) { try @@ -866,16 +862,18 @@ namespace BeWo.Service.ServiceImplementations response.Successful = false; - var user = LoggedInUserOperationContextExt.Current?.User ?? SessionFacade.LoggedInUser; - if (user == null) + var oid = req.GkvAbrechnungOid; + + var entity = DAOFactory.GenericDAO.LoadByID(oid); + + var state = GkvAbrechnung.GetState(entity); + + if (state == GkvState.Waiting || state == GkvState.OldAndNoFeedback || state == GkvState.Paid) { - response.Message = "User unbekannt"; + response.Message = "Validierung fehlgeschlagen"; return response; } - var oid = req.GkvAbrechnungOid; - var entity = DAOFactory.GenericDAO.LoadByID(oid); - var protokoll = new GkvTransferProtokoll(); protokoll.ErstelltAm = DateTime.Now; @@ -883,22 +881,17 @@ namespace BeWo.Service.ServiceImplementations try { - var validation_success = CreateGkvTransferProtokoll(entity, protokoll); - - if(!validation_success) - { - response.Message = "Validierung fehlgeschlagen"; - return response; - } + CreateGkvTransferProtokoll(oid, entity, protokoll); if (entity.GkvTransferProtokolle is null) entity.GkvTransferProtokolle = new List(); entity.GkvTransferProtokolle.Add(protokoll); + // Protokoll braucht Oid DAOFactory.GenericDAO.Update(entity); - var app8_response = SendApp8SendNewRequest(protokoll, user.LoginName, user.BCryptPassword); + var app8_response = SendApp8SendNewRequest(protokoll); var app8_protokoll = app8_response.GkvTransferProtokollDC; @@ -928,8 +921,7 @@ namespace BeWo.Service.ServiceImplementations return response; } - - private bool CreateGkvTransferProtokoll(GkvAbrechnung gkvAbrechnung, GkvTransferProtokoll protokoll) + private void CreateGkvTransferProtokoll(long oid, GkvAbrechnung gkvAbrechnung, GkvTransferProtokoll protokoll) { try { @@ -938,19 +930,38 @@ namespace BeWo.Service.ServiceImplementations DakotaValidator.ValidateMandator(mandatordc); + var orga = gkvAbrechnung.GetOrganisation(); + + DakotaValidator.ValidateOrganisation(orga); + + if (!DakotaValidator.ValidateIKNumbers(orga)) + { + var res = SendApp8GetIKRequest(orga); + + if (!res.Success) + throw new DakotaException(res.Message, "Interner Fehler"); + + if (!res.Gefunden) + throw new DakotaException($"Für die Organisation \"{orga.Name}\" mit dem IK \"{orga.IKKrankenkasse}\" konnte keine Datenannahmestelle gefunden werden. " + + $"Bitte informieren Sie sich bei der Krankenkasse, ob zum elektronischen Datenaustausch " + + $"eine seperate Anmeldung benötigt wird."); + + orga.IKDatenannahmestelle = res.IKDatenannahmestelle; + orga.IKKostentrager = res.IKKostentrager; + // orgadc.IKKrankenkasse = res.IKKrankenkasse; + + if (!DakotaValidator.ValidateIKNumbers(orga)) + throw new DakotaException($"Für die Organisation \"{orga.Name}\" konnten die hinterlegten IKs nicht verifiziert werden: \"{orga.IKDatenannahmestelle}\", \"{orga.IKKostentrager}\", \"{orga.IKKrankenkasse}\"", "Interner Fehler"); + + DAOFactory.GenericDAO.Update(orga); + + gkvAbrechnung = DAOFactory.GenericDAO.LoadByID(oid); + } + var gkvAbrechnungDC = MapperFactory.GkvAbrechnungDC_GkvAbrechnung.MapToNewDC(gkvAbrechnung); var orgadc = gkvAbrechnungDC.Organisation; - DakotaValidator.ValidateOrganisation(orgadc); - - var state = GkvAbrechnung.GetState(gkvAbrechnung); - - if (state == GkvState.Waiting || state == GkvState.OldAndNoFeedback || state == GkvState.Paid) - { - return false; - } - - var datenannahmestelle = gkvAbrechnungDC.Organisation.IKDatenannahmestelle; + var datenannahmestelle = orgadc.IKDatenannahmestelle; var (datenaustauschreferenz, transfernummer) = GetNextDatenaustauschreferenzTransfernummer(gkvAbrechnung, datenannahmestelle); var (dat_short, tra_short) = ClearDatenaustauschreferenzTransfernummer(datenaustauschreferenz, transfernummer); @@ -1010,8 +1021,6 @@ namespace BeWo.Service.ServiceImplementations protokoll.AbsenderBezeichnung = "TestAbsBez"; protokoll.EmpfangerBezeichnung = "TestEmpBez"; - - return true; } catch (DakotaException de) { @@ -1048,7 +1057,6 @@ namespace BeWo.Service.ServiceImplementations return new Tuple(datenaustauschref, transfernummer); } - private Tuple ClearDatenaustauschreferenzTransfernummer(int datenaustauschreferenz, int transfernummer) { int d = datenaustauschreferenz % 99999; @@ -1062,7 +1070,7 @@ namespace BeWo.Service.ServiceImplementations return new Tuple(d, t); } - private GkvApp8SendNewResponse SendApp8SendNewRequest(GkvTransferProtokoll protokoll, string loginName, string hashed_password) + private GkvApp8SendNewResponse SendApp8SendNewRequest(GkvTransferProtokoll protokoll) { var gkv_transfer_dc = MapperFactory.GkvTransferProtokollDC_GkvTransferprotokoll.MapToNewDC(protokoll); @@ -1075,20 +1083,14 @@ namespace BeWo.Service.ServiceImplementations var request_app8 = new GkvApp8SendNewRequest { SendDirect = true, - GkvTransferProtokoll = gkv_transfer_dc, - Username = loginName, - Password = hashed_password, - Tenant = MultitenancyOperationContextExt.Current.Tenant, - DebugMode = "fozc4897b6ztvf978AFCRRG244fvFV2532fvf324AF" + GkvTransferProtokoll = gkv_transfer_dc }; #if DEBUG request_app8.SendDirect = false; #endif - var response_app8_str = SendApp8Request(url, request_app8); - - var response_app8 = JsonConvert.DeserializeObject(response_app8_str); + var response_app8 = SendApp8Request(url, request_app8); if (!response_app8.Success) throw new DakotaException(response_app8.Message); @@ -1102,7 +1104,7 @@ namespace BeWo.Service.ServiceImplementations /// /// /// Returns true, if the states are all up to date. False otherwise. - private bool SendApp8GetStatusRequest(GkvAbrechnungDC[] updateables, string loginName, string hashed_password) + private bool SendApp8GetStatusRequest(GkvAbrechnungDC[] updateables) { var updateables_len = updateables.Count(); @@ -1110,8 +1112,6 @@ namespace BeWo.Service.ServiceImplementations var request = new GkvApp8GetStatusRequest { - Username = loginName, - Password = hashed_password, Tenant = MultitenancyOperationContextExt.Current.Tenant, Oids = new long[updateables_len], DebugMode = "fozc4897b6ztvf978AFCRRG244fvFV2532fvf324AF", @@ -1128,18 +1128,16 @@ namespace BeWo.Service.ServiceImplementations GkvApp8GetStatusResponse response_app8; try { - var response_app8_str = SendApp8Request(url, JsonConvert.SerializeObject(request)); - - response_app8 = JsonConvert.DeserializeObject(response_app8_str); + response_app8 = SendApp8Request(url, request); } - catch(Exception e) + catch (Exception e) { return true; } if (!response_app8.Success) throw new DakotaException(response_app8.Message); - + bool allAreUpToDate = true; for (int id = 0; id < updateables_len; id++) { @@ -1166,7 +1164,39 @@ namespace BeWo.Service.ServiceImplementations return allAreUpToDate; } - private string SendApp8Request(string url, object request) => SendApp8Request(url, JsonConvert.SerializeObject(request)); + private GkvApp8GetIKResponse SendApp8GetIKRequest(Organisation orga) + { + var gruppe = orga.Leistungserbringergruppe; + var abr = DakotaUtils.GetAbrechnungscode(gruppe); + var tarif = DakotaUtils.GetTarifkennzeichen(gruppe); + + var url = "https://app8.bewoplaner.de/service/GkvAbrechnungView.aspx"; + + var request = new GkvApp8GetIKRequest + { + Abrechnungscode = abr, + IKKrankenkasse = orga.IKKrankenkasse, + Tarifkennzeichen = tarif + }; + + return SendApp8Request(url, request); + } + + private T2 SendApp8Request(string url, T request) where T : GkvApp8Request where T2 : GkvApp8Response + { + var user = LoggedInUserOperationContextExt.Current?.User ?? SessionFacade.LoggedInUser; + if (user == null) + { + throw new DakotaException("User unbekannt", "KApp"); + } + + request.Username = user.LoginName; + request.Password = user.BCryptPassword; + request.DebugMode = "fozc4897b6ztvf978AFCRRG244fvFV2532fvf324AF"; + request.Tenant = MultitenancyOperationContextExt.Current.Tenant; + + return JsonConvert.DeserializeObject(SendApp8Request(url, JsonConvert.SerializeObject(request))); + } private string SendApp8Request(string url, string dataString) { string response_app8_str; @@ -1182,7 +1212,7 @@ namespace BeWo.Service.ServiceImplementations response_app8_str = client.UploadString(url, "POST", dataString); } } - catch(Exception e) + catch (Exception e) { throw new DakotaException(e.Message, "App8"); } diff --git a/Shared/DataContracts/GkvAbrechnung/GkvApp8GetIKRequest.cs b/Shared/DataContracts/GkvAbrechnung/GkvApp8GetIKRequest.cs index 14167ce89..751c81fd4 100644 --- a/Shared/DataContracts/GkvAbrechnung/GkvApp8GetIKRequest.cs +++ b/Shared/DataContracts/GkvAbrechnung/GkvApp8GetIKRequest.cs @@ -10,7 +10,13 @@ namespace BS.Shared.DataContracts.GkvAbrechnung public class GkvApp8GetIKRequest : GkvApp8Request { [DataMember] - public string KrankenkassenIK { get; set; } + public string IKKrankenkasse { get; set; } + + [DataMember] + public string Abrechnungscode { get; set; } + + [DataMember] + public string Tarifkennzeichen { get; set; } public GkvApp8GetIKRequest() { diff --git a/Shared/DataContracts/GkvAbrechnung/GkvApp8GetIKResponse.cs b/Shared/DataContracts/GkvAbrechnung/GkvApp8GetIKResponse.cs index e07dfc7b4..75a06cef3 100644 --- a/Shared/DataContracts/GkvAbrechnung/GkvApp8GetIKResponse.cs +++ b/Shared/DataContracts/GkvAbrechnung/GkvApp8GetIKResponse.cs @@ -10,10 +10,13 @@ namespace BS.Shared.DataContracts.GkvAbrechnung public class GkvApp8GetIKResponse : GkvApp8Response { [DataMember] - public string IKVersichertenKarte { get; set; } + public bool Gefunden { get; set; } [DataMember] - public string IKKostenträger { get; set; } + public string IKKrankenkasse { get; set; } + + [DataMember] + public string IKKostentrager { get; set; } [DataMember] public string IKDatenannahmestelle { get; set; }