Added the get filter APIs for contacts and contact notes

This commit is contained in:
ashutosh.nehete 2025-09-04 16:57:37 +05:30
parent 194764c9d6
commit 48562235f5
5 changed files with 238 additions and 1558 deletions

View File

@ -80,6 +80,7 @@ namespace Marco.Pms.Services.Controllers
var response = await _directoryService.GetOrganizationListAsync(tenantId, loggedInEmployee); var response = await _directoryService.GetOrganizationListAsync(tenantId, loggedInEmployee);
return Ok(response); return Ok(response);
} }
[HttpGet("designations")] [HttpGet("designations")]
public async Task<IActionResult> GetDesignationList() public async Task<IActionResult> GetDesignationList()
{ {
@ -88,6 +89,14 @@ namespace Marco.Pms.Services.Controllers
return Ok(response); return Ok(response);
} }
[HttpGet("contact/filter")]
public async Task<IActionResult> GetContactFilterObject()
{
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _directoryService.GetContactFilterObjectAsync(tenantId, loggedInEmployee);
return StatusCode(response.StatusCode, response);
}
#endregion #endregion
[HttpPost] [HttpPost]
@ -161,6 +170,14 @@ namespace Marco.Pms.Services.Controllers
return StatusCode(response.StatusCode, response); return StatusCode(response.StatusCode, response);
} }
[HttpGet("notes/filter")]
public async Task<IActionResult> GetContactNotesFilterObject()
{
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _directoryService.GetContactNotesFilterObjectAsync(tenantId, loggedInEmployee);
return StatusCode(response.StatusCode, response);
}
[HttpPut("note/{id}")] [HttpPut("note/{id}")]
public async Task<IActionResult> UpdateContactNote(Guid id, [FromBody] UpdateContactNoteDto noteDto) public async Task<IActionResult> UpdateContactNote(Guid id, [FromBody] UpdateContactNoteDto noteDto)
{ {

File diff suppressed because it is too large Load Diff

View File

@ -186,7 +186,6 @@ builder.Services.AddScoped<GeneralHelper>();
builder.Services.AddScoped<UserHelper>(); builder.Services.AddScoped<UserHelper>();
builder.Services.AddScoped<RolesHelper>(); builder.Services.AddScoped<RolesHelper>();
builder.Services.AddScoped<EmployeeHelper>(); builder.Services.AddScoped<EmployeeHelper>();
builder.Services.AddScoped<DirectoryHelper>();
builder.Services.AddScoped<MasterHelper>(); builder.Services.AddScoped<MasterHelper>();
builder.Services.AddScoped<ReportHelper>(); builder.Services.AddScoped<ReportHelper>();
builder.Services.AddScoped<CacheUpdateHelper>(); builder.Services.AddScoped<CacheUpdateHelper>();

View File

@ -819,6 +819,106 @@ namespace Marco.Pms.Services.Service
} }
} }
/// <summary>
/// Fetches filter options (Buckets and Contact Categories) based on user permissions
/// for a given tenant.
/// </summary>
/// <param name="tenantId">The tenant ID.</param>
/// <param name="loggedInEmployee">The employee making the request.</param>
/// <returns>ApiResponse with Buckets and Contact Categories.</returns>
public async Task<ApiResponse<object>> GetContactFilterObjectAsync(Guid tenantId, Employee loggedInEmployee)
{
try
{
_logger.LogInfo("Started fetching contact filters for TenantId: {TenantId}, EmployeeId: {EmployeeId}",
tenantId, loggedInEmployee.Id);
// Step 1: Determine accessible bucket IDs based on permissions
var (hasAdminPermission, hasManagerPermission, hasUserPermission) = await CheckPermissionsAsync(loggedInEmployee.Id);
List<Guid>? accessibleBucketIds = null;
if (hasAdminPermission)
{
// Admin → Has access to all buckets in the tenant
accessibleBucketIds = await _context.Buckets
.Where(b => b.TenantId == tenantId)
.Select(b => b.Id)
.ToListAsync();
_logger.LogDebug("Admin access granted. Fetched {Count} tenant buckets.", accessibleBucketIds.Count);
}
else if (hasManagerPermission || hasUserPermission)
{
// Manager/User → Only mapped buckets
accessibleBucketIds = await _context.EmployeeBucketMappings
.Where(eb => eb.EmployeeId == loggedInEmployee.Id)
.Select(eb => eb.BucketId)
.ToListAsync();
_logger.LogDebug("Manager/User access granted. Fetched {Count} mapped buckets for EmployeeId: {EmployeeId}",
accessibleBucketIds.Count, loggedInEmployee.Id);
}
else
{
_logger.LogWarning("No permissions found for EmployeeId: {EmployeeId}. Returning empty bucket list.", loggedInEmployee.Id);
}
// Step 2: Fetch available buckets (accessible OR personally created by the employee)
var buckets = await _context.Buckets
.Where(b =>
(accessibleBucketIds != null && accessibleBucketIds.Contains(b.Id) && b.TenantId == tenantId)
|| b.CreatedByID == loggedInEmployee.Id)
.Select(b => new
{
Id = b.Id,
Name = b.Name
})
.OrderBy(b => b.Name)
.Distinct() // Ensures no duplicates (LINQ to Entities distinct works with anonymous types)
.ToListAsync();
accessibleBucketIds = buckets.Select(b => b.Id).ToList();
_logger.LogInfo("Fetched {Count} buckets for EmployeeId: {EmployeeId}", buckets.Count, loggedInEmployee.Id);
// Step 3: Fetch contact categories mapped to the retrieved buckets
var contactCategories = await _context.ContactBucketMappings
.AsNoTracking() // Optimized since we are reading only
.Include(cb => cb.Contact)
.ThenInclude(c => c!.ContactCategory)
.Where(cb =>
accessibleBucketIds.Contains(cb.BucketId) &&
cb.Contact != null &&
cb.Contact!.ContactCategory != null)
.Select(cb => new
{
Id = cb.Contact!.ContactCategory!.Id,
Name = cb.Contact.ContactCategory.Name
})
.OrderBy(cc => cc.Name)
.Distinct()
.ToListAsync();
_logger.LogInfo("{Count} contact categories fetched for EmployeeId: {EmployeeId}", contactCategories.Count, loggedInEmployee.Id);
// Step 4: Prepare response payload
var response = new
{
Buckets = buckets,
ContactCategories = contactCategories
};
_logger.LogInfo("Successfully returning filters for TenantId: {TenantId}, EmployeeId: {EmployeeId}", tenantId, loggedInEmployee.Id);
return ApiResponse<object>.SuccessResponse(response, "Filters for contact fetched successfully", 200);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while fetching contact filters for TenantId: {TenantId}, EmployeeId: {EmployeeId}", tenantId, loggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("An error occurred while fetching filters", 500);
}
}
#endregion #endregion
#region =================================================================== Contact Post APIs =================================================================== #region =================================================================== Contact Post APIs ===================================================================
@ -1661,6 +1761,125 @@ namespace Marco.Pms.Services.Service
StatusCodes.Status200OK); StatusCodes.Status200OK);
} }
/// <summary>
/// Fetches filter objects (CreatedBy employees and Organizations) for Contact Notes
/// accessible by the logged-in employee, based on permissions.
/// </summary>
/// <param name="tenantId">The tenant ID.</param>
/// <param name="loggedInEmployee">The employee requesting filters.</param>
/// <returns>ApiResponse containing CreatedBy and Organizations filter options.</returns>
public async Task<ApiResponse<object>> GetContactNotesFilterObjectAsync(Guid tenantId, Employee loggedInEmployee)
{
try
{
_logger.LogInfo("Started fetching Contact Notes filters for TenantId: {TenantId}, EmployeeId: {EmployeeId}",
tenantId, loggedInEmployee.Id);
// Step 1: Fetch accessible bucket IDs based on permissions
var (hasAdminPermission, hasManagerPermission, hasUserPermission) = await CheckPermissionsAsync(loggedInEmployee.Id);
List<Guid>? accessibleBucketIds = null;
if (hasAdminPermission)
{
// Admin → Access to all buckets in the tenant
accessibleBucketIds = await _context.Buckets
.Where(b => b.TenantId == tenantId)
.Select(b => b.Id)
.ToListAsync();
_logger.LogDebug("Admin access granted. Found {Count} buckets.", accessibleBucketIds.Count);
}
else if (hasManagerPermission || hasUserPermission)
{
// Manager/User → Access to mapped buckets only
accessibleBucketIds = await _context.EmployeeBucketMappings
.Where(eb => eb.EmployeeId == loggedInEmployee.Id)
.Select(eb => eb.BucketId)
.ToListAsync();
_logger.LogDebug("Manager/User access granted. Found {Count} mapped buckets for EmployeeId: {EmployeeId}.",
accessibleBucketIds.Count, loggedInEmployee.Id);
}
else
{
_logger.LogWarning("No permissions found for EmployeeId: {EmployeeId}. Returning empty filters.", loggedInEmployee.Id);
}
// Step 2: Fetch Contact IDs from ContactBucketMappings
var contactIds = await _context.ContactBucketMappings
.Where(cb => accessibleBucketIds != null && accessibleBucketIds.Contains(cb.BucketId))
.Select(cb => cb.ContactId)
.Distinct() // ensures no duplicate contact IDs
.ToListAsync();
_logger.LogInfo("Fetched {Count} contact Ids from accessible buckets.", contactIds.Count);
// Step 3: Fetch Contacts related to Contact Notes for those contactIds
var contacts = await _context.ContactNotes
.AsNoTracking() // no need to track since its for read-only filters
.Include(cn => cn.Contact)
.ThenInclude(c => c!.CreatedBy)
.Where(cn =>
contactIds.Contains(cn.ContactId) &&
cn.Contact != null &&
cn.Contact.CreatedBy != null &&
cn.TenantId == tenantId)
.Select(cn => cn.Contact!)
.Distinct() // avoid duplicate contacts from multiple notes
.ToListAsync();
_logger.LogInfo("Fetched {Count} unique contacts with notes.", contacts.Count);
// Step 4: Build organization filters
var organizations = contacts
.Where(c => !string.IsNullOrEmpty(c.Organization)) // filter out null/empty orgs
.Select(c => new
{
Id = c.Organization, // Using organization string as unique identifier
Name = c.Organization
})
.Distinct()
.OrderBy(o => o.Name)
.ToList();
_logger.LogInfo("Extracted {Count} unique organizations from contacts.", organizations.Count);
// Step 5: Build CreatedBy filters (employees who created the contacts)
var createdBy = contacts
.Select(c => new
{
Id = c.CreatedBy!.Id,
Name = $"{c.CreatedBy.FirstName} {c.CreatedBy.LastName}".Trim()
})
.Distinct()
.OrderBy(e => e.Name)
.ToList();
_logger.LogInfo("Extracted {Count} unique CreatedBy employees from contacts.", createdBy.Count);
// Step 6: Build response
var response = new
{
CreatedBy = createdBy,
Organizations = organizations
};
_logger.LogInfo("Successfully returning Contact Notes filters for TenantId: {TenantId}, EmployeeId: {EmployeeId}",
tenantId, loggedInEmployee.Id);
return ApiResponse<object>.SuccessResponse(response, "Filters for contact notes fetched successfully", 200);
}
catch (Exception ex)
{
_logger.LogError(ex,
"Error occurred while fetching Contact Notes filters for TenantId: {TenantId}, EmployeeId: {EmployeeId}",
tenantId, loggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("An error occurred while fetching filters", 500);
}
}
/// <summary> /// <summary>
/// Creates a note for a given contact under the specified tenant. /// Creates a note for a given contact under the specified tenant.
/// Ensures that the contact exists and belongs to the tenant before adding the note. /// Ensures that the contact exists and belongs to the tenant before adding the note.
@ -1915,7 +2134,6 @@ namespace Marco.Pms.Services.Service
} }
} }
#endregion #endregion
#region =================================================================== Bucket APIs =================================================================== #region =================================================================== Bucket APIs ===================================================================

