From deadf6fe9f6e776ed0d703135a2017caffd15b28 Mon Sep 17 00:00:00 2001 From: rahiyansafin Date: Thu, 4 May 2023 07:04:01 +0600 Subject: [PATCH] Logger, Email Sender, Leave Request (All Functionalities), Models Added --- .../Contracts/Email/IEmailSender.cs | 7 ++ .../Contracts/Identity/IAuthService.cs | 9 ++ .../Contracts/Identity/IUserService.cs | 9 ++ .../Contracts/Logging/IAppLogger.cs | 6 ++ .../Persistence/ILeaveAllocationRepository.cs | 7 +- .../Persistence/ILeaveRequestRepository.cs | 4 +- .../Exceptions/BadRequestException.cs | 8 +- .../CancelLeaveRequestCommand.cs | 7 ++ .../CancelLeaveRequestCommandHandler.cs | 66 +++++++++++++ .../ChangeLeaveRequestApprovalCommand.cs | 8 ++ ...hangeLeaveRequestApprovalCommandHandler.cs | 72 +++++++++++++++ ...ngeLeaveRequestApprovalCommandValidator.cs | 12 +++ .../CreateLeaveRequestCommand.cs | 9 ++ .../CreateLeaveRequestCommandHandler.cs | 92 +++++++++++++++++++ .../CreateLeaveRequestCommandValidator.cs | 16 ++++ .../DeleteLeaveRequestCommand.cs | 7 ++ .../DeleteLeaveRequestCommandHandler.cs | 21 +++++ .../UpdateLeaveRequestCommand.cs | 11 +++ .../UpdateLeaveRequestCommandHandler.cs | 65 +++++++++++++ .../UpdateLeaveRequestCommandValidator.cs | 31 +++++++ .../GetLeaveRequestDetailQuery.cs | 7 ++ .../GetLeaveRequestDetailQueryHandler.cs | 33 +++++++ .../LeaveRequestDetailsDto.cs | 19 ++++ .../GetLeaveRequestListQuery.cs | 7 ++ .../GetLeaveRequestListQueryHandler.cs | 50 ++++++++++ .../LeaveRequestListDto.cs | 16 ++++ .../LeaveRequest/Shared/BaseLeaveRequest.cs | 7 ++ .../Shared/BaseLeaveRequestValidator.cs | 30 ++++++ .../CreateLeaveTypeCommandHandler.cs | 2 +- .../UpdateLeaveType/UpdateLeaveTypeCommand.cs | 1 + .../UpdateLeaveTypeCommandHandler.cs | 22 ++++- .../UpdateLeaveTypeCommandValidator.cs | 33 +++++++ .../GetLeaveTypesQueryHandler.cs | 6 +- .../MappingProfiles/LeaveAllocationProfile.cs | 13 +++ .../MappingProfiles/LeaveRequestProfile.cs | 14 +++ .../MappingProfiles/LeaveTypeProfile.cs | 4 + .../Models/Email/EmailMessage.cs | 7 ++ .../Models/Email/EmailSettings.cs | 7 ++ .../Models/Identity/AuthRequest.cs | 6 ++ .../Models/Identity/AuthResponse.cs | 8 ++ .../Models/Identity/Employee.cs | 8 ++ .../Models/Identity/JwtSettings.cs | 8 ++ .../Models/Identity/RegistrationRequest.cs | 23 +++++ .../Models/Identity/RegistrationResponse.cs | 5 + .../Common/BaseEntity.cs | 2 + HR.LeaveManagement.Domain/LeaveAllocation.cs | 1 + HR.LeaveManagement.Domain/LeaveRequest.cs | 2 +- 47 files changed, 793 insertions(+), 15 deletions(-) create mode 100644 HR.LeaveManagement.Application/Contracts/Email/IEmailSender.cs create mode 100644 HR.LeaveManagement.Application/Contracts/Identity/IAuthService.cs create mode 100644 HR.LeaveManagement.Application/Contracts/Identity/IUserService.cs create mode 100644 HR.LeaveManagement.Application/Contracts/Logging/IAppLogger.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Commands/CancelLeaveRequest/CancelLeaveRequestCommand.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Commands/CancelLeaveRequest/CancelLeaveRequestCommandHandler.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Commands/ChangeLeaveRequestApproval/ChangeLeaveRequestApprovalCommand.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Commands/ChangeLeaveRequestApproval/ChangeLeaveRequestApprovalCommandHandler.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Commands/ChangeLeaveRequestApproval/ChangeLeaveRequestApprovalCommandValidator.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Commands/CreateLeaveRequest/CreateLeaveRequestCommand.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Commands/CreateLeaveRequest/CreateLeaveRequestCommandHandler.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Commands/CreateLeaveRequest/CreateLeaveRequestCommandValidator.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Commands/DeleteLeaveRequest/DeleteLeaveRequestCommand.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Commands/DeleteLeaveRequest/DeleteLeaveRequestCommandHandler.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Commands/UpdateLeaveRequest/UpdateLeaveRequestCommand.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Commands/UpdateLeaveRequest/UpdateLeaveRequestCommandHandler.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Commands/UpdateLeaveRequest/UpdateLeaveRequestCommandValidator.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestDetail/GetLeaveRequestDetailQuery.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestDetail/GetLeaveRequestDetailQueryHandler.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestDetail/LeaveRequestDetailsDto.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestList/GetLeaveRequestListQuery.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestList/GetLeaveRequestListQueryHandler.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestList/LeaveRequestListDto.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Shared/BaseLeaveRequest.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveRequest/Shared/BaseLeaveRequestValidator.cs create mode 100644 HR.LeaveManagement.Application/Features/LeaveType/Commands/UpdateLeaveType/UpdateLeaveTypeCommandValidator.cs create mode 100644 HR.LeaveManagement.Application/MappingProfiles/LeaveAllocationProfile.cs create mode 100644 HR.LeaveManagement.Application/MappingProfiles/LeaveRequestProfile.cs create mode 100644 HR.LeaveManagement.Application/Models/Email/EmailMessage.cs create mode 100644 HR.LeaveManagement.Application/Models/Email/EmailSettings.cs create mode 100644 HR.LeaveManagement.Application/Models/Identity/AuthRequest.cs create mode 100644 HR.LeaveManagement.Application/Models/Identity/AuthResponse.cs create mode 100644 HR.LeaveManagement.Application/Models/Identity/Employee.cs create mode 100644 HR.LeaveManagement.Application/Models/Identity/JwtSettings.cs create mode 100644 HR.LeaveManagement.Application/Models/Identity/RegistrationRequest.cs create mode 100644 HR.LeaveManagement.Application/Models/Identity/RegistrationResponse.cs diff --git a/HR.LeaveManagement.Application/Contracts/Email/IEmailSender.cs b/HR.LeaveManagement.Application/Contracts/Email/IEmailSender.cs new file mode 100644 index 0000000..5417baf --- /dev/null +++ b/HR.LeaveManagement.Application/Contracts/Email/IEmailSender.cs @@ -0,0 +1,7 @@ +using HR.LeaveManagement.Application.Models.Email; + +namespace HR.LeaveManagement.Application.Contracts.Email; +public interface IEmailSender +{ + Task SendEmail(EmailMessage email); +} diff --git a/HR.LeaveManagement.Application/Contracts/Identity/IAuthService.cs b/HR.LeaveManagement.Application/Contracts/Identity/IAuthService.cs new file mode 100644 index 0000000..1ec648b --- /dev/null +++ b/HR.LeaveManagement.Application/Contracts/Identity/IAuthService.cs @@ -0,0 +1,9 @@ +using HR.LeaveManagement.Application.Models.Identity; + +namespace HR.LeaveManagement.Application.Contracts.Identity; +public interface IAuthService +{ + Task Login(AuthRequest request); + Task Register(RegistrationRequest request); + +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Contracts/Identity/IUserService.cs b/HR.LeaveManagement.Application/Contracts/Identity/IUserService.cs new file mode 100644 index 0000000..0e40747 --- /dev/null +++ b/HR.LeaveManagement.Application/Contracts/Identity/IUserService.cs @@ -0,0 +1,9 @@ +using HR.LeaveManagement.Application.Models.Identity; + +namespace HR.LeaveManagement.Application.Contracts.Identity; +public interface IUserService +{ + Task> GetEmployees(); + Task GetEmployee(string userId); + public string UserId { get; } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Contracts/Logging/IAppLogger.cs b/HR.LeaveManagement.Application/Contracts/Logging/IAppLogger.cs new file mode 100644 index 0000000..a4d443b --- /dev/null +++ b/HR.LeaveManagement.Application/Contracts/Logging/IAppLogger.cs @@ -0,0 +1,6 @@ +namespace HR.LeaveManagement.Application.Contracts.Logging; +public interface IAppLogger +{ + void LogInformation(string message, params object[] args); + void LogWarning(string message, params object[] args); +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Contracts/Persistence/ILeaveAllocationRepository.cs b/HR.LeaveManagement.Application/Contracts/Persistence/ILeaveAllocationRepository.cs index 9fa045d..67643eb 100644 --- a/HR.LeaveManagement.Application/Contracts/Persistence/ILeaveAllocationRepository.cs +++ b/HR.LeaveManagement.Application/Contracts/Persistence/ILeaveAllocationRepository.cs @@ -4,5 +4,10 @@ namespace HR.LeaveManagement.Application.Contracts.Persistence; public interface ILeaveAllocationRepository : IGenericRepository { - + Task GetLeaveAllocationWithDetails(int id); + Task> GetLeaveAllocationsWithDetails(); + Task> GetLeaveAllocationsWithDetails(string userId); + Task AllocationExists(string userId, int leaveTypeId, int period); + Task AddAllocations(List allocations); + Task GetUserAllocations(string userId, int leaveTypeId); } diff --git a/HR.LeaveManagement.Application/Contracts/Persistence/ILeaveRequestRepository.cs b/HR.LeaveManagement.Application/Contracts/Persistence/ILeaveRequestRepository.cs index f917bf7..374c500 100644 --- a/HR.LeaveManagement.Application/Contracts/Persistence/ILeaveRequestRepository.cs +++ b/HR.LeaveManagement.Application/Contracts/Persistence/ILeaveRequestRepository.cs @@ -4,5 +4,7 @@ namespace HR.LeaveManagement.Application.Contracts.Persistence; public interface ILeaveRequestRepository : IGenericRepository { - + Task GetLeaveRequestWithDetails(int id); + Task> GetLeaveRequestsWithDetails(); + Task> GetLeaveRequestsWithDetails(string userId); } \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Exceptions/BadRequestException.cs b/HR.LeaveManagement.Application/Exceptions/BadRequestException.cs index 657906f..9f9d331 100644 --- a/HR.LeaveManagement.Application/Exceptions/BadRequestException.cs +++ b/HR.LeaveManagement.Application/Exceptions/BadRequestException.cs @@ -10,11 +10,7 @@ public BadRequestException(string message) : base(message) } public BadRequestException(string message, ValidationResult validationResult) : base(message) - { - Errors = new(); - foreach (var error in validationResult.Errors) - Errors.Add(error.ErrorMessage); - } + => ValidationErrors = validationResult.ToDictionary(); - public List Errors { get; set; } + public IDictionary ValidationErrors { get; set; } } \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/CancelLeaveRequest/CancelLeaveRequestCommand.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/CancelLeaveRequest/CancelLeaveRequestCommand.cs new file mode 100644 index 0000000..485690f --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/CancelLeaveRequest/CancelLeaveRequestCommand.cs @@ -0,0 +1,7 @@ +using MediatR; + +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Commands.CancelLeaveRequest; +public class CancelLeaveRequestCommand : IRequest +{ + public int Id { get; set; } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/CancelLeaveRequest/CancelLeaveRequestCommandHandler.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/CancelLeaveRequest/CancelLeaveRequestCommandHandler.cs new file mode 100644 index 0000000..8dc4b40 --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/CancelLeaveRequest/CancelLeaveRequestCommandHandler.cs @@ -0,0 +1,66 @@ +using HR.LeaveManagement.Application.Contracts.Email; +using HR.LeaveManagement.Application.Contracts.Logging; +using HR.LeaveManagement.Application.Contracts.Persistence; +using HR.LeaveManagement.Application.Exceptions; +using HR.LeaveManagement.Application.Models.Email; + +using MediatR; + +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Commands.CancelLeaveRequest; +public class CancelLeaveRequestCommandHandler : IRequestHandler +{ + private readonly ILeaveRequestRepository _leaveRequestRepository; + private readonly ILeaveAllocationRepository _leaveAllocationRepository; + private readonly IEmailSender _emailSender; + private readonly IAppLogger _appLogger; + + public CancelLeaveRequestCommandHandler(ILeaveRequestRepository leaveRequestRepository, + ILeaveAllocationRepository leaveAllocationRepository, + IEmailSender emailSender, IAppLogger appLogger) + { + _leaveRequestRepository = leaveRequestRepository; + _leaveAllocationRepository = leaveAllocationRepository; + _emailSender = emailSender; + _appLogger = appLogger; + } + + public async Task Handle(CancelLeaveRequestCommand request, CancellationToken cancellationToken) + { + var leaveRequest = await _leaveRequestRepository.GetAsync(request.Id); + + if (leaveRequest is null) + throw new NotFoundException(nameof(leaveRequest), request.Id); + + leaveRequest.Cancelled = true; + await _leaveRequestRepository.UpdateAsync(leaveRequest); + + // if already approved, re-evaluate the employee's allocations for the leave type + if (leaveRequest.Approved == true) + { + int daysRequested = (int)(leaveRequest.EndDate - leaveRequest.StartDate).TotalDays; + var allocation = await _leaveAllocationRepository.GetUserAllocations(leaveRequest.RequestingEmployeeId, leaveRequest.LeaveTypeId); + allocation.NumberOfDays += daysRequested; + + await _leaveAllocationRepository.UpdateAsync(allocation); + } + + // send confirmation email + try + { + var email = new EmailMessage + { + To = string.Empty, /* Get email from employee record */ + Body = $"Your leave request for {leaveRequest.StartDate:D} to {leaveRequest.EndDate:D} has been cancelled successfully.", + Subject = "Leave Request Cancelled" + }; + + await _emailSender.SendEmail(email); + } + catch (Exception ex) + { + _appLogger.LogWarning(ex.Message); + } + + return Unit.Value; + } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/ChangeLeaveRequestApproval/ChangeLeaveRequestApprovalCommand.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/ChangeLeaveRequestApproval/ChangeLeaveRequestApprovalCommand.cs new file mode 100644 index 0000000..5af7148 --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/ChangeLeaveRequestApproval/ChangeLeaveRequestApprovalCommand.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Commands.ChangeLeaveRequestApproval; +public class ChangeLeaveRequestApprovalCommand : IRequest +{ + public int Id { get; set; } + public bool Approved { get; set; } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/ChangeLeaveRequestApproval/ChangeLeaveRequestApprovalCommandHandler.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/ChangeLeaveRequestApproval/ChangeLeaveRequestApprovalCommandHandler.cs new file mode 100644 index 0000000..d8ccae5 --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/ChangeLeaveRequestApproval/ChangeLeaveRequestApprovalCommandHandler.cs @@ -0,0 +1,72 @@ +using AutoMapper; + +using HR.LeaveManagement.Application.Contracts.Email; +using HR.LeaveManagement.Application.Contracts.Logging; +using HR.LeaveManagement.Application.Contracts.Persistence; +using HR.LeaveManagement.Application.Exceptions; +using HR.LeaveManagement.Application.Models.Email; + +using MediatR; + +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Commands.ChangeLeaveRequestApproval; +public class ChangeLeaveRequestApprovalCommandHandler : IRequestHandler +{ + private readonly IMapper _mapper; + private readonly IEmailSender _emailSender; + private readonly IAppLogger _appLogger; + private readonly ILeaveRequestRepository _leaveRequestRepository; + private readonly ILeaveTypeRepository _leaveTypeRepository; + private readonly ILeaveAllocationRepository _leaveAllocationRepository; + + public ChangeLeaveRequestApprovalCommandHandler( + ILeaveRequestRepository leaveRequestRepository, + ILeaveTypeRepository leaveTypeRepository, + ILeaveAllocationRepository leaveAllocationRepository, + IMapper mapper, + IEmailSender emailSender, + IAppLogger appLogger) + { + _leaveRequestRepository = leaveRequestRepository; + _leaveTypeRepository = leaveTypeRepository; + _leaveAllocationRepository = leaveAllocationRepository; + _mapper = mapper; + _emailSender = emailSender; + _appLogger = appLogger; + } + + public async Task Handle(ChangeLeaveRequestApprovalCommand request, CancellationToken cancellationToken) + { + var leaveRequest = await _leaveRequestRepository.GetAsync(request.Id) ?? throw new NotFoundException(nameof(LeaveRequest), request.Id); + + leaveRequest.Approved = request.Approved; + await _leaveRequestRepository.UpdateAsync(leaveRequest); + + // if request is approved, get and update the employee's allocations + if (request.Approved) + { + int daysRequested = (int)(leaveRequest.EndDate - leaveRequest.StartDate).TotalDays; + var allocation = await _leaveAllocationRepository.GetUserAllocations(leaveRequest.RequestingEmployeeId, leaveRequest.LeaveTypeId); + allocation.NumberOfDays -= daysRequested; + + await _leaveAllocationRepository.UpdateAsync(allocation); + } + + // send confirmation email + try + { + var email = new EmailMessage + { + To = string.Empty, /* Get email from employee record */ + Body = $"The approval status for your leave request for {leaveRequest.StartDate:D} to {leaveRequest.EndDate:D} has been updated.", + Subject = "Leave Request Approval Status Updated" + }; + await _emailSender.SendEmail(email); + } + catch (Exception ex) + { + _appLogger.LogWarning(ex.Message); + } + + return Unit.Value; + } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/ChangeLeaveRequestApproval/ChangeLeaveRequestApprovalCommandValidator.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/ChangeLeaveRequestApproval/ChangeLeaveRequestApprovalCommandValidator.cs new file mode 100644 index 0000000..f4949ec --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/ChangeLeaveRequestApproval/ChangeLeaveRequestApprovalCommandValidator.cs @@ -0,0 +1,12 @@ +using FluentValidation; + +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Commands.ChangeLeaveRequestApproval; +public class ChangeLeaveRequestApprovalCommandValidator : AbstractValidator +{ + public ChangeLeaveRequestApprovalCommandValidator() + { + RuleFor(p => p.Approved) + .NotNull() + .WithMessage("Approval status cannot be null"); + } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/CreateLeaveRequest/CreateLeaveRequestCommand.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/CreateLeaveRequest/CreateLeaveRequestCommand.cs new file mode 100644 index 0000000..445a4b1 --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/CreateLeaveRequest/CreateLeaveRequestCommand.cs @@ -0,0 +1,9 @@ +using HR.LeaveManagement.Application.Features.LeaveRequest.Shared; + +using MediatR; + +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Commands.CreateLeaveRequest; +public class CreateLeaveRequestCommand : BaseLeaveRequest, IRequest +{ + public string RequestComments { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/CreateLeaveRequest/CreateLeaveRequestCommandHandler.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/CreateLeaveRequest/CreateLeaveRequestCommandHandler.cs new file mode 100644 index 0000000..4bcc7cd --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/CreateLeaveRequest/CreateLeaveRequestCommandHandler.cs @@ -0,0 +1,92 @@ +using AutoMapper; + +using HR.LeaveManagement.Application.Contracts.Email; +using HR.LeaveManagement.Application.Contracts.Identity; +using HR.LeaveManagement.Application.Contracts.Logging; +using HR.LeaveManagement.Application.Contracts.Persistence; +using HR.LeaveManagement.Application.Exceptions; +using HR.LeaveManagement.Application.Models.Email; + +using MediatR; + +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Commands.CreateLeaveRequest; +public class CreateLeaveRequestCommandHandler : IRequestHandler +{ + private readonly IEmailSender _emailSender; + private readonly IMapper _mapper; + private readonly ILeaveTypeRepository _leaveTypeRepository; + private readonly ILeaveRequestRepository _leaveRequestRepository; + private readonly ILeaveAllocationRepository _leaveAllocationRepository; + private readonly IUserService _userService; + private readonly IAppLogger _appLogger; + + public CreateLeaveRequestCommandHandler(IEmailSender emailSender, + IMapper mapper, ILeaveTypeRepository leaveTypeRepository, ILeaveRequestRepository leaveRequestRepository, ILeaveAllocationRepository leaveAllocationRepository, IUserService userService, + IAppLogger appLogger) + { + _emailSender = emailSender; + _mapper = mapper; + _leaveTypeRepository = leaveTypeRepository; + _leaveRequestRepository = leaveRequestRepository; + _leaveAllocationRepository = leaveAllocationRepository; + _userService = userService; + _appLogger = appLogger; + } + + public async Task Handle(CreateLeaveRequestCommand request, CancellationToken cancellationToken) + { + var validator = new CreateLeaveRequestCommandValidator(_leaveTypeRepository); + var validationResult = await validator.ValidateAsync(request, cancellationToken); + + if (validationResult.Errors.Any()) + throw new BadRequestException("Invalid Leave Request", validationResult); + + // Get requesting employee's id + var employeeId = _userService.UserId; + + // Check on employee's allocation + var allocation = await _leaveAllocationRepository.GetUserAllocations(employeeId, request.LeaveTypeId); + + // if allocations aren't enough, return validation error with message + if (allocation is null) + { + validationResult.Errors.Add(new FluentValidation.Results.ValidationFailure(nameof(request.LeaveTypeId), + "You do not have any allocations for this leave type.")); + throw new BadRequestException("Invalid Leave Request", validationResult); + } + + int daysRequested = (int)(request.EndDate - request.StartDate).TotalDays; + if (daysRequested > allocation.NumberOfDays) + { + validationResult.Errors.Add(new FluentValidation.Results.ValidationFailure( + nameof(request.EndDate), "You do not have enough days for this request")); + throw new BadRequestException("Invalid Leave Request", validationResult); + } + + // Create leave request + var leaveRequest = _mapper.Map(request); + leaveRequest.RequestingEmployeeId = employeeId; + leaveRequest.DateRequested = DateTime.Now; + await _leaveRequestRepository.CreateAsync(leaveRequest); + + // send confirmation email + try + { + var email = new EmailMessage + { + To = string.Empty, /* Get email from employee record */ + Body = $"Your leave request for {request.StartDate:D} to {request.EndDate:D} " + + $"has been submitted successfully.", + Subject = "Leave Request Submitted" + }; + + await _emailSender.SendEmail(email); + } + catch (Exception ex) + { + _appLogger.LogWarning(ex.Message); + } + + return Unit.Value; + } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/CreateLeaveRequest/CreateLeaveRequestCommandValidator.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/CreateLeaveRequest/CreateLeaveRequestCommandValidator.cs new file mode 100644 index 0000000..0385b6b --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/CreateLeaveRequest/CreateLeaveRequestCommandValidator.cs @@ -0,0 +1,16 @@ +using FluentValidation; + +using HR.LeaveManagement.Application.Contracts.Persistence; +using HR.LeaveManagement.Application.Features.LeaveRequest.Shared; + +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Commands.CreateLeaveRequest; +public class CreateLeaveRequestCommandValidator : AbstractValidator +{ + private readonly ILeaveTypeRepository _leaveTypeRepository; + + public CreateLeaveRequestCommandValidator(ILeaveTypeRepository leaveTypeRepository) + { + _leaveTypeRepository = leaveTypeRepository; + Include(new BaseLeaveRequestValidator(_leaveTypeRepository)); + } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/DeleteLeaveRequest/DeleteLeaveRequestCommand.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/DeleteLeaveRequest/DeleteLeaveRequestCommand.cs new file mode 100644 index 0000000..2949451 --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/DeleteLeaveRequest/DeleteLeaveRequestCommand.cs @@ -0,0 +1,7 @@ +using MediatR; + +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Commands.DeleteLeaveRequest; +public class DeleteLeaveRequestCommand : IRequest +{ + public int Id { get; set; } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/DeleteLeaveRequest/DeleteLeaveRequestCommandHandler.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/DeleteLeaveRequest/DeleteLeaveRequestCommandHandler.cs new file mode 100644 index 0000000..08011b9 --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/DeleteLeaveRequest/DeleteLeaveRequestCommandHandler.cs @@ -0,0 +1,21 @@ +using HR.LeaveManagement.Application.Contracts.Persistence; +using HR.LeaveManagement.Application.Exceptions; + +using MediatR; + +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Commands.DeleteLeaveRequest; +public class DeleteLeaveRequestCommandHandler : IRequestHandler +{ + private readonly ILeaveRequestRepository _leaveRequestRepository; + + public DeleteLeaveRequestCommandHandler(ILeaveRequestRepository leaveRequestRepository) + => _leaveRequestRepository = leaveRequestRepository; + + public async Task Handle(DeleteLeaveRequestCommand request, CancellationToken cancellationToken) + { + var leaveRequest = await _leaveRequestRepository.GetAsync(request.Id) ?? throw new NotFoundException(nameof(LeaveRequest), request.Id); + + await _leaveRequestRepository.DeleteAsync(leaveRequest); + return Unit.Value; + } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/UpdateLeaveRequest/UpdateLeaveRequestCommand.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/UpdateLeaveRequest/UpdateLeaveRequestCommand.cs new file mode 100644 index 0000000..aa30bfe --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/UpdateLeaveRequest/UpdateLeaveRequestCommand.cs @@ -0,0 +1,11 @@ +using HR.LeaveManagement.Application.Features.LeaveRequest.Shared; + +using MediatR; + +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Commands.UpdateLeaveRequest; +public class UpdateLeaveRequestCommand : BaseLeaveRequest, IRequest +{ + public int Id { get; set; } + public string RequestComments { get; set; } = string.Empty; + public bool Cancelled { get; set; } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/UpdateLeaveRequest/UpdateLeaveRequestCommandHandler.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/UpdateLeaveRequest/UpdateLeaveRequestCommandHandler.cs new file mode 100644 index 0000000..e83476f --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/UpdateLeaveRequest/UpdateLeaveRequestCommandHandler.cs @@ -0,0 +1,65 @@ +using AutoMapper; + +using HR.LeaveManagement.Application.Contracts.Email; +using HR.LeaveManagement.Application.Contracts.Logging; +using HR.LeaveManagement.Application.Contracts.Persistence; +using HR.LeaveManagement.Application.Exceptions; +using HR.LeaveManagement.Application.Models.Email; + +using MediatR; + +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Commands.UpdateLeaveRequest; +public class UpdateLeaveRequestCommandHandler : IRequestHandler +{ + private readonly IMapper _mapper; + private readonly IEmailSender _emailSender; + private readonly IAppLogger _appLogger; + private readonly ILeaveRequestRepository _leaveRequestRepository; + private readonly ILeaveTypeRepository _leaveTypeRepository; + + public UpdateLeaveRequestCommandHandler( + ILeaveRequestRepository leaveRequestRepository, ILeaveTypeRepository leaveTypeRepository, IMapper mapper, IEmailSender emailSender, IAppLogger appLogger) + { + _leaveRequestRepository = leaveRequestRepository; + _leaveTypeRepository = leaveTypeRepository; + _mapper = mapper; + _emailSender = emailSender; + _appLogger = appLogger; + } + + public async Task Handle(UpdateLeaveRequestCommand request, CancellationToken cancellationToken) + { + var leaveRequest = await _leaveRequestRepository.GetAsync(request.Id) ?? throw new NotFoundException(nameof(LeaveRequest), request.Id); + + var validator = new UpdateLeaveRequestCommandValidator(_leaveTypeRepository, _leaveRequestRepository); + + var validationResult = await validator.ValidateAsync(request, cancellationToken); + + if (validationResult.Errors.Any()) + throw new BadRequestException("Invalid Leave Request", validationResult); + + _mapper.Map(request, leaveRequest); + + await _leaveRequestRepository.UpdateAsync(leaveRequest); + + try + { + // send confirmation email + var email = new EmailMessage + { + To = string.Empty, /* Get email from employee record */ + Body = $"Your leave request for {request.StartDate:D} to {request.EndDate:D} " + + $"has been updated successfully.", + Subject = "Leave Request Updated" + }; + + await _emailSender.SendEmail(email); + } + catch (Exception ex) + { + _appLogger.LogWarning(ex.Message); + } + + return Unit.Value; + } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/UpdateLeaveRequest/UpdateLeaveRequestCommandValidator.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/UpdateLeaveRequest/UpdateLeaveRequestCommandValidator.cs new file mode 100644 index 0000000..112b7ef --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Commands/UpdateLeaveRequest/UpdateLeaveRequestCommandValidator.cs @@ -0,0 +1,31 @@ +using FluentValidation; + +using HR.LeaveManagement.Application.Contracts.Persistence; +using HR.LeaveManagement.Application.Features.LeaveRequest.Shared; + +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Commands.UpdateLeaveRequest; +public class UpdateLeaveRequestCommandValidator : AbstractValidator +{ + private readonly ILeaveTypeRepository _leaveTypeRepository; + private readonly ILeaveRequestRepository _leaveRequestRepository; + + public UpdateLeaveRequestCommandValidator(ILeaveTypeRepository leaveTypeRepository, ILeaveRequestRepository leaveRequestRepository) + { + _leaveTypeRepository = leaveTypeRepository; + _leaveRequestRepository = leaveRequestRepository; + + Include(new BaseLeaveRequestValidator(_leaveTypeRepository)); + + RuleFor(p => p.Id) + .NotNull() + .MustAsync(LeaveRequestMustExist) + .WithMessage("{PropertyName} must be present"); + } + + private async Task LeaveRequestMustExist(int id, CancellationToken arg2) + { + var leaveAllocation = await _leaveRequestRepository.GetAsync(id); + return leaveAllocation is not null; + } + +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestDetail/GetLeaveRequestDetailQuery.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestDetail/GetLeaveRequestDetailQuery.cs new file mode 100644 index 0000000..331fc1f --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestDetail/GetLeaveRequestDetailQuery.cs @@ -0,0 +1,7 @@ +using MediatR; + +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Queries.GetLeaveRequestDetail; +public class GetLeaveRequestDetailQuery : IRequest +{ + public int Id { get; set; } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestDetail/GetLeaveRequestDetailQueryHandler.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestDetail/GetLeaveRequestDetailQueryHandler.cs new file mode 100644 index 0000000..14d623e --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestDetail/GetLeaveRequestDetailQueryHandler.cs @@ -0,0 +1,33 @@ +using AutoMapper; + +using HR.LeaveManagement.Application.Contracts.Identity; +using HR.LeaveManagement.Application.Contracts.Persistence; +using HR.LeaveManagement.Application.Exceptions; + +using MediatR; + +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Queries.GetLeaveRequestDetail; +public class GetLeaveRequestDetailQueryHandler : IRequestHandler +{ + private readonly ILeaveRequestRepository _leaveRequestRepository; + private readonly IMapper _mapper; + private readonly IUserService _userService; + + public GetLeaveRequestDetailQueryHandler(ILeaveRequestRepository leaveRequestRepository, + IMapper mapper, IUserService userService) + { + _leaveRequestRepository = leaveRequestRepository; + _mapper = mapper; + _userService = userService; + } + + public async Task Handle(GetLeaveRequestDetailQuery request, CancellationToken cancellationToken) + { + var leaveRequest = _mapper.Map(await _leaveRequestRepository.GetLeaveRequestWithDetails(request.Id)) ?? throw new NotFoundException(nameof(LeaveRequest), request.Id); + + // Add Employee details as needed + leaveRequest.Employee = await _userService.GetEmployee(leaveRequest.RequestingEmployeeId); + + return leaveRequest; + } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestDetail/LeaveRequestDetailsDto.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestDetail/LeaveRequestDetailsDto.cs new file mode 100644 index 0000000..e4bef79 --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestDetail/LeaveRequestDetailsDto.cs @@ -0,0 +1,19 @@ +using HR.LeaveManagement.Application.Features.LeaveType.Queries.GetAllLeaveTypes; +using HR.LeaveManagement.Application.Models.Identity; + +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Queries.GetLeaveRequestDetail; +public class LeaveRequestDetailsDto +{ + public int Id { get; set; } + public Employee Employee { get; set; } + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } + public string RequestingEmployeeId { get; set; } + public LeaveTypeDto LeaveType { get; set; } + public int LeaveTypeId { get; set; } + public DateTime DateRequested { get; set; } + public string RequestComments { get; set; } + public DateTime? DateActioned { get; set; } + public bool? Approved { get; set; } + public bool Cancelled { get; set; } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestList/GetLeaveRequestListQuery.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestList/GetLeaveRequestListQuery.cs new file mode 100644 index 0000000..b99239b --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestList/GetLeaveRequestListQuery.cs @@ -0,0 +1,7 @@ +using MediatR; + +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Queries.GetLeaveRequestList; +public class GetLeaveRequestListQuery : IRequest> +{ + public bool IsLoggedInUser { get; set; } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestList/GetLeaveRequestListQueryHandler.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestList/GetLeaveRequestListQueryHandler.cs new file mode 100644 index 0000000..ff07281 --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestList/GetLeaveRequestListQueryHandler.cs @@ -0,0 +1,50 @@ +using AutoMapper; + +using HR.LeaveManagement.Application.Contracts.Identity; +using HR.LeaveManagement.Application.Contracts.Persistence; + +using MediatR; + +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Queries.GetLeaveRequestList; +public class GetLeaveRequestListQueryHandler : IRequestHandler> +{ + private readonly ILeaveRequestRepository _leaveRequestRepository; + private readonly IMapper _mapper; + private readonly IUserService _userService; + + public GetLeaveRequestListQueryHandler(ILeaveRequestRepository leaveRequestRepository, + IMapper mapper, IUserService userService) + { + _leaveRequestRepository = leaveRequestRepository; + _mapper = mapper; + _userService = userService; + } + + public async Task> Handle(GetLeaveRequestListQuery request, CancellationToken cancellationToken) + { + + var leaveRequests = new List(); + var requests = new List(); + + // Check if it is logged in employee + if (request.IsLoggedInUser) + { + var userId = _userService.UserId; + leaveRequests = await _leaveRequestRepository.GetLeaveRequestsWithDetails(userId); + + var employee = await _userService.GetEmployee(userId); + requests = _mapper.Map>(leaveRequests); + foreach (var req in requests) + req.Employee = employee; + } + else + { + leaveRequests = await _leaveRequestRepository.GetLeaveRequestsWithDetails(); + requests = _mapper.Map>(leaveRequests); + foreach (var req in requests) + req.Employee = await _userService.GetEmployee(req.RequestingEmployeeId); + } + + return requests; + } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestList/LeaveRequestListDto.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestList/LeaveRequestListDto.cs new file mode 100644 index 0000000..71a1b80 --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Queries/GetLeaveRequestList/LeaveRequestListDto.cs @@ -0,0 +1,16 @@ +using HR.LeaveManagement.Application.Features.LeaveType.Queries.GetAllLeaveTypes; +using HR.LeaveManagement.Application.Models.Identity; + +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Queries.GetLeaveRequestList; +public class LeaveRequestListDto +{ + public int Id { get; set; } + public Employee Employee { get; set; } + public string RequestingEmployeeId { get; set; } + public LeaveTypeDto LeaveType { get; set; } + public DateTime DateRequested { get; set; } + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } + public bool? Approved { get; set; } + public bool? Cancelled { get; set; } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Shared/BaseLeaveRequest.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Shared/BaseLeaveRequest.cs new file mode 100644 index 0000000..ec6fb3c --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Shared/BaseLeaveRequest.cs @@ -0,0 +1,7 @@ +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Shared; +public abstract class BaseLeaveRequest +{ + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } + public int LeaveTypeId { get; set; } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveRequest/Shared/BaseLeaveRequestValidator.cs b/HR.LeaveManagement.Application/Features/LeaveRequest/Shared/BaseLeaveRequestValidator.cs new file mode 100644 index 0000000..416408c --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveRequest/Shared/BaseLeaveRequestValidator.cs @@ -0,0 +1,30 @@ +using FluentValidation; + +using HR.LeaveManagement.Application.Contracts.Persistence; + +namespace HR.LeaveManagement.Application.Features.LeaveRequest.Shared; +public class BaseLeaveRequestValidator : AbstractValidator +{ + private readonly ILeaveTypeRepository _leaveTypeRepository; + + public BaseLeaveRequestValidator(ILeaveTypeRepository leaveTypeRepository) + { + _leaveTypeRepository = leaveTypeRepository; + RuleFor(p => p.StartDate) + .LessThan(p => p.EndDate).WithMessage("{PropertyName} must be before {ComparisonValue}"); + + RuleFor(p => p.EndDate) + .GreaterThan(p => p.StartDate).WithMessage("{PropertyName} must be after {ComparisonValue}"); + + RuleFor(p => p.LeaveTypeId) + .GreaterThan(0) + .MustAsync(LeaveTypeMustExist) + .WithMessage("{PropertyName} does not exist."); + } + + private async Task LeaveTypeMustExist(int id, CancellationToken arg2) + { + var leaveType = await _leaveTypeRepository.GetAsync(id); + return leaveType is not null; + } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveType/Commands/CreateLeaveType/CreateLeaveTypeCommandHandler.cs b/HR.LeaveManagement.Application/Features/LeaveType/Commands/CreateLeaveType/CreateLeaveTypeCommandHandler.cs index 8e86a7c..d8181af 100644 --- a/HR.LeaveManagement.Application/Features/LeaveType/Commands/CreateLeaveType/CreateLeaveTypeCommandHandler.cs +++ b/HR.LeaveManagement.Application/Features/LeaveType/Commands/CreateLeaveType/CreateLeaveTypeCommandHandler.cs @@ -21,7 +21,7 @@ public async Task Handle(CreateLeaveTypeCommand request, CancellationToken { // Validate incoming data var validator = new CreateLeaveTypeCommandValidator(_leaveTypeRepository); - var validationResult = await validator.ValidateAsync(request); + var validationResult = await validator.ValidateAsync(request, cancellationToken); if (validationResult.Errors.Any()) throw new BadRequestException("Invalid leave type", validationResult); diff --git a/HR.LeaveManagement.Application/Features/LeaveType/Commands/UpdateLeaveType/UpdateLeaveTypeCommand.cs b/HR.LeaveManagement.Application/Features/LeaveType/Commands/UpdateLeaveType/UpdateLeaveTypeCommand.cs index a15f811..dc9a51d 100644 --- a/HR.LeaveManagement.Application/Features/LeaveType/Commands/UpdateLeaveType/UpdateLeaveTypeCommand.cs +++ b/HR.LeaveManagement.Application/Features/LeaveType/Commands/UpdateLeaveType/UpdateLeaveTypeCommand.cs @@ -3,6 +3,7 @@ namespace HR.LeaveManagement.Application.Features.LeaveType.Commands.UpdateLeaveType; public class UpdateLeaveTypeCommand : IRequest { + public int Id { get; set; } public string Name { get; set; } = string.Empty; public int DefaultDays { get; set; } } diff --git a/HR.LeaveManagement.Application/Features/LeaveType/Commands/UpdateLeaveType/UpdateLeaveTypeCommandHandler.cs b/HR.LeaveManagement.Application/Features/LeaveType/Commands/UpdateLeaveType/UpdateLeaveTypeCommandHandler.cs index 7513b09..fa0f557 100644 --- a/HR.LeaveManagement.Application/Features/LeaveType/Commands/UpdateLeaveType/UpdateLeaveTypeCommandHandler.cs +++ b/HR.LeaveManagement.Application/Features/LeaveType/Commands/UpdateLeaveType/UpdateLeaveTypeCommandHandler.cs @@ -1,6 +1,8 @@ using AutoMapper; +using HR.LeaveManagement.Application.Contracts.Logging; using HR.LeaveManagement.Application.Contracts.Persistence; +using HR.LeaveManagement.Application.Exceptions; using MediatR; @@ -9,22 +11,34 @@ public class UpdateLeaveTypeCommandHandler : IRequestHandler _logger; - public UpdateLeaveTypeCommandHandler(IMapper mapper, ILeaveTypeRepository leaveTypeRepository) + public UpdateLeaveTypeCommandHandler(IMapper mapper, ILeaveTypeRepository leaveTypeRepository, IAppLogger logger) { _mapper = mapper; _leaveTypeRepository = leaveTypeRepository; + _logger = logger; } public async Task Handle(UpdateLeaveTypeCommand request, CancellationToken cancellationToken) { - // Convert to domain entity object + // Validate incoming data + var validator = new UpdateLeaveTypeCommandValidator(_leaveTypeRepository); + var validationResult = await validator.ValidateAsync(request, cancellationToken); + + if (validationResult.Errors.Any()) + { + _logger.LogWarning("Validation errors in update request for {0} - {1}", nameof(LeaveType), request.Id); + throw new BadRequestException("Invalid Leave type", validationResult); + } + + // convert to domain entity object var leaveTypeToUpdate = _mapper.Map(request); - // Update in database + // add to database await _leaveTypeRepository.UpdateAsync(leaveTypeToUpdate); - // Return Unit value + // return Unit value return Unit.Value; } } \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveType/Commands/UpdateLeaveType/UpdateLeaveTypeCommandValidator.cs b/HR.LeaveManagement.Application/Features/LeaveType/Commands/UpdateLeaveType/UpdateLeaveTypeCommandValidator.cs new file mode 100644 index 0000000..8d66fe3 --- /dev/null +++ b/HR.LeaveManagement.Application/Features/LeaveType/Commands/UpdateLeaveType/UpdateLeaveTypeCommandValidator.cs @@ -0,0 +1,33 @@ +using FluentValidation; + +using HR.LeaveManagement.Application.Contracts.Persistence; + +namespace HR.LeaveManagement.Application.Features.LeaveType.Commands.UpdateLeaveType; +public class UpdateLeaveTypeCommandValidator : AbstractValidator +{ + private readonly ILeaveTypeRepository _leaveTypeRepository; + + public UpdateLeaveTypeCommandValidator(ILeaveTypeRepository leaveTypeRepository) + { + RuleFor(p => p.Id) + .NotNull() + .MustAsync(LeaveTypeMustExist); + + RuleFor(p => p.Name) + .NotEmpty().WithMessage("{PropertyName} is required") + .NotNull() + .MaximumLength(70).WithMessage("{PropertyName} must be fewer than 70 characters"); + + RuleFor(p => p.DefaultDays) + .LessThan(100).WithMessage("{PropertyName} cannot exceed 100") + .GreaterThan(1).WithMessage("{PropertyName} cannot be less than 1"); + + _leaveTypeRepository = leaveTypeRepository; + } + + private async Task LeaveTypeMustExist(int id, CancellationToken arg2) + { + var leaveType = await _leaveTypeRepository.GetAsync(id); + return leaveType is not null; + } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Features/LeaveType/Queries/GetAllLeaveTypes/GetLeaveTypesQueryHandler.cs b/HR.LeaveManagement.Application/Features/LeaveType/Queries/GetAllLeaveTypes/GetLeaveTypesQueryHandler.cs index 231385d..5c3a881 100644 --- a/HR.LeaveManagement.Application/Features/LeaveType/Queries/GetAllLeaveTypes/GetLeaveTypesQueryHandler.cs +++ b/HR.LeaveManagement.Application/Features/LeaveType/Queries/GetAllLeaveTypes/GetLeaveTypesQueryHandler.cs @@ -1,5 +1,6 @@ using AutoMapper; +using HR.LeaveManagement.Application.Contracts.Logging; using HR.LeaveManagement.Application.Contracts.Persistence; using MediatR; @@ -9,11 +10,13 @@ public class GetLeaveTypesQueryHandler : IRequestHandler _logger; - public GetLeaveTypesQueryHandler(IMapper mapper, ILeaveTypeRepository leaveTypeRepository) + public GetLeaveTypesQueryHandler(IMapper mapper, ILeaveTypeRepository leaveTypeRepository, IAppLogger logger) { _mapper = mapper; _leaveTypeRepository = leaveTypeRepository; + _logger = logger; } public async Task> Handle(GetLeaveTypesQuery request, CancellationToken cancellationToken) @@ -25,6 +28,7 @@ public async Task> Handle(GetLeaveTypesQuery request, Cancell var data = _mapper.Map>(leaveTypes); // return list of DTO object + _logger.LogInformation("Leave types were retrieved successfully"); return data; } } \ No newline at end of file diff --git a/HR.LeaveManagement.Application/MappingProfiles/LeaveAllocationProfile.cs b/HR.LeaveManagement.Application/MappingProfiles/LeaveAllocationProfile.cs new file mode 100644 index 0000000..b8c9fa2 --- /dev/null +++ b/HR.LeaveManagement.Application/MappingProfiles/LeaveAllocationProfile.cs @@ -0,0 +1,13 @@ +using AutoMapper; + +namespace HR.LeaveManagement.Application.MappingProfiles; +public class LeaveAllocationProfile : Profile +{ + public LeaveAllocationProfile() + { + //CreateMap().ReverseMap(); + //CreateMap(); + //CreateMap(); + //CreateMap(); + } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/MappingProfiles/LeaveRequestProfile.cs b/HR.LeaveManagement.Application/MappingProfiles/LeaveRequestProfile.cs new file mode 100644 index 0000000..3de76cc --- /dev/null +++ b/HR.LeaveManagement.Application/MappingProfiles/LeaveRequestProfile.cs @@ -0,0 +1,14 @@ +using AutoMapper; + +namespace HR.LeaveManagement.Application.MappingProfiles; +public class LeaveRequestProfile : Profile +{ + public LeaveRequestProfile() + { + //CreateMap().ReverseMap(); + //CreateMap().ReverseMap(); + //CreateMap(); + //CreateMap(); + //CreateMap(); + } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/MappingProfiles/LeaveTypeProfile.cs b/HR.LeaveManagement.Application/MappingProfiles/LeaveTypeProfile.cs index 3ad7509..35c053f 100644 --- a/HR.LeaveManagement.Application/MappingProfiles/LeaveTypeProfile.cs +++ b/HR.LeaveManagement.Application/MappingProfiles/LeaveTypeProfile.cs @@ -1,5 +1,7 @@ using AutoMapper; +using HR.LeaveManagement.Application.Features.LeaveType.Commands.CreateLeaveType; +using HR.LeaveManagement.Application.Features.LeaveType.Commands.UpdateLeaveType; using HR.LeaveManagement.Application.Features.LeaveType.Queries.GetAllLeaveTypes; using HR.LeaveManagement.Application.Features.LeaveType.Queries.GetLeaveTypeDetails; using HR.LeaveManagement.Domain; @@ -11,5 +13,7 @@ public LeaveTypeProfile() { CreateMap().ReverseMap(); CreateMap(); + CreateMap(); + CreateMap(); } } \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Models/Email/EmailMessage.cs b/HR.LeaveManagement.Application/Models/Email/EmailMessage.cs new file mode 100644 index 0000000..f1dc2c9 --- /dev/null +++ b/HR.LeaveManagement.Application/Models/Email/EmailMessage.cs @@ -0,0 +1,7 @@ +namespace HR.LeaveManagement.Application.Models.Email; +public class EmailMessage +{ + public string To { get; set; } + public string Subject { get; set; } + public string Body { get; set; } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Models/Email/EmailSettings.cs b/HR.LeaveManagement.Application/Models/Email/EmailSettings.cs new file mode 100644 index 0000000..b2eb277 --- /dev/null +++ b/HR.LeaveManagement.Application/Models/Email/EmailSettings.cs @@ -0,0 +1,7 @@ +namespace HR.LeaveManagement.Application.Models.Email; +public class EmailSettings +{ + public string ApiKey { get; set; } + public string FromAddress { get; set; } + public string FromName { get; set; } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Models/Identity/AuthRequest.cs b/HR.LeaveManagement.Application/Models/Identity/AuthRequest.cs new file mode 100644 index 0000000..b399cdb --- /dev/null +++ b/HR.LeaveManagement.Application/Models/Identity/AuthRequest.cs @@ -0,0 +1,6 @@ +namespace HR.LeaveManagement.Application.Models.Identity; +public class AuthRequest +{ + public string Email { get; set; } + public string Password { get; set; } +} diff --git a/HR.LeaveManagement.Application/Models/Identity/AuthResponse.cs b/HR.LeaveManagement.Application/Models/Identity/AuthResponse.cs new file mode 100644 index 0000000..362999e --- /dev/null +++ b/HR.LeaveManagement.Application/Models/Identity/AuthResponse.cs @@ -0,0 +1,8 @@ +namespace HR.LeaveManagement.Application.Models.Identity; +public class AuthResponse +{ + public string Id { get; set; } + public string UserName { get; set; } + public string Email { get; set; } + public string Token { get; set; } +} diff --git a/HR.LeaveManagement.Application/Models/Identity/Employee.cs b/HR.LeaveManagement.Application/Models/Identity/Employee.cs new file mode 100644 index 0000000..59fed3a --- /dev/null +++ b/HR.LeaveManagement.Application/Models/Identity/Employee.cs @@ -0,0 +1,8 @@ +namespace HR.LeaveManagement.Application.Models.Identity; +public class Employee +{ + public string Id { get; set; } + public string Email { get; set; } + public string Firstname { get; set; } + public string Lastname { get; set; } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Models/Identity/JwtSettings.cs b/HR.LeaveManagement.Application/Models/Identity/JwtSettings.cs new file mode 100644 index 0000000..7e8b466 --- /dev/null +++ b/HR.LeaveManagement.Application/Models/Identity/JwtSettings.cs @@ -0,0 +1,8 @@ +namespace HR.LeaveManagement.Application.Models.Identity; +public class JwtSettings +{ + public string Key { get; set; } + public string Issuer { get; set; } + public string Audience { get; set; } + public double DurationInMinutes { get; set; } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Models/Identity/RegistrationRequest.cs b/HR.LeaveManagement.Application/Models/Identity/RegistrationRequest.cs new file mode 100644 index 0000000..7d0127f --- /dev/null +++ b/HR.LeaveManagement.Application/Models/Identity/RegistrationRequest.cs @@ -0,0 +1,23 @@ +using System.ComponentModel.DataAnnotations; + +namespace HR.LeaveManagement.Application.Models.Identity; +public class RegistrationRequest +{ + [Required] + public string FirstName { get; set; } + + [Required] + public string LastName { get; set; } + + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + [MinLength(6)] + public string UserName { get; set; } + + [Required] + [MinLength(6)] + public string Password { get; set; } +} \ No newline at end of file diff --git a/HR.LeaveManagement.Application/Models/Identity/RegistrationResponse.cs b/HR.LeaveManagement.Application/Models/Identity/RegistrationResponse.cs new file mode 100644 index 0000000..284bfc0 --- /dev/null +++ b/HR.LeaveManagement.Application/Models/Identity/RegistrationResponse.cs @@ -0,0 +1,5 @@ +namespace HR.LeaveManagement.Application.Models.Identity; +public class RegistrationResponse +{ + public string UserId { get; set; } +} diff --git a/HR.LeaveManagement.Domain/Common/BaseEntity.cs b/HR.LeaveManagement.Domain/Common/BaseEntity.cs index 59786cf..b00fbc4 100644 --- a/HR.LeaveManagement.Domain/Common/BaseEntity.cs +++ b/HR.LeaveManagement.Domain/Common/BaseEntity.cs @@ -3,5 +3,7 @@ public abstract class BaseEntity { public int Id { get; set; } public DateTime? DateCreated { get; set; } + public string? CreatedBy { get; set; } public DateTime? DateModified { get; set; } + public string? ModifiedBy { get; set; } } diff --git a/HR.LeaveManagement.Domain/LeaveAllocation.cs b/HR.LeaveManagement.Domain/LeaveAllocation.cs index c2e79f7..22954bf 100644 --- a/HR.LeaveManagement.Domain/LeaveAllocation.cs +++ b/HR.LeaveManagement.Domain/LeaveAllocation.cs @@ -8,4 +8,5 @@ public class LeaveAllocation : BaseEntity public LeaveType? LeaveType { get; set; } public int LeaveTypeId { get; set; } public int Period { get; set; } + public string EmployeeId { get; set; } = string.Empty; } diff --git a/HR.LeaveManagement.Domain/LeaveRequest.cs b/HR.LeaveManagement.Domain/LeaveRequest.cs index c0721d5..8ee4404 100644 --- a/HR.LeaveManagement.Domain/LeaveRequest.cs +++ b/HR.LeaveManagement.Domain/LeaveRequest.cs @@ -11,6 +11,6 @@ public class LeaveRequest : BaseEntity public DateTime DateRequested { get; set; } public string? RequestComments { get; set; } public bool? Approved { get; set; } - public bool Canceled { get; set; } + public bool Cancelled { get; set; } public string RequestingEmployeeId { get; set; } = string.Empty; } \ No newline at end of file