initial commit

This commit is contained in:
2026-07-21 14:22:06 +07:00
commit 2d7959f202
572 changed files with 45295 additions and 0 deletions
@@ -0,0 +1,65 @@
using Indotalent.ConfigBackEnd.Extensions;
using Indotalent.Infrastructure.Database;
using MediatR;
namespace Indotalent.Features.Leave.LeaveRequest.Cqrs;
public class CreateLeaveRequestRequest
{
public string EmployeeId { get; set; } = string.Empty;
public string LeaveCategoryId { get; set; } = string.Empty;
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public double Days { get; set; }
public string Reason { get; set; } = string.Empty;
public string Status { get; set; } = "Pending";
public string? AttachmentPath { get; set; }
}
public class CreateLeaveRequestResponse
{
public string? Id { get; set; }
public string? AutoNumber { get; set; }
}
public record CreateLeaveRequestCommand(CreateLeaveRequestRequest Data) : IRequest<CreateLeaveRequestResponse>;
public class CreateLeaveRequestHandler : IRequestHandler<CreateLeaveRequestCommand, CreateLeaveRequestResponse>
{
private readonly AppDbContext _context;
public CreateLeaveRequestHandler(AppDbContext context) => _context = context;
public async Task<CreateLeaveRequestResponse> Handle(CreateLeaveRequestCommand request, CancellationToken cancellationToken)
{
var entityName = nameof(Data.Entities.LeaveRequest);
var autoNo = await _context.GenerateAutoNumberAsync(
entityName: entityName,
prefixTemplate: $"{entityName.ToShortNameConsonant(3)}/{{Year}}/",
ct: cancellationToken
);
var entity = new Data.Entities.LeaveRequest
{
AutoNumber = autoNo,
EmployeeId = request.Data.EmployeeId,
LeaveCategoryId = request.Data.LeaveCategoryId,
StartDate = request.Data.StartDate,
EndDate = request.Data.EndDate,
Days = request.Data.Days,
Reason = request.Data.Reason,
Status = request.Data.Status,
AttachmentPath = request.Data.AttachmentPath
};
_context.LeaveRequest.Add(entity);
await _context.SaveChangesAsync(cancellationToken);
return new CreateLeaveRequestResponse
{
Id = entity.Id,
AutoNumber = entity.AutoNumber
};
}
}
@@ -0,0 +1,15 @@
using FluentValidation;
namespace Indotalent.Features.Leave.LeaveRequest.Cqrs;
public class CreateLeaveRequestValidator : AbstractValidator<CreateLeaveRequestRequest>
{
public CreateLeaveRequestValidator()
{
RuleFor(x => x.EmployeeId).NotEmpty().WithMessage("Employee is required");
RuleFor(x => x.LeaveCategoryId).NotEmpty().WithMessage("Leave Type is required");
RuleFor(x => x.StartDate).NotEmpty();
RuleFor(x => x.EndDate).NotEmpty().GreaterThanOrEqualTo(x => x.StartDate).WithMessage("End Date cannot be before Start Date");
RuleFor(x => x.Reason).NotEmpty().WithMessage("Reason is required");
}
}
@@ -0,0 +1,27 @@
using Indotalent.Infrastructure.Database;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace Indotalent.Features.Leave.LeaveRequest.Cqrs;
public record DeleteLeaveRequestByIdRequest(string Id);
public record DeleteLeaveRequestByIdCommand(DeleteLeaveRequestByIdRequest Data) : IRequest<bool>;
public class DeleteLeaveRequestByIdHandler : IRequestHandler<DeleteLeaveRequestByIdCommand, bool>
{
private readonly AppDbContext _context;
public DeleteLeaveRequestByIdHandler(AppDbContext context) => _context = context;
public async Task<bool> Handle(DeleteLeaveRequestByIdCommand request, CancellationToken cancellationToken)
{
var entity = await _context.LeaveRequest
.FirstOrDefaultAsync(x => x.Id == request.Data.Id, cancellationToken);
if (entity == null) return false;
_context.LeaveRequest.Remove(entity);
return await _context.SaveChangesAsync(cancellationToken) > 0;
}
}
@@ -0,0 +1,35 @@
using Indotalent.Infrastructure.Database;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace Indotalent.Features.Leave.LeaveRequest.Cqrs;
public class EmployeeLeaveReferenceResponse
{
public string? Id { get; set; }
public string? Code { get; set; }
public string? FullName { get; set; }
}
public record GetEmployeeLeaveReferenceQuery() : IRequest<List<EmployeeLeaveReferenceResponse>>;
public class GetEmployeeLeaveReferenceHandler : IRequestHandler<GetEmployeeLeaveReferenceQuery, List<EmployeeLeaveReferenceResponse>>
{
private readonly AppDbContext _context;
public GetEmployeeLeaveReferenceHandler(AppDbContext context) => _context = context;
public async Task<List<EmployeeLeaveReferenceResponse>> Handle(GetEmployeeLeaveReferenceQuery request, CancellationToken cancellationToken)
{
return await _context.Employee
.AsNoTracking()
.Where(x => x.EmployeeStatus == "Active")
.Select(x => new EmployeeLeaveReferenceResponse
{
Id = x.Id,
Code = x.Code,
FullName = $"{x.FirstName} {x.LastName}"
})
.ToListAsync(cancellationToken);
}
}
@@ -0,0 +1,69 @@
using Indotalent.Infrastructure.Database;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace Indotalent.Features.Leave.LeaveRequest.Cqrs;
public class GetLeaveRequestByIdResponse
{
public string? Id { get; set; }
public string? AutoNumber { get; set; }
public string? EmployeeId { get; set; }
public string? EmployeeName { get; set; }
public string? LeaveCategoryId { get; set; }
public string? LeaveType { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public double Days { get; set; }
public string? Reason { get; set; }
public string? Status { get; set; }
public string? AttachmentPath { get; set; }
public string? ApproverId { get; set; }
public DateTime? ActionDate { get; set; }
public string? RejectReason { get; set; }
public DateTimeOffset? CreatedAt { get; set; }
public string? CreatedBy { get; set; }
public DateTimeOffset? UpdatedAt { get; set; }
public string? UpdatedBy { get; set; }
}
public record GetLeaveRequestByIdQuery(string Id) : IRequest<GetLeaveRequestByIdResponse?>;
public class GetLeaveRequestByIdHandler : IRequestHandler<GetLeaveRequestByIdQuery, GetLeaveRequestByIdResponse?>
{
private readonly AppDbContext _context;
public GetLeaveRequestByIdHandler(AppDbContext context) => _context = context;
public async Task<GetLeaveRequestByIdResponse?> Handle(GetLeaveRequestByIdQuery request, CancellationToken cancellationToken)
{
return await _context.LeaveRequest
.AsNoTracking()
.Include(x => x.Employee)
.Include(x => x.LeaveCategory)
.Where(x => x.Id == request.Id)
.Select(x => new GetLeaveRequestByIdResponse
{
Id = x.Id,
AutoNumber = x.AutoNumber,
EmployeeId = x.EmployeeId,
EmployeeName = x.Employee != null ? $"{x.Employee.FirstName} {x.Employee.LastName}" : string.Empty,
LeaveCategoryId = x.LeaveCategoryId,
LeaveType = x.LeaveCategory != null ? x.LeaveCategory.Name : string.Empty,
StartDate = x.StartDate,
EndDate = x.EndDate,
Days = x.Days,
Reason = x.Reason,
Status = x.Status,
AttachmentPath = x.AttachmentPath,
ApproverId = x.ApproverId,
ActionDate = x.ActionDate,
RejectReason = x.RejectReason,
CreatedAt = x.CreatedAt,
CreatedBy = x.CreatedBy,
UpdatedAt = x.UpdatedAt,
UpdatedBy = x.UpdatedBy
})
.FirstOrDefaultAsync(cancellationToken);
}
}
@@ -0,0 +1,53 @@
using Indotalent.ConfigBackEnd.Extensions;
using Indotalent.Infrastructure.Database;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace Indotalent.Features.Leave.LeaveRequest.Cqrs;
public class GetLeaveRequestListResponse
{
public string? Id { get; set; }
public string? AutoNumber { get; set; }
public string? EmployeeId { get; set; }
public string? EmployeeName { get; set; }
public string? LeaveType { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public double Days { get; set; }
public string? Reason { get; set; }
public string? Status { get; set; }
}
public record GetLeaveRequestListQuery() : IRequest<List<GetLeaveRequestListResponse>>;
public class GetLeaveRequestListHandler : IRequestHandler<GetLeaveRequestListQuery, List<GetLeaveRequestListResponse>>
{
private readonly AppDbContext _context;
public GetLeaveRequestListHandler(AppDbContext context) => _context = context;
public async Task<List<GetLeaveRequestListResponse>> Handle(GetLeaveRequestListQuery request, CancellationToken cancellationToken)
{
return await _context.LeaveRequest
.AsNoTracking()
.NotDeletedOnly()
.Include(x => x.Employee)
.Include(x => x.LeaveCategory)
.OrderByDescending(x => x.CreatedAt)
.Select(x => new GetLeaveRequestListResponse
{
Id = x.Id,
AutoNumber = x.AutoNumber,
EmployeeId = x.Employee != null ? x.Employee.Code : string.Empty,
EmployeeName = x.Employee != null ? $"{x.Employee.FirstName} {x.Employee.LastName}" : string.Empty,
LeaveType = x.LeaveCategory != null ? x.LeaveCategory.Name : string.Empty,
StartDate = x.StartDate,
EndDate = x.EndDate,
Days = x.Days,
Reason = x.Reason,
Status = x.Status
})
.ToListAsync(cancellationToken);
}
}
@@ -0,0 +1,65 @@
using Indotalent.Infrastructure.Database;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace Indotalent.Features.Leave.LeaveRequest.Cqrs;
public class UpdateLeaveRequestRequest : CreateLeaveRequestRequest
{
public string? Id { get; set; }
public string? EmployeeName { get; set; }
public string? LeaveType { get; set; }
public DateTimeOffset? CreatedAt { get; set; }
public string? CreatedBy { get; set; }
public DateTimeOffset? UpdatedAt { get; set; }
public string? UpdatedBy { get; set; }
public string? RejectReason { get; set; }
public string? ApproverId { get; set; }
public DateTime? ActionDate { get; set; }
}
public class UpdateLeaveRequestResponse
{
public string? Id { get; set; }
public bool Success { get; set; }
}
public record UpdateLeaveRequestCommand(UpdateLeaveRequestRequest Data) : IRequest<UpdateLeaveRequestResponse>;
public class UpdateLeaveRequestHandler : IRequestHandler<UpdateLeaveRequestCommand, UpdateLeaveRequestResponse>
{
private readonly AppDbContext _context;
public UpdateLeaveRequestHandler(AppDbContext context) => _context = context;
public async Task<UpdateLeaveRequestResponse> Handle(UpdateLeaveRequestCommand request, CancellationToken cancellationToken)
{
var entity = await _context.LeaveRequest
.FirstOrDefaultAsync(x => x.Id == request.Data.Id, cancellationToken);
if (entity == null)
{
return new UpdateLeaveRequestResponse { Id = request.Data.Id, Success = false };
}
entity.EmployeeId = request.Data.EmployeeId;
entity.LeaveCategoryId = request.Data.LeaveCategoryId;
entity.StartDate = request.Data.StartDate;
entity.EndDate = request.Data.EndDate;
entity.Days = request.Data.Days;
entity.Reason = request.Data.Reason;
entity.Status = request.Data.Status;
entity.AttachmentPath = request.Data.AttachmentPath;
entity.RejectReason = request.Data.RejectReason;
entity.ApproverId = request.Data.ApproverId;
entity.ActionDate = request.Data.ActionDate;
await _context.SaveChangesAsync(cancellationToken);
return new UpdateLeaveRequestResponse
{
Id = entity.Id,
Success = true
};
}
}
@@ -0,0 +1,28 @@
using FluentValidation;
namespace Indotalent.Features.Leave.LeaveRequest.Cqrs;
public class UpdateLeaveRequestValidator : AbstractValidator<UpdateLeaveRequestRequest>
{
public UpdateLeaveRequestValidator()
{
RuleFor(x => x.Id)
.NotEmpty().WithMessage("ID is required for update");
RuleFor(x => x.EmployeeId)
.NotEmpty().WithMessage("Employee is required");
RuleFor(x => x.LeaveCategoryId)
.NotEmpty().WithMessage("Leave Type is required");
RuleFor(x => x.StartDate)
.NotEmpty().WithMessage("Start Date is required");
RuleFor(x => x.EndDate)
.NotEmpty()
.GreaterThanOrEqualTo(x => x.StartDate).WithMessage("End Date cannot be before Start Date");
RuleFor(x => x.Reason)
.NotEmpty().WithMessage("Reason is required");
}
}