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
![company]()
![company1]()
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.