adde functionality to delete workItems from cache

This commit is contained in:
ashutosh.nehete 2025-07-15 12:44:38 +05:30
parent 5de59f0292
commit 73aa1d6181
25 changed files with 444 additions and 231 deletions

View File

@ -90,18 +90,18 @@ namespace MarcoBMS.Services.Controllers
if (dateFrom != null && DateTime.TryParse(dateFrom, out fromDate) == false) if (dateFrom != null && DateTime.TryParse(dateFrom, out fromDate) == false)
{ {
_logger.LogError("User sent Invalid from Date while featching attendance logs"); _logger.LogWarning("User sent Invalid from Date while featching attendance logs");
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Date", "Invalid Date", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Date", "Invalid Date", 400));
} }
if (dateTo != null && DateTime.TryParse(dateTo, out toDate) == false) if (dateTo != null && DateTime.TryParse(dateTo, out toDate) == false)
{ {
_logger.LogError("User sent Invalid to Date while featching attendance logs"); _logger.LogWarning("User sent Invalid to Date while featching attendance logs");
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Date", "Invalid Date", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Date", "Invalid Date", 400));
} }
if (employeeId == Guid.Empty) if (employeeId == Guid.Empty)
{ {
_logger.LogError("The employee Id sent by user is empty"); _logger.LogWarning("The employee Id sent by user is empty");
return BadRequest(ApiResponse<object>.ErrorResponse("Employee ID is required and must not be Empty.", "Employee ID is required and must not be empty.", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Employee ID is required and must not be Empty.", "Employee ID is required and must not be empty.", 400));
} }
List<Attendance> attendances = await _context.Attendes.Where(c => c.EmployeeID == employeeId && c.TenantId == TenantId && c.AttendanceDate.Date >= fromDate && c.AttendanceDate.Date <= toDate).ToListAsync(); List<Attendance> attendances = await _context.Attendes.Where(c => c.EmployeeID == employeeId && c.TenantId == TenantId && c.AttendanceDate.Date >= fromDate && c.AttendanceDate.Date <= toDate).ToListAsync();
@ -161,18 +161,18 @@ namespace MarcoBMS.Services.Controllers
if (dateFrom != null && DateTime.TryParse(dateFrom, out fromDate) == false) if (dateFrom != null && DateTime.TryParse(dateFrom, out fromDate) == false)
{ {
_logger.LogError("User sent Invalid fromDate while featching attendance logs"); _logger.LogWarning("User sent Invalid fromDate while featching attendance logs");
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Date", "Invalid Date", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Date", "Invalid Date", 400));
} }
if (dateTo != null && DateTime.TryParse(dateTo, out toDate) == false) if (dateTo != null && DateTime.TryParse(dateTo, out toDate) == false)
{ {
_logger.LogError("User sent Invalid toDate while featching attendance logs"); _logger.LogWarning("User sent Invalid toDate while featching attendance logs");
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Date", "Invalid Date", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Date", "Invalid Date", 400));
} }
if (projectId == Guid.Empty) if (projectId == Guid.Empty)
{ {
_logger.LogError("The project Id sent by user is less than or equal to zero"); _logger.LogWarning("The project Id sent by user is less than or equal to zero");
return BadRequest(ApiResponse<object>.ErrorResponse("Project ID is required and must be greater than zero.", "Project ID is required and must be greater than zero.", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Project ID is required and must be greater than zero.", "Project ID is required and must be greater than zero.", 400));
} }
@ -276,13 +276,13 @@ namespace MarcoBMS.Services.Controllers
if (date != null && DateTime.TryParse(date, out forDate) == false) if (date != null && DateTime.TryParse(date, out forDate) == false)
{ {
_logger.LogError("User sent Invalid Date while featching attendance logs"); _logger.LogWarning("User sent Invalid Date while featching attendance logs");
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Date", "Invalid Date", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Date", "Invalid Date", 400));
} }
if (projectId == Guid.Empty) if (projectId == Guid.Empty)
{ {
_logger.LogError("The project Id sent by user is less than or equal to zero"); _logger.LogWarning("The project Id sent by user is less than or equal to zero");
return BadRequest(ApiResponse<object>.ErrorResponse("Project ID is required and must be greater than zero.", "Project ID is required and must be greater than zero.", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Project ID is required and must be greater than zero.", "Project ID is required and must be greater than zero.", 400));
} }
@ -425,7 +425,7 @@ namespace MarcoBMS.Services.Controllers
.SelectMany(v => v.Errors) .SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage) .Select(e => e.ErrorMessage)
.ToList(); .ToList();
_logger.LogError("User sent Invalid Date while marking attendance"); _logger.LogWarning("User sent Invalid Date while marking attendance \n {Error}", string.Join(",", errors));
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
} }
@ -439,14 +439,14 @@ namespace MarcoBMS.Services.Controllers
if (recordAttendanceDot.MarkTime == null) if (recordAttendanceDot.MarkTime == null)
{ {
_logger.LogError("User sent Invalid Mark Time while marking attendance"); _logger.LogWarning("User sent Invalid Mark Time while marking attendance");
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Mark Time", "Invalid Mark Time", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Mark Time", "Invalid Mark Time", 400));
} }
DateTime finalDateTime = GetDateFromTimeStamp(recordAttendanceDot.Date, recordAttendanceDot.MarkTime); DateTime finalDateTime = GetDateFromTimeStamp(recordAttendanceDot.Date, recordAttendanceDot.MarkTime);
if (recordAttendanceDot.Comment == null) if (recordAttendanceDot.Comment == null)
{ {
_logger.LogError("User sent Invalid comment while marking attendance"); _logger.LogWarning("User sent Invalid comment while marking attendance");
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Comment", "Invalid Comment", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Comment", "Invalid Comment", 400));
} }
@ -480,7 +480,7 @@ namespace MarcoBMS.Services.Controllers
} }
else else
{ {
_logger.LogError("Employee {EmployeeId} sent regularization request but it check-out time is earlier than check-out"); _logger.LogWarning("Employee {EmployeeId} sent regularization request but it check-out time is earlier than check-out");
return BadRequest(ApiResponse<object>.ErrorResponse("Check-out time must be later than check-in time", "Check-out time must be later than check-in time", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Check-out time must be later than check-in time", "Check-out time must be later than check-in time", 400));
} }
// do nothing // do nothing
@ -585,7 +585,7 @@ namespace MarcoBMS.Services.Controllers
catch (Exception ex) catch (Exception ex)
{ {
await transaction.RollbackAsync(); // Rollback on failure await transaction.RollbackAsync(); // Rollback on failure
_logger.LogError("{Error} while marking attendance", ex.Message); _logger.LogError(ex, "An Error occured while marking attendance");
var response = new var response = new
{ {
message = ex.Message, message = ex.Message,
@ -604,7 +604,7 @@ namespace MarcoBMS.Services.Controllers
if (!ModelState.IsValid) if (!ModelState.IsValid)
{ {
var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage).ToList(); var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage).ToList();
_logger.LogError("Invalid attendance model received."); _logger.LogWarning("Invalid attendance model received. \n {Error}", string.Join(",", errors));
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
} }
@ -780,7 +780,7 @@ namespace MarcoBMS.Services.Controllers
catch (Exception ex) catch (Exception ex)
{ {
await transaction.RollbackAsync(); await transaction.RollbackAsync();
_logger.LogError("Error while recording attendance : {Error}", ex.Message); _logger.LogError(ex, "Error while recording attendance");
return BadRequest(ApiResponse<object>.ErrorResponse("Something went wrong", ex.Message, 500)); return BadRequest(ApiResponse<object>.ErrorResponse("Something went wrong", ex.Message, 500));
} }
} }

View File

@ -1,8 +1,4 @@
using System.Net; using Marco.Pms.DataAccess.Data;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.Authentication; using Marco.Pms.Model.Authentication;
using Marco.Pms.Model.Dtos.Authentication; using Marco.Pms.Model.Dtos.Authentication;
using Marco.Pms.Model.Dtos.Util; using Marco.Pms.Model.Dtos.Util;
@ -15,6 +11,10 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Net;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
namespace MarcoBMS.Services.Controllers namespace MarcoBMS.Services.Controllers
{ {
@ -110,7 +110,7 @@ namespace MarcoBMS.Services.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("Unexpected error during login : {Error}", ex.Message); _logger.LogError(ex, "Unexpected error during login");
return StatusCode(500, ApiResponse<object>.ErrorResponse("Unexpected error", ex.Message, 500)); return StatusCode(500, ApiResponse<object>.ErrorResponse("Unexpected error", ex.Message, 500));
} }
} }
@ -270,7 +270,7 @@ namespace MarcoBMS.Services.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("Unexpected error occurred while verifying MPIN : {Error}", ex.Message); _logger.LogError(ex, "Unexpected error occurred while verifying MPIN");
return StatusCode(500, ApiResponse<object>.ErrorResponse("Unexpected error", ex.Message, 500)); return StatusCode(500, ApiResponse<object>.ErrorResponse("Unexpected error", ex.Message, 500));
} }
} }
@ -307,7 +307,7 @@ namespace MarcoBMS.Services.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("Unexpected error during logout : {Error}", ex.Message); _logger.LogError(ex, "Unexpected error during logout");
return StatusCode(500, ApiResponse<object>.ErrorResponse("Unexpected error occurred", ex.Message, 500)); return StatusCode(500, ApiResponse<object>.ErrorResponse("Unexpected error occurred", ex.Message, 500));
} }
} }
@ -351,7 +351,7 @@ namespace MarcoBMS.Services.Controllers
if (string.IsNullOrWhiteSpace(user.UserName)) if (string.IsNullOrWhiteSpace(user.UserName))
{ {
_logger.LogError("Username missing for user ID: {UserId}", user.Id); _logger.LogWarning("Username missing for user ID: {UserId}", user.Id);
return NotFound(ApiResponse<object>.ErrorResponse("Username not found.", "Username not found.", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Username not found.", "Username not found.", 404));
} }
@ -370,7 +370,7 @@ namespace MarcoBMS.Services.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("An unexpected error occurred during token refresh. : {Error}", ex.Message); _logger.LogError(ex, "An unexpected error occurred during token refresh.");
return StatusCode(500, ApiResponse<object>.ErrorResponse("Unexpected error occurred.", ex.Message, 500)); return StatusCode(500, ApiResponse<object>.ErrorResponse("Unexpected error occurred.", ex.Message, 500));
} }
} }
@ -406,7 +406,7 @@ namespace MarcoBMS.Services.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("Error while sending password reset email to: {Error}", ex.Message); _logger.LogError(ex, "Error while sending password reset email to");
return StatusCode(500, ApiResponse<object>.ErrorResponse("Error sending password reset email.", ex.Message, 500)); return StatusCode(500, ApiResponse<object>.ErrorResponse("Error sending password reset email.", ex.Message, 500));
} }
} }
@ -480,7 +480,7 @@ namespace MarcoBMS.Services.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("Error while sending reset password success email to user: {Error}", ex.Message); _logger.LogError(ex, "Error while sending reset password success email to user");
// Continue, do not fail because of email issue // Continue, do not fail because of email issue
} }
@ -547,7 +547,7 @@ namespace MarcoBMS.Services.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("An unexpected error occurred while sending OTP to {Email} : {Error}", generateOTP.Email ?? "", ex.Message); _logger.LogError(ex, "An unexpected error occurred while sending OTP to {Email}", generateOTP.Email ?? "");
return StatusCode(500, ApiResponse<object>.ErrorResponse("An unexpected error occurred.", ex.Message, 500)); return StatusCode(500, ApiResponse<object>.ErrorResponse("An unexpected error occurred.", ex.Message, 500));
} }
} }
@ -638,7 +638,7 @@ namespace MarcoBMS.Services.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("An unexpected error occurred during OTP login for email {Email} : {Error}", verifyOTP.Email ?? string.Empty, ex.Message); _logger.LogError(ex, "An unexpected error occurred during OTP login for email {Email}", verifyOTP.Email ?? string.Empty);
return StatusCode(500, ApiResponse<object>.ErrorResponse("Unexpected error", ex.Message, 500)); return StatusCode(500, ApiResponse<object>.ErrorResponse("Unexpected error", ex.Message, 500));
} }
} }
@ -719,7 +719,7 @@ namespace MarcoBMS.Services.Controllers
if (!result.Succeeded) if (!result.Succeeded)
{ {
var errors = result.Errors.Select(e => e.Description).ToList(); var errors = result.Errors.Select(e => e.Description).ToList();
_logger.LogError("Password reset failed for user {Email}. Errors: {Errors}", changePassword.Email, string.Join("; ", errors)); _logger.LogWarning("Password reset failed for user {Email}. Errors: {Errors}", changePassword.Email, string.Join("; ", errors));
return BadRequest(ApiResponse<object>.ErrorResponse("Failed to change password", errors, 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Failed to change password", errors, 400));
} }
@ -732,7 +732,7 @@ namespace MarcoBMS.Services.Controllers
} }
catch (Exception exp) catch (Exception exp)
{ {
_logger.LogError("An unexpected error occurred while changing password : {Error}", exp.Message); _logger.LogError(exp, "An unexpected error occurred while changing password");
return StatusCode(500, ApiResponse<object>.ErrorResponse("An unexpected error occurred.", exp.Message, 500)); return StatusCode(500, ApiResponse<object>.ErrorResponse("An unexpected error occurred.", exp.Message, 500));
} }
} }
@ -752,7 +752,7 @@ namespace MarcoBMS.Services.Controllers
// Validate employee and MPIN input // Validate employee and MPIN input
if (requestEmployee == null || string.IsNullOrWhiteSpace(generateMPINDto.MPIN) || generateMPINDto.MPIN.Length != 6 || !generateMPINDto.MPIN.All(char.IsDigit)) if (requestEmployee == null || string.IsNullOrWhiteSpace(generateMPINDto.MPIN) || generateMPINDto.MPIN.Length != 6 || !generateMPINDto.MPIN.All(char.IsDigit))
{ {
_logger.LogError("Employee {EmployeeId} provided invalid information to generate MPIN", loggedInEmployee.Id); _logger.LogWarning("Employee {EmployeeId} provided invalid information to generate MPIN", loggedInEmployee.Id);
return BadRequest(ApiResponse<object>.ErrorResponse("Provided invalid information", "Provided invalid information", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Provided invalid information", "Provided invalid information", 400));
} }

View File

@ -221,7 +221,7 @@ namespace Marco.Pms.Services.Controllers
_logger.LogInfo("Number of pending regularization and pending check-out are fetched successfully for employee {EmployeeId}", LoggedInEmployee.Id); _logger.LogInfo("Number of pending regularization and pending check-out are fetched successfully for employee {EmployeeId}", LoggedInEmployee.Id);
return Ok(ApiResponse<object>.SuccessResponse(response, "Pending regularization and pending check-out are fetched successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(response, "Pending regularization and pending check-out are fetched successfully", 200));
} }
_logger.LogError("No attendance entry was found for employee {EmployeeId}", LoggedInEmployee.Id); _logger.LogWarning("No attendance entry was found for employee {EmployeeId}", LoggedInEmployee.Id);
return NotFound(ApiResponse<object>.ErrorResponse("No attendance entry was found for this employee", "No attendance entry was found for this employee", 404)); return NotFound(ApiResponse<object>.ErrorResponse("No attendance entry was found for this employee", "No attendance entry was found for this employee", 404));
} }
@ -235,14 +235,14 @@ namespace Marco.Pms.Services.Controllers
List<ProjectProgressionVM>? projectProgressionVMs = new List<ProjectProgressionVM>(); List<ProjectProgressionVM>? projectProgressionVMs = new List<ProjectProgressionVM>();
if (date != null && DateTime.TryParse(date, out currentDate) == false) if (date != null && DateTime.TryParse(date, out currentDate) == false)
{ {
_logger.LogError($"user send invalid date"); _logger.LogWarning($"user send invalid date");
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid date.", "Invalid date.", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid date.", "Invalid date.", 400));
} }
Project? project = await _context.Projects.FirstOrDefaultAsync(p => p.Id == projectId); Project? project = await _context.Projects.FirstOrDefaultAsync(p => p.Id == projectId);
if (project == null) if (project == null)
{ {
_logger.LogError("Employee {EmployeeId} was attempted to get project attendance for date {Date}, but project not found in database", LoggedInEmployee.Id, currentDate); _logger.LogWarning("Employee {EmployeeId} was attempted to get project attendance for date {Date}, but project not found in database", LoggedInEmployee.Id, currentDate);
return NotFound(ApiResponse<object>.ErrorResponse("Project not found", "Project not found", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Project not found", "Project not found", 404));
} }
List<ProjectAllocation>? projectAllocation = await _context.ProjectAllocations.Where(p => p.ProjectId == projectId && p.IsActive && p.TenantId == tenantId).ToListAsync(); List<ProjectAllocation>? projectAllocation = await _context.ProjectAllocations.Where(p => p.ProjectId == projectId && p.IsActive && p.TenantId == tenantId).ToListAsync();
@ -288,14 +288,14 @@ namespace Marco.Pms.Services.Controllers
DateTime currentDate = DateTime.UtcNow; DateTime currentDate = DateTime.UtcNow;
if (date != null && DateTime.TryParse(date, out currentDate) == false) if (date != null && DateTime.TryParse(date, out currentDate) == false)
{ {
_logger.LogError($"user send invalid date"); _logger.LogWarning($"user send invalid date");
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid date.", "Invalid date.", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid date.", "Invalid date.", 400));
} }
Project? project = await _context.Projects.FirstOrDefaultAsync(p => p.Id == projectId); Project? project = await _context.Projects.FirstOrDefaultAsync(p => p.Id == projectId);
if (project == null) if (project == null)
{ {
_logger.LogError("Employee {EmployeeId} was attempted to get activities performed for date {Date}, but project not found in database", LoggedInEmployee.Id, currentDate); _logger.LogWarning("Employee {EmployeeId} was attempted to get activities performed for date {Date}, but project not found in database", LoggedInEmployee.Id, currentDate);
return NotFound(ApiResponse<object>.ErrorResponse("Project not found", "Project not found", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Project not found", "Project not found", 404));
} }

View File

@ -77,7 +77,7 @@ namespace Marco.Pms.Services.Controllers
.SelectMany(v => v.Errors) .SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage) .Select(e => e.ErrorMessage)
.ToList(); .ToList();
_logger.LogError("User sent Invalid Date while marking attendance"); _logger.LogWarning("User sent Invalid Date while marking attendance");
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
} }
var response = await _directoryHelper.CreateContact(createContact); var response = await _directoryHelper.CreateContact(createContact);
@ -256,7 +256,7 @@ namespace Marco.Pms.Services.Controllers
.SelectMany(v => v.Errors) .SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage) .Select(e => e.ErrorMessage)
.ToList(); .ToList();
_logger.LogError("User sent Invalid Date while marking attendance"); _logger.LogWarning("User sent Invalid Date while marking attendance");
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
} }
var response = await _directoryHelper.CreateBucket(bucketDto); var response = await _directoryHelper.CreateBucket(bucketDto);

