Added a new feature permission for self-attendance and enforced this permission in the attendance module.

This commit is contained in:
ashutosh.nehete 2025-06-04 16:55:41 +05:30
parent 357d615d1b
commit 0467a825ad
7 changed files with 2791 additions and 65 deletions

View File

@ -496,8 +496,9 @@ namespace Marco.Pms.DataAccess.Data
new FeaturePermission { Id = new Guid("fbd213e0-0250-46f1-9f5f-4b2a1e6e76a3"), FeatureId = new Guid("81ab8a87-8ccd-4015-a917-0627cee6a100"), IsEnabled = true, Name = "Assign Roles", Description = "Grants a user the authority to manage employee application roles, enabling them to assign or revoke access privileges within the system." }, new FeaturePermission { Id = new Guid("fbd213e0-0250-46f1-9f5f-4b2a1e6e76a3"), FeatureId = new Guid("81ab8a87-8ccd-4015-a917-0627cee6a100"), IsEnabled = true, Name = "Assign Roles", Description = "Grants a user the authority to manage employee application roles, enabling them to assign or revoke access privileges within the system." },
new FeaturePermission { Id = new Guid("915e6bff-65f6-4e3f-aea8-3fd217d3ea9e"), FeatureId = new Guid("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"), IsEnabled = true, Name = "Perform Attendance ", Description = "Grants a user the ability to record their own work hours or presence within the system. This typically involves checking in and checking out, logging break times, and potentially viewing their own attendance history." }, new FeaturePermission { Id = new Guid("915e6bff-65f6-4e3f-aea8-3fd217d3ea9e"), FeatureId = new Guid("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"), IsEnabled = true, Name = "Team Attendance ", Description = "Team Attendance refers to tracking and managing the attendance of all team members collectively, often monitored by a team lead or manager." },
new FeaturePermission { Id = new Guid("57802c4a-00aa-4a1f-a048-fd2f70dd44b6"), FeatureId = new Guid("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"), IsEnabled = true, Name = "Regularize Attendance", Description = "Grants a user the authority to approve requests from employees to adjust or correct their recorded attendance. This typically involves reviewing the reason for the regularization, verifying any supporting documentation, and then officially accepting the changes to the employee's attendance records" }, new FeaturePermission { Id = new Guid("57802c4a-00aa-4a1f-a048-fd2f70dd44b6"), FeatureId = new Guid("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"), IsEnabled = true, Name = "Regularize Attendance", Description = "Grants a user the authority to approve requests from employees to adjust or correct their recorded attendance. This typically involves reviewing the reason for the regularization, verifying any supporting documentation, and then officially accepting the changes to the employee's attendance records" },
new FeaturePermission { Id = new Guid("ccb0589f-712b-43de-92ed-5b6088e7dc4e"), FeatureId = new Guid("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"), IsEnabled = true, Name = "Self Attendance", Description = "Team Attendance refers to tracking and managing the attendance of all team members collectively, often monitored by a team lead or manager." },
new FeaturePermission { Id = new Guid("5ffbafe0-7ab0-48b1-bb50-c1bf76b65f9d"), FeatureId = new Guid("be3b3afc-6ccf-4566-b9b6-aafcb65546be"), IsEnabled = true, Name = "View Masters", Description = "Grants a user read-only access to foundational or reference data within the system. \"Masters\" typically refer to predefined lists, categories, or templates that are used throughout the application to standardize information and maintain consistency" }, new FeaturePermission { Id = new Guid("5ffbafe0-7ab0-48b1-bb50-c1bf76b65f9d"), FeatureId = new Guid("be3b3afc-6ccf-4566-b9b6-aafcb65546be"), IsEnabled = true, Name = "View Masters", Description = "Grants a user read-only access to foundational or reference data within the system. \"Masters\" typically refer to predefined lists, categories, or templates that are used throughout the application to standardize information and maintain consistency" },
new FeaturePermission { Id = new Guid("588a8824-f924-4955-82d8-fc51956cf323"), FeatureId = new Guid("be3b3afc-6ccf-4566-b9b6-aafcb65546be"), IsEnabled = true, Name = "Manage Masters", Description = "Grants a user the authority to create, modify, and delete foundational or reference data within the system. These \"masters\" are typically the core lists, categories, and configurations that other data and functionalities rely upon, such as departments, job titles, product categories" } new FeaturePermission { Id = new Guid("588a8824-f924-4955-82d8-fc51956cf323"), FeatureId = new Guid("be3b3afc-6ccf-4566-b9b6-aafcb65546be"), IsEnabled = true, Name = "Manage Masters", Description = "Grants a user the authority to create, modify, and delete foundational or reference data within the system. These \"masters\" are typically the core lists, categories, and configurations that other data and functionalities rely upon, such as departments, job titles, product categories" }

