Added Designation Parameter in Contacts and Implement in Related APIs #104

Merged
vikas.nale merged 1 commits from Ashutosh_Enhancement_#631 into Issues_July_4W 2025-07-24 10:04:46 +00:00
11 changed files with 3571 additions and 91 deletions

View File

@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_Designation_Paraneter_In_Contacts_Table : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Designation",
table: "Contacts",
type: "longtext",
nullable: false)
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Designation",
table: "Contacts");
}
}
}

View File

@ -420,6 +420,10 @@ namespace Marco.Pms.DataAccess.Migrations
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Designation")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");

View File

@ -12,6 +12,7 @@ namespace Marco.Pms.Model.Directory
//public Guid? ProjectId { get; set; }
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string Designation { get; set; } = string.Empty;
public string Organization { get; set; } = string.Empty;
public string? Address { get; set; }
public bool IsActive { get; set; } = true;

View File

@ -9,6 +9,7 @@
public List<Guid>? BucketIds { get; set; }
public Guid? ContactCategoryId { get; set; }
public string? Description { get; set; }
public string? Designation { get; set; }
public string? Organization { get; set; }
public string? Address { get; set; }
public List<ContactTagDto>? Tags { get; set; }

View File

@ -10,6 +10,7 @@
public List<Guid>? BucketIds { get; set; }
public Guid? ContactCategoryId { get; set; }
public string? Description { get; set; }
public string? Designation { get; set; }
public string? Organization { get; set; }
public string? Address { get; set; }
public List<ContactTagDto>? Tags { get; set; }

View File

@ -16,6 +16,7 @@ namespace Marco.Pms.Model.Mapper
Name = createContactDto.Name ?? string.Empty,
ContactCategoryId = createContactDto.ContactCategoryId,
Description = createContactDto.Description ?? string.Empty,
Designation = createContactDto.Designation ?? string.Empty,
Organization = createContactDto?.Organization ?? string.Empty,
Address = createContactDto != null ? createContactDto.Address : string.Empty,
CreatedById = employeeId,
@ -34,6 +35,7 @@ namespace Marco.Pms.Model.Mapper
CreatedAt = contact.CreatedAt,
CreatedById = contact.CreatedById,
Description = updateContactDto.Description ?? string.Empty,
Designation = updateContactDto.Designation ?? string.Empty,
Organization = updateContactDto?.Organization ?? string.Empty,
Address = updateContactDto != null ? updateContactDto.Address : string.Empty,
TenantId = tenantId
@ -47,6 +49,7 @@ namespace Marco.Pms.Model.Mapper
Name = contact.Name,
ContactCategory = contact.ContactCategory != null ? contact.ContactCategory.ToContactCategoryVMFromContactCategoryMaster() : null,
Description = contact.Description ?? string.Empty,
Designation = contact.Designation ?? string.Empty,
Organization = contact.Organization ?? string.Empty,
Address = contact.Address ?? string.Empty
};
@ -59,6 +62,7 @@ namespace Marco.Pms.Model.Mapper
Name = contact.Name,
ContactCategory = contact.ContactCategory != null ? contact.ContactCategory.ToContactCategoryVMFromContactCategoryMaster() : null,
Description = contact.Description ?? string.Empty,
Designation = contact.Designation ?? string.Empty,
Organization = contact.Organization ?? string.Empty,
Address = contact.Address ?? string.Empty,
CreatedAt = contact.CreatedAt,

View File

@ -9,6 +9,7 @@ namespace Marco.Pms.Model.ViewModels.Directory
public Guid Id { get; set; }
public string? Name { get; set; }
public string? Description { get; set; }
public string? Designation { get; set; }
public string? Organization { get; set; }
public string? Address { get; set; }
public DateTime CreatedAt { get; set; }

View File

@ -12,6 +12,7 @@ namespace Marco.Pms.Model.ViewModels.Directory
public ContactCategoryVM? ContactCategory { get; set; }
public List<Guid>? BucketIds { get; set; }
public string? Description { get; set; }
public string? Designation { get; set; }
public string? Organization { get; set; }
public string? Address { get; set; }
public List<ContactTagVM>? Tags { get; set; }

View File