View File

@ -382,7 +382,7 @@ namespace MarcoBMS.Services.Controllers
Employee? existingEmployee = await _context.Employees.FirstOrDefaultAsync(e => e.Id == model.Id.Value); Employee? existingEmployee = await _context.Employees.FirstOrDefaultAsync(e => e.Id == model.Id.Value);
if (existingEmployee == null) if (existingEmployee == null)
{ {
_logger.LogError("User tries to update employee {EmployeeId} but not found in database", model.Id); _logger.LogWarning("User tries to update employee {EmployeeId} but not found in database", model.Id);
return NotFound(ApiResponse<object>.ErrorResponse("Employee not found", "Employee not found", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Employee not found", "Employee not found", 404));
} }
byte[]? imageBytes = null; byte[]? imageBytes = null;
@ -495,7 +495,7 @@ namespace MarcoBMS.Services.Controllers
} }
else else
{ {
_logger.LogError("Employee with ID {EmploueeId} not found in database", id); _logger.LogWarning("Employee with ID {EmploueeId} not found in database", id);
} }
return Ok(ApiResponse<object>.SuccessResponse(new { }, "Employee Suspended successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(new { }, "Employee Suspended successfully", 200));
} }

View File

@ -44,7 +44,7 @@ namespace Marco.Pms.Services.Controllers
.SelectMany(v => v.Errors) .SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage) .Select(e => e.ErrorMessage)
.ToList(); .ToList();
_logger.LogError("{error}", errors); _logger.LogWarning("{error}", errors);
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
} }
Guid tenantId = _userHelper.GetTenantId(); Guid tenantId = _userHelper.GetTenantId();
@ -66,7 +66,7 @@ namespace Marco.Pms.Services.Controllers
var Image = attachmentDto; var Image = attachmentDto;
if (string.IsNullOrEmpty(Image.Base64Data)) if (string.IsNullOrEmpty(Image.Base64Data))
{ {
_logger.LogError("Base64 data is missing"); _logger.LogWarning("Base64 data is missing");
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
} }
@ -160,7 +160,7 @@ namespace Marco.Pms.Services.Controllers
.SelectMany(v => v.Errors) .SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage) .Select(e => e.ErrorMessage)
.ToList(); .ToList();
_logger.LogError("{error}", errors); _logger.LogWarning("{error}", errors);
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
} }
Guid tenantId = _userHelper.GetTenantId(); Guid tenantId = _userHelper.GetTenantId();
@ -197,7 +197,7 @@ namespace Marco.Pms.Services.Controllers
var Image = attachmentDto; var Image = attachmentDto;
if (string.IsNullOrEmpty(Image.Base64Data)) if (string.IsNullOrEmpty(Image.Base64Data))
{ {
_logger.LogError("Base64 data is missing"); _logger.LogWarning("Base64 data is missing");
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
} }
@ -336,7 +336,7 @@ namespace Marco.Pms.Services.Controllers
_logger.LogInfo("Ticket {TicketId} updated", updateTicketDto.Id); _logger.LogInfo("Ticket {TicketId} updated", updateTicketDto.Id);
return Ok(ApiResponse<object>.SuccessResponse(ticketVM, "Ticket Updated Successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(ticketVM, "Ticket Updated Successfully", 200));
} }
_logger.LogError("Ticket {TicketId} not Found in database", updateTicketDto.Id); _logger.LogWarning("Ticket {TicketId} not Found in database", updateTicketDto.Id);
return NotFound(ApiResponse<object>.ErrorResponse("Ticket not Found", "Ticket not Found", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Ticket not Found", "Ticket not Found", 404));
} }
@ -349,7 +349,7 @@ namespace Marco.Pms.Services.Controllers
.SelectMany(v => v.Errors) .SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage) .Select(e => e.ErrorMessage)
.ToList(); .ToList();
_logger.LogError("{error}", errors); _logger.LogWarning("{error}", errors);
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
} }
@ -364,7 +364,7 @@ namespace Marco.Pms.Services.Controllers
if (ticket == null) if (ticket == null)
{ {
_logger.LogError("Ticket {TicketId} not Found in database", addCommentDto.TicketId); _logger.LogWarning("Ticket {TicketId} not Found in database", addCommentDto.TicketId);
return NotFound(ApiResponse<object>.ErrorResponse("Ticket not Found", "Ticket not Found", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Ticket not Found", "Ticket not Found", 404));
} }
@ -379,7 +379,7 @@ namespace Marco.Pms.Services.Controllers
var Image = attachmentDto; var Image = attachmentDto;
if (string.IsNullOrEmpty(Image.Base64Data)) if (string.IsNullOrEmpty(Image.Base64Data))
{ {
_logger.LogError("Base64 data is missing"); _logger.LogWarning("Base64 data is missing");
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
} }
@ -437,7 +437,7 @@ namespace Marco.Pms.Services.Controllers
.SelectMany(v => v.Errors) .SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage) .Select(e => e.ErrorMessage)
.ToList(); .ToList();
_logger.LogError("{error}", errors); _logger.LogWarning("{error}", errors);
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
} }
@ -451,7 +451,7 @@ namespace Marco.Pms.Services.Controllers
if (ticket == null) if (ticket == null)
{ {
_logger.LogError("Ticket {TicketId} not Found in database", updateCommentDto.TicketId); _logger.LogWarning("Ticket {TicketId} not Found in database", updateCommentDto.TicketId);
return NotFound(ApiResponse<object>.ErrorResponse("Ticket not Found", "Ticket not Found", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Ticket not Found", "Ticket not Found", 404));
} }
@ -474,7 +474,7 @@ namespace Marco.Pms.Services.Controllers
var Image = attachmentDto; var Image = attachmentDto;
if (string.IsNullOrEmpty(Image.Base64Data)) if (string.IsNullOrEmpty(Image.Base64Data))
{ {
_logger.LogError("Base64 data is missing"); _logger.LogWarning("Base64 data is missing");
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
} }
@ -552,7 +552,7 @@ namespace Marco.Pms.Services.Controllers
.SelectMany(v => v.Errors) .SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage) .Select(e => e.ErrorMessage)
.ToList(); .ToList();
_logger.LogError("{error}", errors); _logger.LogWarning("{error}", errors);
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
} }
@ -568,7 +568,7 @@ namespace Marco.Pms.Services.Controllers
if (tickets == null || tickets.Count > 0) if (tickets == null || tickets.Count > 0)
{ {
_logger.LogError("Tickets not Found in database"); _logger.LogWarning("Tickets not Found in database");
return NotFound(ApiResponse<object>.ErrorResponse("Ticket not Found", "Ticket not Found", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Ticket not Found", "Ticket not Found", 404));
} }
@ -578,12 +578,12 @@ namespace Marco.Pms.Services.Controllers
{ {
if (string.IsNullOrEmpty(forumAttachmentDto.Base64Data)) if (string.IsNullOrEmpty(forumAttachmentDto.Base64Data))
{ {
_logger.LogError("Base64 data is missing"); _logger.LogWarning("Base64 data is missing");
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
} }
if (forumAttachmentDto.TicketId == null) if (forumAttachmentDto.TicketId == null)
{ {
_logger.LogError("ticket ID is missing"); _logger.LogWarning("ticket ID is missing");
return BadRequest(ApiResponse<object>.ErrorResponse("ticket ID is missing", "ticket ID is missing", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("ticket ID is missing", "ticket ID is missing", 400));
} }
var ticket = tickets.FirstOrDefault(t => t.Id == forumAttachmentDto.TicketId); var ticket = tickets.FirstOrDefault(t => t.Id == forumAttachmentDto.TicketId);

View File

@ -168,7 +168,7 @@ namespace Marco.Pms.Services.Controllers
_logger.LogInfo("activity updated successfully from tenant {tenantId}", tenantId); _logger.LogInfo("activity updated successfully from tenant {tenantId}", tenantId);
return Ok(ApiResponse<object>.SuccessResponse(activityVM, "activity updated successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(activityVM, "activity updated successfully", 200));
} }
_logger.LogError("Activity {ActivityId} not found", id); _logger.LogWarning("Activity {ActivityId} not found", id);
return NotFound(ApiResponse<object>.ErrorResponse("Activity not found", "Activity not found", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Activity not found", "Activity not found", 404));
} }
@ -230,7 +230,7 @@ namespace Marco.Pms.Services.Controllers
_logger.LogInfo("Ticket Status master {TicketStatusId} added successfully from tenant {tenantId}", statusMaster.Id, tenantId); _logger.LogInfo("Ticket Status master {TicketStatusId} added successfully from tenant {tenantId}", statusMaster.Id, tenantId);
return Ok(ApiResponse<object>.SuccessResponse(statusVM, "Ticket Status master added successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(statusVM, "Ticket Status master added successfully", 200));
} }
_logger.LogError("User sent empyt payload"); _logger.LogWarning("User sent empyt payload");
return BadRequest(ApiResponse<object>.ErrorResponse("Sent Empty payload", "Sent Empty payload", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Sent Empty payload", "Sent Empty payload", 400));
} }
@ -251,10 +251,10 @@ namespace Marco.Pms.Services.Controllers
_logger.LogInfo("Ticket Status master {TicketStatusId} updated successfully from tenant {tenantId}", statusMaster.Id, tenantId); _logger.LogInfo("Ticket Status master {TicketStatusId} updated successfully from tenant {tenantId}", statusMaster.Id, tenantId);
return Ok(ApiResponse<object>.SuccessResponse(statusVM, "Ticket Status master updated successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(statusVM, "Ticket Status master updated successfully", 200));
} }
_logger.LogError("Ticket Status master {TicketStatusId} not found in database", statusMasterDto.Id != null ? statusMasterDto.Id.Value : Guid.Empty); _logger.LogWarning("Ticket Status master {TicketStatusId} not found in database", statusMasterDto.Id != null ? statusMasterDto.Id.Value : Guid.Empty);
return NotFound(ApiResponse<object>.ErrorResponse("Ticket Status master not found", "Ticket Status master not found", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Ticket Status master not found", "Ticket Status master not found", 404));
} }
_logger.LogError("User sent empyt payload"); _logger.LogWarning("User sent empyt payload");
return BadRequest(ApiResponse<object>.ErrorResponse("Sent Empty payload", "Sent Empty payload", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Sent Empty payload", "Sent Empty payload", 400));
} }
@ -281,7 +281,7 @@ namespace Marco.Pms.Services.Controllers
} }
else else
{ {
_logger.LogError("Ticket Status {TickeStatusId} not found in database", id); _logger.LogWarning("Ticket Status {TickeStatusId} not found in database", id);
return NotFound(ApiResponse<object>.ErrorResponse("Ticket Status not found", "Ticket Status not found", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Ticket Status not found", "Ticket Status not found", 404));
} }
} }
@ -318,7 +318,7 @@ namespace Marco.Pms.Services.Controllers
return Ok(ApiResponse<object>.SuccessResponse(typeVM, "Ticket type master added successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(typeVM, "Ticket type master added successfully", 200));
} }
_logger.LogError("User sent empyt payload"); _logger.LogWarning("User sent empyt payload");
return BadRequest(ApiResponse<object>.ErrorResponse("User sent Empty payload", "User sent Empty payload", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("User sent Empty payload", "User sent Empty payload", 400));
} }
@ -339,10 +339,10 @@ namespace Marco.Pms.Services.Controllers
_logger.LogInfo("Ticket Type master {TicketTypeId} updated successfully from tenant {tenantId}", typeMaster.Id, tenantId); _logger.LogInfo("Ticket Type master {TicketTypeId} updated successfully from tenant {tenantId}", typeMaster.Id, tenantId);
return Ok(ApiResponse<object>.SuccessResponse(typeVM, "Ticket type master updated successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(typeVM, "Ticket type master updated successfully", 200));
} }
_logger.LogError("Ticket type master {TicketTypeId} not found in database", typeMasterDto.Id != null ? typeMasterDto.Id.Value : Guid.Empty); _logger.LogWarning("Ticket type master {TicketTypeId} not found in database", typeMasterDto.Id != null ? typeMasterDto.Id.Value : Guid.Empty);
return NotFound(ApiResponse<object>.ErrorResponse("Ticket type master not found", "Ticket type master not found", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Ticket type master not found", "Ticket type master not found", 404));
} }
_logger.LogError("User sent empyt payload"); _logger.LogWarning("User sent empyt payload");
return BadRequest(ApiResponse<object>.ErrorResponse("User sent Empty payload", "User sent Empty payload", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("User sent Empty payload", "User sent Empty payload", 400));
} }
@ -369,7 +369,7 @@ namespace Marco.Pms.Services.Controllers
} }
else else
{ {
_logger.LogError("Ticket Type {TickeTypeId} not found in database", id); _logger.LogWarning("Ticket Type {TickeTypeId} not found in database", id);
return NotFound(ApiResponse<object>.ErrorResponse("Ticket Type not found", "Ticket Type not found", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Ticket Type not found", "Ticket Type not found", 404));
} }
} }
@ -407,7 +407,7 @@ namespace Marco.Pms.Services.Controllers
return Ok(ApiResponse<object>.SuccessResponse(typeVM, "Ticket Priority master added successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(typeVM, "Ticket Priority master added successfully", 200));
} }
_logger.LogError("User sent empyt payload"); _logger.LogWarning("User sent empyt payload");
return BadRequest(ApiResponse<object>.ErrorResponse("User sent Empty payload", "User sent Empty payload", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("User sent Empty payload", "User sent Empty payload", 400));
} }
[HttpPost("ticket-priorities/edit/{id}")] [HttpPost("ticket-priorities/edit/{id}")]
@ -427,10 +427,10 @@ namespace Marco.Pms.Services.Controllers
_logger.LogInfo("Ticket Priority master {TicketPriorityId} updated successfully from tenant {tenantId}", typeMaster.Id, tenantId); _logger.LogInfo("Ticket Priority master {TicketPriorityId} updated successfully from tenant {tenantId}", typeMaster.Id, tenantId);
return Ok(ApiResponse<object>.SuccessResponse(typeVM, "Ticket Priority master updated successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(typeVM, "Ticket Priority master updated successfully", 200));
} }
_logger.LogError("Ticket Priority master {TicketPriorityId} not found in database", priorityMasterDto.Id != null ? priorityMasterDto.Id.Value : Guid.Empty); _logger.LogWarning("Ticket Priority master {TicketPriorityId} not found in database", priorityMasterDto.Id != null ? priorityMasterDto.Id.Value : Guid.Empty);
return NotFound(ApiResponse<object>.ErrorResponse("Ticket Priority master not found", "Ticket Priority master not found", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Ticket Priority master not found", "Ticket Priority master not found", 404));
} }
_logger.LogError("User sent empyt payload"); _logger.LogWarning("User sent empyt payload");
return BadRequest(ApiResponse<object>.ErrorResponse("User sent Empty payload", "User sent Empty payload", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("User sent Empty payload", "User sent Empty payload", 400));
} }
@ -457,7 +457,7 @@ namespace Marco.Pms.Services.Controllers
} }
else else
{ {
_logger.LogError("Ticket Priority {TickePriorityId} not found in database", id); _logger.LogWarning("Ticket Priority {TickePriorityId} not found in database", id);
return NotFound(ApiResponse<object>.ErrorResponse("Ticket Priority not found", "Ticket Priority not found", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Ticket Priority not found", "Ticket Priority not found", 404));
} }
} }
@ -494,7 +494,7 @@ namespace Marco.Pms.Services.Controllers
return Ok(ApiResponse<object>.SuccessResponse(typeVM, "Ticket tag master added successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(typeVM, "Ticket tag master added successfully", 200));
} }
_logger.LogError("User sent empyt payload"); _logger.LogWarning("User sent empyt payload");
return BadRequest(ApiResponse<object>.ErrorResponse("User sent Empty payload", "User sent Empty payload", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("User sent Empty payload", "User sent Empty payload", 400));
} }
@ -515,10 +515,10 @@ namespace Marco.Pms.Services.Controllers
_logger.LogInfo("Ticket Tag master {TicketTypeId} updated successfully from tenant {tenantId}", tagMaster.Id, tenantId); _logger.LogInfo("Ticket Tag master {TicketTypeId} updated successfully from tenant {tenantId}", tagMaster.Id, tenantId);
return Ok(ApiResponse<object>.SuccessResponse(typeVM, "Ticket tag master updated successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(typeVM, "Ticket tag master updated successfully", 200));
} }
_logger.LogError("Ticket tag master {TicketTypeId} not found in database", tagMasterDto.Id != null ? tagMasterDto.Id.Value : Guid.Empty); _logger.LogWarning("Ticket tag master {TicketTypeId} not found in database", tagMasterDto.Id != null ? tagMasterDto.Id.Value : Guid.Empty);
return NotFound(ApiResponse<object>.ErrorResponse("Ticket tag master not found", "Ticket tag master not found", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Ticket tag master not found", "Ticket tag master not found", 404));
} }
_logger.LogError("User sent empyt payload"); _logger.LogWarning("User sent empyt payload");
return BadRequest(ApiResponse<object>.ErrorResponse("User sent Empty payload", "User sent Empty payload", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("User sent Empty payload", "User sent Empty payload", 400));
} }
@ -545,7 +545,7 @@ namespace Marco.Pms.Services.Controllers
} }
else else
{ {
_logger.LogError("Ticket Tag {TickeTagId} not found in database", id); _logger.LogWarning("Ticket Tag {TickeTagId} not found in database", id);
return NotFound(ApiResponse<object>.ErrorResponse("Ticket tag not found", "Ticket tag not found", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Ticket tag not found", "Ticket tag not found", 404));
} }
} }
@ -609,7 +609,7 @@ namespace Marco.Pms.Services.Controllers
return Ok(ApiResponse<object>.SuccessResponse(workCategoryMasterVM, "Work category master added successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(workCategoryMasterVM, "Work category master added successfully", 200));
} }
_logger.LogError("User sent empyt payload"); _logger.LogWarning("User sent empyt payload");
return BadRequest(ApiResponse<object>.ErrorResponse("User sent Empty payload", "User sent Empty payload", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("User sent Empty payload", "User sent Empty payload", 400));
} }
@ -624,7 +624,7 @@ namespace Marco.Pms.Services.Controllers
{ {
if (workCategory.IsSystem) if (workCategory.IsSystem)
{ {
_logger.LogError("User tries to update system-defined work category"); _logger.LogWarning("User tries to update system-defined work category");
return BadRequest(ApiResponse<object>.ErrorResponse("Cannot update system-defined work", "Cannot update system-defined work", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Cannot update system-defined work", "Cannot update system-defined work", 400));
} }
workCategory = workCategoryMasterDto.ToWorkCategoryMasterFromWorkCategoryMasterDto(tenantId); workCategory = workCategoryMasterDto.ToWorkCategoryMasterFromWorkCategoryMasterDto(tenantId);
@ -635,10 +635,10 @@ namespace Marco.Pms.Services.Controllers
_logger.LogInfo("Work category master {WorkCategoryId} updated successfully from tenant {tenantId}", workCategory.Id, tenantId); _logger.LogInfo("Work category master {WorkCategoryId} updated successfully from tenant {tenantId}", workCategory.Id, tenantId);
return Ok(ApiResponse<object>.SuccessResponse(workCategoryMasterVM, "Work category master updated successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(workCategoryMasterVM, "Work category master updated successfully", 200));
} }
_logger.LogError("Work category master {WorkCategoryId} not found in database", workCategoryMasterDto.Id ?? Guid.Empty); _logger.LogWarning("Work category master {WorkCategoryId} not found in database", workCategoryMasterDto.Id ?? Guid.Empty);
return NotFound(ApiResponse<object>.ErrorResponse("Work category master not found", "Work category master not found", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Work category master not found", "Work category master not found", 404));
} }
_logger.LogError("User sent empyt payload"); _logger.LogWarning("User sent empyt payload");
return BadRequest(ApiResponse<object>.ErrorResponse("User sent Empty payload", "User sent Empty payload", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("User sent Empty payload", "User sent Empty payload", 400));
} }
@ -666,7 +666,7 @@ namespace Marco.Pms.Services.Controllers
} }
else else
{ {
_logger.LogError("Work category {WorkCategoryId} not found in database", id); _logger.LogWarning("Work category {WorkCategoryId} not found in database", id);
return NotFound(ApiResponse<object>.ErrorResponse("Work category not found", "Work category not found", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Work category not found", "Work category not found", 404));
} }
} }
@ -689,7 +689,7 @@ namespace Marco.Pms.Services.Controllers
.SelectMany(v => v.Errors) .SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage) .Select(e => e.ErrorMessage)
.ToList(); .ToList();
_logger.LogError("User sent Invalid Date while marking attendance"); _logger.LogWarning("User sent Invalid Date while marking attendance");
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
} }
var response = await _masterHelper.CreateWorkStatus(createWorkStatusDto); var response = await _masterHelper.CreateWorkStatus(createWorkStatusDto);
@ -803,7 +803,7 @@ namespace Marco.Pms.Services.Controllers
.SelectMany(v => v.Errors) .SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage) .Select(e => e.ErrorMessage)
.ToList(); .ToList();
_logger.LogError("User sent Invalid Date while marking attendance"); _logger.LogWarning("User sent Invalid Date while marking attendance");
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
} }
var response = await _masterHelper.CreateContactTag(contactTagDto); var response = await _masterHelper.CreateContactTag(contactTagDto);

