Files

744 lines
29 KiB
C#

using adas_core.Application.Exceptions;
using adas_core.Application.Repositories.Interfaces;
using adas_core.Application.Services.Interfaces;
using adas_core.Application.Subscriptions;
using adas_core.Domain.Enums;
using adas_core.Domain.Models;
using adas_core.Domain.Models.DTO;
using adas_core.Domain.Models.Masters;
using adas_core.Domain.Models.MongoModels;
using adas_core.Domain.Models.Responses;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
namespace adas_core.Application.Services;
public class AdmissionService(
ILogger<AdmissionService> logger,
ISubscribersService subscribersService,
IAdmissionRepository admissionRepository,
IClientMessageService clientMessageService,
IUnitService unitService,
IPatientService patientService,
IPointOfCareService pointOfCareService,
IDisplayService displayService,
IDischargeService dischargeService,
IPatientArchiveRepository patientArchiveRepository,
IHttpContextAccessor httpContextAccessor,
ILocalAuditService auditService,
IMasterListServiceFactory masterListServiceFactory)
: IAdmissionService
{
// Auditory logs
public async Task DeleteAdmissionAsync(Admission admission)
{
await DeleteAdmissionByIdAsync(admission.Id);
}
public async Task DeleteAdmissionByIdAsync(ObjectId admissionId)
{
var admissionAux = await admissionRepository.FindById(admissionId);
if (admissionAux == null)
{
logger.LogInformation("Error deleting Admission not found, id: {AdmissionId} ", admissionId);
return;
}
await admissionRepository.Delete(admissionId);
if (admissionAux.PointOfCareId.HasValue)
{
var poc = await pointOfCareService.GetInfo(admissionAux.PointOfCareId.Value);
if (poc != null && poc.AdmissionId == admissionId)
{
poc.AdmissionId = null;
poc.Admission = null;
await pointOfCareService.Update(poc);
if (poc.Status != StatusEnum.PointOfCare.Locked && poc.Status != StatusEnum.PointOfCare.InUse)
await pointOfCareService.SetPointOfCareStatus(poc.Id, StatusEnum.PointOfCare.Available);
}
}
logger.LogInformation("Admission id: {AdmissionId} DELETED ", admissionId);
SendAdmissionBroadcast(admissionAux, OperationType.DeleteAdmission);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, admissionAux, null);
}
public async Task DeleteAdmissionsByUnitId(ObjectId unitId)
{
_ = await admissionRepository.DeleteAdmissionsByUnitId(unitId);
}
public async Task<Admission?> GetAdmissionByIdAsync(ObjectId admissionId)
{
var result = await admissionRepository.FindById(admissionId);
if (result?.PointOfCareId != null)
{
var poc = await pointOfCareService.GetInfo(result.PointOfCareId.Value, null,false);
result.PatientLocation = new PatientLocation(poc?.UnitName, poc?.Bed, poc?.Room);
}
return result;
}
public async Task<IEnumerable<Admission>> GetAdmissionsAsync()
{
var resultList = await admissionRepository.FindAll() ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
var admissionsAsync = resultList.ToList();
foreach (var admission in admissionsAsync)
if (admission.PointOfCareId != null)
{
var poc = await pointOfCareService.GetInfo(admission.PointOfCareId.Value, null,false);
admission.PatientLocation = new PatientLocation(poc?.UnitName, poc?.Bed, poc?.Room);
}
return admissionsAsync;
}
public async Task<Admission?> InsertAdmission(Admission admission)
{
var admissionAux = await admissionRepository.FindByNhc(admission.Nhc);
if (admissionAux != null) throw new ConflictException(HttpEnum.ErrorMessage.BadRequestDuplicateData);
PointOfCare? pointOfCare = null;
//Bloqueamos el pointOfCare en el caso de que lo tenga asignado
if (admission.PointOfCareId.HasValue)
{
pointOfCare = await pointOfCareService.GetInfo(admission.PointOfCareId.Value) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
//pointOfCare.AdmissionId = admission.Id;
//pointOfCare.Admission = admission;
admission.PatientLocation = new PatientLocation(pointOfCare.UnitName, pointOfCare.Bed, pointOfCare.Room);
}
var insertedAdmission = await admissionRepository.InsertOneAsyncAndReturn(admission) ??
throw new ConflictException(HttpEnum.ErrorMessage.ConflictCreationFailed);
if (pointOfCare is { AdmissionId: null })
{
pointOfCare.Admission = insertedAdmission;
pointOfCare.AdmissionId = insertedAdmission.Id;
if (pointOfCare.Status == StatusEnum.PointOfCare.Available)
pointOfCare.Status = StatusEnum.PointOfCare.Reserved;
await pointOfCareService.Update(pointOfCare);
}
SendAdmissionBroadcast(insertedAdmission, OperationType.NewAdmission);
// Obtener información el usuario autenticado
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, null, insertedAdmission);
return insertedAdmission;
}
public async Task UpdateAdmissionAsync(Admission admission)
{
var oldAdmission = await admissionRepository.FindById(admission.Id);
if (oldAdmission == null) return;
if (oldAdmission.PointOfCareId.HasValue)
{
var pocOld = await pointOfCareService.GetInfo(oldAdmission.PointOfCareId.Value, null,false);
if (pocOld != null)
oldAdmission.PatientLocation = new PatientLocation(pocOld.UnitName, pocOld.Bed, pocOld.Room);
}
if (admission.PointOfCareId.HasValue)
{
var poc = await pointOfCareService.GetInfo(admission.PointOfCareId.Value, null,false);
if (poc != null) admission.PatientLocation = new PatientLocation(poc.UnitName, poc.Bed, poc.Room);
}
await admissionRepository.Update(admission);
await HandlePointOfCareChange(admission, oldAdmission);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldAdmission, admission);
SendAdmissionBroadcast(admission, OperationType.UpdateAdmission);
}
public async Task AdmitPatient(Admission admission, bool isNew = false)
{
if (admission.PointOfCareId == null)
{
logger.LogError("PointOfCare is required. Admission: {Admission}", admission);
return;
}
var unit = await unitService.FindById(admission.UnitId);
if (unit == null)
{
logger.LogError("Unit {Name} not found. Unit Id: ", admission.UnitId);
return;
}
var pointOfCare = await pointOfCareService.FindById(admission.PointOfCareId.Value);
if (pointOfCare == null)
{
logger.LogError("Point of Care not found. Patient not created. {Admission}", admission);
return;
}
Patient patient = new()
{
PointOfCareId = pointOfCare.Id,
UnitId = pointOfCare.UnitId,
PointOfCare = pointOfCare,
UnitString = pointOfCare.UnitName,
Bed = pointOfCare.Bed,
Room = pointOfCare.Room,
PatientNumber = admission.Nhc,
AdmTime = DateTime.UtcNow,
Person = admission.Person,
CreationDate = DateTime.UtcNow,
DischargeStatus = pointOfCare.Unit?.DischargeStatusList?.Options.FirstOrDefault(),
Origin = admission.Origin,
OriginAux = admission.OriginAux,
Diagnosis = admission.Diagnosis,
DiagnosisAux = admission.DiagnosisAux,
Allergies = admission.Allergies,
Insulation = admission.Insulation,
LanguageBarrier = admission.LanguageBarrier,
PassiveSitting = admission.PassiveSitting,
Altable = new OptionList
{
Name = "NotAltable",
IconDefault = "icNotAltable",
OptionType = "NotAltable"
},
Visits = pointOfCare.Unit?.VisitOptionList?.Options.FirstOrDefault(),
AccessControl = pointOfCare.Unit?.AccessControlList?.Options.FirstOrDefault(),
Location = new PatientLocation
(
unit.Name,
pointOfCare.Bed,
pointOfCare.Room
)
};
if (unit.AltableOptionListId is not null)
{
var list = await masterListServiceFactory.GetMasterListById(MasterListType.AltableOptionList,
unit.AltableOptionListId.Value, LocaleEnum.Default) as AltableOptionList;
var listOpt = list?.Options.FirstOrDefault(c => c.OptionType == "NotAltable");
if (listOpt != null)
patient.Altable = listOpt;
}
await patientService.Insert(patient);
await SetPointOfCareStatus(pointOfCare.Id, StatusEnum.PointOfCare.InUse);
if (!isNew)
await DeleteAdmissionAsync(admission);
if (admission.Insulation != null)
await patientService.UpdatePatientMasterList(
patient.Id,
MasterListType.InsulationList,
[admission.Insulation],
null, null);
if (admission.Allergies != null)
await patientService.UpdatePatientMasterList(
patient.Id,
MasterListType.AllergyList,
admission.Allergies,
null, null);
if (admission.Diagnosis != null)
await patientService.UpdatePatientMasterList(
patient.Id,
MasterListType.DiagnosisList,
[admission.Diagnosis],
null, null);
if (admission.Origin != null)
await patientService.UpdatePatientMasterList(
patient.Id,
MasterListType.OriginList,
[admission.Origin],
null, null);
if (admission.LanguageBarrier != null)
await patientService.UpdatePatientMasterList(
patient.Id,
MasterListType.LanguageBarrierList,
admission.LanguageBarrier,
null, null);
if (admission.PassiveSitting != null)
await patientService.UpdatePatientMasterList(
patient.Id,
MasterListType.PassiveSittingList,
[admission.PassiveSitting],
null, null);
}
public async Task ReturnPatientToAdmissions(ObjectId patientId)
{
var patient = await patientService.FindById(patientId);
if (patient == null)
{
logger.LogError("Error returning the patient Id: {Id} to admission", patientId);
return;
}
var unit = await unitService.FindById(patient.UnitId);
if (unit == null)
{
logger.LogError("Unit not found by Id. {Name}", patient.UnitId);
return;
}
if (patient.PointOfCareId != null)
{
var poc = await pointOfCareService.FindById(patient.PointOfCareId.Value);
Admission admission = new()
{
Nhc = patient.PatientNumber ?? string.Empty,
PointOfCareId = poc?.Id,
UnitId = unit.Id,
Person = patient.Person ??
new Person(), //No debería ser null en este punto, pero así quito el warning
Origin = patient.Origin,
OriginAux = patient.OriginAux,
Diagnosis = patient.Diagnosis,
PassiveSitting = patient.PassiveSitting,
DiagnosisAux = patient.DiagnosisAux,
Allergies = patient.Allergies,
Insulation = patient.Insulation,
LanguageBarrier = patient.LanguageBarrier,
AdmissionDate = patient.AdmTime ?? DateTime.UtcNow,
PatientLocation = patient.Location
};
await InsertAdmission(admission);
}
var dis = await dischargeService.GetDischargeByPatientId(patient.Id);
if (dis != null) await dischargeService.DeleteDischargeByIdAsync(dis.Id);
await patientService.ArchivePatient(patient);
pointOfCareService.CheckNextAdmission(patient.PointOfCareId);
}
// Used for temporal beds like PUSHED
public async Task ReturnPatientToAdmissions(ObjectId patientId, Admission adm)
{
var patient = await patientService.FindById(patientId);
if (patient == null)
{
logger.LogError("Error returning the patient Id: {Id} to admission", patientId);
return;
}
var unit = await unitService.FindById(adm.UnitId);
if (unit == null)
{
logger.LogError("Unit not found by Id. {Name}", patient.UnitId);
return;
}
if (adm.PointOfCareId != null)
{
var poc = await pointOfCareService.FindById(adm.PointOfCareId.Value);
Admission admission = new()
{
Nhc = patient.PatientNumber ?? string.Empty,
PointOfCareId = poc?.Id,
UnitId = unit.Id,
Person = patient.Person ??
new Person(), //No debería ser null en este punto, pero así quito el warning
Origin = patient.Origin,
OriginAux = patient.OriginAux,
Diagnosis = patient.Diagnosis,
DiagnosisAux = patient.DiagnosisAux,
Allergies = patient.Allergies,
PassiveSitting = patient.PassiveSitting,
Insulation = patient.Insulation,
LanguageBarrier = patient.LanguageBarrier,
AdmissionDate = patient.AdmTime ?? DateTime.UtcNow,
PatientLocation = patient.Location
};
await InsertAdmission(admission);
}
var dis = await dischargeService.GetDischargeByPatientId(patient.Id);
if (dis != null) await dischargeService.DeleteDischargeByIdAsync(dis.Id);
await patientService.ArchivePatient(patient);
pointOfCareService.CheckNextAdmission(patient.PointOfCareId);
}
public async Task<List<Admission>> GetAdmissionByLocation(PatientLocation location)
{
try
{
return await admissionRepository.FindByLocation(location);
}
catch (Exception e)
{
logger.LogError("Error getting admission by patient location {Location} exception: {Ex}", location,
e.Message);
return [];
}
}
public async Task<List<Admission>> GetAdmissionByPointOfCareId(ObjectId pocId)
{
try
{
var result = await admissionRepository.FindByPointOfCareId(pocId);
foreach (var admission in result)
{
var poc = await pointOfCareService.GetInfo(pocId, null,false);
if (poc != null) admission.PatientLocation = new PatientLocation(poc.UnitName, poc.Bed, poc.Room);
}
return result;
}
catch (Exception e)
{
logger.LogError("Error getting admission by patient PocId {PocId} exception: {Ex}", pocId, e.Message);
return [];
}
}
public async Task<List<Admission>> GetAdmissionByPointOfCareIdAndLocale(ObjectId pocId, LocaleEnum locale)
{
var admissions = await GetAdmissionByPointOfCareId(pocId);
// Ejecutar todas las traducciones en paralelo
var translatedAdmissions = await Task.WhenAll(
admissions.Select(adm => GetAdmissionWithLocale(adm, locale))
);
return translatedAdmissions.ToList();
}
public async Task<List<Admission>> GetAdmissionByUnitIdWithOutPoC(ObjectId unitId)
{
try
{
return await admissionRepository.GetAdmissionByUnitIdWithOutPoC(unitId);
}
catch (Exception e)
{
logger.LogError("Error getting admission by unitId with out PoCId {UnitId} exception: {Ex}", unitId,
e.Message);
return [];
}
}
public async Task<long> CountAdmissionsByUnitId(ObjectId unitId)
{
try
{
return await admissionRepository.CountByUnitId(unitId);
}
catch (Exception e)
{
logger.LogError("Error getting admission by unitId with out PoCId {UnitId} exception: {Ex}", unitId,
e.Message);
return 0;
}
}
public async Task<PatientSearch?> SearchByPatientNumberAndDistinctUnit(string patientNumber, ObjectId unitId)
{
var patient = await patientService.FindByPatientNumber(patientNumber);
if (patient != null)
{
if (patient.PointOfCareId.HasValue)
patient.PointOfCare = await pointOfCareService.FindById(patient.PointOfCareId.Value);
if (patient.UnitId.HasValue)
{
var uni = await unitService.FindById(patient.UnitId.Value);
if (uni != null)
patient.UnitString = uni.Name;
}
}
var archivePatient =
await patientArchiveRepository.SearchByPatientNumberAndDistinctUnit(patientNumber, unitId);
var admission = await admissionRepository.SearchByPatientNumberAndDistinctUnit(patientNumber, unitId);
var result = new PatientSearch(patient, archivePatient, admission,
patient == null && archivePatient != null,
patient != null || archivePatient != null || admission != null);
return result;
}
public Task<Admission?> GetAdmissionByPatientNumber(string patientNumber)
{
return admissionRepository.FindByNhc(patientNumber);
}
public async Task UpdatePatientMasterListItemChange(UpdateOptionMasterListDto opt, IEnumerable<Unit> unitList,
string typeName)
{
var unitIds = unitList.Select(x => x.Id).ToList();
var oldAdmissionList = await admissionRepository.FindByUnitIds(unitIds);
var admissionUpdatedList = await admissionRepository.UpdateMasterListOption(unitIds, opt, typeName);
foreach (var admission in admissionUpdatedList)
{
var oldAdmission = oldAdmissionList.Find(adm => adm.Id == admission.Id);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldAdmission!, admission);
SendAdmissionBroadcast(admission, OperationType.UpdateAdmission);
}
}
public async Task DeletePatientMasterListItem(OptionList opt, IEnumerable<Unit> unitList, string typeName)
{
var unitIds = unitList.Select(x => x.Id).ToList();
var oldAdmissionList = await admissionRepository.FindByUnitIds(unitIds);
var admissionUpdatedList = await admissionRepository.DeleteMasterListOption(unitIds, opt, typeName);
foreach (var admission in admissionUpdatedList)
{
var admissionUpdated = await GetAdmissionByIdAsync(admission.Id);
var oldAdmission = oldAdmissionList.Find(adm => adm.Id == admission.Id);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldAdmission!,
admissionUpdated);
if (admissionUpdated != null)
SendAdmissionBroadcast(admissionUpdated, OperationType.UpdateAdmission);
}
}
public async Task SaveRequest(ApiRequest apiRequest)
{
try
{
if (apiRequest.Admission == null)
return;
switch (apiRequest.Type)
{
case "NewAdmission":
{
if (string.IsNullOrEmpty(apiRequest.Admission.Nhc) ||
apiRequest.Admission.Origin == null ||
apiRequest.Admission.Diagnosis == null)
{
logger.LogDebug(
"Error saving admission api request. Some values are required. Admission: {Admission}",
apiRequest.Admission);
return;
}
await InsertAdmission(apiRequest.Admission);
break;
}
case "UpdateAdmission":
{
if (string.IsNullOrEmpty(apiRequest.Admission.Nhc) ||
apiRequest.Admission.Origin == null ||
apiRequest.Admission.Diagnosis == null)
{
logger.LogDebug(
"Error updating admission api request. Some values are required. Admission: {Admission}",
apiRequest.Admission);
return;
}
await UpdateAdmissionAsync(apiRequest.Admission);
break;
}
case "DeleteAdmission":
{
await DeleteAdmissionAsync(apiRequest.Admission);
break;
}
}
}
catch (Exception ex)
{
logger.LogError("Exception updating admission {Admission} . Exception: {Ex}", apiRequest.Admission,
ex.Message);
throw;
}
}
public Task SaveRequestAsync(ApiRequest apiRequest)
{
return Task.FromResult(Task.Run(async () => { await SaveRequest(apiRequest); }));
}
private async Task HandlePointOfCareChange(Admission admission, Admission oldAdmission)
{
// Check if PointOfCare has changed.
if (admission.PointOfCareId == oldAdmission.PointOfCareId) return;
// Disable new PointOfCare if it exists.
if (admission.PointOfCareId.HasValue)
{
var newPoc = await pointOfCareService.GetInfo(admission.PointOfCareId.Value);
if (newPoc == null) return;
var patientOnNewPoc = await patientService.FindByPointOfCareId(newPoc.Id);
if (newPoc.AdmissionId == null && newPoc.Status != StatusEnum.PointOfCare.Locked)
{
newPoc.Admission = admission;
newPoc.AdmissionId = admission.Id;
newPoc.Status = patientOnNewPoc != null
? StatusEnum.PointOfCare.InUse
: StatusEnum.PointOfCare.Reserved;
await pointOfCareService.Update(newPoc);
}
if (newPoc.Status == StatusEnum.PointOfCare.Available)
pointOfCareService.CheckNextAdmission(newPoc.Id);
}
// Enable previous PointOfCare if it exists.
if (oldAdmission.PointOfCareId.HasValue)
{
var oldPoc = await pointOfCareService.GetInfo(oldAdmission.PointOfCareId.Value);
if (oldPoc != null)
{
var patientOnOldPoc = await patientService.FindByPointOfCareId(oldPoc.Id);
if (oldPoc.AdmissionId == oldAdmission.Id && oldPoc.Status != StatusEnum.PointOfCare.Locked)
{
oldPoc.Admission = null;
oldPoc.AdmissionId = null;
oldPoc.Status = patientOnOldPoc != null
? StatusEnum.PointOfCare.InUse
: StatusEnum.PointOfCare.Available;
await pointOfCareService.Update(oldPoc);
}
}
if (oldPoc is { Status: StatusEnum.PointOfCare.Available })
pointOfCareService.CheckNextAdmission(oldPoc.Id);
}
}
private async Task SetPointOfCareStatus(ObjectId pointOfCareId, StatusEnum.PointOfCare status)
{
var pointOfCare = await pointOfCareService.FindById(pointOfCareId);
if (pointOfCare == null) return;
await pointOfCareService.SetPointOfCareStatus(pointOfCareId, status);
}
private async void SendAdmissionBroadcast(Admission admission, OperationType operation)
{
try
{
if (admission.PointOfCareId == null)
await SendAdmissionByUnitId(admission, operation);
else
await SendAdmissionBroadcastByPoC(admission, operation);
}
catch (Exception ex)
{
logger.LogError("Exception sending admission broadcast. Operation type: {Op}. Exception: {Ex}",
operation.ToString(), ex.Message);
}
}
private async Task SendAdmissionBroadcastByPoC(Admission admission, OperationType operation)
{
if (!admission.PointOfCareId.HasValue)
{
logger.LogError("Error sending admission broadcast. PointOfCare id {Id} not found",
admission.PointOfCareId);
return;
}
var pointOfCare = await pointOfCareService.FindById(admission.PointOfCareId.Value);
if (pointOfCare == null)
{
logger.LogError("Error sending admission broadcast. PointOfCare id {Id} not found",
admission.PointOfCareId);
return;
}
var pocSubscribers = subscribersService.GetSubscribers().Where(s =>
s.LocationIds.Contains(admission.PointOfCareId.Value)).GroupBy(h => h.Locale);
foreach (var group in pocSubscribers)
{
var locale = group.Key ?? LocaleEnum.Default;
IEnumerable<WsSubscriber> subscribers = group;
foreach (var subscriber in subscribers)
{
var admissionWithLocale = await GetAdmissionWithLocale(admission, locale);
_ = clientMessageService.SendAsync(subscriber.Id, operation, admissionWithLocale);
}
}
}
private async Task SendAdmissionByUnitId(Admission admission, OperationType operation)
{
if (admission.UnitId == ObjectId.Empty)
{
logger.LogError("Error sending admission broadcast. UnitId is null or empty {Admission}", admission);
return;
}
var displays = await displayService.GetByUnitId(admission.UnitId);
var displayIds = displays.Select(c => c.Id).ToList();
var unitSubscribers = subscribersService.GetSubscribers().Where(s =>
s.DisplayId != null && displayIds.Contains(s.DisplayId.Value)).GroupBy(h => h.Locale);
foreach (var group in unitSubscribers)
{
var locale = group.Key ?? LocaleEnum.Default;
IEnumerable<WsSubscriber> subscribers = group;
foreach (var subscriber in subscribers)
{
var admissionWithLocale = await GetAdmissionWithLocale(admission, locale);
_ = clientMessageService.SendAsync(subscriber.Id, operation, admissionWithLocale);
}
}
}
private async Task<Admission> GetAdmissionWithLocale(Admission admission, LocaleEnum locale)
{
var unit = await unitService.FindById(admission.UnitId);
if (unit == null) return admission;
if (admission.Origin?.Name != null && unit.OriginListId.HasValue)
{
if (await masterListServiceFactory.GetMasterListById(MasterListType.OriginList,
unit.OriginListId.Value, locale) is OriginList list)
{
var listOrigin = list.Options.FirstOrDefault(c => c.Id == admission.Origin.Id)?.Name;
if (listOrigin != null)
admission.Origin.Name = listOrigin;
}
}
if (admission.Diagnosis?.Name != null && unit.DiagnosisListId.HasValue)
{
if (await masterListServiceFactory.GetMasterListById(MasterListType.DiagnosisList,
unit.DiagnosisListId.Value, locale) is DiagnosisList list)
{
var listDiagnosis = list.Options.FirstOrDefault(c => c.Id == admission.Diagnosis.Id)?.Name;
if (listDiagnosis != null)
admission.Diagnosis.Name = listDiagnosis;
}
}
if (admission.Insulation?.Name != null && unit.InsulationListId.HasValue)
{
if (await masterListServiceFactory.GetMasterListById(MasterListType.InsulationList,
unit.InsulationListId.Value, locale) is InsulationList list)
{
var listInsulation = list.Options.FirstOrDefault(c => c.Id == admission.Insulation.Id)?.Name;
if (listInsulation != null)
admission.Insulation.Name = listInsulation;
}
}
return admission;
}
}