@ -33,20 +33,7 @@ namespace Marco.Pms.Services.Controllers
CategoryIds = categoryIds
};
var response = await _directoryHelper.GetListOfContacts(search, active, filterDto, projectId);
if (response.StatusCode == 200)
{
return Ok(response);
}
else if (response.StatusCode == 401)
{
return Unauthorized(response);
}
else
{
return BadRequest(response);
}
return StatusCode(response.StatusCode, response);
}
@ -54,18 +41,7 @@ namespace Marco.Pms.Services.Controllers
public async Task<IActionResult> GetContactsListByBucketId(Guid bucketId)
{
var response = await _directoryHelper.GetContactsListByBucketId(bucketId);
if (response.StatusCode == 200)
{
return Ok(response);
}
else if (response.StatusCode == 401)
{
return Unauthorized(response);
}
else
{
return BadRequest(response);
}
return StatusCode(response.StatusCode, response);
}
[HttpPost]
@ -81,61 +57,34 @@ namespace Marco.Pms.Services.Controllers
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
}
var response = await _directoryHelper.CreateContact(createContact);
if (response.StatusCode == 200)
{
return Ok(response);
}
else
{
return BadRequest(response);
}
return StatusCode(response.StatusCode, response);
}
[HttpPut("{id}")]
public async Task<IActionResult> UpdateContact(Guid id, [FromBody] UpdateContactDto updateContact)
{
var response = await _directoryHelper.UpdateContact(id, updateContact);
if (response.StatusCode == 200)
{
return Ok(response);
}
else if (response.StatusCode == 404)
{
return NotFound(response);
}
else if (response.StatusCode == 401)
{
return Unauthorized(response);
}
else
{
return BadRequest(response);
}
return StatusCode(response.StatusCode, response);
}
[HttpGet("profile/{id}")]
public async Task<IActionResult> GetContactProfile(Guid id)
{
var response = await _directoryHelper.GetContactProfile(id);
if (response.StatusCode == 200)
{
return Ok(response);
}
else if (response.StatusCode == 404)
{
return NotFound(response);
}
else
{
return BadRequest(response);
}
return StatusCode(response.StatusCode, response);
}
[HttpGet("organization")]
public async Task<IActionResult> GetOrganizationList()
{
var response = await _directoryHelper.GetOrganizationList();
return Ok(response);
return StatusCode(response.StatusCode, response);
}
[HttpGet("designations")]
public async Task<IActionResult> GetDesignationList()
{
var response = await _directoryHelper.GetDesignationList();
return StatusCode(response.StatusCode, response);
}
[HttpDelete("{id}")]

View File

