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; public class CreatePayrollProcessHandler : IRequestHandler { private readonly AppDbContext _context; public CreatePayrollProcessHandler(AppDbContext context) => _context = context; public async Task 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 }; } }