View File

@ -12,6 +12,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
Task<ApiResponse<object>> GetContactProfileAsync(Guid id, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> GetContactProfileAsync(Guid id, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> GetOrganizationListAsync(Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> GetOrganizationListAsync(Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> GetDesignationListAsync(Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> GetDesignationListAsync(Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> GetContactFilterObjectAsync(Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> CreateContactAsync(CreateContactDto createContact, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> CreateContactAsync(CreateContactDto createContact, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> UpdateContactAsync(Guid id, UpdateContactDto updateContact, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> UpdateContactAsync(Guid id, UpdateContactDto updateContact, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> DeleteContactAsync(Guid id, bool active, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> DeleteContactAsync(Guid id, bool active, Guid tenantId, Employee loggedInEmployee);
@ -22,6 +23,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
Task<ApiResponse<object>> GetListOFAllNotesAsync(Guid? projectId, int pageSize, int pageNumber, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> GetListOFAllNotesAsync(Guid? projectId, int pageSize, int pageNumber, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> GetNoteListByContactIdAsync(Guid id, bool active, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> GetNoteListByContactIdAsync(Guid id, bool active, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> GetContactNotesFilterObjectAsync(Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> CreateContactNoteAsync(CreateContactNoteDto noteDto, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> CreateContactNoteAsync(CreateContactNoteDto noteDto, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> UpdateContactNoteAsync(Guid id, UpdateContactNoteDto noteDto, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> UpdateContactNoteAsync(Guid id, UpdateContactNoteDto noteDto, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> DeleteContactNoteAsync(Guid id, bool active, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> DeleteContactNoteAsync(Guid id, bool active, Guid tenantId, Employee loggedInEmployee);