SHA256
- 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.
218 lines
7.6 KiB
C#
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
|
|
} |