View File

@ -6,19 +6,18 @@ using Marco.Pms.Model.Mapper;
using Marco.Pms.Model.MongoDBModels; using Marco.Pms.Model.MongoDBModels;
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.Employee;
using Marco.Pms.Model.ViewModels.Projects; using Marco.Pms.Model.ViewModels.Projects;
using Marco.Pms.Services.Helpers; using Marco.Pms.Services.Helpers;
using Marco.Pms.Services.Hubs;
using Marco.Pms.Services.Service; using Marco.Pms.Services.Service;
using Marco.Pms.Services.Service.ServiceInterfaces; using Marco.Pms.Services.Service.ServiceInterfaces;
using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Service; using MarcoBMS.Services.Service;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR; using Microsoft.CodeAnalysis;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using MongoDB.Driver; using MongoDB.Driver;
using Project = Marco.Pms.Model.Projects.Project;
namespace MarcoBMS.Services.Controllers namespace MarcoBMS.Services.Controllers
{ {
@ -31,14 +30,20 @@ namespace MarcoBMS.Services.Controllers
private readonly ApplicationDbContext _context; private readonly ApplicationDbContext _context;
private readonly UserHelper _userHelper; private readonly UserHelper _userHelper;
private readonly ILoggingService _logger; private readonly ILoggingService _logger;
private readonly IHubContext<MarcoHub> _signalR; private readonly ISignalRService _signalR;
private readonly PermissionServices _permission; private readonly PermissionServices _permission;
private readonly CacheUpdateHelper _cache; private readonly CacheUpdateHelper _cache;
private readonly Guid tenantId; private readonly Guid tenantId;
public ProjectController(ApplicationDbContext context, UserHelper userHelper, ILoggingService logger, public ProjectController(
IHubContext<MarcoHub> signalR, CacheUpdateHelper cache, PermissionServices permission, IProjectServices projectServices) ApplicationDbContext context,
UserHelper userHelper,
ILoggingService logger,
ISignalRService signalR,
CacheUpdateHelper cache,
PermissionServices permission,
IProjectServices projectServices)
{ {
_context = context; _context = context;
_userHelper = userHelper; _userHelper = userHelper;
@ -174,7 +179,7 @@ namespace MarcoBMS.Services.Controllers
if (response.Success) if (response.Success)
{ {
var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Create_Project", Response = response.Data }; var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Create_Project", Response = response.Data };
await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification); await _signalR.SendNotificationAsync(notification);
} }
return StatusCode(response.StatusCode, response); return StatusCode(response.StatusCode, response);
} }
@ -204,7 +209,7 @@ namespace MarcoBMS.Services.Controllers
if (response.Success) if (response.Success)
{ {
var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Update_Project", Response = response.Data }; var notification = new { LoggedInUserId = loggedInEmployee.Id, Keyword = "Update_Project", Response = response.Data };
await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification); await _signalR.SendNotificationAsync(notification);
} }
return StatusCode(response.StatusCode, response); return StatusCode(response.StatusCode, response);
} }
@ -213,90 +218,38 @@ namespace MarcoBMS.Services.Controllers
#region =================================================================== Project Allocation APIs =================================================================== #region =================================================================== Project Allocation APIs ===================================================================
[HttpGet] [HttpGet("employees/get/{projectid?}/{includeInactive?}")]
[Route("employees/get/{projectid?}/{includeInactive?}")] public async Task<IActionResult> GetEmployeeByProjectId(Guid? projectId, bool includeInactive = false)
public async Task<IActionResult> GetEmployeeByProjectID(Guid? projectid, bool includeInactive = false)
{ {
// --- Step 1: Input Validation ---
if (!ModelState.IsValid) if (!ModelState.IsValid)
{ {
var errors = ModelState.Values var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage).ToList();
.SelectMany(v => v.Errors) _logger.LogWarning("Get employee list by ProjectId called with invalid model state \n Errors: {Errors}", string.Join(", ", errors));
.Select(e => e.ErrorMessage) return BadRequest(ApiResponse<object>.ErrorResponse("Invalid request data provided.", errors, 400));
.ToList();
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
} }
if (projectid != null) // --- Step 2: Prepare data without I/O ---
{ Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
// Fetch assigned project var response = await _projectServices.GetEmployeeByProjectIdAsync(projectId, includeInactive, tenantId, loggedInEmployee);
List<Employee> result = new List<Employee>(); return StatusCode(response.StatusCode, response);
if ((bool)includeInactive)
{
result = await (from rpm in _context.Employees.Include(c => c.JobRole)
join fp in _context.ProjectAllocations.Where(c => c.TenantId == tenantId && c.ProjectId == projectid)
on rpm.Id equals fp.EmployeeId
select rpm).ToListAsync();
}
else
{
result = await (from rpm in _context.Employees.Include(c => c.JobRole)
join fp in _context.ProjectAllocations.Where(c => c.TenantId == tenantId && c.ProjectId == projectid && c.IsActive)
on rpm.Id equals fp.EmployeeId
select rpm).ToListAsync();
}
List<EmployeeVM> resultVM = new List<EmployeeVM>();
foreach (Employee employee in result)
{
EmployeeVM vm = employee.ToEmployeeVMFromEmployee();
resultVM.Add(vm);
}
return Ok(ApiResponse<object>.SuccessResponse(resultVM, "Success.", 200));
}
else
{
return NotFound(ApiResponse<object>.ErrorResponse("Invalid Input Parameter", 404));
}
} }
[HttpGet] [HttpGet("allocation/{projectId}")]
[Route("allocation/{projectId}")]
public async Task<IActionResult> GetProjectAllocation(Guid? projectId) public async Task<IActionResult> GetProjectAllocation(Guid? projectId)
{ {
// --- Step 1: Input Validation ---
if (!ModelState.IsValid) if (!ModelState.IsValid)
{ {
var errors = ModelState.Values var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage).ToList();
.SelectMany(v => v.Errors) _logger.LogWarning("Get employee list by ProjectId called with invalid model state \n Errors: {Errors}", string.Join(", ", errors));
.Select(e => e.ErrorMessage) return BadRequest(ApiResponse<object>.ErrorResponse("Invalid request data provided.", errors, 400));
.ToList();
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
} }
var employees = await _context.ProjectAllocations // --- Step 2: Prepare data without I/O ---
.Where(c => c.TenantId == tenantId && c.ProjectId == projectId && c.Employee != null) Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
.Include(e => e.Employee) var response = await _projectServices.GetProjectAllocationAsync(projectId, tenantId, loggedInEmployee);
.Select(e => new return StatusCode(response.StatusCode, response);
{
ID = e.Id,
EmployeeId = e.EmployeeId,
ProjectId = e.ProjectId,
AllocationDate = e.AllocationDate,
ReAllocationDate = e.ReAllocationDate,
FirstName = e.Employee != null ? e.Employee.FirstName : string.Empty,
LastName = e.Employee != null ? e.Employee.LastName : string.Empty,
MiddleName = e.Employee != null ? e.Employee.MiddleName : string.Empty,
IsActive = e.IsActive,
JobRoleId = (e.JobRoleId != null ? e.JobRoleId : e.Employee != null ? e.Employee.JobRoleId : null)
}).ToListAsync();
return Ok(ApiResponse<object>.SuccessResponse(employees, "Success.", 200));
} }
[HttpPost("allocation")] [HttpPost("allocation")]
@ -375,7 +328,7 @@ namespace MarcoBMS.Services.Controllers
} }
var notification = new { LoggedInUserId = LoggedInEmployee.Id, Keyword = "Assign_Project", ProjectIds = projectIds, EmployeeList = employeeIds }; var notification = new { LoggedInUserId = LoggedInEmployee.Id, Keyword = "Assign_Project", ProjectIds = projectIds, EmployeeList = employeeIds };
await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification); await _signalR.SendNotificationAsync(notification);
return Ok(ApiResponse<object>.SuccessResponse(result, "Data saved successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(result, "Data saved successfully", 200));
} }
@ -494,7 +447,7 @@ namespace MarcoBMS.Services.Controllers
await _cache.ClearAllProjectIds(employeeId); await _cache.ClearAllProjectIds(employeeId);
var notification = new { LoggedInUserId = LoggedInEmployee.Id, Keyword = "Assign_Project", ProjectIds = projectIds, EmployeeId = employeeId }; var notification = new { LoggedInUserId = LoggedInEmployee.Id, Keyword = "Assign_Project", ProjectIds = projectIds, EmployeeId = employeeId };
await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification); await _signalR.SendNotificationAsync(notification);
return Ok(ApiResponse<object>.SuccessResponse(result, "Data saved successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(result, "Data saved successfully", 200));
} }
@ -799,7 +752,7 @@ namespace MarcoBMS.Services.Controllers
var notification = new { LoggedInUserId = LoggedInEmployee.Id, Keyword = "WorkItem", WorkAreaIds = workAreaIds, Message = message }; var notification = new { LoggedInUserId = LoggedInEmployee.Id, Keyword = "WorkItem", WorkAreaIds = workAreaIds, Message = message };
await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification); await _signalR.SendNotificationAsync(notification);
return Ok(ApiResponse<object>.SuccessResponse(responseList, responseMessage, 200)); return Ok(ApiResponse<object>.SuccessResponse(responseList, responseMessage, 200));
} }
@ -826,9 +779,15 @@ namespace MarcoBMS.Services.Controllers
workAreaIds.Add(task.WorkAreaId); workAreaIds.Add(task.WorkAreaId);
var projectId = floor?.Building?.ProjectId;
var notification = new { LoggedInUserId = LoggedInEmployee.Id, Keyword = "WorkItem", WorkAreaIds = workAreaIds, Message = $"Task Deleted in Building: {floor?.Building?.Name}, on Floor: {floor?.FloorName}, in Area: {task.WorkArea?.AreaName} by {LoggedInEmployee.FirstName} {LoggedInEmployee.LastName}" }; var notification = new { LoggedInUserId = LoggedInEmployee.Id, Keyword = "WorkItem", WorkAreaIds = workAreaIds, Message = $"Task Deleted in Building: {floor?.Building?.Name}, on Floor: {floor?.FloorName}, in Area: {task.WorkArea?.AreaName} by {LoggedInEmployee.FirstName} {LoggedInEmployee.LastName}" };
await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification); await _signalR.SendNotificationAsync(notification);
await _cache.DeleteWorkItemByIdAsync(task.Id);
if (projectId != null)
{
await _cache.DeleteProjectByIdAsync(projectId.Value);
}
} }
else else
{ {
@ -847,7 +806,7 @@ namespace MarcoBMS.Services.Controllers
} }
else else
{ {
_logger.LogError("Task with ID {WorkItemId} not found ID database", id); _logger.LogWarning("Task with ID {WorkItemId} not found ID database", id);
} }
return Ok(ApiResponse<object>.SuccessResponse(new { }, "Task deleted successfully", 200)); return Ok(ApiResponse<object>.SuccessResponse(new { }, "Task deleted successfully", 200));
} }
@ -973,7 +932,7 @@ namespace MarcoBMS.Services.Controllers
message = $"{message} by {LoggedInEmployee.FirstName} {LoggedInEmployee.LastName}"; message = $"{message} by {LoggedInEmployee.FirstName} {LoggedInEmployee.LastName}";
var notification = new { LoggedInUserId = LoggedInEmployee.Id, Keyword = "Infra", ProjectIds = projectIds, Message = message }; var notification = new { LoggedInUserId = LoggedInEmployee.Id, Keyword = "Infra", ProjectIds = projectIds, Message = message };
await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification); await _signalR.SendNotificationAsync(notification);
return Ok(ApiResponse<object>.SuccessResponse(responseData, responseMessage, 200)); return Ok(ApiResponse<object>.SuccessResponse(responseData, responseMessage, 200));
} }
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid details.", "Infra Details are not valid.", 400)); return BadRequest(ApiResponse<object>.ErrorResponse("Invalid details.", "Infra Details are not valid.", 400));