View File

@ -0,0 +1,43 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_Self_Attendance_Feature_Permission : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.UpdateData(
table: "FeaturePermissions",
keyColumn: "Id",
keyValue: new Guid("915e6bff-65f6-4e3f-aea8-3fd217d3ea9e"),
columns: new[] { "Description", "Name" },
values: new object[] { "Team Attendance refers to tracking and managing the attendance of all team members collectively, often monitored by a team lead or manager.", "Team Attendance " });
migrationBuilder.InsertData(
table: "FeaturePermissions",
columns: new[] { "Id", "Description", "FeatureId", "IsEnabled", "Name" },
values: new object[] { new Guid("ccb0589f-712b-43de-92ed-5b6088e7dc4e"), "Team Attendance refers to tracking and managing the attendance of all team members collectively, often monitored by a team lead or manager.", new Guid("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"), true, "Self Attendance" });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DeleteData(
table: "FeaturePermissions",
keyColumn: "Id",
keyValue: new Guid("ccb0589f-712b-43de-92ed-5b6088e7dc4e"));
migrationBuilder.UpdateData(
table: "FeaturePermissions",
keyColumn: "Id",
keyValue: new Guid("915e6bff-65f6-4e3f-aea8-3fd217d3ea9e"),
columns: new[] { "Description", "Name" },
values: new object[] { "Grants a user the ability to record their own work hours or presence within the system. This typically involves checking in and checking out, logging break times, and potentially viewing their own attendance history.", "Perform Attendance " });
}
}
}

View File