@ -747,6 +747,7 @@ namespace Marco.Pms.Services.Helpers
{
Guid tenantId = _userHelper.GetTenantId();
var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var hasAdminPermission = await _permissionServices.HasPermission(directoryAdmin, LoggedInEmployee.Id);
if (id != Guid.Empty)
{
Contact? contact = await _context.Contacts.Include(c => c.ContactCategory).Include(c => c.CreatedBy).FirstOrDefaultAsync(c => c.Id == id && c.IsActive);
@ -806,11 +807,19 @@ namespace Marco.Pms.Services.Helpers
}
List<ContactBucketMapping>? contactBuckets = await _context.ContactBucketMappings.Where(cb => cb.ContactId == contact.Id).ToListAsync();
List<EmployeeBucketMapping>? employeeBuckets = await _context.EmployeeBucketMappings.Where(eb => eb.EmployeeId == LoggedInEmployee.Id).ToListAsync();
if (contactBuckets.Any() && employeeBuckets.Any())
if (contactBuckets.Any() && (employeeBuckets.Any() || hasAdminPermission))
{
List<Guid> contactBucketIds = contactBuckets.Select(cb => cb.BucketId).ToList();
List<Guid> employeeBucketIds = employeeBuckets.Select(eb => eb.BucketId).ToList();
List<Bucket>? buckets = await _context.Buckets.Where(b => contactBucketIds.Contains(b.Id) && employeeBucketIds.Contains(b.Id)).ToListAsync();
List<Bucket>? buckets = null;
if (hasAdminPermission)
{
buckets = await _context.Buckets.Where(b => contactBucketIds.Contains(b.Id)).ToListAsync();
}
else
{
buckets = await _context.Buckets.Where(b => contactBucketIds.Contains(b.Id) && employeeBucketIds.Contains(b.Id)).ToListAsync();
}
List<BucketVM>? bucketVMs = new List<BucketVM>();
foreach (var bucket in buckets)
{
@ -860,40 +869,101 @@ namespace Marco.Pms.Services.Helpers
}
public async Task<ApiResponse<object>> GetOrganizationList()
{
// Step 1: Retrieve tenant and employee context
Guid tenantId = _userHelper.GetTenantId();
var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var organizationList = await _context.Contacts.Where(c => c.TenantId == tenantId).Select(c => c.Organization).Distinct().ToListAsync();
_logger.LogInfo("Employee {EmployeeId} fetched list of organizations in a tenant {TenantId}", LoggedInEmployee.Id, tenantId);
return ApiResponse<object>.SuccessResponse(organizationList, $"{organizationList.Count} records of organization names fetched from contacts", 200);
_logger.LogInfo("GetOrganizationList called by EmployeeId: {EmployeeId} for TenantId: {TenantId}",
loggedInEmployee.Id, tenantId);
// Step 2: Fetch distinct, non-empty organization names
var organizationList = await _context.Contacts
.Where(c => c.TenantId == tenantId && !string.IsNullOrWhiteSpace(c.Organization))
.Select(c => c.Organization.Trim())
.Distinct()
.ToListAsync();
_logger.LogInfo("EmployeeId: {EmployeeId} fetched {Count} organization names from TenantId: {TenantId}",
loggedInEmployee.Id, organizationList.Count, tenantId);
// Step 3: Return success response
return ApiResponse<object>.SuccessResponse(
organizationList,
$"{organizationList.Count} records of organization names fetched from contacts",
200
);
}
public async Task<ApiResponse<object>> GetDesignationList()
{
// Step 1: Get tenant and logged-in employee details
Guid tenantId = _userHelper.GetTenantId();
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
_logger.LogInfo("GetDesignationList called by EmployeeId: {EmployeeId} in TenantId: {TenantId}",
loggedInEmployee.Id, tenantId);
// Step 2: Fetch distinct, non-null designations from contacts
var designationList = await _context.Contacts
.Where(c => c.TenantId == tenantId && !string.IsNullOrWhiteSpace(c.Designation))
.Select(c => c.Designation.Trim())
.Distinct()
.ToListAsync();
_logger.LogInfo("EmployeeId: {EmployeeId} fetched {Count} designations from TenantId: {TenantId}",
loggedInEmployee.Id, designationList.Count, tenantId);
// Step 3: Return result
return ApiResponse<object>.SuccessResponse(
designationList,
$"{designationList.Count} records of designation fetched from contacts",
200
);
}
public async Task<ApiResponse<object>> DeleteContact(Guid id, bool active)
{
// Step 1: Get tenant and logged-in employee info
Guid tenantId = _userHelper.GetTenantId();
var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
if (id != Guid.Empty)
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
_logger.LogInfo("DeleteContact called by EmployeeId: {EmployeeId} for ContactId: {ContactId} with Active: {IsActive}",
loggedInEmployee.Id, id, active);
// Step 2: Validate contact ID
if (id == Guid.Empty)
{
Contact? contact = await _context.Contacts.FirstOrDefaultAsync(c => c.Id == id && c.TenantId == tenantId);
if (contact == null)
{
_logger.LogWarning("Employee with ID {LoggedInEmployeeId} tries to delete contact with ID {ContactId} is not found in database", LoggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("Contact not found", "Contact not found", 404);
}
contact.IsActive = active;
_context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog
{
RefereanceId = contact.Id,
UpdatedById = LoggedInEmployee.Id,
UpdateAt = DateTime.UtcNow
});
await _context.SaveChangesAsync();
_logger.LogInfo("Contact {ContactId} has been deleted by Employee {Employee}", id, LoggedInEmployee.Id);
return ApiResponse<object>.SuccessResponse(new { }, "Contact is deleted Successfully", 200);
_logger.LogWarning("Empty contact ID received from EmployeeId: {EmployeeId}", loggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("Contact ID is empty", "Contact ID is empty", 400);
}
_logger.LogInfo("Employee ID {EmployeeId} sent an empty contact id", LoggedInEmployee.Id);
return ApiResponse<object>.ErrorResponse("Contact ID is empty", "Contact ID is empty", 400);
// Step 3: Check if contact exists under current tenant
var contact = await _context.Contacts
.FirstOrDefaultAsync(c => c.Id == id && c.TenantId == tenantId);
if (contact == null)
{
_logger.LogWarning("EmployeeId {EmployeeId} attempted to delete non-existent contact Id: {ContactId}", loggedInEmployee.Id, id);
return ApiResponse<object>.ErrorResponse("Contact not found", "Contact not found", 404);
}
// Step 4: Soft delete or restore contact
contact.IsActive = active;
// Step 5: Log the update in DirectoryUpdateLog
_context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog
{
RefereanceId = contact.Id,
UpdatedById = loggedInEmployee.Id,
UpdateAt = DateTime.UtcNow
});
await _context.SaveChangesAsync();
string status = active ? "restored" : "deleted";
_logger.LogInfo("Contact {ContactId} successfully {Status} by EmployeeId: {EmployeeId}",
contact.Id, status, loggedInEmployee.Id);
// Step 6: Return success response
return ApiResponse<object>.SuccessResponse(new { }, $"Contact {status} successfully", 200);
}
// -------------------------------- Contact Notes --------------------------------