View File

@ -106,7 +106,7 @@ namespace Marco.Pms.Services.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("Database Error: Failed to check existence of MailListId '{MailListId}' for TenantId: {TenantId}. : {Error}", mailDetailsDto.MailListId, tenantId, ex.Message); _logger.LogError(ex, "Database Error: Failed to check existence of MailListId '{MailListId}' for TenantId: {TenantId}.", mailDetailsDto.MailListId, tenantId);
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Server Error", "An error occurred while validating mail template.", 500)); return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Server Error", "An error occurred while validating mail template.", 500));
} }
@ -143,13 +143,13 @@ namespace Marco.Pms.Services.Controllers
} }
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
{ {
_logger.LogError("Database Error: Failed to save new mail details for ProjectId: {ProjectId}, Recipient: '{Recipient}', TenantId: {TenantId}. : {Error}", newMailDetails.ProjectId, newMailDetails.Recipient, tenantId, dbEx.Message); _logger.LogError(dbEx, "Database Error: Failed to save new mail details for ProjectId: {ProjectId}, Recipient: '{Recipient}', TenantId: {TenantId}.", newMailDetails.ProjectId, newMailDetails.Recipient, tenantId);
// Check for specific constraint violations if applicable (e.g., duplicate recipient for a project) // Check for specific constraint violations if applicable (e.g., duplicate recipient for a project)
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Server Error", "An error occurred while saving the mail details.", 500)); return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Server Error", "An error occurred while saving the mail details.", 500));
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("Unexpected Error: An unhandled exception occurred while adding mail details for ProjectId: {ProjectId}, Recipient: '{Recipient}', TenantId: {TenantId}. : {Error}", newMailDetails.ProjectId, newMailDetails.Recipient, tenantId, ex.Message); _logger.LogError(ex, "Unexpected Error: An unhandled exception occurred while adding mail details for ProjectId: {ProjectId}, Recipient: '{Recipient}', TenantId: {TenantId}.", newMailDetails.ProjectId, newMailDetails.Recipient, tenantId);
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Server Error", "An unexpected error occurred.", 500)); return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Server Error", "An unexpected error occurred.", 500));
} }
} }
@ -234,7 +234,7 @@ namespace Marco.Pms.Services.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("Database Error: Failed to check for existing mail template with title '{Title}' for TenantId: {TenantId}.: {Error}", mailTemplateDto.Title, tenantId, ex.Message); _logger.LogError(ex, "Database Error: Failed to check for existing mail template with title '{Title}' for TenantId: {TenantId}.", mailTemplateDto.Title, tenantId);
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Server Error", "An error occurred while checking for existing templates.", 500)); return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Server Error", "An error occurred while checking for existing templates.", 500));
} }
@ -270,12 +270,12 @@ namespace Marco.Pms.Services.Controllers
} }
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
{ {
_logger.LogError("Database Error: Failed to save new mail template '{Title}' for TenantId: {TenantId}. : {Error}", mailTemplateDto.Title, tenantId, dbEx.Message); _logger.LogError(dbEx, "Database Error: Failed to save new mail template '{Title}' for TenantId: {TenantId}. : {Error}", mailTemplateDto.Title, tenantId);
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Server Error", "An error occurred while saving the mail template.", 500)); return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Server Error", "An error occurred while saving the mail template.", 500));
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("Unexpected Error: An unhandled exception occurred while adding mail template '{Title}' for TenantId: {TenantId}. : {Error}", mailTemplateDto.Title, tenantId, ex.Message); _logger.LogError(ex, "Unexpected Error: An unhandled exception occurred while adding mail template '{Title}' for TenantId: {TenantId}.", mailTemplateDto.Title, tenantId);
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Server Error", "An unexpected error occurred.", 500)); return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Server Error", "An unexpected error occurred.", 500));
} }
} }
@ -350,7 +350,7 @@ namespace Marco.Pms.Services.Controllers
{ {
// 3. OPTIMIZATION: Make the process resilient. // 3. OPTIMIZATION: Make the process resilient.
// If one task fails unexpectedly, log it and continue with others. // If one task fails unexpectedly, log it and continue with others.
_logger.LogError("Failed to send report for project {ProjectId} : {Error}", mailGroup.ProjectId, ex.Message); _logger.LogError(ex, "Failed to send report for project {ProjectId}", mailGroup.ProjectId);
Interlocked.Increment(ref failureCount); Interlocked.Increment(ref failureCount);
} }
} }
@ -527,7 +527,7 @@ namespace Marco.Pms.Services.Controllers
catch (Exception ex) catch (Exception ex)
{ {
// It's good practice to log any unexpected errors within a concurrent task. // It's good practice to log any unexpected errors within a concurrent task.
_logger.LogError("Failed to process project report for ProjectId {ProjectId} : {Error}", group.ProjectId, ex.Message); _logger.LogError(ex, "Failed to process project report for ProjectId {ProjectId}", group.ProjectId);
} }
} }
}); });

