Added the RequestedAt and RequestedBy in attendance

This commit is contained in:
ashutosh.nehete 2025-10-10 12:08:53 +05:30
parent 9a8aa4f5ce
commit 522deae8f7
7 changed files with 6475 additions and 61 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,70 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_Requested_In_Attendance_Table : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "ApprovedAt",
table: "Attendes",
type: "datetime(6)",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "RequestedAt",
table: "Attendes",
type: "datetime(6)",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "RequestedById",
table: "Attendes",
type: "char(36)",
nullable: true,
collation: "ascii_general_ci");
migrationBuilder.CreateIndex(
name: "IX_Attendes_RequestedById",
table: "Attendes",
column: "RequestedById");
migrationBuilder.AddForeignKey(
name: "FK_Attendes_Employees_RequestedById",
table: "Attendes",
column: "RequestedById",
principalTable: "Employees",
principalColumn: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Attendes_Employees_RequestedById",
table: "Attendes");
migrationBuilder.DropIndex(
name: "IX_Attendes_RequestedById",
table: "Attendes");
migrationBuilder.DropColumn(
name: "ApprovedAt",
table: "Attendes");
migrationBuilder.DropColumn(
name: "RequestedAt",
table: "Attendes");
migrationBuilder.DropColumn(
name: "RequestedById",
table: "Attendes");
}
}
}

View File

@ -172,6 +172,9 @@ namespace Marco.Pms.DataAccess.Migrations
b.Property<int>("Activity") b.Property<int>("Activity")
.HasColumnType("int"); .HasColumnType("int");
b.Property<DateTime?>("ApprovedAt")
.HasColumnType("datetime(6)");
b.Property<Guid?>("ApprovedById") b.Property<Guid?>("ApprovedById")
.HasColumnType("char(36)"); .HasColumnType("char(36)");
@ -200,6 +203,12 @@ namespace Marco.Pms.DataAccess.Migrations
b.Property<Guid>("ProjectID") b.Property<Guid>("ProjectID")
.HasColumnType("char(36)"); .HasColumnType("char(36)");
b.Property<DateTime?>("RequestedAt")
.HasColumnType("datetime(6)");
b.Property<Guid?>("RequestedById")
.HasColumnType("char(36)");
b.Property<Guid>("TenantId") b.Property<Guid>("TenantId")
.HasColumnType("char(36)"); .HasColumnType("char(36)");
@ -209,6 +218,8 @@ namespace Marco.Pms.DataAccess.Migrations
b.HasIndex("EmployeeId"); b.HasIndex("EmployeeId");
b.HasIndex("RequestedById");
b.HasIndex("TenantId"); b.HasIndex("TenantId");
b.ToTable("Attendes"); b.ToTable("Attendes");
@ -4715,6 +4726,10 @@ namespace Marco.Pms.DataAccess.Migrations
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
b.HasOne("Marco.Pms.Model.Employees.Employee", "RequestedBy")
.WithMany()
.HasForeignKey("RequestedById");
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant") b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
.WithMany() .WithMany()
.HasForeignKey("TenantId") .HasForeignKey("TenantId")
@ -4725,6 +4740,8 @@ namespace Marco.Pms.DataAccess.Migrations
b.Navigation("Employee"); b.Navigation("Employee");
b.Navigation("RequestedBy");
b.Navigation("Tenant"); b.Navigation("Tenant");
}); });

View File

@ -29,5 +29,12 @@ namespace Marco.Pms.Model.AttendanceModule
[ForeignKey("ApprovedById")] [ForeignKey("ApprovedById")]
[ValidateNever] [ValidateNever]
public Employee? Approver { get; set; } public Employee? Approver { get; set; }
public DateTime? RequestedAt { get; set; }
public DateTime? ApprovedAt { get; set; }
public Guid? RequestedById { get; set; }
[ForeignKey("RequestedById")]
[ValidateNever]
public Employee? RequestedBy { get; set; }
} }
} }

View File

