Files
blazor-hrm/Features/Payroll/Payrolls/Cqrs/CreatePayrollProcessHandler.cs
T
2026-07-21 14:08:10 +07:00

142 lines
5.1 KiB
C#

using Indotalent.ConfigBackEnd.Exceptions;
using Indotalent.ConfigBackEnd.Extensions;
using Indotalent.Infrastructure.Database;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace Indotalent.Features.Payroll.Payrolls.Cqrs;
public class CreatePayrollProcessRequest
{
public string? PeriodName { get; set; }
public DateTime? FromDate { get; set; }
public DateTime? ToDate { get; set; }
public string? Description { get; set; }
}
public class CreatePayrollProcessResponse
{
public string? Id { get; set; }
public string? AutoNumber { get; set; }
}
public record CreatePayrollProcessCommand(CreatePayrollProcessRequest Data) : IRequest<CreatePayrollProcessResponse>;
public class CreatePayrollProcessHandler : IRequestHandler<CreatePayrollProcessCommand, CreatePayrollProcessResponse>
{
private readonly AppDbContext _context;
public CreatePayrollProcessHandler(AppDbContext context) => _context = context;
public async Task<CreatePayrollProcessResponse> Handle(CreatePayrollProcessCommand request, CancellationToken cancellationToken)
{
var isExists = await _context.PayrollProcess
.AnyAsync(x => x.PeriodName == request.Data.PeriodName, cancellationToken);
if (isExists)
{
throw new AlreadyExistsException("Payroll Period", request.Data.PeriodName ?? string.Empty);
}
var entityName = nameof(Data.Entities.PayrollProcess);
var autoNo = await _context.GenerateAutoNumberAsync(
entityName: entityName,
prefixTemplate: $"PAY/{request.Data.FromDate?.ToString("yyyyMM") ?? DateTime.Now.ToString("yyyyMM")}/",
ct: cancellationToken
);
var payrollProcess = new Data.Entities.PayrollProcess
{
AutoNumber = autoNo,
PeriodName = request.Data.PeriodName,
FromDate = request.Data.FromDate ?? DateTime.Now,
ToDate = request.Data.ToDate ?? DateTime.Now,
Description = request.Data.Description,
Status = "Draft",
ProcessedBy = "System Admin",
ProcessedAt = DateTime.Now
};
var employees = await _context.Employee
.Include(x => x.EmployeeIncome)
.Include(x => x.EmployeeDeduction)
.Where(x => x.EmployeeStatus == "Active")
.ToListAsync(cancellationToken);
decimal grandBasicSalary = 0;
decimal grandTotalIncome = 0;
decimal grandTotalDeduction = 0;
foreach (var emp in employees)
{
var totalIncome = emp.EmployeeIncome?.Sum(x => x.Amount) ?? 0m;
var totalDeduction = emp.EmployeeDeduction?.Sum(x => x.Amount) ?? 0m;
var takeHomePay = emp.BasicSalary + totalIncome - totalDeduction;
var detail = new Data.Entities.PayrollDetail
{
PayrollProcess = payrollProcess,
EmployeeId = emp.Id,
EmployeeCode = emp.Code,
EmployeeName = $"{emp.FirstName} {emp.LastName}",
BasicSalary = emp.BasicSalary,
TotalIncome = totalIncome,
TotalDeduction = totalDeduction,
TakeHomePay = takeHomePay
};
if (emp.EmployeeIncome != null)
{
foreach (var inc in emp.EmployeeIncome)
{
detail.Components.Add(new Data.Entities.PayrollComponent
{
PayrollDetail = detail,
ComponentName = "Income Component",
ComponentType = "Income",
Amount = inc.Amount,
Description = inc.Description
});
}
}
if (emp.EmployeeDeduction != null)
{
foreach (var ded in emp.EmployeeDeduction)
{
detail.Components.Add(new Data.Entities.PayrollComponent
{
PayrollDetail = detail,
ComponentName = "Deduction Component",
ComponentType = "Deduction",
Amount = ded.Amount,
Description = ded.Description
});
}
}
grandBasicSalary += detail.BasicSalary;
grandTotalIncome += totalIncome;
grandTotalDeduction += totalDeduction;
_context.PayrollDetail.Add(detail);
}
payrollProcess.TotalEmployees = employees.Count;
payrollProcess.TotalBasicSalary = grandBasicSalary;
payrollProcess.TotalIncome = grandTotalIncome;
payrollProcess.TotalDeduction = grandTotalDeduction;
payrollProcess.TotalGross = grandBasicSalary + grandTotalIncome;
payrollProcess.TotalTakeHomePay = payrollProcess.TotalGross - grandTotalDeduction;
_context.PayrollProcess.Add(payrollProcess);
await _context.SaveChangesAsync(cancellationToken);
return new CreatePayrollProcessResponse
{
Id = payrollProcess.Id,
AutoNumber = payrollProcess.AutoNumber
};
}
}