View File

@ -118,8 +118,8 @@ namespace Marco.Pms.Services.Helpers
projectDetails.ProjectStatus = new StatusMasterMongoDB projectDetails.ProjectStatus = new StatusMasterMongoDB
{ {
Id = status?.Id.ToString(), Id = status!.Id.ToString(),
Status = status?.Status Status = status.Status
}; };
// Use fast in-memory lookups instead of .Where() in loops. // Use fast in-memory lookups instead of .Where() in loops.
@ -797,7 +797,7 @@ namespace Marco.Pms.Services.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("Error occured while fetching project report mail bodys: {Error}", ex.Message); _logger.LogError(ex, "Error occured while fetching project report mail bodys");
return null; return null;
} }
} }
@ -809,7 +809,7 @@ namespace Marco.Pms.Services.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("Error occured while adding project report mail bodys: {Error}", ex.Message); _logger.LogError(ex, "Error occured while adding project report mail bodys");
} }
} }
} }

View File

@ -52,7 +52,7 @@ namespace Marco.Pms.Services.Helpers
} }
else else
{ {
_logger.LogError("Employee {EmployeeId} attemped to access a contacts, but do not have permission", LoggedInEmployee.Id); _logger.LogWarning("Employee {EmployeeId} attemped to access a contacts, but do not have permission", LoggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("You don't have permission", "You don't have permission", 401); return ApiResponse<object>.ErrorResponse("You don't have permission", "You don't have permission", 401);
} }
@ -202,7 +202,7 @@ namespace Marco.Pms.Services.Helpers
} }
else else
{ {
_logger.LogError("Employee {EmployeeId} attemped to access a contacts with in bucket {BucketId}, but do not have permission", LoggedInEmployee.Id, id); _logger.LogWarning("Employee {EmployeeId} attemped to access a contacts with in bucket {BucketId}, but do not have permission", LoggedInEmployee.Id, id);
return ApiResponse<object>.ErrorResponse("You don't have permission", "You don't have permission", 401); return ApiResponse<object>.ErrorResponse("You don't have permission", "You don't have permission", 401);
} }
@ -490,7 +490,7 @@ namespace Marco.Pms.Services.Helpers
} }
else else
{ {
_logger.LogError("Employee {EmployeeId} attemped to update a contact, but do not have permission", LoggedInEmployee.Id); _logger.LogWarning("Employee {EmployeeId} attemped to update a contact, but do not have permission", LoggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("You don't have permission", "You don't have permission", 401); return ApiResponse<object>.ErrorResponse("You don't have permission", "You don't have permission", 401);
} }
@ -1169,7 +1169,7 @@ namespace Marco.Pms.Services.Helpers
} }
else else
{ {
_logger.LogError("Employee {EmployeeId} attemped to access a buckets list, but do not have permission", LoggedInEmployee.Id); _logger.LogWarning("Employee {EmployeeId} attemped to access a buckets list, but do not have permission", LoggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("You don't have permission", "You don't have permission", 401); return ApiResponse<object>.ErrorResponse("You don't have permission", "You don't have permission", 401);
} }
@ -1204,7 +1204,7 @@ namespace Marco.Pms.Services.Helpers
var demo = !permissionIds.Contains(PermissionsMaster.DirectoryUser); var demo = !permissionIds.Contains(PermissionsMaster.DirectoryUser);
if (!permissionIds.Contains(PermissionsMaster.DirectoryAdmin) && !permissionIds.Contains(PermissionsMaster.DirectoryAdmin) && !permissionIds.Contains(PermissionsMaster.DirectoryUser)) if (!permissionIds.Contains(PermissionsMaster.DirectoryAdmin) && !permissionIds.Contains(PermissionsMaster.DirectoryAdmin) && !permissionIds.Contains(PermissionsMaster.DirectoryUser))
{ {
_logger.LogError("Employee {EmployeeId} attemped to create a bucket, but do not have permission", LoggedInEmployee.Id); _logger.LogWarning("Employee {EmployeeId} attemped to create a bucket, but do not have permission", LoggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("You don't have permission", "You don't have permission", 401); return ApiResponse<object>.ErrorResponse("You don't have permission", "You don't have permission", 401);
} }
@ -1276,7 +1276,7 @@ namespace Marco.Pms.Services.Helpers
} }
if (accessableBucket == null) if (accessableBucket == null)
{ {
_logger.LogError("Employee {EmployeeId} attempted to access bucket {BucketId} without the necessary permissions.", LoggedInEmployee.Id, bucket.Id); _logger.LogWarning("Employee {EmployeeId} attempted to access bucket {BucketId} without the necessary permissions.", LoggedInEmployee.Id, bucket.Id);
return ApiResponse<object>.ErrorResponse("You don't have permission to access this bucket", "You don't have permission to access this bucket", 401); return ApiResponse<object>.ErrorResponse("You don't have permission to access this bucket", "You don't have permission to access this bucket", 401);
} }
@ -1342,7 +1342,7 @@ namespace Marco.Pms.Services.Helpers
} }
if (accessableBucket == null) if (accessableBucket == null)
{ {
_logger.LogError("Employee {EmployeeId} attempted to access bucket {BucketId} without the necessary permissions.", LoggedInEmployee.Id, bucket.Id); _logger.LogWarning("Employee {EmployeeId} attempted to access bucket {BucketId} without the necessary permissions.", LoggedInEmployee.Id, bucket.Id);
return ApiResponse<object>.ErrorResponse("You don't have permission to access this bucket", "You don't have permission to access this bucket", 401); return ApiResponse<object>.ErrorResponse("You don't have permission to access this bucket", "You don't have permission to access this bucket", 401);
} }
var employeeIds = await _context.Employees.Where(e => e.TenantId == tenantId && e.IsActive).Select(e => e.Id).ToListAsync(); var employeeIds = await _context.Employees.Where(e => e.TenantId == tenantId && e.IsActive).Select(e => e.Id).ToListAsync();
@ -1396,7 +1396,7 @@ namespace Marco.Pms.Services.Helpers
} }
if (removededEmployee > 0) if (removededEmployee > 0)
{ {
_logger.LogError("Employee {EmployeeId} removed {conut} number of employees from bucket {BucketId}", LoggedInEmployee.Id, removededEmployee, bucketId); _logger.LogWarning("Employee {EmployeeId} removed {conut} number of employees from bucket {BucketId}", LoggedInEmployee.Id, removededEmployee, bucketId);
} }
return ApiResponse<object>.SuccessResponse(bucketVM, "Details updated successfully", 200); return ApiResponse<object>.SuccessResponse(bucketVM, "Details updated successfully", 200);
} }
@ -1443,7 +1443,7 @@ namespace Marco.Pms.Services.Helpers
} }
if (accessableBucket == null) if (accessableBucket == null)
{ {
_logger.LogError("Employee {EmployeeId} attempted to access bucket {BucketId} without the necessary permissions.", LoggedInEmployee.Id, bucket.Id); _logger.LogWarning("Employee {EmployeeId} attempted to access bucket {BucketId} without the necessary permissions.", LoggedInEmployee.Id, bucket.Id);
return ApiResponse<object>.ErrorResponse("You don't have permission to access this bucket", "You don't have permission to access this bucket", 401); return ApiResponse<object>.ErrorResponse("You don't have permission to access this bucket", "You don't have permission to access this bucket", 401);
} }

