Added IsArchive field in ob ticket table

This commit is contained in:
ashutosh.nehete 2025-11-20 11:58:45 +05:30
parent d5a7ad0716
commit bd2f9d953f
11 changed files with 8982 additions and 19 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_IsArchive_In_JobTicket_Table : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsArchive",
table: "JobTickets",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsArchive",
table: "JobTickets");
}
}
}

View File

@ -5494,6 +5494,9 @@ namespace Marco.Pms.DataAccess.Migrations
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsArchive")
.HasColumnType("tinyint(1)");
b.Property<Guid?>("ProjectBranchId")
.HasColumnType("char(36)");

View File

@ -12,5 +12,6 @@ namespace Marco.Pms.Model.Dtos.ServiceProject
public DateTime StartDate { get; set; }
public DateTime DueDate { get; set; }
public List<TagDto>? Tags { get; set; }
public bool IsArchive { get; set; } = false;
}
}

View File

@ -30,6 +30,7 @@ namespace Marco.Pms.Model.ServiceProject
public DateTime StartDate { get; set; }
public DateTime DueDate { get; set; }
public bool IsActive { get; set; } = true;
public bool IsArchive { get; set; } = false;
public DateTime CreatedAt { get; set; }
public Guid CreatedById { get; set; }

View File

@ -7,5 +7,6 @@
public string? Description { get; set; }
public string? JobTicketUId { get; set; }
public string? StatusName { get; set; }
public bool IsArchive { get; set; }
}
}

View File

@ -17,6 +17,7 @@ namespace Marco.Pms.Model.ViewModels.ServiceProject
public DateTime StartDate { get; set; }
public DateTime DueDate { get; set; }
public bool IsActive { get; set; }
public bool IsArchive { get; set; }
public Guid? AttendanceId { get; set; }
public TAGGING_MARK_TYPE? TaggingAction { get; set; }
public TAGGING_MARK_TYPE? NextTaggingAction { get; set; }

View File

@ -16,6 +16,7 @@ namespace Marco.Pms.Model.ViewModels.ServiceProject
public DateTime StartDate { get; set; }
public DateTime DueDate { get; set; }
public bool IsActive { get; set; }
public bool IsArchive { get; set; }
public DateTime CreatedAt { get; set; }
public BasicEmployeeVM? CreatedBy { get; set; }
public List<TagVM>? Tags { get; set; }

View File

