Ashutosh_Task#513 #96
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Added_UpdatedBy_In_Contacts_And_ContactNotes_Table : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "UpdatedAt",
|
||||
table: "Contacts",
|
||||
type: "datetime(6)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "UpdatedById",
|
||||
table: "Contacts",
|
||||
type: "char(36)",
|
||||
nullable: true,
|
||||
collation: "ascii_general_ci");
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "UpdatedAt",
|
||||
table: "ContactNotes",
|
||||
type: "datetime(6)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "UpdatedById",
|
||||
table: "ContactNotes",
|
||||
type: "char(36)",
|
||||
nullable: true,
|
||||
collation: "ascii_general_ci");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Contacts_UpdatedById",
|
||||
table: "Contacts",
|
||||
column: "UpdatedById");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ContactNotes_UpdatedById",
|
||||
table: "ContactNotes",
|
||||
column: "UpdatedById");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_ContactNotes_Employees_UpdatedById",
|
||||
table: "ContactNotes",
|
||||
column: "UpdatedById",
|
||||
principalTable: "Employees",
|
||||
principalColumn: "Id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Contacts_Employees_UpdatedById",
|
||||
table: "Contacts",
|
||||
column: "UpdatedById",
|
||||
principalTable: "Employees",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_ContactNotes_Employees_UpdatedById",
|
||||
table: "ContactNotes");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Contacts_Employees_UpdatedById",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Contacts_UpdatedById",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_ContactNotes_UpdatedById",
|
||||
table: "ContactNotes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "UpdatedAt",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "UpdatedById",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "UpdatedAt",
|
||||
table: "ContactNotes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "UpdatedById",
|
||||
table: "ContactNotes");
|
||||
}
|
||||
}
|
||||
}
|
@ -410,6 +410,12 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<Guid?>("UpdatedById")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ContactCategoryId");
|
||||
@ -418,6 +424,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.HasIndex("UpdatedById");
|
||||
|
||||
b.ToTable("Contacts");
|
||||
});
|
||||
|
||||
@ -521,6 +529,12 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<Guid?>("UpdatedById")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ContactId");
|
||||
@ -529,6 +543,8 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.HasIndex("UpdatedById");
|
||||
|
||||
b.ToTable("ContactNotes");
|
||||
});
|
||||
|
||||
@ -2641,11 +2657,17 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Marco.Pms.Model.Employees.Employee", "UpdatedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("UpdatedById");
|
||||
|
||||
b.Navigation("ContactCategory");
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("Tenant");
|
||||
|
||||
b.Navigation("UpdatedBy");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.Directory.ContactBucketMapping", b =>
|
||||
@ -2709,11 +2731,17 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Marco.Pms.Model.Employees.Employee", "UpdatedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("UpdatedById");
|
||||
|
||||
b.Navigation("Contact");
|
||||
|
||||
b.Navigation("Createdby");
|
||||
|
||||
b.Navigation("Tenant");
|
||||
|
||||
b.Navigation("UpdatedBy");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.Directory.ContactPhone", b =>
|
||||
|
@ -20,6 +20,11 @@ namespace Marco.Pms.Model.Directory
|
||||
[ValidateNever]
|
||||
[ForeignKey("CreatedById")]
|
||||
public Employee? CreatedBy { get; set; }
|
||||
public Guid? UpdatedById { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("UpdatedById")]
|
||||
public Employee? UpdatedBy { get; set; }
|
||||
|
||||
[DisplayName("ContactCategoryId")]
|
||||
public Guid? ContactCategoryId { get; set; }
|
||||
@ -27,5 +32,6 @@ namespace Marco.Pms.Model.Directory
|
||||
[ForeignKey(nameof(ContactCategoryId))]
|
||||
public ContactCategoryMaster? ContactCategory { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,13 @@ namespace Marco.Pms.Model.Directory
|
||||
[ValidateNever]
|
||||
[ForeignKey("CreatedById")]
|
||||
public Employee? Createdby { get; set; }
|
||||
public Guid? UpdatedById { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
[ForeignKey("UpdatedById")]
|
||||
public Employee? UpdatedBy { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public Guid ContactId { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
|
@ -234,7 +234,9 @@ namespace Marco.Pms.Model.Mapper
|
||||
Note = note.Note,
|
||||
ContactId = note.ContactId,
|
||||
CreatedAt = note.CreatedAt,
|
||||
UpdatedAt = note.UpdatedAt,
|
||||
CreatedBy = note.Createdby != null ? note.Createdby.ToBasicEmployeeVMFromEmployee() : null,
|
||||
UpdatedBy = note.UpdatedBy != null ? note.UpdatedBy.ToBasicEmployeeVMFromEmployee() : null,
|
||||
IsActive = note.IsActive
|
||||
};
|
||||
}
|
||||
|
@ -158,6 +158,13 @@ namespace Marco.Pms.Services.Controllers
|
||||
|
||||
// -------------------------------- Contact Notes --------------------------------
|
||||
|
||||
[HttpGet("notes")]
|
||||
public async Task<IActionResult> GetListOFAllNotes([FromQuery] int? pageSize, [FromQuery] int pageNumber)
|
||||
{
|
||||
var response = await _directoryHelper.GetListOFAllNotes(pageSize ?? 25, pageNumber);
|
||||
return StatusCode(response.StatusCode, response);
|
||||
}
|
||||
|
||||
[HttpPost("note")]
|
||||
public async Task<IActionResult> CreateContactNote([FromBody] CreateContactNoteDto noteDto)
|
||||
{
|
||||
|
@ -7,6 +7,7 @@ using Marco.Pms.Model.Utilities;
|
||||
using Marco.Pms.Model.ViewModels.Directory;
|
||||
using Marco.Pms.Model.ViewModels.Master;
|
||||
using Marco.Pms.Model.ViewModels.Projects;
|
||||
using Marco.Pms.Services.Service;
|
||||
using MarcoBMS.Services.Helpers;
|
||||
using MarcoBMS.Services.Service;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@ -18,15 +19,17 @@ namespace Marco.Pms.Services.Helpers
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly ILoggingService _logger;
|
||||
private readonly UserHelper _userHelper;
|
||||
private readonly PermissionServices _permissionServices;
|
||||
private readonly Guid directoryAdmin;
|
||||
private readonly Guid directoryManager;
|
||||
private readonly Guid directoryUser;
|
||||
|
||||
public DirectoryHelper(ApplicationDbContext context, ILoggingService logger, UserHelper userHelper)
|
||||
public DirectoryHelper(ApplicationDbContext context, ILoggingService logger, UserHelper userHelper, PermissionServices permissionServices)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
_userHelper = userHelper;
|
||||
_permissionServices = permissionServices;
|
||||
directoryAdmin = Guid.Parse("4286a13b-bb40-4879-8c6d-18e9e393beda");
|
||||
directoryManager = Guid.Parse("62668630-13ce-4f52-a0f0-db38af2230c5");
|
||||
directoryUser = Guid.Parse("0f919170-92d4-4337-abd3-49b66fc871bb");
|
||||
@ -504,6 +507,8 @@ namespace Marco.Pms.Services.Helpers
|
||||
|
||||
|
||||
var newContact = updateContact.ToContactFromUpdateContactDto(tenantId, contact);
|
||||
newContact.UpdatedById = LoggedInEmployee.Id;
|
||||
newContact.UpdatedAt = DateTime.UtcNow;
|
||||
_context.Contacts.Update(newContact);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
@ -893,6 +898,110 @@ namespace Marco.Pms.Services.Helpers
|
||||
|
||||
// -------------------------------- Contact Notes --------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a paginated list of contact notes based on user permissions.
|
||||
/// </summary>
|
||||
/// <param name="pageSize">The number of items per page.</param>
|
||||
/// <param name="pageNumber">The current page number.</param>
|
||||
/// <returns>An ApiResponse containing the paginated notes or an error message.</returns>
|
||||
public async Task<ApiResponse<object>> GetListOFAllNotes(int pageSize, int pageNumber)
|
||||
{
|
||||
_logger.LogInfo("Attempting to fetch list of all notes. PageSize: {PageSize}, PageNumber: {PageNumber}", pageSize, pageNumber);
|
||||
|
||||
Guid tenantId = _userHelper.GetTenantId();
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
if (loggedInEmployee == null)
|
||||
{
|
||||
_logger.LogWarning("GetListOFAllNotes: LoggedInEmployee is null. Cannot proceed.");
|
||||
return ApiResponse<object>.ErrorResponse("Unauthorized", "Employee not found.", 401);
|
||||
}
|
||||
|
||||
// --- Permission Checks ---
|
||||
var hasAdminPermission = await _permissionServices.HasPermission(directoryAdmin, loggedInEmployee.Id);
|
||||
var hasManagerPermission = await _permissionServices.HasPermission(directoryManager, loggedInEmployee.Id);
|
||||
var hasUserPermission = await _permissionServices.HasPermission(directoryUser, loggedInEmployee.Id);
|
||||
|
||||
IQueryable<ContactNote> notesQuery = _context.ContactNotes
|
||||
.Include(cn => cn.UpdatedBy)
|
||||
.Include(cn => cn.Createdby) // Assuming 'CreatedBy' (PascalCase)
|
||||
.Where(cn => cn.TenantId == tenantId)
|
||||
.AsQueryable(); // Start building the query
|
||||
|
||||
if (!hasAdminPermission && !(hasManagerPermission || hasUserPermission))
|
||||
{
|
||||
_logger.LogWarning("GetListOFAllNotes: User {EmployeeId} does not have required permissions to access notes for TenantId: {TenantId}", loggedInEmployee.Id, tenantId);
|
||||
return ApiResponse<object>.ErrorResponse("Access Denied", "You don't have access to view notes.", 403);
|
||||
}
|
||||
|
||||
if (!hasAdminPermission) // If not an admin, apply additional filtering
|
||||
{
|
||||
_logger.LogInfo("GetListOFAllNotes: User {EmployeeId} is not an admin. Applying manager/user specific filters.", loggedInEmployee.Id);
|
||||
var assignedBucketIds = await _context.EmployeeBucketMappings
|
||||
.Where(eb => eb.EmployeeId == loggedInEmployee.Id)
|
||||
.Select(eb => eb.BucketId)
|
||||
.ToListAsync();
|
||||
|
||||
if (!assignedBucketIds.Any())
|
||||
{
|
||||
_logger.LogInfo("GetListOFAllNotes: User {EmployeeId} has no assigned buckets. Returning empty list.", loggedInEmployee.Id);
|
||||
return ApiResponse<object>.SuccessResponse(new { CurrentPage = pageNumber, TotalPages = 0, Data = new List<ContactNoteVM>() }, "No notes found based on assigned buckets.", 200);
|
||||
}
|
||||
|
||||
var contactIds = await _context.ContactBucketMappings
|
||||
.Where(cb => assignedBucketIds.Contains(cb.BucketId))
|
||||
.Select(cb => cb.ContactId)
|
||||
.ToListAsync();
|
||||
|
||||
if (!contactIds.Any())
|
||||
{
|
||||
_logger.LogInfo("GetListOFAllNotes: No contacts found for assigned buckets for user {EmployeeId}. Returning empty list.", loggedInEmployee.Id);
|
||||
return ApiResponse<object>.SuccessResponse(new { CurrentPage = pageNumber, TotalPages = 0, Data = new List<ContactNoteVM>() }, "No notes found for associated contacts.", 200);
|
||||
}
|
||||
|
||||
notesQuery = notesQuery.Where(cn => contactIds.Contains(cn.ContactId));
|
||||
}
|
||||
|
||||
// --- Pagination Logic ---
|
||||
// Ensure pageSize and pageNumber are valid
|
||||
pageSize = pageSize < 1 ? 25 : pageSize; // Default to 25 if less than 1
|
||||
pageNumber = pageNumber < 1 ? 1 : pageNumber; // Default to 1 if less than 1
|
||||
|
||||
// Get total count BEFORE applying Skip/Take for accurate pagination metadata
|
||||
int totalRecords = await notesQuery.CountAsync();
|
||||
int totalPages = (int)Math.Ceiling((double)totalRecords / pageSize);
|
||||
|
||||
int skip = (pageNumber - 1) * pageSize;
|
||||
|
||||
// --- Apply Ordering and Pagination in the database ---
|
||||
List<ContactNote> notes = await notesQuery
|
||||
.OrderByDescending(cn => (cn.UpdatedAt != null ? cn.UpdatedAt : cn.CreatedAt)) // Order by updated date or created date
|
||||
.Skip(skip)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
|
||||
_logger.LogInfo("GetListOFAllNotes: Fetched {Count} notes for page {PageNumber} of {TotalPages} total pages. Total records: {TotalRecords}.",
|
||||
notes.Count, pageNumber, totalPages, totalRecords);
|
||||
|
||||
// --- Map to ViewModel (in-memory) ---
|
||||
// This mapping is done in memory because ToBasicEmployeeVMFromEmployee() is likely a C# method
|
||||
// that cannot be translated to SQL by Entity Framework.
|
||||
List<ContactNoteVM> noteVMS = notes
|
||||
.Select(cn => cn.ToContactNoteVMFromContactNote())
|
||||
.ToList();
|
||||
|
||||
var response = new
|
||||
{
|
||||
CurrentPage = pageNumber,
|
||||
PageSize = pageSize, // Include pageSize in response for client clarity
|
||||
TotalPages = totalPages,
|
||||
TotalRecords = totalRecords, // Add total records for client
|
||||
Data = noteVMS
|
||||
};
|
||||
|
||||
_logger.LogInfo("GetListOFAllNotes: Successfully retrieved notes and mapped to ViewModel for TenantId: {TenantId}.", tenantId);
|
||||
return ApiResponse<object>.SuccessResponse(response, $"{noteVMS.Count} notes fetched successfully.", 200);
|
||||
}
|
||||
public async Task<ApiResponse<object>> GetNoteListByContactId(Guid id, bool active)
|
||||
{
|
||||
Guid tenantId = _userHelper.GetTenantId();
|
||||
@ -903,32 +1012,25 @@ namespace Marco.Pms.Services.Helpers
|
||||
List<ContactNote> notes = new List<ContactNote>();
|
||||
if (active)
|
||||
{
|
||||
notes = await _context.ContactNotes.Where(n => n.ContactId == contact.Id && n.IsActive && n.TenantId == tenantId).ToListAsync();
|
||||
notes = await _context.ContactNotes
|
||||
.Include(n => n.Createdby)
|
||||
.Include(n => n.UpdatedBy)
|
||||
.Where(n => n.ContactId == contact.Id && n.IsActive && n.TenantId == tenantId)
|
||||
.ToListAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
notes = await _context.ContactNotes.Where(n => n.ContactId == contact.Id && n.TenantId == tenantId).ToListAsync();
|
||||
notes = await _context.ContactNotes
|
||||
.Include(n => n.Createdby)
|
||||
.Include(n => n.UpdatedBy)
|
||||
.Where(n => n.ContactId == contact.Id && n.TenantId == tenantId)
|
||||
.ToListAsync();
|
||||
}
|
||||
var noteIds = notes.Select(n => n.Id).ToList();
|
||||
List<DirectoryUpdateLog>? updateLogs = await _context.DirectoryUpdateLogs.Include(l => l.Employee).Where(l => noteIds.Contains(l.RefereanceId)).ToListAsync();
|
||||
List<ContactNoteVM>? noteVMs = new List<ContactNoteVM>();
|
||||
foreach (var note in notes)
|
||||
{
|
||||
ContactNoteVM noteVM = note.ToContactNoteVMFromContactNote();
|
||||
DirectoryUpdateLog? updateLog = updateLogs.Where(l => l.RefereanceId == note.Id).OrderByDescending(l => l.UpdateAt).FirstOrDefault();
|
||||
if (updateLog != null)
|
||||
{
|
||||
noteVM.UpdatedAt = updateLog.UpdateAt;
|
||||
noteVM.UpdatedBy = updateLog.Employee != null ? updateLog.Employee.ToBasicEmployeeVMFromEmployee() : null;
|
||||
}
|
||||
else
|
||||
{
|
||||
noteVM.UpdatedAt = note.CreatedAt;
|
||||
noteVM.UpdatedBy = note.Createdby != null ? note.Createdby.ToBasicEmployeeVMFromEmployee() : null;
|
||||
}
|
||||
noteVMs.Add(noteVM);
|
||||
//List<ContactNoteVM>? noteVMs = new List<ContactNoteVM>();
|
||||
List<ContactNoteVM>? noteVMs = notes.Select(n => n.ToContactNoteVMFromContactNote()).ToList();
|
||||
|
||||
}
|
||||
_logger.LogInfo("{count} contact-notes record from contact {ContactId} fetched by Employee {EmployeeId}", noteVMs.Count, id, LoggedInEmployee.Id);
|
||||
return ApiResponse<object>.SuccessResponse(noteVMs, $"{noteVMs.Count} contact-notes record fetched successfully", 200);
|
||||
}
|
||||
@ -970,6 +1072,8 @@ namespace Marco.Pms.Services.Helpers
|
||||
if (contactNote != null)
|
||||
{
|
||||
contactNote.Note = noteDto.Note;
|
||||
contactNote.UpdatedById = LoggedInEmployee.Id;
|
||||
contactNote.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
_context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog
|
||||
{
|
||||
@ -1335,6 +1439,9 @@ namespace Marco.Pms.Services.Helpers
|
||||
_logger.LogWarning("Employee {EmployeeId} tries to delete bucket {BucketId} but not found in database", LoggedInEmployee.Id, id);
|
||||
return ApiResponse<object>.SuccessResponse(new { }, "Bucket deleted successfully", 200);
|
||||
}
|
||||
|
||||
// -------------------------------- Helper --------------------------------
|
||||
|
||||
private bool Compare(string sentence, string search)
|
||||
{
|
||||
sentence = sentence.Trim().ToLower();
|
||||
|
Loading…
x
Reference in New Issue
Block a user