View File

@ -33,7 +33,7 @@ namespace MarcoBMS.Services.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("{Error}", ex.Message); _logger.LogError(ex, "Error occured while fetching employee by application user ID {ApplicationUserId}", ApplicationUserID);
return new Employee(); return new Employee();
} }
} }
@ -66,7 +66,7 @@ namespace MarcoBMS.Services.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("{Error}", ex.Message); _logger.LogError(ex, "Error occoured while filtering employees by string {SearchString} or project {ProjectId}", searchString, ProjectId ?? Guid.Empty);
return new List<EmployeeVM>(); return new List<EmployeeVM>();
} }
} }
@ -102,7 +102,7 @@ namespace MarcoBMS.Services.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("{Error}", ex.Message); _logger.LogError(ex, "Error occured while featching list of employee by project ID {ProjectId}", ProjectId ?? Guid.Empty);
return new List<EmployeeVM>(); return new List<EmployeeVM>();
} }
} }

View File

@ -218,7 +218,7 @@ namespace Marco.Pms.Services.Helpers
_logger.LogInfo("Contact tag master {ConatctTagId} updated successfully by employee {EmployeeId}", contactTagVm.Id, LoggedInEmployee.Id); _logger.LogInfo("Contact tag master {ConatctTagId} updated successfully by employee {EmployeeId}", contactTagVm.Id, LoggedInEmployee.Id);
return ApiResponse<object>.SuccessResponse(contactTagVm, "Contact Tag master updated successfully", 200); return ApiResponse<object>.SuccessResponse(contactTagVm, "Contact Tag master updated successfully", 200);
} }
_logger.LogError("Contact Tag master {ContactTagId} not found in database", id); _logger.LogWarning("Contact Tag master {ContactTagId} not found in database", id);
return ApiResponse<object>.ErrorResponse("Contact Tag master not found", "Contact tag master not found", 404); return ApiResponse<object>.ErrorResponse("Contact Tag master not found", "Contact tag master not found", 404);
} }
_logger.LogWarning("Employee with ID {LoggedInEmployeeId} sended empty payload", LoggedInEmployee.Id); _logger.LogWarning("Employee with ID {LoggedInEmployeeId} sended empty payload", LoggedInEmployee.Id);
@ -294,7 +294,7 @@ namespace Marco.Pms.Services.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("Error occurred while fetching work status list : {Error}", ex.Message); _logger.LogWarning("Error occurred while fetching work status list : {Error}", ex.Message);
return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to fetch work status list", 500); return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to fetch work status list", 500);
} }
} }
@ -343,7 +343,7 @@ namespace Marco.Pms.Services.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("Error occurred while creating work status : {Error}", ex.Message); _logger.LogWarning("Error occurred while creating work status : {Error}", ex.Message);
return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to create work status", 500); return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to create work status", 500);
} }
} }
@ -403,7 +403,7 @@ namespace Marco.Pms.Services.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("Error occurred while updating work status ID: {Id} : {Error}", id, ex.Message); _logger.LogError(ex, "Error occurred while updating work status ID: {Id}", id);
return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to update the work status at this time", 500); return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to update the work status at this time", 500);
} }
} }
@ -458,7 +458,7 @@ namespace Marco.Pms.Services.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("Error occurred while deleting WorkStatus Id: {Id} : {Error}", id, ex.Message); _logger.LogError(ex, "Error occurred while deleting WorkStatus Id: {Id}", id);
return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to delete work status", 500); return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to delete work status", 500);
} }
} }

View File

@ -289,13 +289,13 @@ namespace Marco.Pms.Services.Helpers
// --- Input Validation --- // --- Input Validation ---
if (projectId == Guid.Empty) if (projectId == Guid.Empty)
{ {
_logger.LogError("Validation Error: Provided empty project ID while fetching project report."); _logger.LogWarning("Validation Error: Provided empty project ID while fetching project report.");
return ApiResponse<object>.ErrorResponse("Provided empty Project ID.", "Provided empty Project ID.", 400); return ApiResponse<object>.ErrorResponse("Provided empty Project ID.", "Provided empty Project ID.", 400);
} }
if (recipientEmails == null || !recipientEmails.Any()) if (recipientEmails == null || !recipientEmails.Any())
{ {
_logger.LogError("Validation Error: No recipient emails provided for project ID {ProjectId}.", projectId); _logger.LogWarning("Validation Error: No recipient emails provided for project ID {ProjectId}.", projectId);
return ApiResponse<object>.ErrorResponse("No recipient emails provided.", "No recipient emails provided.", 400); return ApiResponse<object>.ErrorResponse("No recipient emails provided.", "No recipient emails provided.", 400);
} }
@ -316,7 +316,7 @@ namespace Marco.Pms.Services.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("Email Sending Error: Failed to send project statistics email for project ID {ProjectId}. : {Error}", projectId, ex.Message); _logger.LogError(ex, "Email Sending Error: Failed to send project statistics email for project ID {ProjectId}.", projectId);
return ApiResponse<object>.ErrorResponse("Failed to send email.", "An error occurred while sending the email.", 500); return ApiResponse<object>.ErrorResponse("Failed to send email.", "An error occurred while sending the email.", 500);
} }
@ -350,14 +350,14 @@ namespace Marco.Pms.Services.Helpers
} }
catch (DbUpdateException dbEx) catch (DbUpdateException dbEx)
{ {
_logger.LogError("Database Error: Failed to save mail logs for project ID {ProjectId}. : {Error}", projectId, dbEx.Message); _logger.LogError(dbEx, "Database Error: Failed to save mail logs for project ID {ProjectId}.", projectId);
// Depending on your requirements, you might still return success here as the email was sent. // Depending on your requirements, you might still return success here as the email was sent.
// Or return an error indicating the logging failed. // Or return an error indicating the logging failed.
return ApiResponse<object>.ErrorResponse("Email sent, but failed to log activity.", "Email sent, but an error occurred while logging.", 500); return ApiResponse<object>.ErrorResponse("Email sent, but failed to log activity.", "Email sent, but an error occurred while logging.", 500);
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("Unexpected Error: An unhandled exception occurred while processing project statistics for project ID {ProjectId}. : {Error}", projectId, ex.Message); _logger.LogError(ex, "Unexpected Error: An unhandled exception occurred while processing project statistics for project ID {ProjectId}.", projectId);
return ApiResponse<object>.ErrorResponse("An unexpected error occurred.", "An unexpected error occurred.", 500); return ApiResponse<object>.ErrorResponse("An unexpected error occurred.", "An unexpected error occurred.", 500);
} }
} }

View File

@ -84,7 +84,7 @@ namespace MarcoBMS.Services.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("An error occurred while fetching permissions for EmployeeId {EmployeeId} : {Error}", EmployeeId, ex.Message); _logger.LogError(ex, "An error occurred while fetching permissions for EmployeeId {EmployeeId}", EmployeeId);
return new List<FeaturePermission>(); return new List<FeaturePermission>();
} }
} }
@ -144,7 +144,7 @@ namespace MarcoBMS.Services.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("An error occurred while fetching permissions for RoleId {RoleId}: {Error}", roleId, ex.Message); _logger.LogError(ex, "An error occurred while fetching permissions for RoleId {RoleId}", roleId);
// Return an empty list as a safe default to prevent downstream failures. // Return an empty list as a safe default to prevent downstream failures.
return new List<FeaturePermission>(); return new List<FeaturePermission>();
} }

View File