@ -187,10 +187,11 @@ namespace Marco.Pms.Services.Controllers
#region =================================================================== Job Tickets Functions ===================================================================
[HttpGet("job/list")]
public async Task<IActionResult> GetJobTicketsList([FromQuery] Guid? projectId, [FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 20, [FromQuery] bool isActive = true)
public async Task<IActionResult> GetJobTicketsList([FromQuery] Guid? projectId, [FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 20, [FromQuery] bool isActive = true,
[FromQuery] bool isArchive = false)
{
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _serviceProject.GetJobTicketsListAsync(projectId, pageNumber, pageSize, isActive, loggedInEmployee, tenantId);
var response = await _serviceProject.GetJobTicketsListAsync(projectId, pageNumber, pageSize, isActive, isArchive, loggedInEmployee, tenantId);
return StatusCode(response.StatusCode, response);
}

View File

@ -33,7 +33,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
#endregion
#region =================================================================== Job Tickets Functions ===================================================================
Task<ApiResponse<object>> GetJobTicketsListAsync(Guid? projectId, int pageNumber, int pageSize, bool isActive, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> GetJobTicketsListAsync(Guid? projectId, int pageNumber, int pageSize, bool isActive, bool isArchive, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> GetJobTicketDetailsAsync(Guid id, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> GetJobTagListAsync(Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> CreateJobTicketAsync(CreateJobTicketDto model, Employee loggedInEmployee, Guid tenantId);

View File

@ -1387,7 +1387,7 @@ namespace Marco.Pms.Services.Service
/// <param name="tenantId">Tenant context.</param>
/// <param name="loggedInEmployee">Employee requesting data.</param>
/// <returns>Paged list of JobTicketVM plus metadata, or error response.</returns>
public async Task<ApiResponse<object>> GetJobTicketsListAsync(Guid? projectId, int pageNumber, int pageSize, bool isActive, Employee loggedInEmployee, Guid tenantId)
public async Task<ApiResponse<object>> GetJobTicketsListAsync(Guid? projectId, int pageNumber, int pageSize, bool isActive, bool isArchive, Employee loggedInEmployee, Guid tenantId)
{
if (tenantId == Guid.Empty)
{
@ -1418,6 +1418,7 @@ namespace Marco.Pms.Services.Service
.Where(jt =>
jt.TenantId == tenantId &&
jt.IsActive == isActive &&
jt.IsArchive == isArchive &&
jt.Project != null &&
jt.Status != null &&
jt.CreatedBy != null &&
@ -1540,6 +1541,7 @@ namespace Marco.Pms.Services.Service
jt.Id == id &&
jt.TenantId == tenantId &&
jt.IsActive &&
!jt.IsArchive &&
jt.Project != null &&
jt.Status != null &&
jt.CreatedBy != null &&
@ -1548,7 +1550,7 @@ namespace Marco.Pms.Services.Service
if (jobTicket == null)
{
_logger.LogWarning("Job ticket not found or inactive. JobTicketId: {JobTicketId}, TenantId: {TenantId}", id, tenantId);
return ApiResponse<object>.ErrorResponse("Job not found", "Job ticket not found or inactive.", 404);
return ApiResponse<object>.ErrorResponse("The job could not be found. Please check the job details and try again.", "Job ticket not found or inactive.", 404);
}
// Load all job statuses for status mappings in logs
@ -1782,6 +1784,8 @@ namespace Marco.Pms.Services.Service
jobTicket.StatusId = hasAssignees ? AssignedStatus : NewStatus;
jobTicket.UIDPrefix = uIDPrefix;
jobTicket.UIDPostfix = uIDPostfix;
jobTicket.IsActive = true;
jobTicket.IsArchive = false;
jobTicket.CreatedAt = DateTime.UtcNow;
jobTicket.CreatedById = loggedInEmployee.Id;
jobTicket.TenantId = tenantId;
@ -1946,12 +1950,12 @@ namespace Marco.Pms.Services.Service
var jobTicket = await _context.JobTickets
.Include(jt => jt.Project)
.Include(jt => jt.CreatedBy).ThenInclude(e => e!.JobRole)
.FirstOrDefaultAsync(jt => jt.Id == model.JobTicketId && jt.TenantId == tenantId);
.FirstOrDefaultAsync(jt => jt.Id == model.JobTicketId && jt.TenantId == tenantId && jt.IsActive && !jt.IsArchive);
if (jobTicket == null)
{
_logger.LogWarning("Job ticket {JobTicketId} not found for status change in tenant {TenantId}", model.JobTicketId, tenantId);
return ApiResponse<object>.ErrorResponse("Job Not Found", "Job ticket not found.", 404);
return ApiResponse<object>.ErrorResponse("The job could not be found. Please check the job details and try again.", "Job ticket not found.", 404);
}
var jobStatusMapping = await GetJobStatusMappingAsync(jobTicket.StatusId, model.StatusId, jobTicket.ProjectId, loggedInEmployee.Id, tenantId);
@ -2073,6 +2077,18 @@ namespace Marco.Pms.Services.Service
};
_context.StatusUpdateLogs.Add(updateLog);
}
if (jobTicket.IsArchive != model.IsArchive)
{
// Validate if job ticket status permits archiving
if (model.IsArchive && jobTicket.StatusId != DoneStatus && jobTicket.StatusId != ClosedStatus)
{
_logger.LogWarning("Archiving failed: Job status not eligible. JobTicketId: {JobTicketId}, StatusId: {StatusId}", jobTicket.Id, jobTicket.StatusId);
return ApiResponse<object>.ErrorResponse(
"Archiving failed: Only jobs with status Done or Closed can be archived.",
"Invalid status: Job not eligible for archiving.",
400);
}
}
// Create BSON snapshot of existing entity for audit logging (MongoDB)
BsonDocument existingEntityBson = _updateLogHelper.EntityToBsonDocument(jobTicket);
@ -2280,18 +2296,18 @@ namespace Marco.Pms.Services.Service
.Include(jc => jc.JobTicket).ThenInclude(jt => jt!.Status)
.Include(jc => jc.CreatedBy).ThenInclude(e => e!.JobRole)
.Include(jc => jc.UpdatedBy).ThenInclude(e => e!.JobRole)
.Where(jc => jc.TenantId == tenantId && jc.JobTicket != null && jc.CreatedBy != null && jc.CreatedBy.JobRole != null);
.Where(jc => jc.TenantId == tenantId && jc.JobTicket != null && !jc.JobTicket.IsArchive && jc.CreatedBy != null && jc.CreatedBy.JobRole != null);
// Filter by jobTicketId if provided after verifying existence
if (jobTicketId.HasValue)
{
var jobTicketExists = await _context.JobTickets.AnyAsync(jt =>
jt.Id == jobTicketId && jt.TenantId == tenantId);
jt.Id == jobTicketId && jt.TenantId == tenantId && !jt.IsArchive);
if (!jobTicketExists)
{
_logger.LogWarning("Job ticket {JobTicketId} not found in tenant {TenantId} for comment listing", jobTicketId, tenantId);
return ApiResponse<object>.ErrorResponse("Job not found", "Job ticket not found.", 404);
return ApiResponse<object>.ErrorResponse("The job could not be found or is archived. Please check the job details and try again.", "Job ticket not found.", 404);
}
commentQuery = commentQuery.Where(jc => jc.JobTicketId == jobTicketId.Value);
@ -2398,12 +2414,12 @@ namespace Marco.Pms.Services.Service
var jobTicket = await _context.JobTickets
.Include(jt => jt.Status)
.AsNoTracking()
.FirstOrDefaultAsync(jt => jt.Id == model.JobTicketId && jt.TenantId == tenantId);
.FirstOrDefaultAsync(jt => jt.Id == model.JobTicketId && jt.TenantId == tenantId && !jt.IsArchive);
if (jobTicket == null)
{
_logger.LogWarning("Job ticket {JobTicketId} not found or inaccessible in tenant {TenantId}", model.JobTicketId, tenantId);
return ApiResponse<object>.ErrorResponse("Job Not Found", "Job ticket not found or inaccessible.", 404);
return ApiResponse<object>.ErrorResponse("The job could not be found or is archived. Please check the job details and try again.", "Job ticket not found or inaccessible.", 404);
}
// Create new comment entity
@ -2527,7 +2543,7 @@ namespace Marco.Pms.Services.Service
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.JobTickets
.AsNoTracking()
.FirstOrDefaultAsync(jc => jc.Id == model.JobTicketId && jc.TenantId == tenantId && jc.IsActive);
.FirstOrDefaultAsync(jt => jt.Id == model.JobTicketId && jt.TenantId == tenantId && !jt.IsArchive);
});
await Task.WhenAll(jobCommentTask, jobTicketTask);
@ -2537,7 +2553,7 @@ namespace Marco.Pms.Services.Service
if (jobTicket == null)
{
_logger.LogWarning("Job ticket {JobTicketId} not found for updating comment {CommentId}", model.JobTicketId, id);
return ApiResponse<object>.ErrorResponse("Job not found", "Job not found", 404);
return ApiResponse<object>.ErrorResponse("The job could not be found or is archived. Please check the job details and try again.", "The job could not be found. Please check the job details and try again.", 404);
}
if (jobComment == null)
{
@ -2725,12 +2741,12 @@ namespace Marco.Pms.Services.Service
var jobTicket = await _context.JobTickets
.AsNoTracking()
.Include(jt => jt.Status)
.FirstOrDefaultAsync(jt => jt.Id == jobTicketId && jt.TenantId == tenantId && jt.IsActive);
.FirstOrDefaultAsync(jt => jt.Id == jobTicketId && jt.TenantId == tenantId && !jt.IsArchive);
if (jobTicket == null)
{
_logger.LogWarning("JobTicket not found. JobTicketId: {JobTicketId}, TenantId: {TenantId}", jobTicketId, tenantId);
return ApiResponse<object>.ErrorResponse("Job not found", "Job is not found", 404);
return ApiResponse<object>.ErrorResponse("The job could not be found. Please check the job details and try again.", "Job is not found", 404);
}
var jobEmployeeMapping = await _context.JobEmployeeMappings
@ -2876,7 +2892,7 @@ namespace Marco.Pms.Services.Service
// Validate the existence and active status of the job ticket including its status related data
var jobTicket = await _context.JobTickets
.AsNoTracking()
.FirstOrDefaultAsync(jt => jt.Id == jobTicketId && jt.TenantId == tenantId && jt.IsActive);
.FirstOrDefaultAsync(jt => jt.Id == jobTicketId && jt.TenantId == tenantId && !jt.IsArchive);
if (jobTicket == null)
{
@ -2934,11 +2950,11 @@ namespace Marco.Pms.Services.Service
var jobTicket = await _context.JobTickets
.AsNoTracking()
.Include(jt => jt.Status)
.FirstOrDefaultAsync(jt => jt.Id == model.JobTcketId && jt.TenantId == tenantId && jt.IsActive);
.FirstOrDefaultAsync(jt => jt.Id == model.JobTcketId && jt.TenantId == tenantId && !jt.IsArchive);
if (jobTicket == null)
{
_logger.LogWarning("JobTicket not found. JobTicketId: {JobTicketId}, TenantId: {TenantId}", model.JobTcketId, tenantId);
return ApiResponse<object>.ErrorResponse("Job not found", "Job not found", 404);
return ApiResponse<object>.ErrorResponse("The job could not be found. Please check the job details and try again.", "The job could not be found. Please check the job details and try again.", 404);
}
// Check if the current user is part of the job team