Files
BeWoPlaner/Service/ServiceImplementations/GkvAccountingServiceImp.cs

671 lines
20 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<GkvAbrechnungLight>(oid);
var dc = MapperFactory.GkvAbrechnungLight.MapToNewDC(gkv);
return dc;
}
catch (Exception e)
{
throw Utils.CreateBeWoFaultException(e);
}
}
public List<GkvAbrechnungLightDC> GetAllGkvAbrechnungen()
{
try
{
return GetGkvAbrechnungen(() => DAOFactory.GenericDAO.GetAllActive<GkvAbrechnungLight>());
}
catch (Exception e)
{
throw Utils.CreateBeWoFaultException(e);
}
}
public List<GkvAbrechnungLightDC> 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<GkvAbrechnungLightDC> GetGkvAbrechnungen(Func<IEnumerable<GkvAbrechnungLight>> 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<InvoiceBase>(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<GkvAbrechnungLight>(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<GkvAbrechnungLight>(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<GkvAbrechnung>(oid, version, ActivationTypeId.Deleted);
}
catch (Exception e)
{
throw Utils.CreateBeWoFaultException(e);
}
}
/// <summary>
/// Erstellt GkvTransferProtokoll
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
public GkvAbrechnungSendResponseDC SendNewGkvAbrechnung(GkvAbrechnungSendRequestDC req)
{
var response = new GkvAbrechnungSendResponseDC();
var oid = req.GkvAbrechnungOid;
var entity = DAOFactory.GenericDAO.LoadByID<GkvAbrechnung>(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<GkvTransferProtokoll>();
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<GkvAbrechnungLight>(entity.Oid.Value);
var protokoll_light = DAOFactory.GenericDAO.LoadByID<GkvTransferProtokollLight>(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<GkvServerGetIKRequestDC, GkvServerGetIKResponseDC>(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<AccountingService>();
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^81) bytes
// TEXT | 65,535(2^161) bytes = 64 KiB
// MEDIUMTEXT | 16,777,215(2^241) bytes = 16 MiB
// LONGTEXT | 4,294,967,295(2^321) 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<int, int> 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<int, int>(datenaustauschref, transfernummer);
}
private Tuple<int, int> ClearDatenaustauschreferenzTransfernummer(int datenaustauschreferenz, int transfernummer)
{
int d = datenaustauschreferenz % 99999;
int t = transfernummer % 1000;
if (d == 0)
{
d = 99999;
}
return new Tuple<int, int>(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<GkvServerSendNewRequestDC, GkvServerSendNewResponseDC>(request_app8);
if (!response_app8.Success)
throw new GkvException(response_app8.Message);
return response_app8;
}
/// <summary>
///
/// </summary>
/// <param name="updateables"></param>
/// <param name="loginName"></param>
/// <param name="hashed_password"></param>
/// <returns>Returns true, if the states are all up to date. False otherwise.</returns>
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<GkvServerGetStatusRequestDC, GkvServerGetStatusResponseDC>(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<GkvServerResultDC> results)
{
var protokolls = new List<GkvTransferProtokollLight>();
foreach (var result in results)
{
var protokoll = DAOFactory.GenericDAO.GetByID<GkvTransferProtokollLight>(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, Res>(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<Req, Res>(request, request.Tenant);
}
}
}