@ -1,16 +1,19 @@
using AutoMapper; using AutoMapper;
using Marco.Pms.Model.Dtos.Project; using Marco.Pms.Model.Dtos.Project;
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Master; using Marco.Pms.Model.Master;
using Marco.Pms.Model.MongoDBModels; using Marco.Pms.Model.MongoDBModels;
using Marco.Pms.Model.Projects; using Marco.Pms.Model.Projects;
using Marco.Pms.Model.ViewModels.Employee;
using Marco.Pms.Model.ViewModels.Projects; using Marco.Pms.Model.ViewModels.Projects;
namespace Marco.Pms.Services.MappingProfiles namespace Marco.Pms.Services.MappingProfiles
{ {
public class ProjectMappingProfile : Profile public class MappingProfile : Profile
{ {
public ProjectMappingProfile() public MappingProfile()
{ {
#region ======================================================= Projects =======================================================
// Your mappings // Your mappings
CreateMap<Project, ProjectVM>(); CreateMap<Project, ProjectVM>();
CreateMap<Project, ProjectInfoVM>(); CreateMap<Project, ProjectInfoVM>();
@ -40,6 +43,11 @@ namespace Marco.Pms.Services.MappingProfiles
CreateMap<StatusMasterMongoDB, StatusMaster>(); CreateMap<StatusMasterMongoDB, StatusMaster>();
CreateMap<ProjectVM, Project>(); CreateMap<ProjectVM, Project>();
CreateMap<CreateProjectDto, Project>(); CreateMap<CreateProjectDto, Project>();
#endregion
#region ======================================================= Projects =======================================================
CreateMap<Employee, EmployeeVM>();
#endregion
} }
} }
} }

View File

@ -158,6 +158,7 @@ builder.Services.AddTransient<S3UploadService>();
#region Customs Services #region Customs Services
builder.Services.AddScoped<RefreshTokenService>(); builder.Services.AddScoped<RefreshTokenService>();
builder.Services.AddScoped<PermissionServices>(); builder.Services.AddScoped<PermissionServices>();
builder.Services.AddScoped<ISignalRService, SignalRService>();
builder.Services.AddScoped<IProjectServices, ProjectServices>(); builder.Services.AddScoped<IProjectServices, ProjectServices>();
#endregion #endregion

View File

@ -5,7 +5,7 @@
void LogInfo(string? message, params object[]? args); void LogInfo(string? message, params object[]? args);
void LogDebug(string? message, params object[]? args); void LogDebug(string? message, params object[]? args);
void LogWarning(string? message, params object[]? args); void LogWarning(string? message, params object[]? args);
void LogError(string? message, params object[]? args); void LogError(Exception? ex, string? message, params object[]? args);
} }
} }

View File

@ -11,16 +11,16 @@ namespace MarcoBMS.Services.Service
_logger = logger; _logger = logger;
} }
public void LogError(string? message, params object[]? args) public void LogError(Exception? ex, string? message, params object[]? args)
{ {
using (LogContext.PushProperty("LogLevel", "Error")) using (LogContext.PushProperty("LogLevel", "Error"))
if (args != null) if (args != null)
{ {
_logger.LogError(message, args); _logger.LogError(ex, message, args);
} }
else else
{ {
_logger.LogError(message); _logger.LogError(ex, message);
} }
} }

View File

@ -1,4 +1,5 @@
using AutoMapper; using AutoMapper;
using AutoMapper.QueryableExtensions;
using Marco.Pms.DataAccess.Data; using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.Activities; using Marco.Pms.Model.Activities;
using Marco.Pms.Model.Dtos.Project; using Marco.Pms.Model.Dtos.Project;
@ -7,12 +8,15 @@ using Marco.Pms.Model.Entitlements;
using Marco.Pms.Model.MongoDBModels; using Marco.Pms.Model.MongoDBModels;
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.Employee;
using Marco.Pms.Model.ViewModels.Projects; using Marco.Pms.Model.ViewModels.Projects;
using Marco.Pms.Services.Helpers; using Marco.Pms.Services.Helpers;
using Marco.Pms.Services.Service.ServiceInterfaces; using Marco.Pms.Services.Service.ServiceInterfaces;
using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Service; using MarcoBMS.Services.Service;
using Microsoft.CodeAnalysis;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Project = Marco.Pms.Model.Projects.Project;
namespace Marco.Pms.Services.Service namespace Marco.Pms.Services.Service
{ {
@ -75,7 +79,7 @@ namespace Marco.Pms.Services.Service
catch (Exception ex) catch (Exception ex)
{ {
// --- Step 5: Graceful Error Handling --- // --- Step 5: Graceful Error Handling ---
_logger.LogError("An unexpected error occurred in GetAllProjectsBasic for tenant {TenantId}. \n {Error}", tenantId, ex.Message); _logger.LogError(ex, "An unexpected error occurred in GetAllProjectsBasic for tenant {TenantId}.", tenantId);
return ApiResponse<object>.ErrorResponse("An internal server error occurred. Please try again later.", null, 500); return ApiResponse<object>.ErrorResponse("An internal server error occurred. Please try again later.", null, 500);
} }
} }
@ -134,7 +138,7 @@ namespace Marco.Pms.Services.Service
catch (Exception ex) catch (Exception ex)
{ {
// --- Step 5: Graceful Error Handling --- // --- Step 5: Graceful Error Handling ---
_logger.LogError("An unexpected error occurred in GetAllProjects for tenant {TenantId}. \n {Error}", tenantId, ex.Message); _logger.LogError(ex, "An unexpected error occurred in GetAllProjects for tenant {TenantId}.", tenantId);
return ApiResponse<object>.ErrorResponse("An internal server error occurred. Please try again later.", null, 500); return ApiResponse<object>.ErrorResponse("An internal server error occurred. Please try again later.", null, 500);
} }
} }
@ -178,7 +182,7 @@ namespace Marco.Pms.Services.Service
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("An unexpected error occurred while getting project {ProjectId} : \n {Error}", id, ex.Message); _logger.LogError(ex, "An unexpected error occurred while getting project {ProjectId}", id);
return ApiResponse<object>.ErrorResponse("An internal server error occurred.", null, 500); return ApiResponse<object>.ErrorResponse("An internal server error occurred.", null, 500);
} }
} }
@ -244,7 +248,7 @@ namespace Marco.Pms.Services.Service
catch (Exception ex) catch (Exception ex)
{ {
// --- Step 5: Graceful Error Handling --- // --- Step 5: Graceful Error Handling ---
_logger.LogError("An unexpected error occurred in Get Project Details for project {ProjectId} for tenant {TenantId}. \n {Error}", id, tenantId, ex.Message); _logger.LogError(ex, "An unexpected error occurred in Get Project Details for project {ProjectId} for tenant {TenantId}. ", id, tenantId);
return ApiResponse<object>.ErrorResponse("An internal server error occurred. Please try again later.", null, 500); return ApiResponse<object>.ErrorResponse("An internal server error occurred. Please try again later.", null, 500);
} }
} }
@ -360,7 +364,7 @@ namespace Marco.Pms.Services.Service
catch (Exception ex) catch (Exception ex)
{ {
// Log the detailed exception // Log the detailed exception
_logger.LogError("Failed to create project in database. Rolling back transaction. \n {Error}", ex.Message); _logger.LogError(ex, "Failed to create project in database. Rolling back transaction.");
// Return a server error as the primary operation failed // Return a server error as the primary operation failed
return ApiResponse<object>.ErrorResponse("An error occurred while saving the project.", ex.Message, 500); return ApiResponse<object>.ErrorResponse("An error occurred while saving the project.", ex.Message, 500);
} }
@ -379,7 +383,7 @@ namespace Marco.Pms.Services.Service
{ {
// The project was created successfully, but a side-effect failed. // The project was created successfully, but a side-effect failed.
// Log this as a warning, as the primary operation succeeded. Don't return an error to the user. // Log this as a warning, as the primary operation succeeded. Don't return an error to the user.
_logger.LogWarning("Project {ProjectId} was created, but a post-creation side-effect (caching/notification) failed. \n {Error}", project.Id, ex.Message); _logger.LogError(ex, "Project {ProjectId} was created, but a post-creation side-effect (caching/notification) failed. ", project.Id);
} }
// 4. Return a success response to the user as soon as the critical data is saved. // 4. Return a success response to the user as soon as the critical data is saved.
@ -435,7 +439,7 @@ namespace Marco.Pms.Services.Service
{ {
// --- Step 3: Handle Concurrency Conflicts --- // --- Step 3: Handle Concurrency Conflicts ---
// This happens if another user modified the project after we fetched it. // This happens if another user modified the project after we fetched it.
_logger.LogWarning("Concurrency conflict while updating project {ProjectId} \n {Error}", id, ex.Message); _logger.LogError(ex, "Concurrency conflict while updating project {ProjectId} ", id);
return ApiResponse<object>.ErrorResponse("Conflict occurred.", "This project has been modified by someone else. Please refresh and try again.", 409); return ApiResponse<object>.ErrorResponse("Conflict occurred.", "This project has been modified by someone else. Please refresh and try again.", 409);
} }
@ -458,13 +462,216 @@ namespace Marco.Pms.Services.Service
catch (Exception ex) catch (Exception ex)
{ {
// --- Step 6: Graceful Error Handling for Unexpected Errors --- // --- Step 6: Graceful Error Handling for Unexpected Errors ---
_logger.LogError("An unexpected error occurred while updating project {ProjectId} \n {Error}", id, ex.Message); _logger.LogError(ex, "An unexpected error occurred while updating project {ProjectId} ", id);
return ApiResponse<object>.ErrorResponse("An internal server error occurred.", null, 500); return ApiResponse<object>.ErrorResponse("An internal server error occurred.", null, 500);
} }
} }
#endregion #endregion
#region =================================================================== Project Allocation APIs ===================================================================
public async Task<ApiResponse<object>> GetEmployeeByProjectID(Guid? projectid, bool includeInactive, Guid tenantId, Employee loggedInEmployee)
{
if (projectid == null)
{
return ApiResponse<object>.ErrorResponse("Invalid Input Parameter", 404);
}
// Fetch assigned project
List<Employee> result = new List<Employee>();
var employeeQuery = _context.ProjectAllocations
.Include(pa => pa.Employee)
.Where(pa => pa.ProjectId == projectid && pa.TenantId == tenantId && pa.Employee != null);
if (includeInactive)
{
result = await employeeQuery.Select(pa => pa.Employee ?? new Employee()).ToListAsync();
}
else
{
result = await employeeQuery
.Where(pa => pa.IsActive)
.Select(pa => pa.Employee ?? new Employee()).ToListAsync();
}
List<EmployeeVM> resultVM = new List<EmployeeVM>();
foreach (Employee employee in result)
{
EmployeeVM vm = _mapper.Map<EmployeeVM>(employee);
resultVM.Add(vm);
}
return ApiResponse<object>.SuccessResponse(resultVM, "Successfully fetched the list of employees for seleted project", 200);
}
/// <summary>
/// Retrieves a list of employees for a specific project.
/// This method is optimized to perform all filtering and mapping on the database server.
/// </summary>
/// <param name="projectId">The ID of the project.</param>
/// <param name="includeInactive">Whether to include employees from inactive allocations.</param>
/// <param name="tenantId">The ID of the current tenant.</param>
/// <param name="loggedInEmployee">The current authenticated employee (used for permission checks).</param>
/// <returns>An ApiResponse containing a list of employees or an error.</returns>
public async Task<ApiResponse<object>> GetEmployeeByProjectIdAsync(Guid? projectId, bool includeInactive, Guid tenantId, Employee loggedInEmployee)
{
// --- Step 1: Input Validation ---
if (projectId == null)
{
_logger.LogWarning("GetEmployeeByProjectID called with a null projectId.");
// 400 Bad Request is more appropriate for invalid input than 404 Not Found.
return ApiResponse<object>.ErrorResponse("Project ID is required.", "Invalid Input Parameter", 400);
}
_logger.LogInfo("Fetching employees for ProjectID: {ProjectId}, IncludeInactive: {IncludeInactive}", projectId, includeInactive);
try
{
// --- CRITICAL: Security Check ---
// Before fetching data, you MUST verify the user has permission to see it.
// This is a placeholder for your actual permission logic.
var hasProjectPermission = await _permission.HasProjectPermission(loggedInEmployee, projectId.Value);
var hasAllEmployeePermission = await _permission.HasPermission(PermissionsMaster.ViewAllEmployees, loggedInEmployee.Id);
var hasviewTeamPermission = await _permission.HasPermission(PermissionsMaster.ViewTeamMembers, loggedInEmployee.Id);
if (!(hasProjectPermission && (hasAllEmployeePermission || hasviewTeamPermission)))
{
_logger.LogWarning("Access DENIED for user {UserId} on project {ProjectId}.", loggedInEmployee.Id, projectId);
return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to view this project's team.", 403);
}
// --- Step 2: Build a Single, Efficient IQueryable ---
// We start with the base query and conditionally add filters before executing it.
// This avoids code duplication and is highly performant.
var employeeQuery = _context.ProjectAllocations
.Where(pa => pa.ProjectId == projectId && pa.TenantId == tenantId);
// Conditionally apply the filter for active allocations.
if (!includeInactive)
{
employeeQuery = employeeQuery.Where(pa => pa.IsActive);
}
// --- Step 3: Project Directly to the ViewModel on the Database Server ---
// This is the most significant performance optimization.
// Instead of fetching full Employee entities, we select only the data needed for the EmployeeVM.
// AutoMapper's ProjectTo is perfect for this, as it translates the mapping configuration into an efficient SQL SELECT statement.
var resultVM = await employeeQuery
.Where(pa => pa.Employee != null) // Safety check for data integrity
.Select(pa => pa.Employee) // Navigate to the Employee entity
.ProjectTo<EmployeeVM>(_mapper.ConfigurationProvider) // Let AutoMapper generate the SELECT
.ToListAsync();
_logger.LogInfo("Successfully fetched {EmployeeCount} employees for project {ProjectId}.", resultVM.Count, projectId);
// Note: The original mapping loop is now completely gone, replaced by the single efficient query above.
return ApiResponse<object>.SuccessResponse(resultVM, "Successfully fetched the list of employees for the selected project.", 200);
}
catch (Exception ex)
{
// --- Step 4: Graceful Error Handling ---
_logger.LogError(ex, "An error occurred while fetching employees for project {ProjectId}. ", projectId);
return ApiResponse<object>.ErrorResponse("An internal server error occurred.", "Database Query Failed", 500);
}
}
public async Task<ApiResponse<object>> GetProjectAllocation(Guid? projectId, Guid tenantId, Employee loggedInEmployee)
{
var employees = await _context.ProjectAllocations
.Where(c => c.TenantId == tenantId && c.ProjectId == projectId && c.Employee != null)
.Include(e => e.Employee)
.Select(e => new
{
ID = e.Id,
EmployeeId = e.EmployeeId,
ProjectId = e.ProjectId,
AllocationDate = e.AllocationDate,
ReAllocationDate = e.ReAllocationDate,
FirstName = e.Employee != null ? e.Employee.FirstName : string.Empty,
LastName = e.Employee != null ? e.Employee.LastName : string.Empty,
MiddleName = e.Employee != null ? e.Employee.MiddleName : string.Empty,
IsActive = e.IsActive,
JobRoleId = (e.JobRoleId != null ? e.JobRoleId : e.Employee != null ? e.Employee.JobRoleId : null)
}).ToListAsync();
return ApiResponse<object>.SuccessResponse(employees, "Success.", 200);
}
/// <summary>
/// Retrieves project allocation details for a specific project.
/// This method is optimized for performance and includes security checks.
/// </summary>
/// <param name="projectId">The ID of the project.</param>
/// <param name="tenantId">The ID of the current tenant.</param>
/// <param name="loggedInEmployee">The current authenticated employee for permission checks.</param>
/// <returns>An ApiResponse containing allocation details or an appropriate error.</returns>
public async Task<ApiResponse<object>> GetProjectAllocationAsync(Guid? projectId, Guid tenantId, Employee loggedInEmployee)
{
// --- Step 1: Input Validation ---
if (projectId == null)
{
_logger.LogWarning("GetProjectAllocation called with a null projectId.");
return ApiResponse<object>.ErrorResponse("Project ID is required.", "Invalid Input Parameter", 400);
}
_logger.LogInfo("Fetching allocations for ProjectID: {ProjectId} for user {UserId}", projectId, loggedInEmployee.Id);
try
{
// --- Step 2: Security and Existence Checks ---
// Before fetching data, you MUST verify the user has permission to see it.
// This is a placeholder for your actual permission logic.
var hasPermission = await _permission.HasProjectPermission(loggedInEmployee, projectId.Value);
if (!hasPermission)
{
_logger.LogWarning("Access DENIED for user {UserId} on project {ProjectId}.", loggedInEmployee.Id, projectId);
return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to view this project's team.", 403);
}
// --- Step 3: Execute a Single, Optimized Database Query ---
// This query projects directly to a new object on the database server, which is highly efficient.
var allocations = await _context.ProjectAllocations
// Filter down to the relevant records first.
.Where(pa => pa.ProjectId == projectId && pa.TenantId == tenantId && pa.Employee != null)
// Project directly to the final shape. This tells EF Core which columns to select.
// The redundant .Include() is removed as EF Core infers the JOIN from this Select.
.Select(pa => new
{
// Fields from ProjectAllocation
ID = pa.Id,
pa.EmployeeId,
pa.ProjectId,
pa.AllocationDate,
pa.ReAllocationDate,
pa.IsActive,
// Fields from the joined Employee table (no null checks needed due to the 'Where' clause)
FirstName = pa.Employee!.FirstName,
LastName = pa.Employee.LastName,
MiddleName = pa.Employee.MiddleName,
// Simplified JobRoleId logic: Use the allocation's role if it exists, otherwise fall back to the employee's default role.
JobRoleId = pa.JobRoleId ?? pa.Employee.JobRoleId
})
.ToListAsync();
_logger.LogInfo("Successfully fetched {AllocationCount} allocations for project {ProjectId}.", allocations.Count, projectId);
return ApiResponse<object>.SuccessResponse(allocations, "Project allocations retrieved successfully.", 200);
}
catch (Exception ex)
{
// --- Step 4: Graceful Error Handling ---
// Log the full exception for debugging, but return a generic, safe error message.
_logger.LogError(ex, "An error occurred while fetching allocations for project {ProjectId}.", projectId);
return ApiResponse<object>.ErrorResponse("An internal server error occurred.", "Database query failed.", 500);
}
}
#endregion
#region =================================================================== Helper Functions =================================================================== #region =================================================================== Helper Functions ===================================================================
/// <summary> /// <summary>
@ -661,7 +868,7 @@ namespace Marco.Pms.Services.Service
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogWarning("Failed to update cache for project {ProjectId} : \n {Error}", projectId, ex.Message); _logger.LogError(ex, "Failed to update cache for project {ProjectId} : ", projectId);
} }
// Map from the database entity to the response ViewModel. // Map from the database entity to the response ViewModel.
@ -682,7 +889,7 @@ namespace Marco.Pms.Services.Service
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("Background cache update failed for project {ProjectId} \n {Error}", project.Id, ex.Message); _logger.LogError(ex, "Background cache update failed for project {ProjectId} ", project.Id);
} }
} }

