Files
SomniaGuestLog/src/Somnia.GuestLog/Services/Entity/StayEntryEntityService.cs
T
Daniel-Garmig c5d8487046 Small fixes and improvements:
- GuestDocument - At least one contact field is required.
- GuestDocument - List Page - Hide not useful columns: "version"
- StayEntry - Show stayEntry ID in UI msg after creation.
- StayEntry - Create Page - Time fields don't reset anymore when dates change.
- Show local times in UI instead of UTC times.
- Validators - Use expresions for dates so they are recalculated each time.
2026-09-12 15:28:25 +02:00

218 lines
7.6 KiB
C#

using FluentValidation;
using Microsoft.EntityFrameworkCore;
using Somnia.GuestLog.Enums.Services;
using Somnia.GuestLog.Infrastructure;
using Somnia.GuestLog.Models;
using Somnia.GuestLog.Models.DTOs;
using Somnia.GuestLog.Models.Entities;
using Somnia.GuestLog.Models.QueryConfigs;
using Somnia.GuestLog.Utils;
namespace Somnia.GuestLog.Services.Entity;
public class StayEntryEntityService(
ILogger<StayEntryEntityService> logger,
IDbContextFactory<GuestLogContext> contextFactory,
IValidator<StayEntryDTO> dtoValidator
) : IStayEntryEntityService
{
public async Task<Result> CreateEntryAsync(StayEntryDTO dto)
{
ArgumentNullException.ThrowIfNull(dto);
var valResult = await dtoValidator.ValidateAsync(dto);
if (!valResult.IsValid)
{
logger.LogWarning("StayEntryService - CreateEntryAsync: Dto validation failed. Fix the errors: {errorList}", valResult.Errors);
return Result.Fail(ResultCode.FailedValidation);
}
DateTime createTime = DateTime.UtcNow;
StayEntry dbStay = new()
{
DateIn = dto.DateIn!.Value.ToUniversalTime(),
DateOut = dto.DateOut!.Value.ToUniversalTime(),
RoomCount = dto.RoomCount!.Value,
RoomNumber = dto.RoomNumber,
PaymentMethod = dto.PaymentMethod!,
CreateTime = createTime,
UpdateTime = createTime,
};
try
{
using var dbContext = contextFactory.CreateDbContext();
//Check and add GuestDocuments
foreach (GuestDocumentDTO guestDto in dto.GuestDocuments!)
{
//Get guest from db.
GuestDocument dbGuest = await dbContext.GuestDocuments
.Where(gd => gd.Id == guestDto.Id && gd.Version == guestDto.Version)
.SingleAsync();
//Check if Guest is already in other stay during the same period.
bool isInOtherStay = await this.IsGuestInStayInPeriod(dbGuest.Id, dbStay.DateIn, dbStay.DateOut);
if (isInOtherStay)
{
string period = $"{dbStay.DateIn}-{dbStay.DateOut}";
return Result.FailWithContext(ResultCode.GuestCurrentlyInStay, dbGuest.FullDocument, period);
}
//Add dbGuest to dbStay.
dbStay.GuestDocuments.Add(dbGuest);
}
await dbContext.AddAsync(dbStay);
await dbContext.SaveChangesAsync();
logger.LogInformation("StayEntryService - CreateEntryAsync: StayEntry Created: '{id}'", dbStay.GetFullKey());
return Result.OkWithContext(dbStay.GetFullKey());
}
catch (Exception ex)
{
logger.LogError(ex, "StayEntryService - CreateEntryAsync: Error creating StayEntry");
return Result.FailUnknownError();
}
}
public Task<Result> UpdateEntryAsync(StayEntryDTO updatedDto)
{
throw new NotImplementedException();
}
public async Task<Result> DeleteEntryAsync(StayEntryDTO dto)
{
if (dto.Id is null)
{
throw new InvalidOperationException("StayEntryService - DeleteEntry: A DTO without key can't be deleted!");
}
return await DeleteEntryAsync(dto.Id.Value);
}
public async Task<Result> DeleteEntryAsync(long id)
{
using var dbContext = contextFactory.CreateDbContext();
StayEntry? dbItem = await dbContext.StayEntries.FindAsync(id);
if (dbItem is null)
{
logger.LogWarning("StayEntryService - DeleteEntry: Error deleting StayEntry. Entity not found with key: '{key}'", id);
return Result.Fail(ResultCode.NotFound);
}
try
{
dbContext.StayEntries.Remove(dbItem);
await dbContext.SaveChangesAsync();
logger.LogInformation("StayEntryService - DeleteEntry: StayEntry deleted: '{id}'", dbItem.GetFullKey());
return Result.Ok();
}
catch (Exception ex)
{
logger.LogError(ex, "StayEntryService - DeleteEntry: Error deleting StayEntry: '{id}'", dbItem.GetFullKey());
return Result.FailUnknownError();
}
}
public async Task<StayEntryDTO?> GetDtoByIdAsync(long id, StayEntryQueryConfig? queryConfig = null)
{
using var dbContext = contextFactory.CreateDbContext();
StayEntry? dbItem = await dbContext.StayEntries
.Where(e => e.Id == id)
.AddQueryConfig(queryConfig)
.FirstOrDefaultAsync();
return dbItem is not null ? GetDtoFromDb(dbItem, queryConfig) : null;
}
public async Task<IEnumerable<StayEntryDTO>> GetDtoListAsync(StayEntryQueryConfig? queryConfig = null)
{
using var dbContext = contextFactory.CreateDbContext();
var dbList = await dbContext.StayEntries
.AddQueryConfig(queryConfig)
.OrderByDescending(e => e.UpdateTime)
.ToListAsync();
return dbList.Select(e => GetDtoFromDb(e, queryConfig));
}
public async Task<IEnumerable<StayEntryDTO>> GetDtoListByFilterAsync(StayEntryFilterDTO filter, int limit = 0, StayEntryQueryConfig? queryConfig = null)
{
using var dbContext = contextFactory.CreateDbContext();
var query = dbContext.StayEntries
.AddQueryConfig(queryConfig)
.AsQueryable();
query = query.WhereIf(filter.DateInStart is not null && filter.DateInEnd is not null,
e => e.DateIn >= filter.DateInStart!.Value.ToUniversalTime()
&& e.DateIn < filter.DateInEnd!.Value.ToUniversalTime());
query = query.WhereIf(!string.IsNullOrEmpty(filter.RoomNumber),
e => EF.Functions.Like(e.RoomNumber, $"%{filter.RoomNumber}%"));
query = query.WhereIf(!string.IsNullOrEmpty(filter.PaymentMethod),
e => e.PaymentMethod == filter.PaymentMethod);
query = query.OrderByDescending(e => e.DateIn)
.ThenByDescending(e => e.UpdateTime);
if (limit > 0)
{
query = query.Take(limit);
}
var dbList = await query
.ToListAsync();
return dbList.Select(x => GetDtoFromDb(x, queryConfig));
}
public async Task<bool> IsGuestInStay(long guestDocId)
{
using var dbContext = contextFactory.CreateDbContext();
bool rs = await dbContext.StayEntries
.AnyAsync(s => s.GuestDocuments.Any(g => g.Id == guestDocId));
return rs;
}
public async Task<bool> IsGuestInStayInPeriod(long guestDocId, DateTime startTime, DateTime endTime)
{
using var dbContext = contextFactory.CreateDbContext();
bool rs = await dbContext.StayEntries
.AnyAsync(s => s.DateIn < endTime && s.DateOut > startTime
&& s.GuestDocuments.Any(g => g.Id == guestDocId)
);
return rs;
}
public async Task<IEnumerable<StayEntryDTO>> GetStaysWithGuest(long guestDocId, StayEntryQueryConfig? queryConfig = null)
{
using var dbContext = contextFactory.CreateDbContext();
var rs = await dbContext.StayEntries
.Where(s => s.GuestDocuments.Any(g => g.Id == guestDocId))
.AddQueryConfig(queryConfig)
.OrderByDescending(e => e.DateIn)
.ToListAsync();
return rs.Select(e => GetDtoFromDb(e, queryConfig));
}
#region Internal Methods
private static StayEntryDTO GetDtoFromDb(StayEntry dbItem, StayEntryQueryConfig? queryConfig)
{
queryConfig ??= StayEntryQueryConfig.DefaultQueryConfig;
return EntityMapping.MapStayEntryEntityToDTO(dbItem, queryConfig.IncludeGuests);
}
#endregion
}