@ -607,10 +607,10 @@ namespace Marco.Pms.DataAccess.Migrations
new new
{ {
Id = new Guid("915e6bff-65f6-4e3f-aea8-3fd217d3ea9e"), Id = new Guid("915e6bff-65f6-4e3f-aea8-3fd217d3ea9e"),
Description = "Grants a user the ability to record their own work hours or presence within the system. This typically involves checking in and checking out, logging break times, and potentially viewing their own attendance history.", Description = "Team Attendance refers to tracking and managing the attendance of all team members collectively, often monitored by a team lead or manager.",
FeatureId = new Guid("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"), FeatureId = new Guid("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"),
IsEnabled = true, IsEnabled = true,
Name = "Perform Attendance " Name = "Team Attendance "
}, },
new new
{ {
@ -621,6 +621,14 @@ namespace Marco.Pms.DataAccess.Migrations
Name = "Regularize Attendance" Name = "Regularize Attendance"
}, },
new new
{
Id = new Guid("ccb0589f-712b-43de-92ed-5b6088e7dc4e"),
Description = "Team Attendance refers to tracking and managing the attendance of all team members collectively, often monitored by a team lead or manager.",
FeatureId = new Guid("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"),
IsEnabled = true,
Name = "Self Attendance"
},
new
{ {
Id = new Guid("5ffbafe0-7ab0-48b1-bb50-c1bf76b65f9d"), Id = new Guid("5ffbafe0-7ab0-48b1-bb50-c1bf76b65f9d"),
Description = "Grants a user read-only access to foundational or reference data within the system. \"Masters\" typically refer to predefined lists, categories, or templates that are used throughout the application to standardize information and maintain consistency", Description = "Grants a user read-only access to foundational or reference data within the system. \"Masters\" typically refer to predefined lists, categories, or templates that are used throughout the application to standardize information and maintain consistency",

View File

@ -28,11 +28,12 @@ namespace MarcoBMS.Services.Controllers
private readonly ProjectsHelper _projectsHelper; private readonly ProjectsHelper _projectsHelper;
private readonly UserHelper _userHelper; private readonly UserHelper _userHelper;
private readonly S3UploadService _s3Service; private readonly S3UploadService _s3Service;
private readonly PermissionServices _permission;
private readonly ILoggingService _logger; private readonly ILoggingService _logger;
public AttendanceController( public AttendanceController(
ApplicationDbContext context, EmployeeHelper employeeHelper, ProjectsHelper projectsHelper, UserHelper userHelper, S3UploadService s3Service, ILoggingService logger) ApplicationDbContext context, EmployeeHelper employeeHelper, ProjectsHelper projectsHelper, UserHelper userHelper, S3UploadService s3Service, ILoggingService logger, PermissionServices permission)
{ {
_context = context; _context = context;
_employeeHelper = employeeHelper; _employeeHelper = employeeHelper;
@ -40,6 +41,7 @@ namespace MarcoBMS.Services.Controllers
_userHelper = userHelper; _userHelper = userHelper;
_s3Service = s3Service; _s3Service = s3Service;
_logger = logger; _logger = logger;
_permission = permission;
} }
private Guid GetTenantId() private Guid GetTenantId()
@ -132,6 +134,10 @@ namespace MarcoBMS.Services.Controllers
public async Task<IActionResult> EmployeeAttendanceByDateRange([FromQuery] Guid projectId, [FromQuery] string? dateFrom = null, [FromQuery] string? dateTo = null) public async Task<IActionResult> EmployeeAttendanceByDateRange([FromQuery] Guid projectId, [FromQuery] string? dateFrom = null, [FromQuery] string? dateTo = null)
{ {
Guid TenantId = GetTenantId(); Guid TenantId = GetTenantId();
var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var hasTeamAttendancePermission = await _permission.HasPermission(new Guid("915e6bff-65f6-4e3f-aea8-3fd217d3ea9e"), LoggedInEmployee.Id);
var hasSelfAttendancePermission = await _permission.HasPermission(new Guid("ccb0589f-712b-43de-92ed-5b6088e7dc4e"), LoggedInEmployee.Id);
DateTime fromDate = new DateTime(); DateTime fromDate = new DateTime();
DateTime toDate = new DateTime(); DateTime toDate = new DateTime();
@ -159,42 +165,68 @@ namespace MarcoBMS.Services.Controllers
if (dateFrom == null) fromDate = DateTime.UtcNow.Date; if (dateFrom == null) fromDate = DateTime.UtcNow.Date;
if (dateTo == null && dateFrom != null) toDate = fromDate.AddDays(-1); if (dateTo == null && dateFrom != null) toDate = fromDate.AddDays(-1);
if (hasTeamAttendancePermission)
List<Attendance> lstAttendance = await _context.Attendes.Where(c => c.ProjectID == projectId && c.AttendanceDate.Date >= fromDate && c.AttendanceDate.Date <= toDate && c.TenantId == TenantId).ToListAsync();
List<ProjectAllocation> projectteam = await _projectsHelper.GetTeamByProject(TenantId, projectId, true);
var jobRole = await _context.JobRoles.ToListAsync();
foreach (Attendance? attendance in lstAttendance)
{ {
var result1 = new EmployeeAttendanceVM() List<Attendance> lstAttendance = await _context.Attendes.Where(c => c.ProjectID == projectId && c.AttendanceDate.Date >= fromDate.Date && c.AttendanceDate.Date <= toDate.Date && c.TenantId == TenantId).ToListAsync();
List<ProjectAllocation> projectteam = await _projectsHelper.GetTeamByProject(TenantId, projectId, true);
var jobRole = await _context.JobRoles.ToListAsync();
foreach (Attendance? attendance in lstAttendance)
{ {
Id = attendance.Id, var result1 = new EmployeeAttendanceVM()
CheckInTime = attendance.InTime,
CheckOutTime = attendance.OutTime,
Activity = attendance.Activity
};
teamMember = projectteam.Find(x => x.EmployeeId == attendance.EmployeeID);
if (teamMember != null)
{
result1.EmployeeAvatar = null;
result1.EmployeeId = teamMember.EmployeeId;
if (teamMember.Employee != null)
{ {
result1.FirstName = teamMember.Employee.FirstName; Id = attendance.Id,
result1.LastName = teamMember.Employee.LastName; CheckInTime = attendance.InTime,
result1.JobRoleName = teamMember.Employee.JobRole != null ? teamMember.Employee.JobRole.Name : null; CheckOutTime = attendance.OutTime,
} Activity = attendance.Activity
else };
teamMember = projectteam.Find(x => x.EmployeeId == attendance.EmployeeID);
if (teamMember != null)
{ {
result1.FirstName = null; result1.EmployeeAvatar = null;
result1.LastName = null; result1.EmployeeId = teamMember.EmployeeId;
result1.JobRoleName = null; if (teamMember.Employee != null)
{
result1.FirstName = teamMember.Employee.FirstName;
result1.LastName = teamMember.Employee.LastName;
result1.JobRoleName = teamMember.Employee.JobRole != null ? teamMember.Employee.JobRole.Name : null;
}
else
{
result1.FirstName = null;
result1.LastName = null;
result1.JobRoleName = null;
}
result.Add(result1);
} }
result.Add(result1);
} }
}
else if (hasSelfAttendancePermission)
{
List<Attendance> lstAttendances = await _context.Attendes.Where(c => c.ProjectID == projectId && c.EmployeeID == LoggedInEmployee.Id && c.AttendanceDate.Date >= fromDate.Date && c.AttendanceDate.Date <= toDate.Date && c.TenantId == TenantId).ToListAsync();
ProjectAllocation? projectAllocation = await _context.ProjectAllocations.Include(pa => pa.Employee).FirstOrDefaultAsync(pa => pa.ProjectId == projectId && pa.EmployeeId == LoggedInEmployee.Id && pa.TenantId == TenantId && pa.IsActive);
foreach (var attendance in lstAttendances)
{
if (projectAllocation != null)
{
EmployeeAttendanceVM result1 = new EmployeeAttendanceVM
{
Id = attendance.Id,
EmployeeAvatar = null,
EmployeeId = projectAllocation.EmployeeId,
FirstName = projectAllocation.Employee?.FirstName,
LastName = projectAllocation.Employee?.LastName,
JobRoleName = projectAllocation.Employee?.JobRole?.Name,
CheckInTime = attendance.InTime,
CheckOutTime = attendance.OutTime,
Activity = attendance.Activity
};
result.Add(result1);
}
}
} }
_logger.LogInfo("{count} Attendance records fetched successfully", result.Count); _logger.LogInfo("{count} Attendance records fetched successfully", result.Count);
return Ok(ApiResponse<object>.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200)); return Ok(ApiResponse<object>.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200));
@ -211,6 +243,10 @@ namespace MarcoBMS.Services.Controllers
public async Task<IActionResult> EmployeeAttendanceByProject([FromQuery] Guid projectId, [FromQuery] bool IncludeInActive, [FromQuery] string? date = null) public async Task<IActionResult> EmployeeAttendanceByProject([FromQuery] Guid projectId, [FromQuery] bool IncludeInActive, [FromQuery] string? date = null)
{ {
Guid TenantId = GetTenantId(); Guid TenantId = GetTenantId();
var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var hasTeamAttendancePermission = await _permission.HasPermission(new Guid("915e6bff-65f6-4e3f-aea8-3fd217d3ea9e"), LoggedInEmployee.Id);
var hasSelfAttendancePermission = await _permission.HasPermission(new Guid("ccb0589f-712b-43de-92ed-5b6088e7dc4e"), LoggedInEmployee.Id);
DateTime forDate = new DateTime(); DateTime forDate = new DateTime();
if (date != null && DateTime.TryParse(date, out forDate) == false) if (date != null && DateTime.TryParse(date, out forDate) == false)
@ -229,49 +265,72 @@ namespace MarcoBMS.Services.Controllers
Attendance? attendance = null; Attendance? attendance = null;
if (date == null) forDate = DateTime.UtcNow.Date; if (date == null) forDate = DateTime.UtcNow.Date;
if (hasTeamAttendancePermission)
List<Attendance> lstAttendance = await _context.Attendes.Where(c => c.ProjectID == projectId && c.AttendanceDate.Date == forDate && c.TenantId == TenantId).ToListAsync();
List<ProjectAllocation> projectteam = await _projectsHelper.GetTeamByProject(TenantId, projectId, IncludeInActive);
var idList = projectteam.Select(p => p.EmployeeId).ToList();
//var emp = await _context.Employees.Where(e => idList.Contains(e.Id)).Include(e => e.JobRole).ToListAsync();
var jobRole = await _context.JobRoles.ToListAsync();
foreach (ProjectAllocation teamMember in projectteam)
{ {
if (teamMember.Employee != null && teamMember.Employee.JobRole != null) List<Attendance> lstAttendance = await _context.Attendes.Where(c => c.ProjectID == projectId && c.AttendanceDate.Date == forDate && c.TenantId == TenantId).ToListAsync();
List<ProjectAllocation> projectteam = await _projectsHelper.GetTeamByProject(TenantId, projectId, IncludeInActive);
var idList = projectteam.Select(p => p.EmployeeId).ToList();
//var emp = await _context.Employees.Where(e => idList.Contains(e.Id)).Include(e => e.JobRole).ToListAsync();
var jobRole = await _context.JobRoles.ToListAsync();
foreach (ProjectAllocation teamMember in projectteam)
{ {
var result1 = new EmployeeAttendanceVM() if (teamMember.Employee != null && teamMember.Employee.JobRole != null)
{ {
EmployeeAvatar = null, var result1 = new EmployeeAttendanceVM()
EmployeeId = teamMember.EmployeeId, {
FirstName = teamMember.Employee.FirstName, EmployeeAvatar = null,
LastName = teamMember.Employee.LastName, EmployeeId = teamMember.EmployeeId,
JobRoleName = teamMember.Employee.JobRole.Name, FirstName = teamMember.Employee.FirstName,
}; LastName = teamMember.Employee.LastName,
JobRoleName = teamMember.Employee.JobRole.Name,
};
//var member = emp.Where(e => e.Id == teamMember.EmployeeId); //var member = emp.Where(e => e.Id == teamMember.EmployeeId);
attendance = lstAttendance.Find(x => x.EmployeeID == teamMember.EmployeeId) ?? new Attendance(); attendance = lstAttendance.Find(x => x.EmployeeID == teamMember.EmployeeId) ?? new Attendance();
if (attendance != null) if (attendance != null)
{ {
result1.Id = attendance.Id; result1.Id = attendance.Id;
result1.CheckInTime = attendance.InTime; result1.CheckInTime = attendance.InTime;
result1.CheckOutTime = attendance.OutTime; result1.CheckOutTime = attendance.OutTime;
result1.Activity = attendance.Activity; result1.Activity = attendance.Activity;
}
result.Add(result1);
} }
}
result.Sort(delegate (EmployeeAttendanceVM x, EmployeeAttendanceVM y)
{
//return x.FirstName.CompareTo(y.FirstName);
return string.Compare(x.FirstName, y.FirstName, StringComparison.Ordinal);
});
}
else if (hasSelfAttendancePermission)
{
Attendance lstAttendance = await _context.Attendes.FirstOrDefaultAsync(c => c.ProjectID == projectId && c.EmployeeID == LoggedInEmployee.Id && c.AttendanceDate.Date == forDate && c.TenantId == TenantId) ?? new Attendance();
ProjectAllocation? projectAllocation = await _context.ProjectAllocations.Include(pa => pa.Employee).FirstOrDefaultAsync(pa => pa.ProjectId == projectId && pa.EmployeeId == LoggedInEmployee.Id && pa.TenantId == TenantId && pa.IsActive);
if (projectAllocation != null)
{
EmployeeAttendanceVM result1 = new EmployeeAttendanceVM
{
Id = lstAttendance.Id,
EmployeeAvatar = null,
EmployeeId = projectAllocation.EmployeeId,
FirstName = projectAllocation.Employee?.FirstName,
LastName = projectAllocation.Employee?.LastName,
JobRoleName = projectAllocation.Employee?.JobRole?.Name,
CheckInTime = lstAttendance.InTime,
CheckOutTime = lstAttendance.OutTime,
Activity = lstAttendance.Activity
};
result.Add(result1); result.Add(result1);
} }
} }
result.Sort(delegate (EmployeeAttendanceVM x, EmployeeAttendanceVM y)
{
//return x.FirstName.CompareTo(y.FirstName);
return string.Compare(x.FirstName, y.FirstName, StringComparison.Ordinal);
});
_logger.LogInfo("{count} Attendance records fetched successfully", result.Count); _logger.LogInfo("{count} Attendance records fetched successfully", result.Count);
return Ok(ApiResponse<object>.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200)); return Ok(ApiResponse<object>.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200));

View File

@ -125,6 +125,7 @@ builder.Services.AddMemoryCache();
//builder.Services.AddScoped<IProjectAllocationRepository, ProjectAllocationRepository>(); //builder.Services.AddScoped<IProjectAllocationRepository, ProjectAllocationRepository>();
builder.Services.AddScoped<RefreshTokenService>(); builder.Services.AddScoped<RefreshTokenService>();
builder.Services.AddScoped<PermissionServices>();
builder.Services.AddScoped<UserHelper>(); builder.Services.AddScoped<UserHelper>();
builder.Services.AddScoped<RolesHelper>(); builder.Services.AddScoped<RolesHelper>();

View File

@ -0,0 +1,25 @@
using Marco.Pms.DataAccess.Data;
using Microsoft.EntityFrameworkCore;
namespace Marco.Pms.Services.Service
{
public class PermissionServices
{
private readonly ApplicationDbContext _context;
public PermissionServices(ApplicationDbContext context)
{
_context = context;
}
public async Task<bool> HasPermission(Guid featurePermissionId, Guid employeeId)
{
var hasPermission = await _context.EmployeeRoleMappings
.Where(er => er.EmployeeId == employeeId)
.Select(er => er.RoleId)
.Distinct()
.AnyAsync(roleId => _context.RolePermissionMappings
.Any(rp => rp.FeaturePermissionId == featurePermissionId && rp.ApplicationRoleId == roleId));
return hasPermission;
}
}
}