View File

@ -1,11 +1,11 @@
using System.IdentityModel.Tokens.Jwt; using Marco.Pms.DataAccess.Data;
using System.Security.Claims;
using System.Text;
using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.Authentication; using Marco.Pms.Model.Authentication;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
#nullable disable #nullable disable
namespace MarcoBMS.Services.Service namespace MarcoBMS.Services.Service
@ -94,7 +94,7 @@ namespace MarcoBMS.Services.Service
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("{Error}", ex.Message); _logger.LogError(ex, "Error occured while creating new JWT token for user {UserId}", userId);
throw; throw;
} }
} }
@ -132,7 +132,7 @@ namespace MarcoBMS.Services.Service
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("Error creating MPIN token for userId: {UserId}, tenantId: {TenantId}, error : {Error}", userId, tenantId, ex.Message); _logger.LogError(ex, "Error creating MPIN token for userId: {UserId}, tenantId: {TenantId}", userId, tenantId);
throw; throw;
} }
} }
@ -218,7 +218,7 @@ namespace MarcoBMS.Services.Service
catch (Exception ex) catch (Exception ex)
{ {
// Token is invalid // Token is invalid
_logger.LogError($"Token validation failed: {ex.Message}"); _logger.LogError(ex, "Token validation failed");
return null; return null;
} }
} }

View File

@ -64,7 +64,7 @@ namespace Marco.Pms.Services.Service
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("{error} while uploading file to S3", ex.Message); _logger.LogError(ex, "error occured while uploading file to S3");
} }
@ -87,7 +87,7 @@ namespace Marco.Pms.Services.Service
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("{error} while requesting presigned url from Amazon S3", ex.Message); _logger.LogError(ex, "error occured while requesting presigned url from Amazon S3", ex.Message);
return string.Empty; return string.Empty;
} }
} }
@ -107,7 +107,7 @@ namespace Marco.Pms.Services.Service
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("{error} while deleting from Amazon S3", ex.Message); _logger.LogError(ex, "error ocured while deleting from Amazon S3");
return false; return false;
} }
} }
@ -202,7 +202,7 @@ namespace Marco.Pms.Services.Service
} }
else else
{ {
_logger.LogError("Warning: Could not find MimeType, Type, or ContentType property in Definition."); _logger.LogWarning("Warning: Could not find MimeType, Type, or ContentType property in Definition.");
return "application/octet-stream"; return "application/octet-stream";
} }
} }
@ -211,16 +211,16 @@ namespace Marco.Pms.Services.Service
return "application/octet-stream"; // Default if type cannot be determined return "application/octet-stream"; // Default if type cannot be determined
} }
} }
catch (FormatException) catch (FormatException fEx)
{ {
// Handle cases where the input string is not valid Base64 // Handle cases where the input string is not valid Base64
_logger.LogError("Invalid Base64 string."); _logger.LogError(fEx, "Invalid Base64 string.");
return string.Empty; return string.Empty;
} }
catch (Exception ex) catch (Exception ex)
{ {
// Handle other potential errors during decoding or inspection // Handle other potential errors during decoding or inspection
_logger.LogError($"An error occurred: {ex.Message}"); _logger.LogError(ex, "errors during decoding or inspection");
return string.Empty; return string.Empty;
} }
} }

View File

@ -13,5 +13,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
Task<ApiResponse<object>> GetProjectDetailsOldAsync(Guid id, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> GetProjectDetailsOldAsync(Guid id, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> CreateProjectAsync(CreateProjectDto projectDto, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> CreateProjectAsync(CreateProjectDto projectDto, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> UpdateProjectAsync(Guid id, UpdateProjectDto updateProjectDto, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> UpdateProjectAsync(Guid id, UpdateProjectDto updateProjectDto, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> GetEmployeeByProjectIdAsync(Guid? projectId, bool includeInactive, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> GetProjectAllocationAsync(Guid? projectId, Guid tenantId, Employee loggedInEmployee);
} }
} }

View File

@ -0,0 +1,7 @@
namespace Marco.Pms.Services.Service.ServiceInterfaces
{
public interface ISignalRService
{
Task SendNotificationAsync(object notification);
}
}

View File

@ -0,0 +1,29 @@
using Marco.Pms.Services.Hubs;
using Marco.Pms.Services.Service.ServiceInterfaces;
using MarcoBMS.Services.Service;
using Microsoft.AspNetCore.SignalR;
namespace Marco.Pms.Services.Service
{
public class SignalRService : ISignalRService
{
private readonly IHubContext<MarcoHub> _signalR;
private readonly ILoggingService _logger;
public SignalRService(IHubContext<MarcoHub> signalR, ILoggingService logger)
{
_signalR = signalR ?? throw new ArgumentNullException(nameof(signalR));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task SendNotificationAsync(object notification)
{
try
{
await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception occured during sending notification through signalR");
}
}
}
}