using BeWo.Data; using BeWo.Data.Access; using BeWo.Data.Entities; using BeWo.Data.Entities.Light; using BeWo.Service.Core; using BeWo.Service.DCEntityMapper; using BeWo.Service.Plugins; using BeWo.Service.ServiceContracts; using BS.Shared; using BS.Shared.AppSender; using BS.Shared.Attributes; using BS.Shared.Core; using BS.Shared.DataContracts; using BS.Shared.DataContracts.Compact; using BS.Shared.DataContracts.GkvAbrechnung; using BS.Shared.DataContracts.Light; using BS.Shared.Exceptions; using BS.Shared.Extensions; using BS.Shared.Interface; using Dakota; using Dakota.Logic; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.Text; using System.Threading.Tasks; using Utils = BeWo.Service.Core.Utils; namespace BeWo.Service.ServiceImplementations { [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)] public class GkvAccountingServiceImp : IGkvAccountingService { public string Tenant => MultitenancyOperationContextExt.Current?.Tenant; public GkvAbrechnungLightDC GetGkvAbrechnung(long oid) { try { var gkv = DAOFactory.GenericDAO.GetByID(oid); var dc = MapperFactory.GkvAbrechnungLight.MapToNewDC(gkv); return dc; } catch (Exception e) { throw Utils.CreateBeWoFaultException(e); } } public List GetAllGkvAbrechnungen() { try { return GetGkvAbrechnungen(() => DAOFactory.GenericDAO.GetAllActive()); } catch (Exception e) { throw Utils.CreateBeWoFaultException(e); } } public List GetAllGkvAbrechnungenBy(GkvAbrechnungViewFilterDC gkvAbrechnungViewFilter) { if (gkvAbrechnungViewFilter == null || !gkvAbrechnungViewFilter.FilterTime) { var dcs = GetAllGkvAbrechnungen(); ServiceValidator.CheckSerializationError(dcs); return dcs; } try { var start = gkvAbrechnungViewFilter.Start; var end = gkvAbrechnungViewFilter.End; var filter = gkvAbrechnungViewFilter.FilterForInvoiceDate; return GetGkvAbrechnungen(() => DAOFactory.FinanceDAO.GetGkvAbrechnungen(start, end, filter)); } catch (Exception e) { throw Utils.CreateBeWoFaultException(e); } } private List GetGkvAbrechnungen(Func> get_entities) { var entities = get_entities(); var dcs = MapperFactory.GkvAbrechnungLight.MapToNewDCs(entities); var updateables = dcs.Where(x => x.IsWaiting()).ToArray(); if (!updateables.Any()) return dcs; var isUpToDate = SendApp8GetStatusRequest(updateables); if (isUpToDate) return dcs; entities = get_entities(); dcs = MapperFactory.GkvAbrechnungLight.MapToNewDCs(entities); return dcs; } public string GetProtokollRawDataString(long oid, GkvRawDataType gkvRawData) { try { if (oid <= 0) { throw new ArgumentOutOfRangeException(nameof(oid), $"oid \'{oid}\' nicht positiv"); } var str = DAOFactory.FinanceDAO.GetGkvRawData(oid, gkvRawData); return str; } catch (Exception e) { throw Utils.CreateBeWoFaultException(e); } } public GkvAbrechnungCreateResponseDC CreateNewGkvAbrechnung(GkvAbrechnungCreateRequestDC request) { var response = new GkvAbrechnungCreateResponseDC(); try { var invoice_bases = DAOFactory.GenericDAO.LoadByIDs(request.SelectedInvoiceBases); ValidatorExtended.ValidateGkvAbrechnung(request.GkvAbrechnungDC, invoice_bases); var gkv_abrechnung = MapperFactory.GkvAbrechnungDC_GkvAbrechnung.MapToNewEntity(request.GkvAbrechnungDC); if (gkv_abrechnung.AbrechnungsZeitraumEnde.Value.Hour == 0) { gkv_abrechnung.AbrechnungsZeitraumEnde = gkv_abrechnung.AbrechnungsZeitraumEnde.Value.Date.AddDays(1).AddMilliseconds(-1); } var orga = gkv_abrechnung.Organisation; gkv_abrechnung.Verfahrensstufe = orga.Verfahrensstufe; decimal gesamt = 0; foreach (var invoicebase_oid in request.SelectedInvoiceBases) { var service = DAOFactory.SearchDAO.GetServiceInvoiceByInvoiceBaseOid(invoicebase_oid); foreach (var peroid in service.ServiceInvoicePeriodList) { foreach (var invoice in peroid.InvoiceItemList) { gesamt += invoice.AmountTotal.Value; } } } gkv_abrechnung.Betrag = gesamt; gkv_abrechnung.Bezahlt = false; gkv_abrechnung.ErstelltAm = DateTime.Now; DAOFactory.GenericDAO.Insert(gkv_abrechnung); var gkv_light = DAOFactory.GenericDAO.LoadByID(gkv_abrechnung.Oid.Value); var gkv_abrechnung_dc = MapperFactory.GkvAbrechnungLight.MapToNewDC(gkv_light); response.Success = true; response.GkvAbrechnungLightDC = gkv_abrechnung_dc; } catch (GkvException exc) { MailUtils.SendGkvModuleErrorMail("CreateNewGkvAbrechnung", exc, Tenant); if (exc.Title.IsNotNullOrWhiteSpace()) response.TitleMessage = exc.Title; response.Message = exc.Message; } catch (Exception e) { MailUtils.SendGkvModuleErrorMail("CreateNewGkvAbrechnung", e, Tenant); throw Utils.CreateBeWoFaultException(e); } return response; } public GkvAbrechnungLightDC UpdateGkvAbrechnung(GkvAbrechnungLightDC abrechnung) { try { var entity = DAOFactory.GenericDAO.GetByID(abrechnung.Oid); MapperFactory.GkvAbrechnungLight.MergeWithEntity(abrechnung, entity); DAOFactory.GenericDAO.Update(entity); var dc = MapperFactory.GkvAbrechnungLight.MapToNewDC(entity); return dc; } catch (Exception e) { throw Utils.CreateBeWoFaultException(e); } } public void DeleteGkvAbrechnung(long oid, long version) { try { ServiceLogic.SetActivationType(oid, version, ActivationTypeId.Deleted); } catch (Exception e) { throw Utils.CreateBeWoFaultException(e); } } /// /// Erstellt GkvTransferProtokoll /// /// /// public GkvAbrechnungSendResponseDC SendNewGkvAbrechnung(GkvAbrechnungSendRequestDC req) { var response = new GkvAbrechnungSendResponseDC(); var oid = req.GkvAbrechnungOid; var entity = DAOFactory.GenericDAO.LoadByID(oid); var state = entity.GetState(); if (entity.IsWaiting() || state == GkvState.Paid) { response.Message = "Validierung fehlgeschlagen"; return response; } var protokoll = CreateGkvTransferProtokoll(entity, out var error); if (error is object) { MailUtils.SendGkvModuleErrorMail("SendNewGkvAbrechnung - CreateGkvTransferProtokoll", error, Tenant); response.TitleMessage = "Protokoll Erstellung"; response.Message = error; return response; } if (entity.GkvTransferProtokolle is null) entity.GkvTransferProtokolle = new List(); entity.GkvTransferProtokolle.Add(protokoll); // Protokoll braucht Oid DAOFactory.GenericDAO.Update(entity); try { var app8_response = SendApp8SendNewRequest(protokoll); var app8_protokoll = app8_response.GkvTransferProtokollDC; MapperFactory.GkvTransferProtokollDC_GkvTransferprotokoll.MergeWithEntity(app8_protokoll, protokoll); response.Success = app8_response.Success; response.Message = app8_response.Message; } catch (GkvException de) { protokoll.ResultType = GkvTransferResultType.FailSoft; protokoll.Fehlerdatum = DateTime.Now; protokoll.Fehlernachricht = de.Message; protokoll.Fehlertitel = de.Title ?? "Default"; } catch (Exception e) { protokoll.ResultType = GkvTransferResultType.FailFatal; protokoll.Fehlerdatum = DateTime.Now; protokoll.Fehlernachricht = e.ToString(); protokoll.Fehlertitel = "Erstellung (kritisch)"; } DAOFactory.GenericDAO.Update(entity); var entity_light = DAOFactory.GenericDAO.LoadByID(entity.Oid.Value); var protokoll_light = DAOFactory.GenericDAO.LoadByID(protokoll.Oid.Value); response.GkvAbrechnungDC = MapperFactory.GkvAbrechnungLight.MapToNewDC(entity_light); response.TransferProtokoll = MapperFactory.GkvTransferprotokollLight.MapToNewDC(protokoll_light); return response; } public GkvAbrechnungGetIKResponseDC SendGetIKRequest(GkvAbrechnungGetIKRequestDC req) { try { var app8req = new GkvServerGetIKRequestDC { IKKrankenkasse = req.IKKrankenkasse, Leistungserbringergruppe = req.Leistungserbringergruppe, Abrechnungscode = DakotaUtils.GetAbrechnungscode(req.Leistungserbringergruppe), Tarifkennzeichen = DakotaUtils.GetTarifkennzeichen(req.Leistungserbringergruppe) }; var app8res = SendApp8Request(app8req); // Erstelle Response var res = new GkvAbrechnungGetIKResponseDC { BezDatenannahmestelle = app8res.BezDatenannahmestelle, Gefunden = app8res.Gefunden, IKDatenannahmestelle = app8res.IKDatenannahmestelle, IKKostentrager = app8res.IKKostentrager, IKKrankenkasse = app8res.IKKrankenkasse, Message = app8res.Message, Success = app8res.Success, Hausanschrift = app8res.Hausanschrift ?? new AddressDC(), Postfachanschrift=app8res.Postfachanschrift ?? new AddressDC(), Grosskundenanschrift =app8res.Grosskundenanschrift ?? new AddressDC() }; // Schaue, ob IK Krankenkassen bereits bei einer anderen Organisation hinterlegt ist if (res.Gefunden) { // Holt alle Organisationen var orgas = DAOFactory.OrganisationDAO.GetAllActiveOrganisationByIKKrankenkasse(req.IKKrankenkasse); // Entfernt sich selber if (req.OrganisationOid is long oid) { orgas.RemoveAll(x => x.Oid == oid); } if (orgas.Any()) { res.BereitsEnthalten = true; res.BereitsEnthaltenBei = orgas.Select(x => x.Name).ToList(); } } return res; } catch (Exception e) { throw Utils.CreateBeWoFaultException(e); } } private GkvTransferProtokoll CreateGkvTransferProtokoll(GkvAbrechnung gkvAbrechnung, out string error) { var protokoll = new GkvTransferProtokoll { ErstelltAm = DateTime.Now, GkvAbrechnungOid = gkvAbrechnung.Oid.Value }; error = null; try { ValidatorExtended.ValidateGkvAbrechnung(gkvAbrechnung, gkvAbrechnung.InvoiceBases); var manager = new DakotaManager(); var mandatordc = new OperationsServiceImp().GetMandator(); Validator.ValidateMandator(mandatordc); var gkvAbrechnungDC = MapperFactory.GkvAbrechnungDC_GkvAbrechnung.MapToNewDC(gkvAbrechnung); var orgadc = gkvAbrechnungDC.Organisation; Validator.ValidateOrganisation(orgadc); var datenannahmestelle = orgadc.IKDatenannahmestelle; var (datenaustauschreferenz, transfernummer) = GetNextDatenaustauschreferenzTransfernummer(gkvAbrechnung, datenannahmestelle); var (dat_short, tra_short) = ClearDatenaustauschreferenzTransfernummer(datenaustauschreferenz, transfernummer); var first_cost = gkvAbrechnung.InvoiceBases.First().CostBearer2SupportConcept.SupportConcept.Customer; var plugin = PluginLoader.FindClass(); var addressInfo = plugin.GetAddressInformation(first_cost.Oid); foreach (var invoicebase in gkvAbrechnung.InvoiceBases) { var customer = invoicebase.CostBearer2SupportConcept.SupportConcept.Customer; var addressInfo2 = plugin.GetAddressInformation(customer.Oid); if (addressInfo.IKLeistungserbringer != addressInfo2.IKLeistungserbringer) { throw new GkvException($"Klient '{first_cost.Person.LastNameFirstName}' ({addressInfo.IKLeistungserbringer}) und '{customer.Person.LastNameFirstName}' ({addressInfo2.IKLeistungserbringer}) verweisen auf unterschiedliche Leistungserbringer."); } } foreach (var invoicebase in gkvAbrechnung.InvoiceBases) { var service = DAOFactory.SearchDAO.GetServiceInvoiceByInvoiceBaseOid(invoicebase.Oid.Value); var orga2 = invoicebase.CostBearer2SupportConcept.CostBearer.Organisation; var customer = invoicebase.CostBearer2SupportConcept.SupportConcept.Customer; var invoicebase_dc = MapperFactory.InvoiceBaseDC_InvoiceBase.MapToNewDC(invoicebase); var servicedc = MapperFactory.ServiceInvoiceDC_ServiceInvoice.MapToNewDC(service); var customerdc = MapperFactory.CustomerDC_Customer.MapToNewDC(customer); Validator.ValidateCustomer(customerdc); manager.Add(servicedc, orgadc, customerdc, addressInfo, dat_short, tra_short); } manager.Apply(); if (manager.DakotaFileDict.Count != 1 || manager.DakotaFileDict.First().Value.GetKostenträgerCount() != 1) { var msg = "Unerwartete Anzahl an Dakota Files berechnet."; throw new GkvException(msg); } var file_from_manager = manager.DakotaFileDict.First().Value; var nutzdatendatei = file_from_manager.GetNutzdatendatei(); // https://dev.mysql.com/doc/refman/8.3/en/string-type-syntax.html // A TEXT column with a maximum length of 16,777,215 (224 − 1) characters. // The effective maximum length is less if the value contains multibyte characters. // Each MEDIUMTEXT value is stored using a 3-byte length prefix that indicates the number of bytes in the value. // // Type | Maximum length // -----------+------------------------------------- // TINYTEXT | 255(2^8−1) bytes // TEXT | 65,535(2^16−1) bytes = 64 KiB // MEDIUMTEXT | 16,777,215(2^24−1) bytes = 16 MiB // LONGTEXT | 4,294,967,295(2^32−1) bytes = 4 GiB if (nutzdatendatei.Length > 16777215) { var msg = $"nutzdatendatei - Länger als 16777215: {nutzdatendatei.Length}"; throw new GkvException(msg); } protokoll.Datenaustauschreferenz = datenaustauschreferenz; protokoll.Transfernummer = transfernummer; protokoll.IKDatenannahmestelle = file_from_manager.GetIKDatenannahmestelle(); protokoll.IKKostentrager = file_from_manager.GetIKKostenträger(); protokoll.IKLeistungserbringer = file_from_manager.GetIKLeistungserbringer(); protokoll.IKKrankenkasse = file_from_manager.GetIKVersichertenKarte(); protokoll.Rechnungsnummer = file_from_manager.GetRechnungsnummer(); protokoll.Auftragsdatei = file_from_manager.GetAuftragsdatei(); protokoll.Nutzdatendatei = nutzdatendatei; protokoll.Transfername = file_from_manager.GetTransferName(); protokoll.Dateigrosse = nutzdatendatei.Length; protokoll.Dateityp = "Nutzdatendatei"; protokoll.Dateiname = protokoll.Transfername; protokoll.AbsenderBezeichnung = "ownSoft GmbH"; protokoll.EmpfangerBezeichnung = orgadc.BezDatenannahmestelle; return protokoll; } catch (GkvException de) { error = de.ToString(); } catch (Exception e) { error = e.ToString(); } return null; } private Tuple GetNextDatenaustauschreferenzTransfernummer(GkvAbrechnung gkvAbrechnung, string datenannahmestelle) { var datenaustauschref = DAOFactory.SearchDAO.GetNextDatenaustauschreferenz(datenannahmestelle); int transfernummer; var state = gkvAbrechnung.GetState(); if (state == GkvState.NoSend) { transfernummer = DAOFactory.SearchDAO.GetNextTransfernummer(datenannahmestelle); } else if (state == GkvState.ErrorSoft || state == GkvState.ErrorFatal || state == GkvState.ErrorFolgefehler) { transfernummer = gkvAbrechnung.GkvTransferProtokolle.Last().Transfernummer; } else { throw new ArgumentException($"Versucht Transfernummer zu laden, invalid state: {state}", nameof(state)); } return new Tuple(datenaustauschref, transfernummer); } private Tuple ClearDatenaustauschreferenzTransfernummer(int datenaustauschreferenz, int transfernummer) { int d = datenaustauschreferenz % 99999; int t = transfernummer % 1000; if (d == 0) { d = 99999; } return new Tuple(d, t); } private GkvServerSendNewResponseDC SendApp8SendNewRequest(GkvTransferProtokoll protokoll) { var gkv_transfer_dc = MapperFactory.GkvTransferProtokollDC_GkvTransferprotokoll.MapToNewDC(protokoll); var request_app8 = new GkvServerSendNewRequestDC { Protokoll = gkv_transfer_dc }; var response_app8 = SendApp8Request(request_app8); if (!response_app8.Success) throw new GkvException(response_app8.Message); return response_app8; } /// /// /// /// /// /// /// Returns true, if the states are all up to date. False otherwise. private bool SendApp8GetStatusRequest(IGkvAbrechnung[] updateables) { var updateables_len = updateables.Count(); var request = new GkvServerGetStatusRequestDC { Tenant = MultitenancyOperationContextExt.Current.Tenant, Oids = new long[updateables_len] }; for (int id = 0; id < updateables_len; id++) { var gkv_abrechnung = updateables[id]; var gkv_transfer = gkv_abrechnung.TransferProtokolle.Last(); request.Oids[id] = gkv_transfer.Oid.Value; } GkvServerGetStatusResponseDC response_app8; try { response_app8 = SendApp8Request(request); } catch (Exception e) { MailUtils.SendGkvModuleErrorMail("GkvServerGetStatusRequest Error", e, Tenant); return true; } if (!response_app8.Success) throw new GkvException("Success false - " + response_app8.Message); if (response_app8.Results is null) throw new GkvException("Result null - " + response_app8.Message); var results = response_app8.Results.Where(x => x is object); if (!results.Any()) return true; HandleGkvServerResults(results); return false; } public void HandleGkvServerResults(IEnumerable results) { var protokolls = new List(); foreach (var result in results) { var protokoll = DAOFactory.GenericDAO.GetByID(result.ProtokollOid); protokoll.ResultType = result.ResultType; protokoll.Fehlertitel = result.Titel; protokoll.Fehlerdatum = result.Datum; protokoll.Fehlernachricht = result.Nachricht; switch (result.ResultType) { case GkvTransferResultType.Success: protokoll.SentDakotaSuccess = true; break; case GkvTransferResultType.FailSoft: protokoll.SentApp8Success = false; break; case GkvTransferResultType.FailFatal: protokoll.SentApp8Success = false; break; case GkvTransferResultType.FailFollowUp: protokoll.SentApp8Success = false; break; } protokolls.Add(protokoll); } if (protokolls.Any()) DAOFactory.GenericDAO.Update(protokolls); var follows = results.Where(x => x.ResultType == GkvTransferResultType.FailFollowUp); if (follows.Any()) { foreach (var follow in follows) { var protokoll_oid = follow.ProtokollOid; var abrechnung_oid = DAOFactory.FinanceDAO.GetGkvParentByProtokollOid(protokoll_oid); var new_req = new GkvAbrechnungSendRequestDC() { GkvAbrechnungOid = abrechnung_oid }; var res = SendNewGkvAbrechnung(new_req); if (!res.Success) throw new GkvException("Success - " + res.Message); } } } private Res SendApp8Request(Req request) where Req : GkvServerRequestDC where Res : GkvResponseDC { var user = LoggedInUserOperationContextExt.Current?.User ?? SessionFacade.LoggedInUser; if (user == null) { throw new GkvException("User unbekannt", "KundenApp"); } request.Username = user.LoginName; request.Password = user.BCryptPassword; request.Tenant = MultitenancyOperationContextExt.Current.Tenant; if (request is GkvServerSendNewRequestDC send_new_req) { send_new_req.ApplicationUserOid = user.Oid.Value; } return GkvSender.SendApp8Request(request, request.Tenant); } } }