@ -1,4 +1,5 @@
using Marco.Pms.Model.Dtos.Attendance; using Marco.Pms.Model.Dtos.Attendance;
using Marco.Pms.Model.ViewModels.Activities;
namespace Marco.Pms.Model.ViewModels.AttendanceVM namespace Marco.Pms.Model.ViewModels.AttendanceVM
{ {
@ -6,15 +7,20 @@ namespace Marco.Pms.Model.ViewModels.AttendanceVM
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public Guid EmployeeId { get; set; } public Guid EmployeeId { get; set; }
public Guid ProjectId { get; set; }
public string? FirstName { get; set; } public string? FirstName { get; set; }
public string? LastName { get; set; } public string? LastName { get; set; }
public string? EmployeeAvatar { get; set; } public string? EmployeeAvatar { get; set; }
public string? OrganizationName { get; set; } public string? OrganizationName { get; set; }
public string? ProjectName { get; set; }
public DateTime? CheckInTime { get; set; } public DateTime? CheckInTime { get; set; }
public DateTime? CheckOutTime { get; set; } public DateTime? CheckOutTime { get; set; }
public DateTime? RequestedAt { get; set; }
public DateTime? ApprovedAt { get; set; }
public string? JobRoleName { get; set; } public string? JobRoleName { get; set; }
public ATTENDANCE_MARK_TYPE Activity { get; set; } public ATTENDANCE_MARK_TYPE Activity { get; set; }
public BasicEmployeeVM? Approver { get; set; }
public BasicEmployeeVM? RequestedBy { get; set; }
public Guid? DocumentId { get; set; } public Guid? DocumentId { get; set; }
public string? ThumbPreSignedUrl { get; set; } public string? ThumbPreSignedUrl { get; set; }
public string? PreSignedUrl { get; set; } public string? PreSignedUrl { get; set; }

View File

@ -1,4 +1,5 @@
using Marco.Pms.DataAccess.Data; using AutoMapper;
using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.AttendanceModule; using Marco.Pms.Model.AttendanceModule;
using Marco.Pms.Model.Dtos.Attendance; using Marco.Pms.Model.Dtos.Attendance;
using Marco.Pms.Model.Employees; using Marco.Pms.Model.Employees;
@ -6,6 +7,7 @@ using Marco.Pms.Model.Entitlements;
using Marco.Pms.Model.Mapper; using Marco.Pms.Model.Mapper;
using Marco.Pms.Model.Projects; using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Utilities; using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.Activities;
using Marco.Pms.Model.ViewModels.AttendanceVM; using Marco.Pms.Model.ViewModels.AttendanceVM;
using Marco.Pms.Services.Hubs; using Marco.Pms.Services.Hubs;
using Marco.Pms.Services.Service; using Marco.Pms.Services.Service;
@ -28,50 +30,41 @@ namespace MarcoBMS.Services.Controllers
public class AttendanceController : ControllerBase public class AttendanceController : ControllerBase
{ {
private readonly ApplicationDbContext _context; private readonly ApplicationDbContext _context;
private readonly EmployeeHelper _employeeHelper; private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly IProjectServices _projectServices;
private readonly UserHelper _userHelper; private readonly UserHelper _userHelper;
private readonly S3UploadService _s3Service;
private readonly PermissionServices _permission; private readonly PermissionServices _permission;
private readonly ILoggingService _logger; private readonly ILoggingService _logger;
private readonly IHubContext<MarcoHub> _signalR;
private readonly IFirebaseService _firebase;
private readonly Guid tenantId; private readonly Guid tenantId;
private readonly IMapper _mapper;
public AttendanceController( public AttendanceController(
ApplicationDbContext context, EmployeeHelper employeeHelper, IProjectServices projectServices, UserHelper userHelper, ApplicationDbContext context,
S3UploadService s3Service, ILoggingService logger, PermissionServices permission, IHubContext<MarcoHub> signalR, IFirebaseService firebase) UserHelper userHelper,
IServiceScopeFactory serviceScopeFactory,
ILoggingService logger,
PermissionServices permission,
IMapper mapper)
{ {
_context = context; _context = context;
_employeeHelper = employeeHelper; _serviceScopeFactory = serviceScopeFactory;
_projectServices = projectServices;
_userHelper = userHelper; _userHelper = userHelper;
_s3Service = s3Service;
_logger = logger; _logger = logger;
_permission = permission; _permission = permission;
_signalR = signalR; _mapper = mapper;
_firebase = firebase;
tenantId = userHelper.GetTenantId(); tenantId = userHelper.GetTenantId();
} }
private Guid GetTenantId()
{
return _userHelper.GetTenantId();
//var tenant = User.FindFirst("TenantId")?.Value;
//return (tenant != null ? Convert.ToInt32(tenant) : 1);
}
[HttpGet("log/attendance/{attendanceid}")] [HttpGet("log/attendance/{attendanceid}")]
public async Task<IActionResult> GetAttendanceLogById(Guid attendanceid) public async Task<IActionResult> GetAttendanceLogById(Guid attendanceid)
{ {
Guid TenantId = GetTenantId(); using var scope = _serviceScopeFactory.CreateScope();
var _s3Service = scope.ServiceProvider.GetRequiredService<S3UploadService>();
List<AttendanceLog> lstAttendance = await _context.AttendanceLogs List<AttendanceLog> lstAttendance = await _context.AttendanceLogs
.Include(a => a.Document) .Include(a => a.Document)
.Include(a => a.Employee) .Include(a => a.Employee)
.Include(a => a.UpdatedByEmployee) .Include(a => a.UpdatedByEmployee)
.Where(c => c.AttendanceId == attendanceid && c.TenantId == TenantId) .Where(c => c.AttendanceId == attendanceid && c.TenantId == tenantId)
.ToListAsync(); .ToListAsync();
List<AttendanceLogVM> attendanceLogVMs = new List<AttendanceLogVM>(); List<AttendanceLogVM> attendanceLogVMs = new List<AttendanceLogVM>();
@ -113,8 +106,16 @@ namespace MarcoBMS.Services.Controllers
} }
List<Attendance> attendances = await _context.Attendes List<Attendance> attendances = await _context.Attendes
.Include(a => a.RequestedBy)
.ThenInclude(e => e!.JobRole)
.Include(a => a.RequestedBy)
.ThenInclude(e => e!.JobRole)
.Where(c => c.EmployeeId == employeeId && c.TenantId == tenantId && c.AttendanceDate.Date >= dateFrom && c.AttendanceDate.Date <= dateTo).ToListAsync(); .Where(c => c.EmployeeId == employeeId && c.TenantId == tenantId && c.AttendanceDate.Date >= dateFrom && c.AttendanceDate.Date <= dateTo).ToListAsync();
var projectIds = attendances.Select(a => a.ProjectID).Distinct().ToList();
var projects = await _context.Projects.Where(p => projectIds.Contains(p.Id) && p.TenantId == tenantId).ToListAsync();
List<EmployeeAttendanceVM> results = new List<EmployeeAttendanceVM>(); List<EmployeeAttendanceVM> results = new List<EmployeeAttendanceVM>();
if (employee != null) if (employee != null)
@ -127,11 +128,17 @@ namespace MarcoBMS.Services.Controllers
EmployeeId = employee.Id, EmployeeId = employee.Id,
FirstName = employee.FirstName, FirstName = employee.FirstName,
LastName = employee.LastName, LastName = employee.LastName,
ProjectId = attendance.ProjectID,
ProjectName = projects.Where(p => p.Id == attendance.ProjectID).Select(p => p.Name).FirstOrDefault(),
CheckInTime = attendance.InTime, CheckInTime = attendance.InTime,
CheckOutTime = attendance.OutTime, CheckOutTime = attendance.OutTime,
JobRoleName = employee.JobRole != null ? employee.JobRole.Name : "", JobRoleName = employee.JobRole != null ? employee.JobRole.Name : "",
Activity = attendance.Activity, Activity = attendance.Activity,
EmployeeAvatar = null EmployeeAvatar = null,
RequestedAt = attendance.RequestedAt,
RequestedBy = _mapper.Map<BasicEmployeeVM>(attendance.RequestedBy),
ApprovedAt = attendance.ApprovedAt,
Approver = _mapper.Map<BasicEmployeeVM>(attendance.Approver)
}; };
results.Add(result); results.Add(result);
} }
@ -151,11 +158,12 @@ namespace MarcoBMS.Services.Controllers
/// <returns></returns> /// <returns></returns>
[HttpGet("project/log")] [HttpGet("project/log")]
public async Task<IActionResult> EmployeeAttendanceByDateRange([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] string? dateFrom = null, [FromQuery] string? dateTo = null) public async Task<IActionResult> EmployeeAttendanceByDateRange([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] string? dateFrom = null, [FromQuery] string? dateTo = null)
{ {
Guid tenantId = GetTenantId(); using var scope = _serviceScopeFactory.CreateScope();
var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); var _projectServices = scope.ServiceProvider.GetRequiredService<IProjectServices>();
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var project = await _context.Projects.AsNoTracking().FirstOrDefaultAsync(p => p.Id == projectId && p.TenantId == tenantId); var project = await _context.Projects.AsNoTracking().FirstOrDefaultAsync(p => p.Id == projectId && p.TenantId == tenantId);
if (project == null) if (project == null)
@ -164,13 +172,13 @@ namespace MarcoBMS.Services.Controllers
return NotFound(ApiResponse<object>.ErrorResponse("Project not found.")); return NotFound(ApiResponse<object>.ErrorResponse("Project not found."));
} }
var hasTeamAttendancePermission = await _permission.HasPermission(PermissionsMaster.TeamAttendance, LoggedInEmployee.Id); var hasTeamAttendancePermission = await _permission.HasPermission(PermissionsMaster.TeamAttendance, loggedInEmployee.Id);
var hasSelfAttendancePermission = await _permission.HasPermission(PermissionsMaster.SelfAttendance, LoggedInEmployee.Id); var hasSelfAttendancePermission = await _permission.HasPermission(PermissionsMaster.SelfAttendance, loggedInEmployee.Id);
var hasProjectPermission = await _permission.HasProjectPermission(LoggedInEmployee, projectId); var hasProjectPermission = await _permission.HasProjectPermission(loggedInEmployee, projectId);
if (!hasProjectPermission) if (!hasProjectPermission)
{ {
_logger.LogWarning("Employee {EmployeeId} tries to access attendance of project {ProjectId}, but don't have access", LoggedInEmployee.Id, projectId); _logger.LogWarning("Employee {EmployeeId} tries to access attendance of project {ProjectId}, but don't have access", loggedInEmployee.Id, projectId);
return Unauthorized(ApiResponse<object>.ErrorResponse("Unauthorized access", "Unauthorized access", 404)); return Unauthorized(ApiResponse<object>.ErrorResponse("Unauthorized access", "Unauthorized access", 404));
} }
@ -203,7 +211,13 @@ namespace MarcoBMS.Services.Controllers
if (hasTeamAttendancePermission) if (hasTeamAttendancePermission)
{ {
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<Attendance> lstAttendance = await _context.Attendes
.Include(a => a.RequestedBy)
.ThenInclude(e => e!.JobRole)
.Include(a => a.Approver)
.ThenInclude(e => e!.JobRole)
.Where(c => c.ProjectID == projectId && c.AttendanceDate.Date >= fromDate.Date && c.AttendanceDate.Date <= toDate.Date && c.TenantId == tenantId)
.ToListAsync();
List<ProjectAllocation> projectteam = await _projectServices.GetTeamByProject(tenantId, projectId, organizationId, true); List<ProjectAllocation> projectteam = await _projectServices.GetTeamByProject(tenantId, projectId, organizationId, true);
@ -215,7 +229,11 @@ namespace MarcoBMS.Services.Controllers
Id = attendance.Id, Id = attendance.Id,
CheckInTime = attendance.InTime, CheckInTime = attendance.InTime,
CheckOutTime = attendance.OutTime, CheckOutTime = attendance.OutTime,
Activity = attendance.Activity Activity = attendance.Activity,
ApprovedAt = attendance.ApprovedAt,
Approver = _mapper.Map<BasicEmployeeVM>(attendance.Approver),
RequestedAt = attendance.RequestedAt,
RequestedBy = _mapper.Map<BasicEmployeeVM>(attendance.RequestedBy)
}; };
teamMember = projectteam.Find(x => x.EmployeeId == attendance.EmployeeId); teamMember = projectteam.Find(x => x.EmployeeId == attendance.EmployeeId);
if (teamMember != null) if (teamMember != null)
@ -228,6 +246,8 @@ namespace MarcoBMS.Services.Controllers
result1.LastName = teamMember.Employee.LastName; result1.LastName = teamMember.Employee.LastName;
result1.JobRoleName = teamMember.Employee.JobRole != null ? teamMember.Employee.JobRole.Name : null; result1.JobRoleName = teamMember.Employee.JobRole != null ? teamMember.Employee.JobRole.Name : null;
result1.OrganizationName = teamMember.Employee.Organization?.Name; result1.OrganizationName = teamMember.Employee.Organization?.Name;
result1.ProjectId = projectId;
result1.ProjectName = teamMember.Project?.Name;
} }
else else
{ {
@ -245,13 +265,23 @@ namespace MarcoBMS.Services.Controllers
else if (hasSelfAttendancePermission) else if (hasSelfAttendancePermission)
{ {
List<Attendance> lstAttendances = await _context.Attendes 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) .Include(a => a.RequestedBy)
.ThenInclude(e => e!.JobRole)
.Include(a => a.Approver)
.ThenInclude(e => e!.JobRole)
.Where(c => c.ProjectID == projectId && c.EmployeeId == loggedInEmployee.Id && c.AttendanceDate.Date >= fromDate.Date &&
c.AttendanceDate.Date <= toDate.Date && c.TenantId == tenantId)
.ToListAsync(); .ToListAsync();
var projectAllocationQuery = _context.ProjectAllocations var projectAllocationQuery = _context.ProjectAllocations
.Include(pa => pa.Project)
.Include(pa => pa.Employee) .Include(pa => pa.Employee)
.ThenInclude(e => e!.Organization) .ThenInclude(e => e!.Organization)
.Where(pa => pa.ProjectId == projectId && pa.EmployeeId == LoggedInEmployee.Id && pa.TenantId == tenantId && pa.IsActive); .Include(pa => pa.Employee)
.ThenInclude(e => e!.JobRole)
.Where(pa => pa.EmployeeId == loggedInEmployee.Id && pa.TenantId == tenantId && pa.IsActive &&
pa.ProjectId == projectId && pa.Project != null &&
pa.Employee != null && pa.Employee.Organization != null && pa.Employee.JobRole != null);
if (organizationId.HasValue) if (organizationId.HasValue)
{ {
@ -273,9 +303,15 @@ namespace MarcoBMS.Services.Controllers
LastName = projectAllocation.Employee?.LastName, LastName = projectAllocation.Employee?.LastName,
JobRoleName = projectAllocation.Employee?.JobRole?.Name, JobRoleName = projectAllocation.Employee?.JobRole?.Name,
OrganizationName = projectAllocation.Employee?.Organization?.Name, OrganizationName = projectAllocation.Employee?.Organization?.Name,
ProjectId = attendance.ProjectID,
ProjectName = projectAllocation.Project?.Name,
CheckInTime = attendance.InTime, CheckInTime = attendance.InTime,
CheckOutTime = attendance.OutTime, CheckOutTime = attendance.OutTime,
Activity = attendance.Activity Activity = attendance.Activity,
ApprovedAt = attendance.ApprovedAt,
Approver = _mapper.Map<BasicEmployeeVM>(attendance.Approver),
RequestedAt = attendance.RequestedAt,
RequestedBy = _mapper.Map<BasicEmployeeVM>(attendance.RequestedBy)
}; };
result.Add(result1); result.Add(result1);
} }
@ -297,7 +333,6 @@ namespace MarcoBMS.Services.Controllers
/// <returns>An IActionResult containing a list of employee attendance records or an error response.</returns> /// <returns>An IActionResult containing a list of employee attendance records or an error response.</returns>
public async Task<IActionResult> EmployeeAttendanceByProjectAsync([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool includeInactive, [FromQuery] string? date = null) public async Task<IActionResult> EmployeeAttendanceByProjectAsync([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool includeInactive, [FromQuery] string? date = null)
{ {
var tenantId = GetTenantId();
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
// --- 1. Initial Validation and Permission Checks --- // --- 1. Initial Validation and Permission Checks ---
@ -359,7 +394,9 @@ namespace MarcoBMS.Services.Controllers
[HttpGet("regularize")] [HttpGet("regularize")]
public async Task<IActionResult> GetRequestRegularizeAttendance([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool IncludeInActive) public async Task<IActionResult> GetRequestRegularizeAttendance([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool IncludeInActive)
{ {
Guid TenantId = GetTenantId(); using var scope = _serviceScopeFactory.CreateScope();
var _projectServices = scope.ServiceProvider.GetRequiredService<IProjectServices>();
Employee LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); Employee LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var result = new List<EmployeeAttendanceVM>(); var result = new List<EmployeeAttendanceVM>();
var hasProjectPermission = await _permission.HasProjectPermission(LoggedInEmployee, projectId); var hasProjectPermission = await _permission.HasProjectPermission(LoggedInEmployee, projectId);
@ -370,11 +407,16 @@ namespace MarcoBMS.Services.Controllers
return Unauthorized(ApiResponse<object>.ErrorResponse("Unauthorized access", "Unauthorized access", 404)); return Unauthorized(ApiResponse<object>.ErrorResponse("Unauthorized access", "Unauthorized access", 404));
} }
List<Attendance> lstAttendance = await _context.Attendes.Where(c => c.ProjectID == projectId && c.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE && c.TenantId == TenantId).ToListAsync(); List<Attendance> lstAttendance = await _context.Attendes
.Include(a => a.RequestedBy)
.ThenInclude(e => e!.JobRole)
.Include(a => a.Approver)
.ThenInclude(e => e!.JobRole)
.Where(c => c.ProjectID == projectId && c.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE && c.TenantId == tenantId)
.ToListAsync();
List<ProjectAllocation> projectteam = await _projectServices.GetTeamByProject(TenantId, projectId, organizationId, true); List<ProjectAllocation> projectteam = await _projectServices.GetTeamByProject(tenantId, projectId, organizationId, true);
var idList = projectteam.Select(p => p.EmployeeId).ToList(); var idList = projectteam.Select(p => p.EmployeeId).ToList();
var jobRole = await _context.JobRoles.ToListAsync();
foreach (Attendance attende in lstAttendance) foreach (Attendance attende in lstAttendance)
{ {
@ -392,7 +434,13 @@ namespace MarcoBMS.Services.Controllers
FirstName = teamMember.Employee.FirstName, FirstName = teamMember.Employee.FirstName,
LastName = teamMember.Employee.LastName, LastName = teamMember.Employee.LastName,
JobRoleName = teamMember.Employee.JobRole.Name, JobRoleName = teamMember.Employee.JobRole.Name,
OrganizationName = teamMember.Employee.Organization?.Name OrganizationName = teamMember.Employee.Organization?.Name,
ProjectId = projectId,
ProjectName = teamMember.Project?.Name,
ApprovedAt = attende.ApprovedAt,
Approver = _mapper.Map<BasicEmployeeVM>(attende.Approver),
RequestedAt = attende.RequestedAt,
RequestedBy = _mapper.Map<BasicEmployeeVM>(attende.RequestedBy)
}; };
result.Add(result1); result.Add(result1);
} }
@ -422,13 +470,17 @@ namespace MarcoBMS.Services.Controllers
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
} }
Guid TenantId = GetTenantId(); using var scope = _serviceScopeFactory.CreateScope();
var currentEmployee = await _userHelper.GetCurrentEmployeeAsync(); var _signalR = scope.ServiceProvider.GetRequiredService<IHubContext<MarcoHub>>();
var _employeeHelper = scope.ServiceProvider.GetRequiredService<EmployeeHelper>();
var _firebase = scope.ServiceProvider.GetRequiredService<IFirebaseService>();
var currentEmployee = await _userHelper.GetCurrentEmployeeAsync();
using var transaction = await _context.Database.BeginTransactionAsync(); using var transaction = await _context.Database.BeginTransactionAsync();
try try
{ {
Attendance? attendance = await _context.Attendes.FirstOrDefaultAsync(a => a.Id == recordAttendanceDot.Id && a.TenantId == TenantId); ; Attendance? attendance = await _context.Attendes.FirstOrDefaultAsync(a => a.Id == recordAttendanceDot.Id && a.TenantId == tenantId); ;
if (recordAttendanceDot.MarkTime == null) if (recordAttendanceDot.MarkTime == null)
{ {
@ -456,10 +508,6 @@ namespace MarcoBMS.Services.Controllers
{ {
attendance.IsApproved = true; attendance.IsApproved = true;
attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE; attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE;
//string timeString = "10:30 PM"; // Format: "hh:mm tt"
attendance.OutTime = finalDateTime; attendance.OutTime = finalDateTime;
} }
else if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE) else if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE)
@ -470,6 +518,8 @@ namespace MarcoBMS.Services.Controllers
{ {
attendance.OutTime = finalDateTime; attendance.OutTime = finalDateTime;
attendance.Activity = ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE; attendance.Activity = ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE;
attendance.RequestedById = currentEmployee.Id;
attendance.RequestedAt = DateTime.UtcNow;
} }
else else
{ {
@ -483,12 +533,15 @@ namespace MarcoBMS.Services.Controllers
attendance.IsApproved = true; attendance.IsApproved = true;
attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE; attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE;
attendance.ApprovedById = currentEmployee.Id; attendance.ApprovedById = currentEmployee.Id;
attendance.ApprovedAt = DateTime.UtcNow;
// do nothing // do nothing
} }
else if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT) else if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT)
{ {
attendance.IsApproved = false; attendance.IsApproved = false;
attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT; attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT;
attendance.ApprovedById = currentEmployee.Id;
attendance.ApprovedAt = DateTime.UtcNow;
// do nothing // do nothing
} }
attendance.Date = DateTime.UtcNow; attendance.Date = DateTime.UtcNow;
@ -499,7 +552,7 @@ namespace MarcoBMS.Services.Controllers
else else
{ {
attendance = new Attendance(); attendance = new Attendance();
attendance.TenantId = TenantId; attendance.TenantId = tenantId;
attendance.AttendanceDate = recordAttendanceDot.Date; attendance.AttendanceDate = recordAttendanceDot.Date;
// attendance.Activity = recordAttendanceDot.Action; // attendance.Activity = recordAttendanceDot.Action;
attendance.Comment = recordAttendanceDot.Comment; attendance.Comment = recordAttendanceDot.Comment;
@ -531,7 +584,7 @@ namespace MarcoBMS.Services.Controllers
Latitude = recordAttendanceDot.Latitude, Latitude = recordAttendanceDot.Latitude,
Longitude = recordAttendanceDot.Longitude, Longitude = recordAttendanceDot.Longitude,
TenantId = TenantId, TenantId = tenantId,
UpdatedBy = currentEmployee.Id, UpdatedBy = currentEmployee.Id,
UpdatedOn = recordAttendanceDot.Date UpdatedOn = recordAttendanceDot.Date
}; };
@ -578,7 +631,7 @@ namespace MarcoBMS.Services.Controllers
var name = $"{vm.FirstName} {vm.LastName}"; var name = $"{vm.FirstName} {vm.LastName}";
await _firebase.SendAttendanceMessageAsync(attendance.ProjectID, name, recordAttendanceDot.Action, attendance.EmployeeId, TenantId); await _firebase.SendAttendanceMessageAsync(attendance.ProjectID, name, recordAttendanceDot.Action, attendance.EmployeeId, tenantId);
}); });
@ -614,7 +667,12 @@ namespace MarcoBMS.Services.Controllers
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
} }
Guid tenantId = GetTenantId(); using var scope = _serviceScopeFactory.CreateScope();
var _s3Service = scope.ServiceProvider.GetRequiredService<S3UploadService>();
var _signalR = scope.ServiceProvider.GetRequiredService<IHubContext<MarcoHub>>();
var _employeeHelper = scope.ServiceProvider.GetRequiredService<EmployeeHelper>();
var _firebase = scope.ServiceProvider.GetRequiredService<IFirebaseService>();
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var batchId = Guid.NewGuid(); var batchId = Guid.NewGuid();
@ -678,6 +736,8 @@ namespace MarcoBMS.Services.Controllers
{ {
attendance.OutTime = finalDateTime; attendance.OutTime = finalDateTime;
attendance.Activity = ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE; attendance.Activity = ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE;
attendance.RequestedById = loggedInEmployee.Id;
attendance.RequestedAt = DateTime.UtcNow;
} }
else else
{ {
@ -688,10 +748,14 @@ namespace MarcoBMS.Services.Controllers
case ATTENDANCE_MARK_TYPE.REGULARIZE: case ATTENDANCE_MARK_TYPE.REGULARIZE:
attendance.IsApproved = true; attendance.IsApproved = true;
attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE; attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE;
attendance.ApprovedById = loggedInEmployee.Id;
attendance.ApprovedAt = DateTime.UtcNow;
break; break;
case ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT: case ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT:
attendance.IsApproved = false; attendance.IsApproved = false;
attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT; attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT;
attendance.ApprovedById = loggedInEmployee.Id;
attendance.ApprovedAt = DateTime.UtcNow;
break; break;
} }
@ -824,6 +888,7 @@ namespace MarcoBMS.Services.Controllers
// This single query joins ProjectAllocations with Employees and performs a LEFT JOIN with Attendances. // This single query joins ProjectAllocations with Employees and performs a LEFT JOIN with Attendances.
// This is far more efficient than fetching collections and joining them in memory. // This is far more efficient than fetching collections and joining them in memory.
var query = _context.ProjectAllocations var query = _context.ProjectAllocations
.Include(pa => pa.Project)
.Include(pa => pa.Employee) .Include(pa => pa.Employee)
.ThenInclude(e => e!.Organization) .ThenInclude(e => e!.Organization)
.Include(pa => pa.Employee) .Include(pa => pa.Employee)
@ -840,7 +905,12 @@ namespace MarcoBMS.Services.Controllers
query = query.Where(pa => pa.Employee != null && pa.Employee.OrganizationId == organizationId); query = query.Where(pa => pa.Employee != null && pa.Employee.OrganizationId == organizationId);
} }
List<Attendance> lstAttendance = await _context.Attendes.Where(c => c.ProjectID == projectId && c.AttendanceDate.Date == forDate && c.TenantId == tenantId).ToListAsync(); List<Attendance> lstAttendance = await _context.Attendes
.Include(a => a.RequestedBy)
.ThenInclude(e => e!.JobRole)
.Include(a => a.Approver)
.ThenInclude(e => e!.JobRole)
.Where(c => c.ProjectID == projectId && c.AttendanceDate.Date == forDate && c.TenantId == tenantId).ToListAsync();
var teamAttendance = await query var teamAttendance = await query
.AsNoTracking() .AsNoTracking()
@ -857,11 +927,10 @@ namespace MarcoBMS.Services.Controllers
LastName = teamMember.Employee?.LastName, LastName = teamMember.Employee?.LastName,
OrganizationName = teamMember.Employee?.Organization?.Name, OrganizationName = teamMember.Employee?.Organization?.Name,
JobRoleName = teamMember.Employee?.JobRole?.Name, JobRoleName = teamMember.Employee?.JobRole?.Name,
ProjectId = projectId,
ProjectName = teamMember.Project?.Name
}; };
//var member = emp.Where(e => e.Id == teamMember.EmployeeId);
var attendance = lstAttendance.Find(x => x.EmployeeId == teamMember.EmployeeId) ?? new Attendance(); var attendance = lstAttendance.Find(x => x.EmployeeId == teamMember.EmployeeId) ?? new Attendance();
if (attendance != null) if (attendance != null)
{ {
@ -869,6 +938,10 @@ namespace MarcoBMS.Services.Controllers
result1.CheckInTime = attendance.InTime; result1.CheckInTime = attendance.InTime;
result1.CheckOutTime = attendance.OutTime; result1.CheckOutTime = attendance.OutTime;
result1.Activity = attendance.Activity; result1.Activity = attendance.Activity;
result1.ApprovedAt = attendance.ApprovedAt;
result1.Approver = _mapper.Map<BasicEmployeeVM>(attendance.Approver);
result1.RequestedAt = attendance.RequestedAt;
result1.RequestedBy = _mapper.Map<BasicEmployeeVM>(attendance.RequestedBy);
} }
return result1; return result1;
}) })
@ -887,12 +960,21 @@ namespace MarcoBMS.Services.Controllers
// This query fetches the employee's project allocation and their attendance in a single trip. // This query fetches the employee's project allocation and their attendance in a single trip.
Attendance lstAttendance = await _context.Attendes Attendance lstAttendance = await _context.Attendes
.Include(a => a.RequestedBy)
.ThenInclude(e => e!.JobRole)
.Include(a => a.Approver)
.ThenInclude(e => e!.JobRole)
.FirstOrDefaultAsync(c => c.ProjectID == projectId && c.EmployeeId == employeeId && c.AttendanceDate.Date == forDate && c.TenantId == tenantId) ?? new Attendance(); .FirstOrDefaultAsync(c => c.ProjectID == projectId && c.EmployeeId == employeeId && c.AttendanceDate.Date == forDate && c.TenantId == tenantId) ?? new Attendance();
var projectAllocationQuery = _context.ProjectAllocations var projectAllocationQuery = _context.ProjectAllocations
.Include(pa => pa.Project)
.Include(pa => pa.Employee) .Include(pa => pa.Employee)
.ThenInclude(e => e!.Organization) .ThenInclude(e => e!.Organization)
.Where(pa => pa.ProjectId == projectId && pa.EmployeeId == employeeId && pa.TenantId == tenantId && pa.IsActive); .Include(pa => pa.Employee)
.ThenInclude(e => e!.JobRole)
.Where(pa => pa.EmployeeId == employeeId && pa.TenantId == tenantId && pa.IsActive &&
pa.ProjectId == projectId && pa.Project != null &&
pa.Employee != null && pa.Employee.Organization != null && pa.Employee.JobRole != null);
if (organizationId.HasValue) if (organizationId.HasValue)
{ {
@ -912,9 +994,15 @@ namespace MarcoBMS.Services.Controllers
OrganizationName = projectAllocation.Employee?.Organization?.Name, OrganizationName = projectAllocation.Employee?.Organization?.Name,
LastName = projectAllocation.Employee?.LastName, LastName = projectAllocation.Employee?.LastName,
JobRoleName = projectAllocation.Employee?.JobRole?.Name, JobRoleName = projectAllocation.Employee?.JobRole?.Name,
ProjectId = projectId,
ProjectName = projectAllocation.Project?.Name,
CheckInTime = lstAttendance.InTime, CheckInTime = lstAttendance.InTime,
CheckOutTime = lstAttendance.OutTime, CheckOutTime = lstAttendance.OutTime,
Activity = lstAttendance.Activity Activity = lstAttendance.Activity,
ApprovedAt = lstAttendance.ApprovedAt,
Approver = _mapper.Map<BasicEmployeeVM>(lstAttendance.Approver),
RequestedAt = lstAttendance.RequestedAt,
RequestedBy = _mapper.Map<BasicEmployeeVM>(lstAttendance.RequestedBy)
}; };
result.Add(result1); result.Add(result1);
} }

View File

@ -9,7 +9,7 @@
"Title": "Dev" "Title": "Dev"
}, },
"ConnectionStrings": { "ConnectionStrings": {
"DefaultConnectionString": "Server=147.93.98.152;User ID=devuser;Password=AppUser@123$;Database=MarcoBMSStage" "DefaultConnectionString": "Server=147.93.98.152;User ID=devuser;Password=AppUser@123$;Database=MarcoBMS1"
}, },
"SmtpSettings": { "SmtpSettings": {
"SmtpServer": "smtp.gmail.com", "SmtpServer": "smtp.gmail.com",