From ec85fd8eb3676914f6f7e43f77539526eda68bd7 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Fri, 29 Aug 2025 15:44:15 +0530 Subject: [PATCH] Added the API to get old version list --- .../DocumentManager/AttachmentVersionVM.cs | 18 ++ .../Controllers/DocumentController.cs | 160 +++++++++++++----- .../MappingProfiles/MappingProfile.cs | 2 +- 3 files changed, 140 insertions(+), 40 deletions(-) create mode 100644 Marco.Pms.Model/ViewModels/DocumentManager/AttachmentVersionVM.cs diff --git a/Marco.Pms.Model/ViewModels/DocumentManager/AttachmentVersionVM.cs b/Marco.Pms.Model/ViewModels/DocumentManager/AttachmentVersionVM.cs new file mode 100644 index 0000000..e0c45af --- /dev/null +++ b/Marco.Pms.Model/ViewModels/DocumentManager/AttachmentVersionVM.cs @@ -0,0 +1,18 @@ +using Marco.Pms.Model.ViewModels.Activities; + +namespace Marco.Pms.Model.ViewModels.DocumentManager +{ + public class AttachmentVersionVM + { + public Guid Id { get; set; } + public string? Name { get; set; } + public string? DocumentId { get; set; } + public string? PreSignedUrl { get; set; } + public int Version { get; set; } + public DateTime UploadedAt { get; set; } + public BasicEmployeeVM? UploadedBy { get; set; } + public DateTime? UpdatedAt { get; set; } + public BasicEmployeeVM? UpdatedBy { get; set; } + public bool? IsVerified { get; set; } + } +} diff --git a/Marco.Pms.Services/Controllers/DocumentController.cs b/Marco.Pms.Services/Controllers/DocumentController.cs index 85e4e3b..1d6101d 100644 --- a/Marco.Pms.Services/Controllers/DocumentController.cs +++ b/Marco.Pms.Services/Controllers/DocumentController.cs @@ -223,23 +223,27 @@ namespace Marco.Pms.Services.Controllers // GET api//5 [HttpGet("{id}")] - public async Task Get(int id) + public async Task GetDetails(int id) { - await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + await using var _context = await _dbContextFactory.CreateDbContextAsync(); using var scope = _serviceScope.CreateScope(); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); - - //string preSignedUrl = _s3Service.GeneratePreSignedUrl(objectKey); + //if (versionMapping.ChildAttachment != null && versionMapping.ChildAttachment.Document != null) + //{ + // var s3Service = scope.ServiceProvider.GetRequiredService(); + // documentVM.PreSignedUrl = s3Service.GeneratePreSignedUrl(versionMapping.ChildAttachment.Document.S3Key); + //} + return Ok(ApiResponse.SuccessResponse(new { }, "Filters for documents fetched successfully", 200)); } - [HttpGet("get/filter{entityTypeId}")] + [HttpGet("get/filter/{entityTypeId}")] public async Task GetFilterObjectAsync(Guid entityTypeId) { // Log: Starting filter fetch process _logger.LogInfo("Initiating GetFilterObjectAsync to retrieve document filter data."); - await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + await using var _context = await _dbContextFactory.CreateDbContextAsync(); using var scope = _serviceScope.CreateScope(); // Get current logged-in employee @@ -247,7 +251,7 @@ namespace Marco.Pms.Services.Controllers _logger.LogDebug("Fetched current employee: {EmployeeId}", loggedInEmployee.Id); // Fetch all relevant document attachments for the tenant with related data - var documentList = await dbContext.DocumentAttachments + var documentList = await _context.DocumentAttachments .Include(da => da.UploadedBy) .Include(da => da.DocumentType) .ThenInclude(dt => dt!.DocumentCategory) @@ -262,7 +266,7 @@ namespace Marco.Pms.Services.Controllers var documentIds = documentList.Select(da => da.Id).ToList(); // Preload tags for given ids - var documentTags = await dbContext.AttachmentTagMappings + var documentTags = await _context.AttachmentTagMappings .Where(at => documentIds.Contains(at.AttachmentId) && at.DocumentTag != null) .Select(at => new { @@ -322,6 +326,60 @@ namespace Marco.Pms.Services.Controllers return Ok(ApiResponse.SuccessResponse(response, "Filters for documents fetched successfully", 200)); } + [HttpGet("list/versions/{parentAttachmentId}")] + public async Task GetAllVersionsAsync(Guid parentAttachmentId) + { + _logger.LogInfo("Start fetching document versions for ParentAttachmentId: {ParentAttachmentId}", parentAttachmentId); + + // Create a new DbContext instance asynchronously + await using var _context = await _dbContextFactory.CreateDbContextAsync(); + + try + { + // Retrieve currently logged in employee details for potential security or filtering checks + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + if (loggedInEmployee == null) + { + _logger.LogWarning("No logged in employee found while fetching versions for ParentAttachmentId: {ParentAttachmentId}", parentAttachmentId); + return Unauthorized(ApiResponse.ErrorResponse("Unauthorized access", 401)); + } + + // Retrieve all version mappings linked to the parent attachment and tenant + var versionMappings = await _context.AttachmentVersionMappings + .Include(av => av.ChildAttachment) + .ThenInclude(da => da!.UploadedBy) + .Include(av => av.ChildAttachment) + .ThenInclude(da => da!.UpdatedBy) + .Where(av => av.ParentAttachmentId == parentAttachmentId && av.TenantId == tenantId) + .ToListAsync(); + + _logger.LogInfo("Found {Count} versions for ParentAttachmentId: {ParentAttachmentId}", versionMappings.Count, parentAttachmentId); + + // Map the retrieved child attachments to view models with version info + var response = versionMappings.Select(versionMapping => + { + var documentVM = _mapper.Map(versionMapping.ChildAttachment); + documentVM.Version = versionMapping.Version; + return documentVM; + }).ToList(); + + _logger.LogInfo("Successfully mapped version data for ParentAttachmentId: {ParentAttachmentId}", parentAttachmentId); + + return Ok(ApiResponse.SuccessResponse(response, "Document versions fetched successfully", 200)); + } + catch (Exception ex) + { + // Log the exception and return an internal server error response + _logger.LogError(ex, "Error occurred while fetching document versions for ParentAttachmentId: {ParentAttachmentId}", parentAttachmentId); + return StatusCode(500, ApiResponse.ErrorResponse("An error occurred while fetching document versions", 500)); + } + finally + { + _logger.LogInfo("End processing GetAllVersions for ParentAttachmentId: {ParentAttachmentId}", parentAttachmentId); + } + } + + /// /// Uploads a document attachment for an Employee or Project. @@ -331,7 +389,7 @@ namespace Marco.Pms.Services.Controllers [HttpPost("upload")] public async Task UploadDocumentAsync([FromBody] DocumentAttachmentDto model) { - await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + await using var _context = await _dbContextFactory.CreateDbContextAsync(); using var scope = _serviceScope.CreateScope(); _logger.LogInfo("Document upload initiated for EntityId: {EntityId}, DocumentTypeId: {DocumentTypeId}", model.EntityId, model.DocumentTypeId); @@ -352,7 +410,7 @@ namespace Marco.Pms.Services.Controllers } // Validate Document Type - var documentType = await dbContext.DocumentTypeMasters + var documentType = await _context.DocumentTypeMasters .Include(dt => dt.DocumentCategory) .FirstOrDefaultAsync(dt => dt.Id == model.DocumentTypeId && dt.TenantId == tenantId && dt.DocumentCategory != null); @@ -384,11 +442,11 @@ namespace Marco.Pms.Services.Controllers bool entityExists = false; if (entityType.Equals(EmployeeEntity)) { - entityExists = await dbContext.Employees.AnyAsync(e => e.Id == model.EntityId && e.TenantId == tenantId); + entityExists = await _context.Employees.AnyAsync(e => e.Id == model.EntityId && e.TenantId == tenantId); } else if (entityType.Equals(ProjectEntity)) { - entityExists = await dbContext.Projects.AnyAsync(p => p.Id == model.EntityId && p.TenantId == tenantId); + entityExists = await _context.Projects.AnyAsync(p => p.Id == model.EntityId && p.TenantId == tenantId); } if (!entityExists) @@ -405,7 +463,8 @@ namespace Marco.Pms.Services.Controllers attachment.TenantId = tenantId; // Validate Attachment - if (model.Attachment.FileSize > documentType.MaxSizeAllowedInMB) + var allowedSize = documentType.MaxSizeAllowedInMB * 1024; + if (model.Attachment.FileSize > allowedSize) { _logger.LogWarning("File size {FileSize} exceeded max allowed {MaxSize}MB", model.Attachment.FileSize, documentType.MaxSizeAllowedInMB); return BadRequest(ApiResponse.ErrorResponse("File size limit exceeded", $"Max allowed {documentType.MaxSizeAllowedInMB} MB.", 400)); @@ -463,11 +522,11 @@ namespace Marco.Pms.Services.Controllers TenantId = tenantId }; - dbContext.Documents.Add(document); + _context.Documents.Add(document); attachment.DocumentDataId = document.Id; - dbContext.DocumentAttachments.Add(attachment); + _context.DocumentAttachments.Add(attachment); //Process Versioning @@ -478,13 +537,13 @@ namespace Marco.Pms.Services.Controllers Version = 1, TenantId = tenantId }; - dbContext.AttachmentVersionMappings.Add(versionMapping); + _context.AttachmentVersionMappings.Add(versionMapping); // Process Tags if (model.Tags?.Any() == true) { var names = model.Tags.Select(t => t.Name).ToList(); - var existingTags = await dbContext.DocumentTagMasters + var existingTags = await _context.DocumentTagMasters .Where(t => names.Contains(t.Name) && t.TenantId == tenantId) .ToListAsync(); @@ -503,7 +562,7 @@ namespace Marco.Pms.Services.Controllers if (existingTag == null) { - dbContext.DocumentTagMasters.Add(tagEntity); + _context.DocumentTagMasters.Add(tagEntity); } attachmentTagMappings.Add(new AttachmentTagMapping @@ -514,11 +573,11 @@ namespace Marco.Pms.Services.Controllers }); } - dbContext.AttachmentTagMappings.AddRange(attachmentTagMappings); + _context.AttachmentTagMappings.AddRange(attachmentTagMappings); } - await dbContext.SaveChangesAsync(); + await _context.SaveChangesAsync(); _logger.LogInfo("Document uploaded successfully. AttachmentId: {AttachmentId}, DocumentId: {DocumentId}", attachment.Id, document.Id); @@ -546,7 +605,7 @@ namespace Marco.Pms.Services.Controllers public async Task VerifyDocumentAsync(Guid id, [FromQuery] bool isVerify = true) { // Begin: Create DbContext and DI scope - await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + await using var _context = await _dbContextFactory.CreateDbContextAsync(); using var scope = _serviceScope.CreateScope(); try @@ -559,7 +618,7 @@ namespace Marco.Pms.Services.Controllers loggedInEmployee.Id, id, isVerify); // Fetch active/current document by Id, TenantId, and relevant conditions - var documentAttachment = await dbContext.DocumentAttachments + var documentAttachment = await _context.DocumentAttachments .FirstOrDefaultAsync(da => da.Id == id && da.IsActive && da.IsCurrentVersion && da.TenantId == tenantId); if (documentAttachment == null) @@ -588,7 +647,7 @@ namespace Marco.Pms.Services.Controllers documentAttachment.VerifiedById = loggedInEmployee.Id; // Commit changes - await dbContext.SaveChangesAsync(); + await _context.SaveChangesAsync(); // Log the update to MongoDB for change tracking await updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject @@ -599,7 +658,7 @@ namespace Marco.Pms.Services.Controllers UpdatedAt = DateTime.UtcNow }, Collection); - var versionMapping = await dbContext.AttachmentVersionMappings.FirstOrDefaultAsync(av => av.ChildAttachmentId == documentAttachment.Id); + var versionMapping = await _context.AttachmentVersionMappings.FirstOrDefaultAsync(av => av.ChildAttachmentId == documentAttachment.Id); var response = _mapper.Map(documentAttachment); if (versionMapping != null) @@ -629,7 +688,7 @@ namespace Marco.Pms.Services.Controllers try { - await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + await using var _context = await _dbContextFactory.CreateDbContextAsync(); // Get logged-in employee details var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); @@ -638,7 +697,7 @@ namespace Marco.Pms.Services.Controllers var hasUploadPermission = await permissionService.HasPermission(PermissionsMaster.UploadDocument, loggedInEmployee.Id); // Fetch the existing attachment - var oldAttachment = await dbContext.DocumentAttachments + var oldAttachment = await _context.DocumentAttachments .Include(da => da.DocumentType) .ThenInclude(dt => dt!.DocumentCategory) .FirstOrDefaultAsync(da => da.Id == id && da.IsActive && da.IsCurrentVersion && da.TenantId == tenantId); @@ -686,11 +745,11 @@ namespace Marco.Pms.Services.Controllers bool entityExists; if (entityType.Equals(EmployeeEntity)) { - entityExists = await dbContext.Employees.AnyAsync(e => e.Id == oldAttachment.EntityId && e.TenantId == tenantId); + entityExists = await _context.Employees.AnyAsync(e => e.Id == oldAttachment.EntityId && e.TenantId == tenantId); } else if (entityType.Equals(ProjectEntity)) { - entityExists = await dbContext.Projects.AnyAsync(p => p.Id == oldAttachment.EntityId && p.TenantId == tenantId); + entityExists = await _context.Projects.AnyAsync(p => p.Id == oldAttachment.EntityId && p.TenantId == tenantId); if (entityExists) { entityExists = await permissionService.HasProjectPermission(loggedInEmployee, oldAttachment.EntityId); @@ -708,7 +767,7 @@ namespace Marco.Pms.Services.Controllers } // Prepare for versioning - var oldVersionMapping = await dbContext.AttachmentVersionMappings + var oldVersionMapping = await _context.AttachmentVersionMappings .FirstOrDefaultAsync(av => av.ChildAttachmentId == oldAttachment.Id && av.TenantId == tenantId); var updateLogHelper = scope.ServiceProvider.GetRequiredService(); @@ -720,7 +779,8 @@ namespace Marco.Pms.Services.Controllers if (model.Attachment != null) { // File size check - if (model.Attachment.FileSize > documentType.MaxSizeAllowedInMB) + var allowedSize = documentType.MaxSizeAllowedInMB * 1024; + if (model.Attachment.FileSize > allowedSize) { _logger.LogWarning("Attachment exceeded max file size for DocumentTypeId: {DocumentTypeId}", documentType.Id); return BadRequest(ApiResponse.ErrorResponse("File size limit exceeded", $"Max allowed {documentType.MaxSizeAllowedInMB} MB.", 400)); @@ -779,7 +839,7 @@ namespace Marco.Pms.Services.Controllers TenantId = tenantId }; - dbContext.Documents.Add(document); + _context.Documents.Add(document); if (oldAttachment.IsVerified == true) { @@ -798,7 +858,7 @@ namespace Marco.Pms.Services.Controllers TenantId = oldAttachment.TenantId }; - dbContext.DocumentAttachments.Add(attachment); + _context.DocumentAttachments.Add(attachment); // Mark old version as not current oldAttachment.IsCurrentVersion = false; @@ -825,7 +885,7 @@ namespace Marco.Pms.Services.Controllers TenantId = tenantId }; } - dbContext.AttachmentVersionMappings.Add(versionMapping); + _context.AttachmentVersionMappings.Add(versionMapping); newAttachment = attachment; newVersionMapping = versionMapping; @@ -837,6 +897,13 @@ namespace Marco.Pms.Services.Controllers oldAttachment.DocumentId = model.DocumentId; oldAttachment.Description = model.Description; oldAttachment.DocumentDataId = document.Id; + if (oldAttachment.IsVerified == true) + { + oldAttachment.IsVerified = null; + _logger.LogInfo("Reset verification flag for AttachmentId: {AttachmentId}", oldAttachment.Id); + } + oldAttachment.UpdatedAt = DateTime.UtcNow; + oldAttachment.UpdatedById = loggedInEmployee.Id; newAttachment = oldAttachment; newVersionMapping = oldVersionMapping ?? new AttachmentVersionMapping(); @@ -867,17 +934,18 @@ namespace Marco.Pms.Services.Controllers if (model.Tags?.Any() == true) { var names = model.Tags.Select(t => t.Name).ToList(); - var existingTags = await dbContext.DocumentTagMasters + var existingTags = await _context.DocumentTagMasters .Where(t => names.Contains(t.Name) && t.TenantId == tenantId) .ToListAsync(); var attachmentTagMappings = new List(); - var oldTagNames = await dbContext.AttachmentTagMappings + var oldTags = await _context.AttachmentTagMappings .Include(dt => dt.DocumentTag) .Where(dt => dt.DocumentTag != null && dt.AttachmentId == newAttachment.Id && dt.TenantId == tenantId) - .Select(dt => dt.DocumentTag!.Name) .ToListAsync(); + var oldTagNames = oldTags.Select(dt => dt.DocumentTag!.Name).ToList(); + foreach (var tag in model.Tags.Where(t => t.IsActive && !oldTagNames.Contains(t.Name))) { var existingTag = existingTags.FirstOrDefault(t => t.Name == tag.Name); @@ -891,7 +959,7 @@ namespace Marco.Pms.Services.Controllers if (existingTag == null) { - dbContext.DocumentTagMasters.Add(tagEntity); + _context.DocumentTagMasters.Add(tagEntity); } attachmentTagMappings.Add(new AttachmentTagMapping @@ -902,12 +970,26 @@ namespace Marco.Pms.Services.Controllers }); } - dbContext.AttachmentTagMappings.AddRange(attachmentTagMappings); + _context.AttachmentTagMappings.AddRange(attachmentTagMappings); + + var deletedTagMappings = new List(); + + foreach (var tag in model.Tags.Where(t => !t.IsActive && oldTagNames.Contains(t.Name))) + { + var deletedTagMapping = oldTags.FirstOrDefault(at => at.DocumentTag!.Name == tag.Name); + + if (deletedTagMapping != null) + { + deletedTagMappings.Add(deletedTagMapping); + } + } + _context.AttachmentTagMappings.RemoveRange(deletedTagMappings); + _logger.LogInfo("Tags processed for AttachmentId: {AttachmentId}", newAttachment.Id); } // Persist changes to database - await dbContext.SaveChangesAsync(); + await _context.SaveChangesAsync(); _logger.LogInfo("Database changes committed for AttachmentId: {AttachmentId}", newAttachment.Id); // Update logs diff --git a/Marco.Pms.Services/MappingProfiles/MappingProfile.cs b/Marco.Pms.Services/MappingProfiles/MappingProfile.cs index 4b05f39..ea49414 100644 --- a/Marco.Pms.Services/MappingProfiles/MappingProfile.cs +++ b/Marco.Pms.Services/MappingProfiles/MappingProfile.cs @@ -310,7 +310,7 @@ namespace Marco.Pms.Services.MappingProfiles CreateMap(); CreateMap(); - CreateMap(); + CreateMap(); CreateMap(); CreateMap();