1117 lines
		
	
	
		
			59 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			1117 lines
		
	
	
		
			59 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using Marco.Pms.DataAccess.Data;
 | 
						|
using Marco.Pms.Model.Directory;
 | 
						|
using Marco.Pms.Model.Dtos.Activities;
 | 
						|
using Marco.Pms.Model.Dtos.Master;
 | 
						|
using Marco.Pms.Model.Employees;
 | 
						|
using Marco.Pms.Model.Entitlements;
 | 
						|
using Marco.Pms.Model.Mapper;
 | 
						|
using Marco.Pms.Model.Master;
 | 
						|
using Marco.Pms.Model.Utilities;
 | 
						|
using Marco.Pms.Model.ViewModels.Activities;
 | 
						|
using Marco.Pms.Model.ViewModels.Master;
 | 
						|
using Marco.Pms.Services.Service;
 | 
						|
using MarcoBMS.Services.Helpers;
 | 
						|
using MarcoBMS.Services.Service;
 | 
						|
using Microsoft.EntityFrameworkCore;
 | 
						|
 | 
						|
namespace Marco.Pms.Services.Helpers
 | 
						|
{
 | 
						|
    public class MasterHelper
 | 
						|
    {
 | 
						|
        private readonly ApplicationDbContext _context;
 | 
						|
        private readonly ILoggingService _logger;
 | 
						|
        private readonly UserHelper _userHelper;
 | 
						|
        private readonly PermissionServices _permissionService;
 | 
						|
        private readonly Guid View_Master;
 | 
						|
        private readonly Guid Manage_Master;
 | 
						|
        private readonly Guid tenantId;
 | 
						|
 | 
						|
 | 
						|
        public MasterHelper(ApplicationDbContext context, ILoggingService logger, UserHelper userHelper, PermissionServices permissionServices)
 | 
						|
        {
 | 
						|
            _context = context;
 | 
						|
            _logger = logger;
 | 
						|
            _userHelper = userHelper;
 | 
						|
            _permissionService = permissionServices;
 | 
						|
            View_Master = Guid.Parse("5ffbafe0-7ab0-48b1-bb50-c1bf76b65f9d");
 | 
						|
            Manage_Master = Guid.Parse("588a8824-f924-4955-82d8-fc51956cf323");
 | 
						|
            tenantId = _userHelper.GetTenantId();
 | 
						|
        }
 | 
						|
 | 
						|
        // --------------------------------  Services  --------------------------------
 | 
						|
        public async Task<ApiResponse<object>> GetServices()
 | 
						|
        {
 | 
						|
            _logger.LogInfo("GetServices called");
 | 
						|
 | 
						|
            try
 | 
						|
            {
 | 
						|
                // Step 1: Get current logged-in employee
 | 
						|
                var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
 | 
						|
                // Step 2: Fetch services for the tenant
 | 
						|
                var services = await _context.ServicesMasters
 | 
						|
                    .Where(s => s.TenantId == tenantId && s.IsActive)
 | 
						|
                    .Select(s => s.ToServiceMasterVMFromServiceMaster())
 | 
						|
                    .ToListAsync();
 | 
						|
 | 
						|
                _logger.LogInfo("Fetched {Count} service records for tenantId: {TenantId}", services.Count, tenantId);
 | 
						|
 | 
						|
                return ApiResponse<object>.SuccessResponse(services, $"{services.Count} record(s) of services fetched successfully", 200);
 | 
						|
            }
 | 
						|
            catch (Exception ex)
 | 
						|
            {
 | 
						|
                _logger.LogError("Error fetching services: {Message}", ex.Message);
 | 
						|
                return ApiResponse<object>.ErrorResponse("An error occurred while fetching services", ex.Message, 500);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        public async Task<ApiResponse<object>> CreateService(ServiceMasterDto serviceMasterDto)
 | 
						|
        {
 | 
						|
            _logger.LogInfo("CreateService called with Name: {ServiceName}", serviceMasterDto?.Name ?? "null");
 | 
						|
 | 
						|
            try
 | 
						|
            {
 | 
						|
                var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
 | 
						|
                // Step 1: Permission check
 | 
						|
                var hasManagePermission = await _permissionService.HasPermission(Manage_Master, loggedInEmployee.Id);
 | 
						|
                if (!hasManagePermission)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Access denied for employeeId: {EmployeeId}", loggedInEmployee.Id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("You don't have access", "You don't have permission to take this action", 403);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 2: Validate input
 | 
						|
                if (string.IsNullOrWhiteSpace(serviceMasterDto?.Name) ||
 | 
						|
                    string.IsNullOrWhiteSpace(serviceMasterDto?.Description))
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Invalid input data for service creation by employeeId: {EmployeeId}", loggedInEmployee.Id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Invalid data", "Name and Description are required", 400);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 3: Check for duplicate name
 | 
						|
                bool isExist = await _context.ServicesMasters
 | 
						|
                    .AnyAsync(s => s.TenantId == tenantId && s.Name.ToLower() == serviceMasterDto.Name.ToLower());
 | 
						|
 | 
						|
                if (isExist)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Duplicate service name '{ServiceName}' attempted by employeeId: {EmployeeId}", serviceMasterDto.Name, loggedInEmployee.Id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse(
 | 
						|
                        $"Service with name '{serviceMasterDto.Name}' already exists",
 | 
						|
                        $"Service with name '{serviceMasterDto.Name}' already exists", 400);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 4: Save new service
 | 
						|
                ServicesMaster service = serviceMasterDto.ToServicesMasterFromServiceMasterDto(tenantId);
 | 
						|
                _context.ServicesMasters.Add(service);
 | 
						|
                await _context.SaveChangesAsync();
 | 
						|
 | 
						|
                var response = service.ToServiceMasterVMFromServiceMaster();
 | 
						|
 | 
						|
                _logger.LogInfo("New service '{ServiceName}' created successfully by employeeId: {EmployeeId}", service.Name, loggedInEmployee.Id);
 | 
						|
 | 
						|
                return ApiResponse<object>.SuccessResponse(response, "New service created successfully", 201);
 | 
						|
            }
 | 
						|
            catch (Exception ex)
 | 
						|
            {
 | 
						|
                _logger.LogError("Error creating service: {Error}", ex.Message);
 | 
						|
                return ApiResponse<object>.ErrorResponse("Failed to create service", ex.Message, 500);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        public async Task<ApiResponse<object>> UpdateService(Guid id, ServiceMasterDto serviceMasterDto)
 | 
						|
        {
 | 
						|
            _logger.LogInfo("UpdateService called for Id: {Id}", id);
 | 
						|
 | 
						|
            try
 | 
						|
            {
 | 
						|
                var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
 | 
						|
                // Step 1: Permission check
 | 
						|
                var hasPermission = await _permissionService.HasPermission(Manage_Master, loggedInEmployee.Id);
 | 
						|
                if (!hasPermission)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Access denied. EmployeeId: {EmployeeId} lacks Manage_Master permission.", loggedInEmployee.Id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Access denied", "You don't have permission to take this action", 403);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 2: Input validation
 | 
						|
                if (serviceMasterDto == null || serviceMasterDto.Id != id ||
 | 
						|
                    string.IsNullOrWhiteSpace(serviceMasterDto.Name) ||
 | 
						|
                    string.IsNullOrWhiteSpace(serviceMasterDto.Description))
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Invalid input data provided for UpdateService. Id: {Id}", id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Invalid input", "Please provide valid service data", 400);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 3: Retrieve service
 | 
						|
                var service = await _context.ServicesMasters
 | 
						|
                    .FirstOrDefaultAsync(s => s.Id == id && s.TenantId == tenantId && s.IsActive);
 | 
						|
 | 
						|
                if (service == null)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Service not found for Id: {Id}, Tenant: {TenantId}", id, tenantId);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Service not found", "The requested service does not exist", 404);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 4: Update and save
 | 
						|
                service.Name = serviceMasterDto.Name.Trim();
 | 
						|
                service.Description = serviceMasterDto.Description.Trim();
 | 
						|
 | 
						|
                await _context.SaveChangesAsync();
 | 
						|
 | 
						|
                var response = service.ToServiceMasterVMFromServiceMaster();
 | 
						|
 | 
						|
                _logger.LogInfo("Service updated successfully. Id: {Id}, TenantId: {TenantId}", service.Id, tenantId);
 | 
						|
                return ApiResponse<object>.SuccessResponse(response, "Service updated successfully", 200);
 | 
						|
            }
 | 
						|
            catch (Exception ex)
 | 
						|
            {
 | 
						|
                _logger.LogError("Error while updating service Id: {Id}. Exception: {Message}", id, ex.Message);
 | 
						|
                return ApiResponse<object>.ErrorResponse("An error occurred while updating the service", ex.Message, 500);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        public async Task<ApiResponse<object>> DeleteService(Guid id, bool isActive)
 | 
						|
        {
 | 
						|
            _logger.LogInfo("DeleteService called with ServiceId: {ServiceId}, IsActive: {IsActive}", id, isActive);
 | 
						|
 | 
						|
            try
 | 
						|
            {
 | 
						|
                // Step 1: Get current user and validate permission
 | 
						|
                var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
                var hasPermission = await _permissionService.HasPermission(Manage_Master, loggedInEmployee.Id);
 | 
						|
 | 
						|
                if (!hasPermission)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Access denied. EmployeeId: {EmployeeId} does not have Manage_Master permission.", loggedInEmployee.Id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Access denied", "You don't have permission to delete services", 403);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 2: Check if the service exists
 | 
						|
                var service = await _context.ServicesMasters
 | 
						|
                    .FirstOrDefaultAsync(s => s.Id == id && s.TenantId == tenantId);
 | 
						|
 | 
						|
                if (service == null)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Service not found. ServiceId: {ServiceId}", id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Service not found", "Service not found or already deleted", 404);
 | 
						|
                }
 | 
						|
 | 
						|
                // Protect system-defined service
 | 
						|
                if (service.IsSystem)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Attempt to delete system-defined service. ServiceId: {ServiceId}", id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Cannot delete system-defined service", "This service is system-defined and cannot be deleted", 400);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 3: Soft delete or restore
 | 
						|
                service.IsActive = isActive;
 | 
						|
                await _context.SaveChangesAsync();
 | 
						|
 | 
						|
                var status = isActive ? "restored" : "deactivated";
 | 
						|
                _logger.LogInfo("Service {ServiceId} has been {Status} successfully by EmployeeId: {EmployeeId}", id, status, loggedInEmployee.Id);
 | 
						|
 | 
						|
                return ApiResponse<object>.SuccessResponse(new { }, $"Service {status} successfully", 200);
 | 
						|
            }
 | 
						|
            catch (Exception ex)
 | 
						|
            {
 | 
						|
                _logger.LogError("Unexpected error occurred while deleting service {ServiceId} : {Error}", id, ex.Message);
 | 
						|
                return ApiResponse<object>.ErrorResponse("Error deleting service", ex.Message, 500);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // --------------------------------  Activity Group  --------------------------------
 | 
						|
 | 
						|
        public async Task<ApiResponse<object>> GetActivityGroups()
 | 
						|
        {
 | 
						|
            _logger.LogInfo("GetActivityGroups called");
 | 
						|
 | 
						|
            try
 | 
						|
            {
 | 
						|
                // Step 1: Get logged-in employee
 | 
						|
                var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
 | 
						|
                // Step 2: Fetch all activity groups for the tenant
 | 
						|
                var activityGroups = await _context.ActivityGroupMasters
 | 
						|
                    .Include(ag => ag.ServicesMaster)
 | 
						|
                    .Where(ag => ag.TenantId == tenantId && ag.IsActive)
 | 
						|
                    .Select(ag => ag.ToActivityGroupMasterVMFromActivityGroupMaster(ag.ServicesMaster != null ? ag.ServicesMaster.Name : ""))
 | 
						|
                    .ToListAsync();
 | 
						|
 | 
						|
                _logger.LogInfo("{Count} activity group(s) fetched for tenantId: {TenantId}", activityGroups.Count, tenantId);
 | 
						|
 | 
						|
                return ApiResponse<object>.SuccessResponse(activityGroups, $"{activityGroups.Count} record(s) of activity groups fetched successfully", 200);
 | 
						|
            }
 | 
						|
            catch (Exception ex)
 | 
						|
            {
 | 
						|
                _logger.LogError("Error fetching activity groups: {Message}", ex.Message);
 | 
						|
                return ApiResponse<object>.ErrorResponse("An error occurred while fetching activity groups", ex.Message, 500);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        public async Task<ApiResponse<object>> CreateActivityGroup(ActivityGroupDto activityGroupDto)
 | 
						|
        {
 | 
						|
            _logger.LogInfo("CreateActivityGroup called with Name: {Name}", activityGroupDto?.Name ?? "null");
 | 
						|
 | 
						|
            try
 | 
						|
            {
 | 
						|
                var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
 | 
						|
                // Step 1: Check Manage Master permission
 | 
						|
                var hasPermission = await _permissionService.HasPermission(Manage_Master, loggedInEmployee.Id);
 | 
						|
                if (!hasPermission)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Access denied. EmployeeId: {EmployeeId} lacks Manage_Master permission", loggedInEmployee.Id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Access denied", "You don't have permission to take this action", 403);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 2: Validate input
 | 
						|
                if (string.IsNullOrWhiteSpace(activityGroupDto?.Name) || string.IsNullOrWhiteSpace(activityGroupDto.Description))
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Invalid input data for activity group by employeeId: {EmployeeId}", loggedInEmployee.Id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Invalid input", "Name and Description are required", 400);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 3: Check for duplicate name within ActivityGroupMasters
 | 
						|
                bool isExist = await _context.ActivityGroupMasters
 | 
						|
                    .AnyAsync(ag => ag.TenantId == tenantId && ag.Name.ToLower() == activityGroupDto.Name.ToLower());
 | 
						|
 | 
						|
                if (isExist)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Duplicate activity group name '{Name}' attempted by employeeId: {EmployeeId}", activityGroupDto.Name, loggedInEmployee.Id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse(
 | 
						|
                        $"Activity group with name '{activityGroupDto.Name}' already exists",
 | 
						|
                        $"Activity group with name '{activityGroupDto.Name}' already exists", 400);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 4: Map and persist
 | 
						|
                var activityGroup = activityGroupDto.ToActivityGroupMasterFromActivityGroupDto(tenantId);
 | 
						|
                _context.ActivityGroupMasters.Add(activityGroup);
 | 
						|
                await _context.SaveChangesAsync();
 | 
						|
 | 
						|
                var service = await _context.ServicesMasters.FirstOrDefaultAsync(s => s.Id == activityGroup.ServiceId);
 | 
						|
 | 
						|
                var response = activityGroup.ToActivityGroupMasterVMFromActivityGroupMaster(service?.Name ?? "");
 | 
						|
 | 
						|
                _logger.LogInfo("New activity group '{Name}' created successfully by employeeId: {EmployeeId}", activityGroup.Name, loggedInEmployee.Id);
 | 
						|
                return ApiResponse<object>.SuccessResponse(response, "New activity group created successfully", 201);
 | 
						|
            }
 | 
						|
            catch (Exception ex)
 | 
						|
            {
 | 
						|
                _logger.LogError("Error creating activity group: {Error}", ex.Message);
 | 
						|
                return ApiResponse<object>.ErrorResponse("Failed to create activity group", ex.Message, 500);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        public async Task<ApiResponse<object>> UpdateActivityGroup(Guid id, ActivityGroupDto activityGroupDto)
 | 
						|
        {
 | 
						|
            _logger.LogInfo("UpdateActivityGroup called for Id: {Id}", id);
 | 
						|
 | 
						|
            try
 | 
						|
            {
 | 
						|
                // Step 1: Permission check
 | 
						|
                var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
                var hasPermission = await _permissionService.HasPermission(Manage_Master, loggedInEmployee.Id);
 | 
						|
                if (!hasPermission)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Access denied. EmployeeId: {EmployeeId} lacks Manage_Master permission.", loggedInEmployee.Id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Access denied", "You don't have permission to take this action", 403);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 2: Input validation
 | 
						|
                if (activityGroupDto == null || activityGroupDto.Id != id ||
 | 
						|
                    string.IsNullOrWhiteSpace(activityGroupDto.Name) ||
 | 
						|
                    string.IsNullOrWhiteSpace(activityGroupDto.Description))
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Invalid input for activity group update. Id: {Id}", id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Invalid input", "Please provide valid data to update activity group", 400);
 | 
						|
                }
 | 
						|
 | 
						|
                var service = await _context.ServicesMasters.FirstOrDefaultAsync(s => s.Id == activityGroupDto.ServiceId && s.IsActive);
 | 
						|
 | 
						|
                if (service == null)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("User tries to update activity group, but service not found");
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Invalid service ID", "Please provide valid service ID to update activity group", 400);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 3: Retrieve existing activity group
 | 
						|
                var activityGroup = await _context.ActivityGroupMasters
 | 
						|
                    .Include(ag => ag.ServicesMaster)
 | 
						|
                    .FirstOrDefaultAsync(ag => ag.Id == id && ag.TenantId == tenantId && ag.IsActive);
 | 
						|
 | 
						|
                if (activityGroup == null)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Activity group not found. Id: {Id}, TenantId: {TenantId}", id, tenantId);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Activity group not found", "No such activity group exists", 404);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 4: Update and save
 | 
						|
                activityGroup.Name = activityGroupDto.Name.Trim();
 | 
						|
                activityGroup.Description = activityGroupDto.Description.Trim();
 | 
						|
 | 
						|
                await _context.SaveChangesAsync();
 | 
						|
 | 
						|
                var response = activityGroup.ToActivityGroupMasterVMFromActivityGroupMaster(service.Name ?? "");
 | 
						|
 | 
						|
                _logger.LogInfo("Activity group updated successfully. Id: {Id}, TenantId: {TenantId}", activityGroup.Id, tenantId);
 | 
						|
                return ApiResponse<object>.SuccessResponse(response, "Activity group updated successfully", 200);
 | 
						|
            }
 | 
						|
            catch (Exception ex)
 | 
						|
            {
 | 
						|
                _logger.LogError("Error while updating activity group Id: {Id}. Exception: {Message}", id, ex.Message);
 | 
						|
                return ApiResponse<object>.ErrorResponse("An error occurred while updating the activity group", ex.Message, 500);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        public async Task<ApiResponse<object>> DeleteActivityGroup(Guid id, bool isActive)
 | 
						|
        {
 | 
						|
            _logger.LogInfo("DeleteActivityGroup called with ActivityGroupId: {ActivityGroupId}, IsActive: {IsActive}", id, isActive);
 | 
						|
 | 
						|
            try
 | 
						|
            {
 | 
						|
                // Step 1: Permission check
 | 
						|
                var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
                var hasPermission = await _permissionService.HasPermission(Manage_Master, loggedInEmployee.Id);
 | 
						|
 | 
						|
                if (!hasPermission)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Access denied. EmployeeId: {EmployeeId} does not have Manage_Master permission.", loggedInEmployee.Id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Access denied", "You don't have permission to delete activity groups", 403);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 2: Fetch the activity group
 | 
						|
                var activityGroup = await _context.ActivityGroupMasters
 | 
						|
                    .FirstOrDefaultAsync(ag => ag.Id == id && ag.TenantId == tenantId);
 | 
						|
 | 
						|
                if (activityGroup == null)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("ActivityGroup not found. Id: {ActivityGroupId}", id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Activity group not found", "Activity group not found or already deleted", 404);
 | 
						|
                }
 | 
						|
 | 
						|
                //Protect system-defined activity group
 | 
						|
                if (activityGroup.IsSystem)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Attempt to delete system-defined activity group. Id: {ActivityGroupId}", id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Cannot delete system-defined activity group", "This activity group is system-defined and cannot be deleted", 400);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 3: Perform soft delete or restore
 | 
						|
                activityGroup.IsActive = isActive;
 | 
						|
                await _context.SaveChangesAsync();
 | 
						|
 | 
						|
                var status = isActive ? "restored" : "deactivated";
 | 
						|
                _logger.LogInfo("ActivityGroup {ActivityGroupId} has been {Status} by EmployeeId: {EmployeeId}", id, status, loggedInEmployee.Id);
 | 
						|
 | 
						|
                return ApiResponse<object>.SuccessResponse(new { }, $"Activity group {status} successfully", 200);
 | 
						|
            }
 | 
						|
            catch (Exception ex)
 | 
						|
            {
 | 
						|
                _logger.LogError("Unexpected error occurred while deleting ActivityGroup {ActivityGroupId} : {Error}", id, ex.Message);
 | 
						|
                return ApiResponse<object>.ErrorResponse("Error deleting activity group", ex.Message, 500);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // --------------------------------  Activity  --------------------------------
 | 
						|
        public async Task<ApiResponse<object>> GetActivitiesMaster()
 | 
						|
        {
 | 
						|
            _logger.LogInfo("GetActivitiesMaster called");
 | 
						|
 | 
						|
            try
 | 
						|
            {
 | 
						|
 | 
						|
                // Step 1: Fetch all active activities for the tenant
 | 
						|
                var activities = await _context.ActivityMasters
 | 
						|
                    .Include(c => c.ServicesMaster)
 | 
						|
                    .Include(c => c.ActivityGroupMaster)
 | 
						|
                    .Where(c => c.TenantId == tenantId && c.IsActive)
 | 
						|
                    .ToListAsync();
 | 
						|
 | 
						|
                if (activities.Count == 0)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("No active activities found for tenantId: {TenantId}", tenantId);
 | 
						|
                    return ApiResponse<object>.SuccessResponse(new List<ActivityVM>(), "No activity records found", 200);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 2: Fetch all checklists for those activities in a single query to avoid N+1
 | 
						|
                var activityIds = activities.Select(a => a.Id).ToList();
 | 
						|
                var checkLists = await _context.ActivityCheckLists
 | 
						|
                    .Where(c => c.TenantId == tenantId && activityIds.Contains(c.ActivityId))
 | 
						|
                    .ToListAsync();
 | 
						|
 | 
						|
                // Step 3: Group checklists by activity
 | 
						|
                var groupedChecklists = checkLists
 | 
						|
                    .GroupBy(c => c.ActivityId)
 | 
						|
                    .ToDictionary(g => g.Key, g => g.ToList());
 | 
						|
 | 
						|
                // Step 4: Map to ViewModel
 | 
						|
                var activityVMs = activities.Select(activity =>
 | 
						|
                {
 | 
						|
                    var checklistForActivity = groupedChecklists.ContainsKey(activity.Id)
 | 
						|
                        ? groupedChecklists[activity.Id]
 | 
						|
                        : new List<ActivityCheckList>();
 | 
						|
 | 
						|
                    var checklistVMs = checklistForActivity
 | 
						|
                        .Select(cl => cl.ToCheckListVMFromActivityCheckList(activity.Id, false))
 | 
						|
                        .ToList();
 | 
						|
 | 
						|
                    return activity.ToActivityVMFromActivityMaster(checklistVMs, activity.ServicesMaster?.Name, activity.ActivityGroupMaster?.Name);
 | 
						|
                }).ToList();
 | 
						|
 | 
						|
                _logger.LogInfo("{Count} activity records fetched successfully for tenantId: {TenantId}", activityVMs.Count, tenantId);
 | 
						|
 | 
						|
                return ApiResponse<object>.SuccessResponse(activityVMs, $"{activityVMs.Count} activity records fetched successfully", 200);
 | 
						|
            }
 | 
						|
            catch (Exception ex)
 | 
						|
            {
 | 
						|
                _logger.LogError("Error occurred in GetActivitiesMaster: {Error}", ex.Message);
 | 
						|
                return ApiResponse<object>.ErrorResponse("Failed to fetch activity records", ex.Message, 500);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        public async Task<ApiResponse<object>> CreateActivity(CreateActivityMasterDto createActivity)
 | 
						|
        {
 | 
						|
            _logger.LogInfo("CreateActivity called with ActivityName: {Name}", createActivity?.ActivityName ?? "null");
 | 
						|
 | 
						|
            try
 | 
						|
            {
 | 
						|
                // Step 1: Validate input
 | 
						|
                if (createActivity == null)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Null request body received in CreateActivity");
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Invalid input", "Activity data is required", 400);
 | 
						|
                }
 | 
						|
 | 
						|
                var activityGroup = await _context.ActivityGroupMasters
 | 
						|
                    .Include(ag => ag.ServicesMaster)
 | 
						|
                    .FirstOrDefaultAsync(ag => ag.Id == createActivity.ActitvityGroupId && ag.ServiceId == createActivity.ServiceId);
 | 
						|
                if (activityGroup == null)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("User tried to create new activity, but not found activity group");
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Invalid input", "Activity data is required", 400);
 | 
						|
                }
 | 
						|
 | 
						|
                var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
 | 
						|
                // Step 2: Check permissions
 | 
						|
                var hasPermission = await _permissionService.HasPermission(Manage_Master, loggedInEmployee.Id);
 | 
						|
                if (!hasPermission)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Access denied. EmployeeId: {EmployeeId} does not have Manage_Master permission.", loggedInEmployee.Id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Access denied", "You don't have permission to perform this action", 403);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 3: Convert DTO to entity and add activity
 | 
						|
                var activityMaster = createActivity.ToActivityMasterFromCreateActivityMasterDto(tenantId);
 | 
						|
                _context.ActivityMasters.Add(activityMaster);
 | 
						|
                await _context.SaveChangesAsync();
 | 
						|
 | 
						|
                List<CheckListVM> checkListVMs = new();
 | 
						|
 | 
						|
                // Step 4: Handle checklist items if present
 | 
						|
                if (createActivity.CheckList?.Any() == true)
 | 
						|
                {
 | 
						|
                    var activityCheckLists = createActivity.CheckList
 | 
						|
                        .Select(c => c.ToActivityCheckListFromCreateCheckListDto(tenantId, activityMaster.Id))
 | 
						|
                        .ToList();
 | 
						|
 | 
						|
                    _context.ActivityCheckLists.AddRange(activityCheckLists);
 | 
						|
                    await _context.SaveChangesAsync();
 | 
						|
 | 
						|
                    checkListVMs = activityCheckLists
 | 
						|
                        .Select(c => c.ToCheckListVMFromActivityCheckList(activityMaster.Id, false))
 | 
						|
                        .ToList();
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 5: Prepare final response
 | 
						|
                var activityVM = activityMaster.ToActivityVMFromActivityMaster(checkListVMs, activityGroup.ServicesMaster?.Name, activityGroup.Name);
 | 
						|
 | 
						|
                _logger.LogInfo("Activity '{Name}' created successfully for tenant {TenantId}", activityMaster.ActivityName, tenantId);
 | 
						|
                return ApiResponse<object>.SuccessResponse(activityVM, "Activity created successfully", 201);
 | 
						|
            }
 | 
						|
            catch (Exception ex)
 | 
						|
            {
 | 
						|
                _logger.LogError("Error occurred while creating activity: {Message}", ex.Message);
 | 
						|
                return ApiResponse<object>.ErrorResponse("An error occurred while creating activity", ex.Message, 500);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        public async Task<ApiResponse<object>> UpdateActivity(Guid id, CreateActivityMasterDto createActivity)
 | 
						|
        {
 | 
						|
            _logger.LogInfo("UpdateActivity called for ActivityId: {ActivityId}", id);
 | 
						|
 | 
						|
            try
 | 
						|
            {
 | 
						|
                // Step 1: Validate input
 | 
						|
                if (createActivity == null || string.IsNullOrWhiteSpace(createActivity.ActivityName) || string.IsNullOrWhiteSpace(createActivity.UnitOfMeasurement))
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Invalid activity update input for ActivityId: {ActivityId}", id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Invalid input", "ActivityName and UnitOfMeasurement are required", 400);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 2: Check permissions
 | 
						|
                var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
                var hasPermission = await _permissionService.HasPermission(Manage_Master, loggedInEmployee.Id);
 | 
						|
                if (!hasPermission)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Access denied for employeeId: {EmployeeId}", loggedInEmployee.Id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Access denied", "You don't have permission to update activities", 403);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 3: Validate service, activity group, and activity existence
 | 
						|
                var activityGroup = await _context.ActivityGroupMasters.Include(ag => ag.ServicesMaster).FirstOrDefaultAsync(ag => ag.Id == createActivity.ActitvityGroupId && ag.IsActive);
 | 
						|
 | 
						|
                if (activityGroup == null || activityGroup.ServiceId != createActivity.ServiceId)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("User tries to update activity, but cannot able found activity group or service");
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Invalid activity group ID or service ID", "Please provide valid activity group ID or service ID to update activity group", 400);
 | 
						|
                }
 | 
						|
 | 
						|
                var activity = await _context.ActivityMasters
 | 
						|
                    .Include(a => a.ServicesMaster)
 | 
						|
                    .Include(a => a.ActivityGroupMaster)
 | 
						|
                    .FirstOrDefaultAsync(a => a.Id == id && a.IsActive && a.TenantId == tenantId);
 | 
						|
 | 
						|
                if (activity == null)
 | 
						|
                {
 | 
						|
                    _logger.LogError("Activity not found for ActivityId: {ActivityId}, TenantId: {TenantId}", id, tenantId);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Activity not found", "Activity not found", 404);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 4: Update activity core data
 | 
						|
                activity.ActivityName = createActivity.ActivityName.Trim();
 | 
						|
                activity.UnitOfMeasurement = createActivity.UnitOfMeasurement.Trim();
 | 
						|
                if (activity.ServiceId == null)
 | 
						|
                {
 | 
						|
                    activity.ServiceId = createActivity.ServiceId;
 | 
						|
                }
 | 
						|
                activity.ActitvityGroupId = createActivity.ActitvityGroupId;
 | 
						|
 | 
						|
                await _context.SaveChangesAsync();
 | 
						|
 | 
						|
                // Step 5: Handle checklist updates
 | 
						|
                var existingChecklists = await _context.ActivityCheckLists
 | 
						|
                    .AsNoTracking()
 | 
						|
                    .Where(c => c.ActivityId == activity.Id)
 | 
						|
                    .ToListAsync();
 | 
						|
 | 
						|
                var updatedChecklistVMs = new List<CheckListVM>();
 | 
						|
 | 
						|
                if (createActivity.CheckList != null && createActivity.CheckList.Any())
 | 
						|
                {
 | 
						|
                    var incomingCheckIds = createActivity.CheckList.Where(c => c.Id != null).Select(c => c.Id!.Value).ToHashSet();
 | 
						|
 | 
						|
                    // Prepare lists
 | 
						|
                    var newChecks = createActivity.CheckList.Where(c => c.Id == null);
 | 
						|
                    var updates = createActivity.CheckList.Where(c => c.Id != null && existingChecklists.Any(ec => ec.Id == c.Id));
 | 
						|
                    var deletes = existingChecklists.Where(ec => !incomingCheckIds.Contains(ec.Id)).ToList();
 | 
						|
 | 
						|
                    var toAdd = newChecks
 | 
						|
                        .Select(c => c.ToActivityCheckListFromCreateCheckListDto(tenantId, activity.Id))
 | 
						|
                        .ToList();
 | 
						|
 | 
						|
                    var toUpdate = updates
 | 
						|
                        .Select(c => c.ToActivityCheckListFromCreateCheckListDto(tenantId, activity.Id))
 | 
						|
                        .ToList();
 | 
						|
 | 
						|
                    _context.ActivityCheckLists.AddRange(toAdd);
 | 
						|
                    _context.ActivityCheckLists.UpdateRange(toUpdate);
 | 
						|
                    _context.ActivityCheckLists.RemoveRange(deletes);
 | 
						|
 | 
						|
                    await _context.SaveChangesAsync();
 | 
						|
 | 
						|
                    // Prepare view model
 | 
						|
                    updatedChecklistVMs = toAdd.Concat(toUpdate)
 | 
						|
                        .Select(c => c.ToCheckListVMFromActivityCheckList(activity.Id, false))
 | 
						|
                        .ToList();
 | 
						|
                }
 | 
						|
                else if (existingChecklists.Any())
 | 
						|
                {
 | 
						|
                    // If no checklist provided, remove all existing
 | 
						|
                    _context.ActivityCheckLists.RemoveRange(existingChecklists);
 | 
						|
                    await _context.SaveChangesAsync();
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 6: Prepare response
 | 
						|
                var activityVM = activity.ToActivityVMFromActivityMaster(updatedChecklistVMs, activityGroup.ServicesMaster?.Name, activityGroup.Name);
 | 
						|
 | 
						|
                _logger.LogInfo("Activity updated successfully. ActivityId: {ActivityId}, TenantId: {TenantId}", activity.Id, tenantId);
 | 
						|
                return ApiResponse<object>.SuccessResponse(activityVM, "Activity updated successfully", 200);
 | 
						|
            }
 | 
						|
            catch (Exception ex)
 | 
						|
            {
 | 
						|
                _logger.LogError("Exception in UpdateActivity: {Message}", ex.Message);
 | 
						|
                return ApiResponse<object>.ErrorResponse("Error updating activity", ex.Message, 500);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        public async Task<ApiResponse<object>> DeleteActivity(Guid id, bool isActive)
 | 
						|
        {
 | 
						|
            _logger.LogInfo("DeleteActivity called with ActivityId: {ActivityId}, IsActive: {IsActive}", id, isActive);
 | 
						|
 | 
						|
            try
 | 
						|
            {
 | 
						|
                // Step 1: Validate permission
 | 
						|
                var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
                var hasPermission = await _permissionService.HasPermission(Manage_Master, loggedInEmployee.Id);
 | 
						|
 | 
						|
                if (!hasPermission)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Access denied for EmployeeId: {EmployeeId}", loggedInEmployee.Id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Access denied", "You don't have permission to delete activities", 403);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 2: Fetch the activity
 | 
						|
                var activity = await _context.ActivityMasters
 | 
						|
                    .FirstOrDefaultAsync(a => a.Id == id && a.TenantId == tenantId);
 | 
						|
 | 
						|
                if (activity == null)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Activity not found. ActivityId: {ActivityId}", id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Activity not found", "Activity not found or already deleted", 404);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 3: Perform soft delete/restore
 | 
						|
                activity.IsActive = isActive;
 | 
						|
                await _context.SaveChangesAsync();
 | 
						|
 | 
						|
                string status = isActive ? "restored" : "deactivated";
 | 
						|
                _logger.LogInfo("Activity {ActivityId} {Status} successfully by EmployeeId: {EmployeeId}", id, status, loggedInEmployee.Id);
 | 
						|
 | 
						|
                return ApiResponse<object>.SuccessResponse(new { }, $"Activity {status} successfully", 200);
 | 
						|
            }
 | 
						|
            catch (Exception ex)
 | 
						|
            {
 | 
						|
                _logger.LogError("Unexpected error while deleting activity {ActivityId} : {Error}", id, ex.Message);
 | 
						|
                return ApiResponse<object>.ErrorResponse("Error deleting activity", ex.Message, 500);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // -------------------------------- Contact Category  --------------------------------
 | 
						|
        public async Task<ApiResponse<object>> CreateContactCategory(CreateContactCategoryDto contactCategoryDto)
 | 
						|
        {
 | 
						|
            Guid tenantId = _userHelper.GetTenantId();
 | 
						|
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
            if (contactCategoryDto != null)
 | 
						|
            {
 | 
						|
                ContactCategoryMaster? existingContactCategory = await _context.ContactCategoryMasters.FirstOrDefaultAsync(c => c.TenantId == tenantId && c.Name.ToLower() == (contactCategoryDto.Name != null ? contactCategoryDto.Name.ToLower() : ""));
 | 
						|
                if (existingContactCategory == null)
 | 
						|
                {
 | 
						|
                    ContactCategoryMaster contactCategory = contactCategoryDto.ToContactCategoryMasterFromCreateContactCategoryDto(tenantId);
 | 
						|
                    _context.ContactCategoryMasters.Add(contactCategory);
 | 
						|
                    await _context.SaveChangesAsync();
 | 
						|
                    ContactCategoryVM categoryVM = contactCategory.ToContactCategoryVMFromContactCategoryMaster();
 | 
						|
 | 
						|
                    _logger.LogInfo("Employee ID {LoggedInEmployeeId} created a contact category {ContactCategoryId}.", LoggedInEmployee.Id, contactCategory.Id);
 | 
						|
                    return ApiResponse<object>.SuccessResponse(categoryVM, "Category Created Successfully", 200);
 | 
						|
                }
 | 
						|
                _logger.LogWarning("Employee ID {LoggedInEmployeeId} attempted to create an existing contact category.", LoggedInEmployee.Id);
 | 
						|
                return ApiResponse<object>.ErrorResponse("Category already existed", "Category already existed", 409);
 | 
						|
            }
 | 
						|
            _logger.LogWarning("Employee with ID {LoggedInEmployeeId} sended empty payload", LoggedInEmployee.Id);
 | 
						|
            return ApiResponse<object>.ErrorResponse("User Send empty Payload", "User Send empty Payload", 400);
 | 
						|
        }
 | 
						|
        public async Task<ApiResponse<object>> UpdateContactCategory(Guid id, UpdateContactCategoryDto contactCategoryDto)
 | 
						|
        {
 | 
						|
            Guid tenantId = _userHelper.GetTenantId();
 | 
						|
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
            if (contactCategoryDto != null && id == contactCategoryDto.Id)
 | 
						|
            {
 | 
						|
                ContactCategoryMaster? contactCategory = await _context.ContactCategoryMasters.FirstOrDefaultAsync(c => c.TenantId == tenantId && c.Id == id);
 | 
						|
                if (contactCategory != null)
 | 
						|
                {
 | 
						|
                    contactCategory.Name = contactCategoryDto.Name ?? "";
 | 
						|
                    contactCategory.Description = contactCategoryDto.Description ?? "";
 | 
						|
 | 
						|
                    _context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog
 | 
						|
                    {
 | 
						|
                        RefereanceId = contactCategory.Id,
 | 
						|
                        UpdatedById = LoggedInEmployee.Id,
 | 
						|
                        UpdateAt = DateTime.UtcNow
 | 
						|
                    });
 | 
						|
 | 
						|
                    await _context.SaveChangesAsync();
 | 
						|
                    ContactCategoryVM categoryVM = contactCategory.ToContactCategoryVMFromContactCategoryMaster();
 | 
						|
 | 
						|
                    _logger.LogInfo("Employee ID {LoggedInEmployeeId} created a contact category {ContactCategoryId}.", LoggedInEmployee.Id, contactCategory.Id);
 | 
						|
                    return ApiResponse<object>.SuccessResponse(categoryVM, "Category Created Successfully", 200);
 | 
						|
                }
 | 
						|
                _logger.LogWarning("Employee ID {LoggedInEmployeeId} attempted to update a contact category but not found in database.", LoggedInEmployee.Id);
 | 
						|
                return ApiResponse<object>.ErrorResponse("Category not found", "Category not found", 404);
 | 
						|
            }
 | 
						|
            _logger.LogWarning("Employee with ID {LoggedInEmployeeId} sended empty payload", LoggedInEmployee.Id);
 | 
						|
            return ApiResponse<object>.ErrorResponse("User Send empty Payload", "User Send empty Payload", 400);
 | 
						|
        }
 | 
						|
        public async Task<ApiResponse<object>> GetContactCategoriesList()
 | 
						|
        {
 | 
						|
            Guid tenantId = _userHelper.GetTenantId();
 | 
						|
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
 | 
						|
            var categoryList = await _context.ContactCategoryMasters.Where(c => c.TenantId == tenantId).ToListAsync();
 | 
						|
            List<ContactCategoryVM> contactCategories = new List<ContactCategoryVM>();
 | 
						|
            foreach (var category in categoryList)
 | 
						|
            {
 | 
						|
                ContactCategoryVM categoryVM = category.ToContactCategoryVMFromContactCategoryMaster();
 | 
						|
                contactCategories.Add(categoryVM);
 | 
						|
            }
 | 
						|
            _logger.LogInfo("{count} contact categoires are fetched by Employee with ID {LoggedInEmployeeId}", contactCategories.Count, LoggedInEmployee.Id);
 | 
						|
            return ApiResponse<object>.SuccessResponse(contactCategories, System.String.Format("{0} contact categories fetched successfully", contactCategories.Count), 200);
 | 
						|
        }
 | 
						|
        public async Task<ApiResponse<object>> GetContactCategoryById(Guid id)
 | 
						|
        {
 | 
						|
            Guid tenantId = _userHelper.GetTenantId();
 | 
						|
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
 | 
						|
            var category = await _context.ContactCategoryMasters.FirstOrDefaultAsync(c => c.Id == id && c.TenantId == tenantId);
 | 
						|
            if (category != null)
 | 
						|
            {
 | 
						|
                ContactCategoryVM categoryVM = category.ToContactCategoryVMFromContactCategoryMaster();
 | 
						|
                _logger.LogInfo("Employee {EmployeeId} fetched contact category {ContactCategoryID}", LoggedInEmployee.Id, category.Id);
 | 
						|
                return ApiResponse<object>.SuccessResponse(categoryVM, "Category fetched successfully", 200);
 | 
						|
            }
 | 
						|
 | 
						|
            _logger.LogWarning("Employee {EmployeeId} attempted to fetch contact category {ContactCategoryID} but not found in database", LoggedInEmployee.Id, id);
 | 
						|
            return ApiResponse<object>.ErrorResponse("Category not found", "Category not found", 404);
 | 
						|
        }
 | 
						|
        public async Task<ApiResponse<object>> DeleteContactCategory(Guid id)
 | 
						|
        {
 | 
						|
            Guid tenantId = _userHelper.GetTenantId();
 | 
						|
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
            ContactCategoryMaster? contactCategory = await _context.ContactCategoryMasters.FirstOrDefaultAsync(c => c.Id == id && c.TenantId == tenantId);
 | 
						|
            if (contactCategory != null)
 | 
						|
            {
 | 
						|
                List<Contact>? existingContacts = await _context.Contacts.AsNoTracking().Where(c => c.ContactCategoryId == contactCategory.Id).ToListAsync();
 | 
						|
                if (existingContacts.Count > 0)
 | 
						|
                {
 | 
						|
                    List<Contact>? contacts = new List<Contact>();
 | 
						|
                    foreach (var contact in existingContacts)
 | 
						|
                    {
 | 
						|
                        contact.ContactCategoryId = null;
 | 
						|
                        contacts.Add(contact);
 | 
						|
                    }
 | 
						|
                    _context.Contacts.UpdateRange(contacts);
 | 
						|
                }
 | 
						|
                _context.ContactCategoryMasters.Remove(contactCategory);
 | 
						|
 | 
						|
                _context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog
 | 
						|
                {
 | 
						|
                    RefereanceId = id,
 | 
						|
                    UpdatedById = LoggedInEmployee.Id,
 | 
						|
                    UpdateAt = DateTime.UtcNow
 | 
						|
                });
 | 
						|
                await _context.SaveChangesAsync();
 | 
						|
                _logger.LogInfo("Employee {EmployeeId} deleted contact category {ContactCategoryId}", LoggedInEmployee.Id, id);
 | 
						|
            }
 | 
						|
 | 
						|
            _logger.LogWarning("Employee {EmployeeId} tries to delete Category {CategoryId} but not found in database", LoggedInEmployee.Id, id);
 | 
						|
            return ApiResponse<object>.SuccessResponse(new { }, "Category deleted successfully", 200);
 | 
						|
        }
 | 
						|
 | 
						|
        // -------------------------------- Contact Tag  --------------------------------
 | 
						|
 | 
						|
        public async Task<ApiResponse<Object>> GetContactTags()
 | 
						|
        {
 | 
						|
            Guid tenantId = _userHelper.GetTenantId();
 | 
						|
 | 
						|
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
 | 
						|
            var taglist = await _context.ContactTagMasters.Where(t => t.TenantId == tenantId).ToListAsync();
 | 
						|
            List<ContactTagVM> contactTags = new List<ContactTagVM>();
 | 
						|
            foreach (var tag in taglist)
 | 
						|
            {
 | 
						|
                ContactTagVM tagVm = tag.ToContactTagVMFromContactTagMaster();
 | 
						|
                contactTags.Add(tagVm);
 | 
						|
            }
 | 
						|
            _logger.LogInfo("{count} contact Tags are fetched by Employee with ID {LoggedInEmployeeId}", contactTags.Count, LoggedInEmployee.Id);
 | 
						|
            return ApiResponse<object>.SuccessResponse(contactTags, System.String.Format("{0} contact tags fetched successfully", contactTags.Count), 200);
 | 
						|
        }
 | 
						|
        public async Task<ApiResponse<object>> CreateContactTag(CreateContactTagDto contactTagDto)
 | 
						|
        {
 | 
						|
            Guid tenantId = _userHelper.GetTenantId();
 | 
						|
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
            if (contactTagDto != null)
 | 
						|
            {
 | 
						|
                ContactTagMaster? existingContactTag = await _context.ContactTagMasters.FirstOrDefaultAsync(c => c.TenantId == tenantId && c.Name.ToLower() == (contactTagDto.Name != null ? contactTagDto.Name.ToLower() : ""));
 | 
						|
                if (existingContactTag == null)
 | 
						|
                {
 | 
						|
                    ContactTagMaster contactTag = contactTagDto.ToContactTagMasterFromCreateContactTagDto(tenantId);
 | 
						|
                    _context.ContactTagMasters.Add(contactTag);
 | 
						|
                    await _context.SaveChangesAsync();
 | 
						|
                    ContactTagVM tagVM = contactTag.ToContactTagVMFromContactTagMaster();
 | 
						|
 | 
						|
                    _logger.LogInfo("Employee ID {LoggedInEmployeeId} created a contact tag {ContactTagId}.", LoggedInEmployee.Id, contactTag.Id);
 | 
						|
                    return ApiResponse<object>.SuccessResponse(tagVM, "Tag Created Successfully", 200);
 | 
						|
                }
 | 
						|
                _logger.LogWarning("Employee ID {LoggedInEmployeeId} attempted to create an existing contact tag.", LoggedInEmployee.Id);
 | 
						|
                return ApiResponse<object>.ErrorResponse("Tag already existed", "Tag already existed", 409);
 | 
						|
            }
 | 
						|
            _logger.LogWarning("Employee with ID {LoggedInEmployeeId} sended empty payload", LoggedInEmployee.Id);
 | 
						|
            return ApiResponse<object>.ErrorResponse("User Send empty Payload", "User Send empty Payload", 400);
 | 
						|
        }
 | 
						|
        public async Task<ApiResponse<object>> UpdateContactTag(Guid id, UpdateContactTagDto contactTagDto)
 | 
						|
        {
 | 
						|
            var tenantId = _userHelper.GetTenantId();
 | 
						|
            Employee LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
            if (contactTagDto != null && contactTagDto.Id == id)
 | 
						|
            {
 | 
						|
                ContactTagMaster? contactTag = await _context.ContactTagMasters.AsNoTracking().FirstOrDefaultAsync(t => t.TenantId == tenantId && t.Id == contactTagDto.Id);
 | 
						|
                if (contactTag != null)
 | 
						|
                {
 | 
						|
                    contactTag = contactTagDto.ToContactTagMasterFromUpdateContactTagDto(tenantId);
 | 
						|
                    _context.ContactTagMasters.Update(contactTag);
 | 
						|
 | 
						|
                    _context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog
 | 
						|
                    {
 | 
						|
                        RefereanceId = contactTag.Id,
 | 
						|
                        UpdatedById = LoggedInEmployee.Id,
 | 
						|
                        UpdateAt = DateTime.UtcNow
 | 
						|
                    });
 | 
						|
                    await _context.SaveChangesAsync();
 | 
						|
 | 
						|
                    ContactTagVM contactTagVm = contactTag.ToContactTagVMFromContactTagMaster();
 | 
						|
 | 
						|
 | 
						|
 | 
						|
                    _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);
 | 
						|
                }
 | 
						|
                _logger.LogError("Contact Tag master {ContactTagId} not found in database", id);
 | 
						|
                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);
 | 
						|
            return ApiResponse<object>.ErrorResponse("User Send empty Payload", "User Send empty Payload", 400);
 | 
						|
        }
 | 
						|
        public async Task<ApiResponse<object>> DeleteContactTag(Guid id)
 | 
						|
        {
 | 
						|
            Guid tenantId = _userHelper.GetTenantId();
 | 
						|
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
            ContactTagMaster? contactTag = await _context.ContactTagMasters.FirstOrDefaultAsync(c => c.Id == id && c.TenantId == tenantId);
 | 
						|
            if (contactTag != null)
 | 
						|
            {
 | 
						|
                List<ContactTagMapping>? tagMappings = await _context.ContactTagMappings.Where(t => t.ContactTagId == contactTag.Id).ToListAsync();
 | 
						|
 | 
						|
                _context.ContactTagMasters.Remove(contactTag);
 | 
						|
                if (tagMappings.Any())
 | 
						|
                {
 | 
						|
                    _context.ContactTagMappings.RemoveRange(tagMappings);
 | 
						|
                }
 | 
						|
                _context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog
 | 
						|
                {
 | 
						|
                    RefereanceId = id,
 | 
						|
                    UpdatedById = LoggedInEmployee.Id,
 | 
						|
                    UpdateAt = DateTime.UtcNow
 | 
						|
                });
 | 
						|
                await _context.SaveChangesAsync();
 | 
						|
                _logger.LogInfo("Employee {EmployeeId} deleted contact tag {ContactTagId}", LoggedInEmployee.Id, id);
 | 
						|
            }
 | 
						|
 | 
						|
            _logger.LogWarning("Employee {EmployeeId} tries to delete Tag {ContactTagId} but not found in database", LoggedInEmployee.Id, id);
 | 
						|
            return ApiResponse<object>.SuccessResponse(new { }, "Tag deleted successfully", 200);
 | 
						|
        }
 | 
						|
 | 
						|
        // -------------------------------- Work Status  --------------------------------
 | 
						|
        public async Task<ApiResponse<object>> GetWorkStatusList()
 | 
						|
        {
 | 
						|
            _logger.LogInfo("GetWorkStatusList called.");
 | 
						|
 | 
						|
            try
 | 
						|
            {
 | 
						|
                // Step 1: Get tenant and logged-in employee info
 | 
						|
                Guid tenantId = _userHelper.GetTenantId();
 | 
						|
                var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
 | 
						|
                // Step 2: Check permission to view master data
 | 
						|
                bool hasViewPermission = await _permissionService.HasPermission(View_Master, loggedInEmployee.Id);
 | 
						|
                if (!hasViewPermission)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Access denied for employeeId: {EmployeeId}", loggedInEmployee.Id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("You don't have access", "Don't have access to take action", 403);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 3: Fetch work statuses for the tenant
 | 
						|
                var workStatusList = await _context.WorkStatusMasters
 | 
						|
                    .Where(ws => ws.TenantId == tenantId)
 | 
						|
                    .Select(ws => new
 | 
						|
                    {
 | 
						|
                        ws.Id,
 | 
						|
                        ws.Name,
 | 
						|
                        ws.Description,
 | 
						|
                        ws.IsSystem
 | 
						|
                    })
 | 
						|
                    .ToListAsync();
 | 
						|
 | 
						|
                _logger.LogInfo("{Count} work statuses fetched for tenantId: {TenantId}", workStatusList.Count, tenantId);
 | 
						|
 | 
						|
                // Step 4: Return successful response
 | 
						|
                return ApiResponse<object>.SuccessResponse(
 | 
						|
                    workStatusList,
 | 
						|
                    $"{workStatusList.Count} work status records fetched successfully",
 | 
						|
                    200
 | 
						|
                );
 | 
						|
            }
 | 
						|
            catch (Exception ex)
 | 
						|
            {
 | 
						|
                _logger.LogError("Error occurred while fetching work status list : {Error}", ex.Message);
 | 
						|
                return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to fetch work status list", 500);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        public async Task<ApiResponse<object>> CreateWorkStatus(CreateWorkStatusMasterDto createWorkStatusDto)
 | 
						|
        {
 | 
						|
            _logger.LogInfo("CreateWorkStatus called with Name: {Name}", createWorkStatusDto.Name ?? "");
 | 
						|
 | 
						|
            try
 | 
						|
            {
 | 
						|
                // Step 1: Get tenant and logged-in employee
 | 
						|
                Guid tenantId = _userHelper.GetTenantId();
 | 
						|
                var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
 | 
						|
                // Step 2: Check if user has permission to manage master data
 | 
						|
                var hasManageMasterPermission = await _permissionService.HasPermission(Manage_Master, loggedInEmployee.Id);
 | 
						|
                if (!hasManageMasterPermission)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Access denied for employeeId: {EmployeeId}", loggedInEmployee.Id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("You don't have access", "Don't have access to take action", 403);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 3: Check if work status with the same name already exists
 | 
						|
                var existingWorkStatus = await _context.WorkStatusMasters
 | 
						|
                    .FirstOrDefaultAsync(ws => ws.Name == createWorkStatusDto.Name && ws.TenantId == tenantId);
 | 
						|
 | 
						|
                if (existingWorkStatus != null)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Work status already exists: {Name}", createWorkStatusDto.Name ?? "");
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Work status already exists", "Work status already exists", 400);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 4: Create new WorkStatusMaster entry
 | 
						|
                var workStatus = new WorkStatusMaster
 | 
						|
                {
 | 
						|
                    Name = createWorkStatusDto.Name?.Trim() ?? "",
 | 
						|
                    Description = createWorkStatusDto.Description?.Trim() ?? "",
 | 
						|
                    IsSystem = false,
 | 
						|
                    TenantId = tenantId
 | 
						|
                };
 | 
						|
 | 
						|
                _context.WorkStatusMasters.Add(workStatus);
 | 
						|
                await _context.SaveChangesAsync();
 | 
						|
 | 
						|
                _logger.LogInfo("Work status created successfully: {Id}, Name: {Name}", workStatus.Id, workStatus.Name);
 | 
						|
                return ApiResponse<object>.SuccessResponse(workStatus, "Work status created successfully", 200);
 | 
						|
            }
 | 
						|
            catch (Exception ex)
 | 
						|
            {
 | 
						|
                _logger.LogError("Error occurred while creating work status : {Error}", ex.Message);
 | 
						|
                return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to create work status", 500);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        public async Task<ApiResponse<object>> UpdateWorkStatus(Guid id, UpdateWorkStatusMasterDto updateWorkStatusDto)
 | 
						|
        {
 | 
						|
            _logger.LogInfo("UpdateWorkStatus called for WorkStatus ID: {Id}, New Name: {Name}", id, updateWorkStatusDto.Name ?? "");
 | 
						|
 | 
						|
            try
 | 
						|
            {
 | 
						|
                // Step 1: Validate input
 | 
						|
                if (id == Guid.Empty || id != updateWorkStatusDto.Id)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Invalid ID provided for update. Route ID: {RouteId}, DTO ID: {DtoId}", id, updateWorkStatusDto.Id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Invalid data provided", "The provided work status ID is invalid", 400);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 2: Get tenant and logged-in employee
 | 
						|
                Guid tenantId = _userHelper.GetTenantId();
 | 
						|
                var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
 | 
						|
                // Step 3: Check permissions
 | 
						|
                var hasManageMasterPermission = await _permissionService.HasPermission(Manage_Master, loggedInEmployee.Id);
 | 
						|
                if (!hasManageMasterPermission)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Access denied. EmployeeId: {EmployeeId} does not have Manage Master permission.", loggedInEmployee.Id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Access denied", "You do not have permission to update this work status", 403);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 4: Retrieve the work status record
 | 
						|
                var workStatus = await _context.WorkStatusMasters
 | 
						|
                    .FirstOrDefaultAsync(ws => ws.Id == id && ws.TenantId == tenantId);
 | 
						|
 | 
						|
                if (workStatus == null)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Work status not found for ID: {Id}", id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Work status not found", "No work status found with the provided ID", 404);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 5: Check for duplicate name (optional)
 | 
						|
                var isDuplicate = await _context.WorkStatusMasters
 | 
						|
                    .AnyAsync(ws => ws.Name == updateWorkStatusDto.Name && ws.Id != id && ws.TenantId == tenantId);
 | 
						|
 | 
						|
                if (isDuplicate)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Duplicate work status name '{Name}' detected during update. ID: {Id}", updateWorkStatusDto.Name ?? "", id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Work status with the same name already exists", "Duplicate name", 400);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 6: Update fields
 | 
						|
                workStatus.Name = updateWorkStatusDto.Name?.Trim() ?? "";
 | 
						|
                workStatus.Description = updateWorkStatusDto.Description?.Trim() ?? "";
 | 
						|
 | 
						|
                await _context.SaveChangesAsync();
 | 
						|
 | 
						|
                _logger.LogInfo("Work status updated successfully. ID: {Id}", id);
 | 
						|
                return ApiResponse<object>.SuccessResponse(workStatus, "Work status updated successfully", 200);
 | 
						|
            }
 | 
						|
            catch (Exception ex)
 | 
						|
            {
 | 
						|
                _logger.LogError("Error occurred while updating work status ID: {Id} : {Error}", id, ex.Message);
 | 
						|
                return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to update the work status at this time", 500);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        public async Task<ApiResponse<object>> DeleteWorkStatus(Guid id)
 | 
						|
        {
 | 
						|
            _logger.LogInfo("DeleteWorkStatus called for Id: {Id}", id);
 | 
						|
 | 
						|
            try
 | 
						|
            {
 | 
						|
                // Step 1: Get current tenant and logged-in employee
 | 
						|
                Guid tenantId = _userHelper.GetTenantId();
 | 
						|
                var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
 | 
						|
                // Step 2: Check permission to manage master data
 | 
						|
                var hasManageMasterPermission = await _permissionService.HasPermission(Manage_Master, loggedInEmployee.Id);
 | 
						|
                if (!hasManageMasterPermission)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Delete denied. EmployeeId: {EmployeeId} lacks Manage_Master permission.", loggedInEmployee.Id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("You don't have access", "Access denied for deleting work status", 403);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 3: Find the work status
 | 
						|
                var workStatus = await _context.WorkStatusMasters
 | 
						|
                    .FirstOrDefaultAsync(ws => ws.Id == id && ws.TenantId == tenantId);
 | 
						|
 | 
						|
                if (workStatus == null)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Work status not found for Id: {Id}", id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse("Work status not found", "Work status not found", 404);
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 4: Check for dependencies in TaskAllocations
 | 
						|
                bool hasDependency = await _context.TaskAllocations
 | 
						|
                    .AnyAsync(ta => ta.TenantId == tenantId && ta.WorkStatusId == id);
 | 
						|
 | 
						|
                if (hasDependency)
 | 
						|
                {
 | 
						|
                    _logger.LogWarning("Cannot delete WorkStatus Id: {Id} due to existing task dependency", id);
 | 
						|
                    return ApiResponse<object>.ErrorResponse(
 | 
						|
                        "Work status has a dependency in assigned tasks and cannot be deleted",
 | 
						|
                        "Deletion failed due to associated tasks",
 | 
						|
                        400
 | 
						|
                    );
 | 
						|
                }
 | 
						|
 | 
						|
                // Step 5: Delete and persist
 | 
						|
                _context.WorkStatusMasters.Remove(workStatus);
 | 
						|
                await _context.SaveChangesAsync();
 | 
						|
 | 
						|
                _logger.LogInfo("Work status deleted successfully. Id: {Id}", id);
 | 
						|
                return ApiResponse<object>.SuccessResponse(new { }, "Work status deleted successfully", 200);
 | 
						|
            }
 | 
						|
            catch (Exception ex)
 | 
						|
            {
 | 
						|
                _logger.LogError("Error occurred while deleting WorkStatus Id: {Id} : {Error}", id, ex.Message);
 | 
						|
                return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to delete work status", 500);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |