Files
adas-core/adas-core.Application/Services/UnitService.cs
2026-06-26 10:29:23 +02:00

584 lines
34 KiB
C#

using adas_core.Application.Exceptions;
using adas_core.Application.Repositories.Interfaces;
using adas_core.Application.Services.Interfaces;
using adas_core.Domain.Enums;
using adas_core.Domain.Models;
using adas_core.Domain.Models.DTO;
using adas_core.Domain.Models.Filter;
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;
using MongoDB.Driver;
using Serilog;
namespace adas_core.Application.Services;
public class UnitService(
IUnitRepository unitRepository,
Lazy<IPatientService> patientService,
ILogger<UnitService> logger,
IMasterListServiceFactory masterListServiceFactory,
ISubscribersService subscribersService,
Lazy<IClientMessageService> clientMessageService,
IPointOfCareService pointOfCareService,
IHttpContextAccessor httpContextAccessor,
ILocalAuditService auditService)
: IUnitService
{
/// <summary>
/// Retrieves all units, optionally including their associated PointOfCares.
/// When <paramref name="withPoCs"/> is <c>true</c>, the PointOfCares collection is populated for each unit using the point of care service; otherwise, only the unit data is returned.
/// </summary>
/// <param name="withPoCs">If <c>true</c>, loads and assigns the PointOfCares for each unit; if <c>false</c>, returns units without their PointOfCares.</param>
/// <returns>A task representing the asynchronous operation, containing the list of all units, with PointOfCares populated when requested.</returns>
public async Task<List<Unit>> GetAll(bool withPoCs = false)
{
var units = await unitRepository.GetAll();
if (withPoCs)
foreach (var unit in units)
{
var pocList = await pointOfCareService.FindAllByUnitId(unit.Id);
unit.PointOfCares = pocList?.ToList();
}
return units;
}
/// <summary>
/// Retrieves all units in a compact representation, mapping each unit to a <see cref="UnitInfoDto"/> containing its identifier, name, and title, with null name and title values safely replaced by empty strings.
/// </summary>
/// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="UnitInfoDto"/> objects for all available units.</returns>
public async Task<List<UnitInfoDto>> GetAllCompact()
{
var units = await unitRepository.GetAll();
var result = new List<UnitInfoDto>();
foreach (var unit in units)
result.Add(new UnitInfoDto
{
Id = unit.Id,
Name = unit.Name ?? string.Empty,
Title = unit.Title ?? string.Empty
});
return result;
}
/// <summary>
/// Retrieves a compact representation of a unit by its identifier, returning a <see cref="UnitInfoDto"/> populated with the unit's id, name, and title. If no unit is found for the given id, the returned DTO contains a null id with empty name and title values.
/// </summary>
/// <param name="id">The unique identifier of the unit to retrieve.</param>
/// <returns>A <see cref="Task{UnitInfoDto}"/> containing the compact unit information, or a DTO with null/empty fields if the unit does not exist.</returns>
public async Task<UnitInfoDto> GetOneCompact(ObjectId id)
{
var unit = await unitRepository.FindById(id);
var result = new UnitInfoDto
{
Id = unit?.Id,
Name = unit?.Name ?? string.Empty,
Title = unit?.Title ?? string.Empty
};
return result;
}
/// <summary>
/// Retrieves a paginated list of units, optionally enriched with their associated Points of Care (PoCs).
/// When <paramref name="withPoCs"/> is true, all PoCs for the returned units are loaded in a single batch call and assigned to each unit.
/// </summary>
/// <param name="filter">The pagination parameters controlling the page number, page size, and total count.</param>
/// <param name="withPoCs">Indicates whether the response units should be populated with their related Points of Care. Defaults to false.</param>
/// <returns>A <see cref="Task{PaginationResponse{Unit}}"/> containing the requested page of units along with pagination metadata.</returns>
public async Task<PaginationResponse<Unit>> GetPaginatedUnits(PaginationFilter filter, bool withPoCs = false)
{
var result = unitRepository.GetPaginatedUnits(filter);
var count = await result.CountDocumentsAsync();
var data = await result.Skip((filter.PageNumber - 1) * filter.PageSize)
.Limit(filter.PageSize)
.ToListAsync();
if (withPoCs && data.Any())
{
var unitIds = data.Select(u => u.Id).ToList();
var allPocsForUnits = await pointOfCareService.FindAllByUnitIds(unitIds);
foreach (var unit in data) unit.PointOfCares = allPocsForUnits.Where(poc => poc.UnitId == unit.Id).ToList();
}
return new PaginationResponse<Unit>(data, filter.PageNumber, filter.PageSize, count);
}
// public Task<Unit?> GetByPointOfCare(PointOfCare pointOfCare)
// {
// return _unitRepository.FindByPointOfCare(pointOfCare);
// }
/// <summary>
/// Retrieves a <see cref="Unit"/> by its identifier, optionally hydrating its related master lists (e.g., allergy, diagnosis, origin, doctor, procedure, service, treatment, visit option, access control, language barrier, passive sitting, generic lists) for the specified locale, and optionally including its associated Points of Care.
/// </summary>
/// <param name="id">The unique identifier of the unit to retrieve.</param>
/// <param name="dataLocale">The locale used to resolve localized values for the related master lists; can be <see langword="null"/>.</param>
/// <param name="fillLists">When <see langword="true"/> (default), populates every available related master list referenced by the unit using the given locale; when <see langword="false"/>, only the base unit is returned.</param>
/// <param name="withPoCs">When <see langword="true"/>, also loads and assigns the Points of Care associated with the unit; when <see langword="false"/> (default), the Points of Care collection is not populated.</param>
/// <returns>A <see cref="Task{Unit}"/> that yields the requested <see cref="Unit"/> with its optional related lists and Points of Care, or <see langword="null"/> when no unit matches the identifier.</returns>
/// <exception cref="NotFoundException">Thrown when no unit is found for the supplied <paramref name="id"/>.</exception>
public async Task<Unit?> GetInfo(ObjectId id, LocaleEnum? dataLocale, bool fillLists = true, bool withPoCs = false)
{
var unit = await Get(id.ToString()) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
//if (unit == null )// || unit.PointOfCares == null tiene sentido?
//{
// _logger.LogError("Section not found: {id}", id);
// return null;
//}
if (fillLists)
{
if (unit.AltableOptionListId.HasValue)
unit.AltableOptionList =
await masterListServiceFactory.GetMasterListById(MasterListType.AltableOptionList,
unit.AltableOptionListId.Value, dataLocale) as AltableOptionList;
if (unit.AllergyListId.HasValue)
unit.AllergyList =
await masterListServiceFactory.GetMasterListById(MasterListType.AllergyList,
unit.AllergyListId.Value, dataLocale) as AllergyList;
if (unit.DestinationListId.HasValue)
unit.DestinationList =
await masterListServiceFactory.GetMasterListById(MasterListType.DestinationList,
unit.DestinationListId.Value, dataLocale) as DestinationList;
if (unit.InternalDestinationListId.HasValue)
unit.InternalDestinationList =
await masterListServiceFactory.GetMasterListById(MasterListType.InternalDestinationList,
unit.InternalDestinationListId.Value, dataLocale) as InternalDestinationList;
if (unit.DiagnosisListId.HasValue)
unit.DiagnosisList =
await masterListServiceFactory.GetMasterListById(MasterListType.DiagnosisList,
unit.DiagnosisListId.Value, dataLocale) as DiagnosisList;
if (unit.DischargeStatusListId.HasValue)
unit.DischargeStatusList =
await masterListServiceFactory.GetMasterListById(MasterListType.DischargeStatusList,
unit.DischargeStatusListId.Value, dataLocale) as DischargeStatusList;
if (unit.DoctorListId.HasValue)
unit.DoctorList =
await masterListServiceFactory.GetMasterListById(MasterListType.DoctorList, unit.DoctorListId.Value,
dataLocale) as DoctorList;
if (unit.DoctorTypeListId.HasValue)
unit.DoctorTypeList =
await masterListServiceFactory.GetMasterListById(MasterListType.DoctorTypeList,
unit.DoctorTypeListId.Value, dataLocale) as DoctorTypeList;
if (unit.InsulationListId.HasValue)
unit.InsulationList =
await masterListServiceFactory.GetMasterListById(MasterListType.InsulationList,
unit.InsulationListId.Value, dataLocale) as InsulationList;
if (unit.MobilityOptionListId.HasValue)
unit.MobilityOptionList =
await masterListServiceFactory.GetMasterListById(MasterListType.MobilityOptionList,
unit.MobilityOptionListId.Value, dataLocale) as MobilityOptionList;
if (unit.OriginListId.HasValue)
unit.OriginList =
await masterListServiceFactory.GetMasterListById(MasterListType.OriginList, unit.OriginListId.Value,
dataLocale) as OriginList;
if (unit.PatientStatusListId.HasValue)
unit.PatientStatusList =
await masterListServiceFactory.GetMasterListById(MasterListType.PatientStatusList,
unit.PatientStatusListId.Value, dataLocale) as PatientStatusList;
if (unit.ProcedureListId.HasValue)
unit.ProcedureList =
await masterListServiceFactory.GetMasterListById(MasterListType.ProcedureList,
unit.ProcedureListId.Value, dataLocale) as ProcedureList;
if (unit.TestListId.HasValue)
unit.TestList =
await masterListServiceFactory.GetMasterListById(MasterListType.TestList, unit.TestListId.Value,
dataLocale) as TestList;
if (unit.ServiceListId.HasValue)
unit.ServiceList =
await masterListServiceFactory.GetMasterListById(MasterListType.ServiceList,
unit.ServiceListId.Value, dataLocale) as ServiceList;
if (unit.TherapeuticCeilingListId.HasValue)
unit.TherapeuticCeilingList =
await masterListServiceFactory.GetMasterListById(MasterListType.TherapeuticCeilingList,
unit.TherapeuticCeilingListId.Value, dataLocale) as TherapeuticCeilingList;
if (unit.TreatmentListId.HasValue)
unit.TreatmentList =
await masterListServiceFactory.GetMasterListById(MasterListType.TreatmentList,
unit.TreatmentListId.Value, dataLocale) as TreatmentList;
if (unit.VisitOptionListId.HasValue)
unit.VisitOptionList =
await masterListServiceFactory.GetMasterListById(MasterListType.VisitOptionList,
unit.VisitOptionListId.Value, dataLocale) as VisitOptionList;
if (unit.AccessControlListId.HasValue)
unit.AccessControlList =
await masterListServiceFactory.GetMasterListById(MasterListType.AccessControlList,
unit.AccessControlListId.Value, dataLocale) as AccessControlList;
if (unit.LanguageBarrierListId.HasValue)
unit.LanguageBarrierList =
await masterListServiceFactory.GetMasterListById(MasterListType.LanguageBarrierList,
unit.LanguageBarrierListId.Value, dataLocale) as LanguageBarrierList;
if (unit.PassiveSittingListId.HasValue)
unit.PassiveSittingList =
await masterListServiceFactory.GetMasterListById(MasterListType.PassiveSittingList,
unit.PassiveSittingListId.Value, dataLocale) as PassiveSittingList;
if (unit.GenericListId.HasValue)
unit.GenericList =
await masterListServiceFactory.GetMasterListById(MasterListType.GenericList,
unit.GenericListId.Value, dataLocale) as GenericList;
}
if (withPoCs)
{
var pocList = await pointOfCareService.FindAllByUnitId(unit.Id);
unit.PointOfCares = pocList?.ToList();
}
return unit;
}
/// <summary>
/// Retrieves a unit by its identifier and, when requested, enriches it with its associated points of care (including their devices).
/// </summary>
/// <param name="id">The identifier of the unit to look up.</param>
/// <param name="withPoCs">When true, loads and assigns the unit's points of care to the result.</param>
/// <param name="withDevices">Flag intended to control device inclusion alongside the points of care.</param>
/// <returns>The matching <see cref="Unit"/> instance.</returns>
/// <exception cref="NotFoundException">Thrown when no unit exists for the supplied <paramref name="id"/>.</exception>
public async Task<Unit?> GetInfo(ObjectId id, bool withPoCs = true, bool withDevices = true)
{
var unit = await Get(id.ToString()) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
if (withPoCs)
{
var pocList = await pointOfCareService.FindAllByUnitIdWithDevices(unit.Id);
unit.PointOfCares = pocList?.ToList();
}
return unit;
}
/// <summary>
/// Retrieves a unit by its name from the repository. If no matching unit is found, a not-found exception is thrown.
/// </summary>
/// <param name="itemUnitName">The name of the unit to search for.</param>
/// <returns>The unit matching the specified name.</returns>
/// <exception cref="NotFoundException">Thrown when no unit exists with the provided name.</exception>
public async Task<Unit?> GetByName(string itemUnitName)
{
return await unitRepository.FindByName(itemUnitName) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
}
/// <summary>
/// Finds the <see cref="Unit"/> associated with a given patient by resolving the patient record and returning its linked unit.
/// Returns the unit only when the patient exists and has a non-null <c>UnitId</c>; otherwise, throws a not-found exception.
/// </summary>
/// <param name="patientId">The identifier of the patient whose associated unit should be retrieved.</param>
/// <returns>A <see cref="Task{Unit}"/> containing the associated <see cref="Unit"/> if found, or <c>null</c> when no matching unit exists.</returns>
/// <exception cref="NotFoundException">Thrown when the patient has no associated <c>UnitId</c> (i.e., the resource is missing).</exception>
public async Task<Unit?> FindByPatientId(ObjectId patientId)
{
//var sections = await GetAll();
var patient = await patientService.Value.FindById(patientId);
// if (patient != null && !string.IsNullOrEmpty(patient.Bed) && patient.IsInActivePoC())
// {
// // Deberia ser una lista? revisar como gestionar varias unidades con eel mismo pointOfCare
// return sections.FirstOrDefault(s => s.PointOfCares.Any(c=>c.Bed==patient.Bed && c.UnitName == patient.UnitString));
// }
if (patient is { UnitId: not null }) return await FindById(patient.UnitId);
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
}
/// <summary>
/// Asynchronously retrieves a collection of <see cref="Unit"/> entities associated with the specified master list identifier and type.
/// If an exception occurs during the lookup, the error is logged and <c>null</c> is returned.
/// </summary>
/// <param name="masterListId">The identifier of the master list used to find the associated units.</param>
/// <param name="masterListType">The type of the master list used to filter the units.</param>
/// <returns>A task that represents the asynchronous operation, containing a collection of <see cref="Unit"/> entities if found, or <c>null</c> if an error occurs.</returns>
public async Task<IEnumerable<Unit>?> FindUnitsByMasterListId(ObjectId masterListId, MasterListType masterListType)
{
try
{
var result = await unitRepository.FindByMasterListId(masterListId, masterListType);
return result;
}
catch (Exception ex)
{
Log.Error("Exception finding by MasterId{id} section exception:{e} ", masterListId, ex.Message);
return null;
}
}
/// <summary>
/// Asynchronously counts the number of units associated with the specified master list identifier and master list type by delegating to the unit repository.
/// </summary>
/// <param name="masterListId">The unique identifier of the master list whose units should be counted.</param>
/// <param name="masterListType">The type of the master list used to filter the units to be counted.</param>
/// <returns>A <see cref="Task{Int64}"/> representing the asynchronous operation, containing the total number of units that match the given master list identifier and type.</returns>
public async Task<long> CountUnitsByMasterListId(ObjectId masterListId, MasterListType masterListType)
{
return await unitRepository.CountUnitsByMasterListId(masterListId, masterListType);
}
/// <summary>
/// Retrieves a collection of units associated with the specified master list identifier.
/// On failure, logs the exception and returns an empty list as a fallback.
/// </summary>
/// <param name="masterListId">The identifier of the master list whose units should be retrieved.</param>
/// <returns>A task that yields the collection of <see cref="Unit"/> items matching the master list identifier, or an empty list if an error occurs.</returns>
public async Task<IEnumerable<Unit>> FindUnitsByMasterListId(ObjectId masterListId)
{
try
{
var result = await unitRepository.FindByMasterListId(masterListId);
return result;
}
catch (Exception ex)
{
Log.Error("Exception finding by MasterId{id} section exception:{e} ", masterListId, ex.Message);
return new List<Unit>();
}
}
/// <summary>
/// Updates the master list information of an existing unit identified by the provided identifier.
/// Throws a not found exception if the unit does not exist and a conflict exception if the update operation fails.
/// </summary>
/// <param name="updateUnitListDto">The data transfer object containing the unit identifier and the updated master list information.</param>
/// <returns>The updated <see cref="Unit"/> if the operation succeeds; otherwise, <c>null</c> when the underlying update returns no result.</returns>
/// <exception cref="NotFoundException">Thrown when no unit is found matching the identifier specified in <paramref name="updateUnitListDto"/>.</exception>
/// <exception cref="ConflictException">Thrown when the update operation performed by the repository fails to produce a result.</exception>
public async Task<Unit?> UpdateUnitMasterList(UpdateUnitIdListDto updateUnitListDto)
{
var unit = await FindById(updateUnitListDto.UnitId) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
var updatedUnit = await unitRepository.UpdateUnitMasterList(updateUnitListDto) ??
throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, unit, updatedUnit);
return updatedUnit;
}
/// <summary>
/// Updates the configuration of an existing unit and records an audit log entry capturing the previous and resulting state.
/// </summary>
/// <param name="unitIdParsed">The parsed identifier of the unit whose configuration should be updated.</param>
/// <param name="unitConfiguration">The new configuration values to apply to the unit.</param>
/// <returns>A task that resolves to <c>true</c> when the configuration was successfully updated; otherwise, <c>false</c>.</returns>
/// <exception cref="NotFoundException">Thrown when the unit cannot be found either before or after the update operation.</exception>
/// <exception cref="ConflictException">Thrown when the underlying update operation fails to persist the new configuration.</exception>
public async Task<bool> UpdateConfiguration(ObjectId unitIdParsed, UnitConfiguration unitConfiguration)
{
var oldConfig = await FindById(unitIdParsed) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
var result = await unitRepository.UpdateConfiguration(unitIdParsed, unitConfiguration);
var newConfig = await FindById(unitIdParsed) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
if (!result) throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldConfig, newConfig);
return result;
}
/// <summary>
/// Retrieves a unit by its identifier. Returns null when the provided identifier is null; otherwise, delegates the lookup to the unit repository.
/// </summary>
/// <param name="id">The identifier of the unit to find.</param>
/// <returns>The unit matching the specified identifier, or null if the identifier is null.</returns>
public async Task<Unit?> FindById(ObjectId? id)
{
if (id == null)
return null;
return await unitRepository.FindById(id);
}
/// <summary>
/// Retrieves a <see cref="Unit"/> by its name, returning <see langword="null"/> when the provided name is null or empty.
/// Otherwise, delegates the lookup to the unit repository.
/// </summary>
/// <param name="name">The name of the unit to look up. Can be <see langword="null"/> or empty.</param>
/// <returns>A <see cref="Task{Unit}"/> containing the matching <see cref="Unit"/>, or <see langword="null"/> if no name was provided.</returns>
public async Task<Unit?> FindByName(string? name)
{
if (string.IsNullOrEmpty(name))
return null;
return await unitRepository.FindByName(name);
}
/// <summary>
/// Finds a unit by its name, or alternatively by a point of care bed identifier when no name is provided.
/// If the name is supplied, the unit is looked up directly; otherwise, the point of care is resolved from the bed and the associated unit is returned.
/// Returns <c>null</c> when neither a name nor a matching point of care is available, or when no unit is found.
/// </summary>
/// <param name="name">The name of the unit to search for. Takes precedence over <paramref name="pocName"/> when provided.</param>
/// <param name="pocName">The point of care bed identifier used as a fallback to locate the unit when <paramref name="name"/> is not supplied.</param>
/// <returns>A task containing the matching <see cref="Unit"/>, or <c>null</c> if no unit can be resolved from the given inputs.</returns>
public async Task<Unit?> FindByUnitNameOrPocName(string? name, string? pocName)
{
if (!string.IsNullOrEmpty(name))
return await unitRepository.FindByName(name);
if (!string.IsNullOrEmpty(pocName))
{
var poc = await pointOfCareService.FindByBed(pocName);
if (poc != null)
return await FindById(poc.FirstOrDefault()?.UnitId);
}
return null;
}
// public async Task<Unit?> FindByPointOfCare(PointOfCare pointOfCare)
// {
// return await _unitRepository.FindByPointOfCare(pointOfCare);
// }
/// <summary>
/// Inserts a new unit into the repository and records an audit log entry for the operation using the current HTTP context user. Throws a <see cref="ConflictException"/> when the repository fails to create the unit.
/// </summary>
/// <param name="unit">The unit entity to be inserted.</param>
/// <returns>The newly created unit returned by the repository.</returns>
/// <exception cref="ConflictException">Thrown when the repository returns null, indicating that the unit could not be created.</exception>
public async Task<Unit?> InsertOne(Unit unit)
{
var newUnit = await unitRepository.InsertOneUnit(unit) ??
throw new ConflictException(HttpEnum.ErrorMessage.ConflictCreationFailed);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, null, newUnit);
return newUnit;
}
/// <summary>
/// Updates an existing <see cref="Unit"/> in the repository, creating an audit log entry and broadcasting the change to interested parties.
/// </summary>
/// <param name="unit">The unit containing the updated information, including the identifier of the existing unit to modify.</param>
/// <returns>The updated <see cref="Unit"/> if the operation succeeded; <c>null</c> if the repository could not persist the update.</returns>
/// <exception cref="NotFoundException">Thrown when no unit exists with the specified identifier.</exception>
public async Task<Unit?> UpdateUnit(Unit unit)
{
var oldUnit = await FindById(unit.Id) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
var newUnit = await unitRepository.UpdateUnit(unit);
if (newUnit == null)
return null;
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldUnit, newUnit);
SendUnitBroadcast(newUnit, OperationType.UpdateUnit);
return newUnit;
}
/// <summary>
/// Updates the name and title of an existing unit, records the change in the audit log, and broadcasts the update to subscribed clients.
/// </summary>
/// <param name="unitId">The unique identifier of the unit to update.</param>
/// <param name="name">The new name to assign to the unit.</param>
/// <param name="title">The new title to assign to the unit.</param>
/// <param name="configObsId">Optional configuration observer identifier associated with the update.</param>
/// <returns>The updated unit when the operation succeeds; otherwise, null.</returns>
/// <exception cref="NotFoundException">Thrown when no unit is found for the provided identifier.</exception>
/// <exception cref="ConflictException">Thrown when the underlying unit update operation fails.</exception>
public async Task<Unit?> UpdateUnitInfo(ObjectId unitId, string name, string title, string? configObsId = null)
{
var oldUnit = await FindById(unitId) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
var newUnit = await unitRepository.UpdateUnitInfo(unitId, name, title) ??
throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldUnit, newUnit);
if (newUnit != null) SendUnitBroadcast(newUnit, OperationType.UpdateUnit);
return newUnit;
}
/// <summary>
/// Deletes a unit from the repository by its identifier and records an audit log entry for the operation.
/// Throws a <see cref="ConflictException"/> when the underlying delete operation does not complete successfully.
/// </summary>
/// <param name="unit">The unit entity to delete, identified by its <c>Id</c>.</param>
/// <returns>A task that resolves to <c>true</c> when the unit is successfully deleted.</returns>
/// <exception cref="ConflictException">Thrown when the delete operation fails (returns <c>null</c>).</exception>
public async Task<bool> DeleteUnitById(Unit unit)
{
//if (unit is not { Status: null }) throw new ConflictException(ErrorMessage.Conflict_ResourceInUse);
_ = await unitRepository.DeleteAsync(unit.Id) ??
throw new ConflictException(HttpEnum.ErrorMessage.ConflictDeleteFailed);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, unit, null);
return true;
}
/// <summary>
/// Retrieves a <see cref="Unit"/> by attempting multiple lookup strategies: first by ObjectId, then by title, and finally by name.
/// </summary>
/// <param name="id">The identifier used to locate the unit. It can be an ObjectId, a title, or a name.</param>
/// <returns>The matching <see cref="Unit"/> if found; otherwise, <c>null</c>.</returns>
public async Task<Unit?> Get(string id)
{
Unit? section = null;
if (ObjectId.TryParse(id, out var oid)) section = await FindById(oid);
var sections = await GetAll();
section ??= sections.FirstOrDefault(s => s.Title == id);
section ??= sections.FirstOrDefault(s => s.Name == id);
return section;
}
/// <summary>
/// Retrieves a list of <see cref="Unit"/> entries that contain a point of care matching the specified <see cref="PatientLocation"/>, based on bed and unit name criteria.
/// </summary>
/// <param name="location">The <see cref="PatientLocation"/> providing the <c>Bed</c> and <c>UnitName</c> values used to filter the results.</param>
/// <returns>A task containing a list of <see cref="Unit"/> entries whose point of care matches the given location, or <c>null</c> if an error occurs while retrieving the data.</returns>
public async Task<List<Unit>?> FindByLocation(PatientLocation location)
{
try
{
var sections = await GetAll();
// Cambiar por comparacion con Location?
return sections.Where(section => section.PointOfCares != null &&
section.PointOfCares.Any(c =>
c.Bed == location.Bed && c.UnitName == location.UnitName)).ToList();
}
catch (Exception e)
{
Log.Error("Exception finding by location section exception:{e} location: {location} ", e.Message,
location);
return null;
}
}
/// <summary>
/// Sends an asynchronous broadcast message about a unit operation to all subscribers whose location IDs match the points of care associated with the given unit. If no points of care are found for the unit, the method returns without sending any messages. Any errors encountered during the broadcast are logged without rethrowing.
/// </summary>
/// <param name="unit">The unit whose operation is being broadcast.</param>
/// <param name="operation">The type of operation performed on the unit, sent as part of the broadcast message.</param>
private async void SendUnitBroadcast(Unit unit, OperationType operation)
{
try
{
var locations = new List<ObjectId>();
var pocs = await pointOfCareService.FindAllByUnitId(unit.Id);
if (pocs == null)
return;
locations.AddRange(pocs.Select(c => c.Id));
var subscribers = subscribersService.GetSubscribers()
.Where(s => s.LocationIds.Any(id => locations.Contains(id)))
.ToList();
foreach (var subscriber in subscribers)
_ = clientMessageService.Value.SendAsync(subscriber.Id, operation, unit);
}
catch (Exception e)
{
logger.LogError(
"Error sending Unit Broadcast {unit} with operation type {operationToString()} message: {eMessage}",
unit, operation.ToString(), e.Message);
}
}
}