diff --git a/Marco.Pms.Model/Dtos/DocumentManager/DocumentAttachmentDto.cs b/Marco.Pms.Model/Dtos/DocumentManager/DocumentAttachmentDto.cs new file mode 100644 index 0000000..acf2003 --- /dev/null +++ b/Marco.Pms.Model/Dtos/DocumentManager/DocumentAttachmentDto.cs @@ -0,0 +1,15 @@ +using Marco.Pms.Model.Utilities; + +namespace Marco.Pms.Model.Dtos.DocumentManager +{ + public class DocumentAttachmentDto + { + public required string Name { get; set; } + public string? DocumentId { get; set; } + public required string Description { get; set; } + public Guid EntityId { get; set; } + public required Guid DocumentTypeId { get; set; } // References the type of the document + public required FileUploadModel Attachment { get; set; } + public List? Tags { get; set; } + } +} diff --git a/Marco.Pms.Model/Dtos/DocumentManager/DocumentTagDto.cs b/Marco.Pms.Model/Dtos/DocumentManager/DocumentTagDto.cs new file mode 100644 index 0000000..24c1913 --- /dev/null +++ b/Marco.Pms.Model/Dtos/DocumentManager/DocumentTagDto.cs @@ -0,0 +1,8 @@ +namespace Marco.Pms.Model.Dtos.DocumentManager +{ + public class DocumentTagDto + { + public required string Name { get; set; } + public required bool IsActive { get; set; } + } +} diff --git a/Marco.Pms.Model/Filters/DocumentFilter.cs b/Marco.Pms.Model/Filters/DocumentFilter.cs new file mode 100644 index 0000000..4b5538e --- /dev/null +++ b/Marco.Pms.Model/Filters/DocumentFilter.cs @@ -0,0 +1,13 @@ +namespace Marco.Pms.Model.Filters +{ + public class DocumentFilter + { + public List? UploadedByIds { get; set; } + public List? DocumentTypeIds { get; set; } + public bool IsActive { get; set; } = true; + public bool IsUploadedAt { get; set; } = true; + public bool? IsVerified { get; set; } + public DateTime? StartDate { get; set; } + public DateTime? EndDate { get; set; } + } +} diff --git a/Marco.Pms.Model/Utilities/ExpensesFilter.cs b/Marco.Pms.Model/Filters/ExpensesFilter.cs similarity index 100% rename from Marco.Pms.Model/Utilities/ExpensesFilter.cs rename to Marco.Pms.Model/Filters/ExpensesFilter.cs diff --git a/Marco.Pms.Model/Utilities/ImageFilter.cs b/Marco.Pms.Model/Filters/ImageFilter.cs similarity index 92% rename from Marco.Pms.Model/Utilities/ImageFilter.cs rename to Marco.Pms.Model/Filters/ImageFilter.cs index a5cb7f7..d044a9b 100644 --- a/Marco.Pms.Model/Utilities/ImageFilter.cs +++ b/Marco.Pms.Model/Filters/ImageFilter.cs @@ -1,4 +1,4 @@ -namespace Marco.Pms.Model.Utilities +namespace Marco.Pms.Model.Filters { public class ImageFilter { diff --git a/Marco.Pms.Model/Utilities/TenantFilter.cs b/Marco.Pms.Model/Filters/TenantFilter.cs similarity index 90% rename from Marco.Pms.Model/Utilities/TenantFilter.cs rename to Marco.Pms.Model/Filters/TenantFilter.cs index 2c0a477..1635643 100644 --- a/Marco.Pms.Model/Utilities/TenantFilter.cs +++ b/Marco.Pms.Model/Filters/TenantFilter.cs @@ -1,4 +1,4 @@ -namespace Marco.Pms.Model.Utilities +namespace Marco.Pms.Model.Filters { public class TenantFilter { diff --git a/Marco.Pms.Model/Master/EntityTypeMaster.cs b/Marco.Pms.Model/Master/EntityTypeMaster.cs new file mode 100644 index 0000000..9def961 --- /dev/null +++ b/Marco.Pms.Model/Master/EntityTypeMaster.cs @@ -0,0 +1,9 @@ +namespace Marco.Pms.Model.Master +{ + public class EntityTypeMaster + { + public Guid Id { get; set; } + public string Name { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + } +} diff --git a/Marco.Pms.Model/Utilities/FileUploadModel.cs b/Marco.Pms.Model/Utilities/FileUploadModel.cs index 98a6a26..1552e5a 100644 --- a/Marco.Pms.Model/Utilities/FileUploadModel.cs +++ b/Marco.Pms.Model/Utilities/FileUploadModel.cs @@ -3,9 +3,9 @@ public class FileUploadModel { public Guid? DocumentId { get; set; } - public string? FileName { get; set; } // Name of the file (e.g., "image1.png") - public string? Base64Data { get; set; } // Base64-encoded string of the file - public string? ContentType { get; set; } // MIME type (e.g., "image/png", "application/pdf") + public required string FileName { get; set; } // Name of the file (e.g., "image1.png") + public required string Base64Data { get; set; } // Base64-encoded string of the file + public required string ContentType { get; set; } // MIME type (e.g., "image/png", "application/pdf") public long FileSize { get; set; } // File size in bytes public string? Description { get; set; } // Optional: Description or purpose of the file public bool IsActive { get; set; } = true; diff --git a/Marco.Pms.Model/ViewModels/DocumentManager/DocumentCategoryVM.cs b/Marco.Pms.Model/ViewModels/DocumentManager/DocumentCategoryVM.cs new file mode 100644 index 0000000..6da8b02 --- /dev/null +++ b/Marco.Pms.Model/ViewModels/DocumentManager/DocumentCategoryVM.cs @@ -0,0 +1,10 @@ +namespace Marco.Pms.Model.ViewModels.DocumentManager +{ + public class DocumentCategoryVM + { + public Guid Id { get; set; } + public string Name { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public Guid EntityTypeId { get; set; } + } +} diff --git a/Marco.Pms.Model/ViewModels/DocumentManager/DocumentListVM.cs b/Marco.Pms.Model/ViewModels/DocumentManager/DocumentListVM.cs new file mode 100644 index 0000000..f900959 --- /dev/null +++ b/Marco.Pms.Model/ViewModels/DocumentManager/DocumentListVM.cs @@ -0,0 +1,17 @@ +using Marco.Pms.Model.ViewModels.Activities; + +namespace Marco.Pms.Model.ViewModels.DocumentManager +{ + public class DocumentListVM + { + public Guid Id { get; set; } + public string? Name { get; set; } + public string? DocumentId { get; set; } + public string? Description { get; set; } + public DateTime UploadedAt { get; set; } + public BasicEmployeeVM? UploadedBy { get; set; } + public DocumentTypeVM? DocumentType { get; set; } + public bool IsActive { get; set; } + public bool IsVerified { get; set; } + } +} diff --git a/Marco.Pms.Model/ViewModels/DocumentManager/DocumentTypeVM.cs b/Marco.Pms.Model/ViewModels/DocumentManager/DocumentTypeVM.cs new file mode 100644 index 0000000..a7c3d0a --- /dev/null +++ b/Marco.Pms.Model/ViewModels/DocumentManager/DocumentTypeVM.cs @@ -0,0 +1,16 @@ +namespace Marco.Pms.Model.ViewModels.DocumentManager +{ + public class DocumentTypeVM + { + public Guid Id { get; set; } + public string Name { get; set; } = string.Empty; + public string? RegexExpression { get; set; } + public string AllowedContentType { get; set; } = string.Empty; + public int MaxFilesAllowed { get; set; } + public double MaxSizeAllowedInKB { get; set; } + public bool IsValidationRequired { get; set; } + public bool IsSystem { get; set; } + public bool IsActive { get; set; } + public DocumentCategoryVM? DocumentCategory { get; set; } + } +} diff --git a/Marco.Pms.Services/Controllers/ImageController.cs b/Marco.Pms.Services/Controllers/ImageController.cs index cf046a8..c8724de 100644 --- a/Marco.Pms.Services/Controllers/ImageController.cs +++ b/Marco.Pms.Services/Controllers/ImageController.cs @@ -2,6 +2,7 @@ using Marco.Pms.Model.Activities; using Marco.Pms.Model.Dtos.DocumentManager; using Marco.Pms.Model.Employees; +using Marco.Pms.Model.Filters; using Marco.Pms.Model.Mapper; using Marco.Pms.Model.Projects; using Marco.Pms.Model.Utilities; diff --git a/Marco.Pms.Services/Controllers/MasterController.cs b/Marco.Pms.Services/Controllers/MasterController.cs index bcc4264..0c374d6 100644 --- a/Marco.Pms.Services/Controllers/MasterController.cs +++ b/Marco.Pms.Services/Controllers/MasterController.cs @@ -949,5 +949,17 @@ namespace Marco.Pms.Services.Controllers } #endregion + + #region =================================================================== Document Category APIs =================================================================== + + [HttpGet("document-category/list")] + public async Task GetDocumentCategoryMasterList([FromQuery] Guid? entityTypeId) + { + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + var response = await _masterService.GetDocumentCategoryMasterListAsync(entityTypeId, loggedInEmployee, tenantId); + return StatusCode(response.StatusCode, response); + } + + #endregion } } diff --git a/Marco.Pms.Services/Controllers/ReportController.cs b/Marco.Pms.Services/Controllers/ReportController.cs index e71061c..872f128 100644 --- a/Marco.Pms.Services/Controllers/ReportController.cs +++ b/Marco.Pms.Services/Controllers/ReportController.cs @@ -154,34 +154,6 @@ namespace Marco.Pms.Services.Controllers } } - [HttpPost("mail-template1")] - public async Task AddMailTemplate1([FromBody] MailTemeplateDto mailTemeplateDto) - { - Guid tenantId = _userHelper.GetTenantId(); - if (string.IsNullOrWhiteSpace(mailTemeplateDto.Body) && string.IsNullOrWhiteSpace(mailTemeplateDto.Title)) - { - _logger.LogWarning("User tries to set email template but send invalid data"); - return BadRequest(ApiResponse.ErrorResponse("Provided Invalid data", "Provided Invalid data", 400)); - } - var existngTemalate = await _context.MailingList.FirstOrDefaultAsync(t => t.Title.ToLower() == mailTemeplateDto.Title.ToLower()); - if (existngTemalate != null) - { - _logger.LogWarning("User tries to set email template, but title already existed in database"); - return BadRequest(ApiResponse.ErrorResponse("Email title is already existed", "Email title is already existed", 400)); - } - MailingList mailingList = new MailingList - { - Title = mailTemeplateDto.Title, - Body = mailTemeplateDto.Body, - Subject = mailTemeplateDto.Subject, - Keywords = mailTemeplateDto.Keywords, - TenantId = tenantId - }; - _context.MailingList.Add(mailingList); - await _context.SaveChangesAsync(); - return Ok("Success"); - } - /// /// Adds a new mail template. /// diff --git a/Marco.Pms.Services/Controllers/TenantController.cs b/Marco.Pms.Services/Controllers/TenantController.cs index 38f72df..be5b1ed 100644 --- a/Marco.Pms.Services/Controllers/TenantController.cs +++ b/Marco.Pms.Services/Controllers/TenantController.cs @@ -4,6 +4,7 @@ using Marco.Pms.Helpers.Utility; using Marco.Pms.Model.Dtos.Tenant; using Marco.Pms.Model.Employees; using Marco.Pms.Model.Entitlements; +using Marco.Pms.Model.Filters; using Marco.Pms.Model.MongoDBModels.Utility; using Marco.Pms.Model.Projects; using Marco.Pms.Model.Roles; diff --git a/Marco.Pms.Services/MappingProfiles/MappingProfile.cs b/Marco.Pms.Services/MappingProfiles/MappingProfile.cs index 1c516e1..f283bd8 100644 --- a/Marco.Pms.Services/MappingProfiles/MappingProfile.cs +++ b/Marco.Pms.Services/MappingProfiles/MappingProfile.cs @@ -1,6 +1,8 @@ using AutoMapper; using Marco.Pms.Model.AppMenu; +using Marco.Pms.Model.DocumentManager; using Marco.Pms.Model.Dtos.AppMenu; +using Marco.Pms.Model.Dtos.DocumentManager; using Marco.Pms.Model.Dtos.Expenses; using Marco.Pms.Model.Dtos.Master; using Marco.Pms.Model.Dtos.Project; @@ -306,6 +308,16 @@ namespace Marco.Pms.Services.MappingProfiles dest => dest.DocumentId, opt => opt.MapFrom(src => Guid.Parse(src.DocumentId))); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + #endregion #region ======================================================= AppMenu ======================================================= diff --git a/Marco.Pms.Services/Service/MasterService.cs b/Marco.Pms.Services/Service/MasterService.cs index 88467d1..11e64ba 100644 --- a/Marco.Pms.Services/Service/MasterService.cs +++ b/Marco.Pms.Services/Service/MasterService.cs @@ -1,12 +1,14 @@ using AutoMapper; using Marco.Pms.DataAccess.Data; using Marco.Pms.Helpers.Utility; +using Marco.Pms.Model.DocumentManager; using Marco.Pms.Model.Dtos.Master; using Marco.Pms.Model.Employees; using Marco.Pms.Model.Entitlements; using Marco.Pms.Model.Master; using Marco.Pms.Model.MongoDBModels.Utility; using Marco.Pms.Model.Utilities; +using Marco.Pms.Model.ViewModels.DocumentManager; using Marco.Pms.Model.ViewModels.Master; using Marco.Pms.Services.Helpers; using Marco.Pms.Services.Service.ServiceInterfaces; @@ -487,6 +489,61 @@ namespace Marco.Pms.Services.Service } } + #endregion + + #region =================================================================== Document Category APIs =================================================================== + + /// + /// Fetches the list of Document Categories for a given tenant and optional entity type. + /// Ensures tenant validation, mapping, and proper logging. + /// + /// Optional entity type filter (e.g., EmployeeEntity, ProjectEntity). + /// Currently logged-in employee. + /// Tenant Id context. + /// ApiResponse containing the document categories or error details. + public async Task> GetDocumentCategoryMasterListAsync(Guid? entityTypeId, Employee loggedInEmployee, Guid tenantId) + { + try + { + // ✅ Tenant validation + if (tenantId != loggedInEmployee.TenantId) + { + _logger.LogWarning("Access denied. Employee {EmployeeId} (TenantId: {EmployeeTenantId}) attempted to fetch document categories for TenantId: {RequestedTenantId}", + loggedInEmployee.Id, loggedInEmployee.TenantId, tenantId); + + return ApiResponse.ErrorResponse("Access Denied", "You do not have access to this information", 403); + } + + // ✅ Build query + IQueryable documentCategoryQuery = _context.DocumentCategoryMasters + .AsNoTracking() // optimization: read-only + .Where(dc => dc.TenantId == tenantId); + + // ✅ Apply optional filter + if (entityTypeId.HasValue) + { + documentCategoryQuery = documentCategoryQuery.Where(dc => dc.EntityTypeId == entityTypeId.Value); + } + + // ✅ Fetch and map + var documentCategories = await documentCategoryQuery.ToListAsync(); + var response = _mapper.Map>(documentCategories); + + _logger.LogInfo("{Count} document categories fetched successfully for TenantId: {TenantId} by Employee {EmployeeId}", + response.Count, tenantId, loggedInEmployee.Id); + + return ApiResponse.SuccessResponse(response, $"{response.Count} document categories have been fetched successfully.", 200); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error occurred while fetching document categories for TenantId: {TenantId} by Employee {EmployeeId}", + tenantId, loggedInEmployee.Id); + + return ApiResponse.ErrorResponse("Internal Server Error", "Server Error occured", 500); + } + } + + #endregion #region =================================================================== Helper Function =================================================================== diff --git a/Marco.Pms.Services/Service/ServiceInterfaces/IMasterService.cs b/Marco.Pms.Services/Service/ServiceInterfaces/IMasterService.cs index 2cde277..23f3c51 100644 --- a/Marco.Pms.Services/Service/ServiceInterfaces/IMasterService.cs +++ b/Marco.Pms.Services/Service/ServiceInterfaces/IMasterService.cs @@ -26,5 +26,13 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces Task> DeletePaymentModeAsync(Guid id, bool isActive, Employee loggedInEmployee, Guid tenantId); #endregion + + #region =================================================================== Payment mode APIs =================================================================== + Task> GetDocumentCategoryMasterListAsync(Guid? entityTypeId, Employee loggedInEmployee, Guid tenantId); + //Task> CreatePaymentModeAsync(PaymentModeMatserDto model, Employee loggedInEmployee, Guid tenantId); + //Task> UpdatePaymentModeAsync(Guid id, PaymentModeMatserDto model, Employee loggedInEmployee, Guid tenantId); + //Task> DeletePaymentModeAsync(Guid id, bool isActive, Employee loggedInEmployee, Guid tenantId); + + #endregion } }