ASP.NET Core  

Human Resource Management System in ASP.NET Core MVC – Part 2

Introduction

In the first article, we covered the Admin and Employee (Attendance) modules of the HRM project. Now, in this second article, we’ll continue with the Leave Requests module — enabling employees to apply for leave, and HR/Managers to approve or reject requests. This workflow is critical for any HRMS as it directly impacts payroll, attendance, and employee satisfaction.

Leave Request Model

Define the entity to store leave applications:

csharp

public class LeaveRequest
{
    public int Id { get; set; }
    public int EmployeeId { get; set; }
    public Employee Employee { get; set; }

    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
    public string Reason { get; set; }

    public string Status { get; set; } = "Pending"; // Pending, Approved, Rejected
}

Leave Request Controller

Create an MVC controller to handle leave applications:

csharp

public class LeaveRequestsController : Controller
{
    private readonly ApplicationDbContext _context;
    public LeaveRequestsController(ApplicationDbContext context) => _context = context;

    // List all leave requests
    public async Task<IActionResult> Index()
    {
        var requests = await _context.LeaveRequests
            .Include(l => l.Employee)
            .ToListAsync();
        return View(requests);
    }

    // Apply for leave
    public IActionResult Create()
    {
        ViewBag.Employees = new SelectList(_context.Employees, "Id", "FullName");
        return View();
    }

    [HttpPost]
    public async Task<IActionResult> Create(LeaveRequest request)
    {
        if (ModelState.IsValid)
        {
            _context.Add(request);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
        return View(request);
    }

    // Approve leave
    public async Task<IActionResult> Approve(int id)
    {
        var request = await _context.LeaveRequests.FindAsync(id);
        if (request == null) return NotFound();

        request.Status = "Approved";
        _context.Update(request);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }

    // Reject leave
    public async Task<IActionResult> Reject(int id)
    {
        var request = await _context.LeaveRequests.FindAsync(id);
        if (request == null) return NotFound();

        request.Status = "Rejected";
        _context.Update(request);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }
}

Leave Request Views

Index.cshtml

html

@model IEnumerable<LeaveRequest>

<h2>Leave Requests</h2>
<a asp-action="Create" class="btn btn-primary">Apply for Leave</a>

<table class="table">
    <tr><th>Employee</th><th>Start</th><th>End</th><th>Reason</th><th>Status</th><th>Actions</th></tr>
    @foreach (var l in Model)
    {
        <tr>
            <td>@l.Employee.FullName</td>
            <td>@l.StartDate.ToShortDateString()</td>
            <td>@l.EndDate.ToShortDateString()</td>
            <td>@l.Reason</td>
            <td>@l.Status</td>
            <td>
                @if (l.Status == "Pending")
                {
                    <a asp-action="Approve" asp-route-id="@l.Id">Approve</a> |
                    <a asp-action="Reject" asp-route-id="@l.Id">Reject</a>
                }
            </td>
        </tr>
    }
</table>

Create.cshtml

html

@model LeaveRequest

<h2>Apply for Leave</h2>
<form asp-action="Create">
    <div class="form-group">
        <label>Employee</label>
        <select asp-for="EmployeeId" asp-items="ViewBag.Employees" class="form-control"></select>
    </div>
    <div class="form-group">
        <label>Start Date</label>
        <input asp-for="StartDate" type="date" class="form-control" />
    </div>
    <div class="form-group">
        <label>End Date</label>
        <input asp-for="EndDate" type="date" class="form-control" />
    </div>
    <div class="form-group">
        <label>Reason</label>
        <textarea asp-for="Reason" class="form-control"></textarea>
    </div>
    <button type="submit" class="btn btn-success">Submit</button>
</form>

📸 Screenshot

companycompany1

Payroll Management

Payroll Model

csharp

public class Payroll
{
    public int Id { get; set; }
    public int EmployeeId { get; set; }
    public Employee Employee { get; set; }

    public decimal BasicSalary { get; set; }
    public decimal Allowances { get; set; }
    public decimal Deductions { get; set; }
    public decimal NetSalary => BasicSalary + Allowances - Deductions;

    public DateTime SalaryMonth { get; set; }
}

Payroll Controller

csharp

public class PayrollController : Controller
{
    private readonly ApplicationDbContext _context;
    public PayrollController(ApplicationDbContext context) => _context = context;

    public async Task<IActionResult> Index()
    {
        var payrolls = await _context.Payrolls.Include(p => p.Employee).ToListAsync();
        return View(payrolls);
    }

    public IActionResult Create()
    {
        ViewBag.Employees = new SelectList(_context.Employees, "Id", "FullName");
        return View();
    }

    [HttpPost]
    public async Task<IActionResult> Create(Payroll payroll)
    {
        if (ModelState.IsValid)
        {
            _context.Add(payroll);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
        return View(payroll);
    }
}

Payroll Views

Index.cshtml

html

@model IEnumerable<Payroll>

<h2>Payroll Records</h2>
<a asp-action="Create" class="btn btn-primary">Generate Payroll</a>

<table class="table">
    <tr><th>Employee</th><th>Month</th><th>Net Salary</th></tr>
    @foreach (var p in Model)
    {
        <tr>
            <td>@p.Employee.FullName</td>
            <td>@p.SalaryMonth.ToString("MMMM yyyy")</td>
            <td>@p.NetSalary</td>
        </tr>
    }
</table>

📸 Screenshot : Payroll list page with salary slips.

company

Recruitment Module

Offer Letter Model

csharp

public class OfferLetter
{
    public int Id { get; set; }
    public int EmployeeId { get; set; }
    public Employee Employee { get; set; }

    public string Position { get; set; }
    public decimal Salary { get; set; }
    public DateTime IssueDate { get; set; }
    public string Status { get; set; } = "Issued"; // Issued, Accepted, Rejected
}

Recruitment Controller

csharp

public class RecruitmentController : Controller
{
    private readonly ApplicationDbContext _context;
    public RecruitmentController(ApplicationDbContext context) => _context = context;

    public async Task<IActionResult> Index()
    {
        var offers = await _context.OfferLetters.Include(o => o.Employee).ToListAsync();
        return View(offers);
    }

    public IActionResult Create()
    {
        ViewBag.Employees = new SelectList(_context.Employees, "Id", "FullName");
        return View();
    }

    [HttpPost]
    public async Task<IActionResult> Create(OfferLetter offer)
    {
        if (ModelState.IsValid)
        {
            _context.Add(offer);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
        return View(offer);
    }

    public async Task<IActionResult> Accept(int id)
    {
        var offer = await _context.OfferLetters.FindAsync(id);
        if (offer == null) return NotFound();

        offer.Status = "Accepted";
        _context.Update(offer);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }

    public async Task<IActionResult> Reject(int id)
    {
        var offer = await _context.OfferLetters.FindAsync(id);
        if (offer == null) return NotFound();

        offer.Status = "Rejected";
        _context.Update(offer);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }
}

Recruitment Views

Index.cshtml

html

@model IEnumerable<OfferLetter>

<h2>Offer Letters</h2>
<a asp-action="Create" class="btn btn-primary">Generate Offer Letter</a>

<table class="table">
    <tr><th>Employee</th><th>Position</th><th>Salary</th><th>Status</th><th>Actions</th></tr>
    @foreach (var o in Model)
    {
        <tr>
            <td>@o.Employee.FullName</td>
            <td>@o.Position</td>
            <td>@o.Salary</td>
            <td>@o.Status</td>
            <td>
                @if (o.Status == "Issued")
                {
                    <a asp-action="Accept" asp-route-id="@o.Id">Accept</a> |
                    <a asp-action="Reject" asp-route-id="@o.Id">Reject</a>
                }
            </td>
        </tr>
    }
</table>

📸 Screenshot Placeholder: Offer letter list page with Accept/Reject options.

Summary

  • Leave Request Model stores employee leave applications.

  • Controller provides CRUD + approval/rejection actions.

  • Views allow employees to apply for leave and HR/Managers to manage requests.

  • Status workflow: Pending → Approved/Rejected.

  • Leave Requests → Employees apply, HR/Managers approve/reject.

  • Payroll Management → Salary calculation, slips, monthly records.

  • Recruitment → Offer letters, acceptance/rejection workflow.