From 6af2a3feec3b24c32567519f839416136b605422 Mon Sep 17 00:00:00 2001 From: makyluizp Date: Mon, 13 Jul 2026 15:32:04 +0800 Subject: [PATCH 1/5] feat: add Daily Accomplishment Report backend (sections 1-8) --- apps/api/HRIS.Api/Data/AppDbContext.cs | 2 + .../DailyReportConfiguration.cs | 45 + .../DailyReportTaskConfiguration.cs | 25 + .../Controllers/DailyReportsController.cs | 54 + .../DTOs/CreateDailyReportRequest.cs | 41 + .../Attendance/DTOs/DailyReportDto.cs | 76 + .../Attendance/DTOs/GetDailyReportsQuery.cs | 9 + .../DTOs/SupervisorRemarksRequest.cs | 15 + .../DTOs/UpdateDailyReportRequest.cs | 24 + .../Services/DailyReportsService.cs | 271 ++++ .../Services/IDailyReportsService.cs | 14 + ...20260601170756_AddDailyReports.Designer.cs | 1270 ++++++++++++++++ .../20260601170756_AddDailyReports.cs | 133 ++ ...707_AddDailyReportSections4to8.Designer.cs | 1344 +++++++++++++++++ ...260712212707_AddDailyReportSections4to8.cs | 258 ++++ .../Migrations/AppDbContextModelSnapshot.cs | 260 ++++ .../Models/DailyAccomplishmentReport.cs | 46 + apps/api/HRIS.Api/Models/DailyReport.cs | 62 + apps/api/HRIS.Api/Models/DailyReportTask.cs | 24 + apps/api/HRIS.Api/Models/DarTask.cs | 26 + apps/api/HRIS.Api/Program.cs | 6 + 21 files changed, 4005 insertions(+) create mode 100644 apps/api/HRIS.Api/Data/Configurations/DailyReportConfiguration.cs create mode 100644 apps/api/HRIS.Api/Data/Configurations/DailyReportTaskConfiguration.cs create mode 100644 apps/api/HRIS.Api/Features/Attendance/Controllers/DailyReportsController.cs create mode 100644 apps/api/HRIS.Api/Features/Attendance/DTOs/CreateDailyReportRequest.cs create mode 100644 apps/api/HRIS.Api/Features/Attendance/DTOs/DailyReportDto.cs create mode 100644 apps/api/HRIS.Api/Features/Attendance/DTOs/GetDailyReportsQuery.cs create mode 100644 apps/api/HRIS.Api/Features/Attendance/DTOs/SupervisorRemarksRequest.cs create mode 100644 apps/api/HRIS.Api/Features/Attendance/DTOs/UpdateDailyReportRequest.cs create mode 100644 apps/api/HRIS.Api/Features/Attendance/Services/DailyReportsService.cs create mode 100644 apps/api/HRIS.Api/Features/Attendance/Services/IDailyReportsService.cs create mode 100644 apps/api/HRIS.Api/Migrations/20260601170756_AddDailyReports.Designer.cs create mode 100644 apps/api/HRIS.Api/Migrations/20260601170756_AddDailyReports.cs create mode 100644 apps/api/HRIS.Api/Migrations/20260712212707_AddDailyReportSections4to8.Designer.cs create mode 100644 apps/api/HRIS.Api/Migrations/20260712212707_AddDailyReportSections4to8.cs create mode 100644 apps/api/HRIS.Api/Models/DailyAccomplishmentReport.cs create mode 100644 apps/api/HRIS.Api/Models/DailyReport.cs create mode 100644 apps/api/HRIS.Api/Models/DailyReportTask.cs create mode 100644 apps/api/HRIS.Api/Models/DarTask.cs diff --git a/apps/api/HRIS.Api/Data/AppDbContext.cs b/apps/api/HRIS.Api/Data/AppDbContext.cs index 20925ca..089fe71 100644 --- a/apps/api/HRIS.Api/Data/AppDbContext.cs +++ b/apps/api/HRIS.Api/Data/AppDbContext.cs @@ -9,6 +9,8 @@ public AppDbContext(DbContextOptions options) : base(options) { } public DbSet Users => Set(); public DbSet Roles => Set(); + public DbSet DailyReports => Set(); + public DbSet DailyReportTasks => Set(); public DbSet Permissions => Set(); public DbSet ActivityLogs => Set(); public DbSet Employees => Set(); diff --git a/apps/api/HRIS.Api/Data/Configurations/DailyReportConfiguration.cs b/apps/api/HRIS.Api/Data/Configurations/DailyReportConfiguration.cs new file mode 100644 index 0000000..ac0f799 --- /dev/null +++ b/apps/api/HRIS.Api/Data/Configurations/DailyReportConfiguration.cs @@ -0,0 +1,45 @@ +using HRIS.Api.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace HRIS.Api.Data.Configurations; + +public class DailyReportConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("daily_reports"); + builder.HasKey(x => x.Id); + builder.Property(x => x.WorkArrangement).HasMaxLength(50).IsRequired(); + builder.Property(x => x.Project).HasMaxLength(200).IsRequired(); + builder.Property(x => x.SprintIteration).HasMaxLength(100); + builder.Property(x => x.TeamUnit).HasMaxLength(100); + builder.Property(x => x.AvgResponseTime).HasMaxLength(50); + builder.Property(x => x.ConnectivityIssues).HasMaxLength(500); + builder.Property(x => x.CollaborationLog).HasMaxLength(1000); + + builder.Property(x => x.KeyAccomplishments).HasMaxLength(2000); + builder.Property(x => x.BlockersIssues).HasMaxLength(1000); + builder.Property(x => x.RisksEarlyWarnings).HasMaxLength(1000); + builder.Property(x => x.PlanForTomorrow).HasMaxLength(1000); + builder.Property(x => x.SupportEscalationNeeded).HasMaxLength(500); + builder.Property(x => x.WorkArrangementTomorrow).HasMaxLength(50); + builder.Property(x => x.LeaveAbsenceNotice).HasMaxLength(500); + builder.Property(x => x.SupervisorNotes).HasMaxLength(2000); + builder.Property(x => x.PerformanceRating).HasMaxLength(50); + builder.Property(x => x.ManagerActionItems).HasMaxLength(1000); + builder.Property(x => x.ReviewedBy).HasMaxLength(200); + + builder.HasOne(x => x.Employee) + .WithMany() + .HasForeignKey(x => x.EmployeeId) + .OnDelete(DeleteBehavior.Restrict); + + builder.HasMany(x => x.Tasks) + .WithOne(x => x.DailyReport) + .HasForeignKey(x => x.DailyReportId) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasIndex(x => new { x.EmployeeId, x.ReportDate }).IsUnique(); + } +} \ No newline at end of file diff --git a/apps/api/HRIS.Api/Data/Configurations/DailyReportTaskConfiguration.cs b/apps/api/HRIS.Api/Data/Configurations/DailyReportTaskConfiguration.cs new file mode 100644 index 0000000..b06e358 --- /dev/null +++ b/apps/api/HRIS.Api/Data/Configurations/DailyReportTaskConfiguration.cs @@ -0,0 +1,25 @@ +using HRIS.Api.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace HRIS.Api.Data.Configurations; + +public class DailyReportTaskConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("daily_report_tasks"); + builder.HasKey(x => x.Id); + builder.Property(x => x.Priority).HasMaxLength(20).IsRequired(); + builder.Property(x => x.TaskType).HasMaxLength(50).IsRequired(); + builder.Property(x => x.TicketRefNo).HasMaxLength(100); + builder.Property(x => x.Description).HasMaxLength(500).IsRequired(); + builder.Property(x => x.Module).HasMaxLength(100); + builder.Property(x => x.Status).HasMaxLength(50).IsRequired(); + builder.Property(x => x.EstimatedHours).HasPrecision(4, 2); + builder.Property(x => x.ActualHours).HasPrecision(4, 2); + builder.Property(x => x.OutputDeliverable).HasMaxLength(500); + builder.Property(x => x.CommitPrLink).HasMaxLength(500); + builder.Property(x => x.BlockedByRemarks).HasMaxLength(500); + } +} \ No newline at end of file diff --git a/apps/api/HRIS.Api/Features/Attendance/Controllers/DailyReportsController.cs b/apps/api/HRIS.Api/Features/Attendance/Controllers/DailyReportsController.cs new file mode 100644 index 0000000..3b35af3 --- /dev/null +++ b/apps/api/HRIS.Api/Features/Attendance/Controllers/DailyReportsController.cs @@ -0,0 +1,54 @@ +using HRIS.Api.Features.Attendance.DTOs; +using HRIS.Api.Features.Attendance.Services; +using HRIS.Api.Features.IAM.Controllers; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace HRIS.Api.Features.Attendance.Controllers; + +[ApiController] +[Route("api/daily-reports")] +[Authorize] +public class DailyReportsController : ControllerBase +{ + private readonly IDailyReportsService _service; + + public DailyReportsController(IDailyReportsService service) => _service = service; + + [HttpPost] + public async Task Create([FromBody] CreateDailyReportRequest request) + { + var result = await _service.CreateAsync(User, request); + return CreatedAtAction(nameof(GetById), new { id = result.Id }, result); + } + + [HttpPatch("{id}")] + public async Task Update(int id, [FromBody] UpdateDailyReportRequest request) + { + var result = await _service.UpdateAsync(id, User, request); + return Ok(result); + } + + [HttpPatch("{id}/supervisor-remarks")] + [PermissionAuthorize("ATTENDANCE", "Update")] + public async Task AddSupervisorRemarks(int id, [FromBody] SupervisorRemarksRequest request) + { + var result = await _service.AddSupervisorRemarksAsync(id, request); + return Ok(result); + } + + [HttpGet("{id}")] + public async Task GetById(int id) + { + var result = await _service.GetByIdAsync(id); + return result is null ? NotFound() : Ok(result); + } + + [HttpGet] + [PermissionAuthorize("ATTENDANCE", "View")] + public async Task GetAll([FromQuery] GetDailyReportsQuery query) + { + var result = await _service.GetAllAsync(query); + return Ok(result); + } +} \ No newline at end of file diff --git a/apps/api/HRIS.Api/Features/Attendance/DTOs/CreateDailyReportRequest.cs b/apps/api/HRIS.Api/Features/Attendance/DTOs/CreateDailyReportRequest.cs new file mode 100644 index 0000000..af46f8d --- /dev/null +++ b/apps/api/HRIS.Api/Features/Attendance/DTOs/CreateDailyReportRequest.cs @@ -0,0 +1,41 @@ +public class CreateDailyReportRequest +{ + // Section 1 + public DateOnly ReportDate { get; set; } + public string WorkArrangement { get; set; } = string.Empty; + public string Project { get; set; } = string.Empty; + public string? SprintIteration { get; set; } + public string? TeamUnit { get; set; } + public int? SubmittedToUserId { get; set; } + public TimeOnly? TimeIn { get; set; } + public TimeOnly? TimeOut { get; set; } + public int BreakDurationMinutes { get; set; } = 60; + + // Section 2 + public bool AttendedStandup { get; set; } + public bool ReachableViaComms { get; set; } + public string? AvgResponseTime { get; set; } + public string? ConnectivityIssues { get; set; } + public string? CollaborationLog { get; set; } + + // Section 3 + public List Tasks { get; set; } = new(); +} + +public class CreateDailyReportTaskRequest +{ + public int TaskNumber { get; set; } + public bool IsCarryOver { get; set; } + public string Priority { get; set; } = string.Empty; + public string TaskType { get; set; } = string.Empty; + public string? TicketRefNo { get; set; } + public string Description { get; set; } = string.Empty; + public string? Module { get; set; } + public string Status { get; set; } = string.Empty; + public int PercentDone { get; set; } + public decimal EstimatedHours { get; set; } + public decimal ActualHours { get; set; } + public string? OutputDeliverable { get; set; } + public string? CommitPrLink { get; set; } + public string? BlockedByRemarks { get; set; } +} \ No newline at end of file diff --git a/apps/api/HRIS.Api/Features/Attendance/DTOs/DailyReportDto.cs b/apps/api/HRIS.Api/Features/Attendance/DTOs/DailyReportDto.cs new file mode 100644 index 0000000..5e946d9 --- /dev/null +++ b/apps/api/HRIS.Api/Features/Attendance/DTOs/DailyReportDto.cs @@ -0,0 +1,76 @@ +namespace HRIS.Api.Features.Attendance.DTOs; + +public class DailyReportDto +{ + public int Id { get; set; } + public Guid EmployeeId { get; set; } + public string EmployeeName { get; set; } = string.Empty; + public DateOnly ReportDate { get; set; } + public string WorkArrangement { get; set; } = string.Empty; + public DateTime SubmissionTime { get; set; } + public string Project { get; set; } = string.Empty; + public string? SprintIteration { get; set; } + public string? TeamUnit { get; set; } + public int? SubmittedToUserId { get; set; } + public TimeOnly? TimeIn { get; set; } + public TimeOnly? TimeOut { get; set; } + public int BreakDurationMinutes { get; set; } + public bool AttendedStandup { get; set; } + public bool ReachableViaComms { get; set; } + public string? AvgResponseTime { get; set; } + public string? ConnectivityIssues { get; set; } + public string? CollaborationLog { get; set; } + + // Section 4 + public string? KeyAccomplishments { get; set; } + public string? BlockersIssues { get; set; } + public string? RisksEarlyWarnings { get; set; } + public string? PlanForTomorrow { get; set; } + public string? SupportEscalationNeeded { get; set; } + + // Section 5 + public bool CodeCommitted { get; set; } + public bool TicketsUpdated { get; set; } + public bool PullRequestCreated { get; set; } + public bool DocumentationUpdated { get; set; } + public bool TestsPassing { get; set; } + public bool ReportSubmittedOnTime { get; set; } + public int ChecklistCompletedCount { get; set; } + + // Section 6 + public string? WorkArrangementTomorrow { get; set; } + public TimeOnly? ExpectedTimeIn { get; set; } + public string? LeaveAbsenceNotice { get; set; } + + // Section 7 + public string? SupervisorNotes { get; set; } + public string? PerformanceRating { get; set; } + public bool FollowUpRequired { get; set; } + public DateOnly? ReviewDate { get; set; } + public string? ManagerActionItems { get; set; } + + // Section 8 + public string? ReviewedBy { get; set; } + public DateOnly? DateReviewed { get; set; } + + public List Tasks { get; set; } = new(); +} + +public class DailyReportTaskDto +{ + public int Id { get; set; } + public int TaskNumber { get; set; } + public bool IsCarryOver { get; set; } + public string Priority { get; set; } = string.Empty; + public string TaskType { get; set; } = string.Empty; + public string? TicketRefNo { get; set; } + public string Description { get; set; } = string.Empty; + public string? Module { get; set; } + public string Status { get; set; } = string.Empty; + public int PercentDone { get; set; } + public decimal EstimatedHours { get; set; } + public decimal ActualHours { get; set; } + public string? OutputDeliverable { get; set; } + public string? CommitPrLink { get; set; } + public string? BlockedByRemarks { get; set; } +} \ No newline at end of file diff --git a/apps/api/HRIS.Api/Features/Attendance/DTOs/GetDailyReportsQuery.cs b/apps/api/HRIS.Api/Features/Attendance/DTOs/GetDailyReportsQuery.cs new file mode 100644 index 0000000..28f4027 --- /dev/null +++ b/apps/api/HRIS.Api/Features/Attendance/DTOs/GetDailyReportsQuery.cs @@ -0,0 +1,9 @@ +namespace HRIS.Api.Features.Attendance.DTOs; + +public class GetDailyReportsQuery +{ + public Guid? EmployeeId { get; set; } + public DateOnly? Date { get; set; } + public int Page { get; set; } = 1; + public int PageSize { get; set; } = 20; +} \ No newline at end of file diff --git a/apps/api/HRIS.Api/Features/Attendance/DTOs/SupervisorRemarksRequest.cs b/apps/api/HRIS.Api/Features/Attendance/DTOs/SupervisorRemarksRequest.cs new file mode 100644 index 0000000..860ec84 --- /dev/null +++ b/apps/api/HRIS.Api/Features/Attendance/DTOs/SupervisorRemarksRequest.cs @@ -0,0 +1,15 @@ +namespace HRIS.Api.Features.Attendance.DTOs; + +public class SupervisorRemarksRequest +{ + // Section 7 – Supervisor Remarks + public string? SupervisorNotes { get; set; } + public string? PerformanceRating { get; set; } + public bool FollowUpRequired { get; set; } + public DateOnly? ReviewDate { get; set; } + public string? ManagerActionItems { get; set; } + + // Section 8 – Acknowledgment + public string? ReviewedBy { get; set; } + public DateOnly? DateReviewed { get; set; } +} \ No newline at end of file diff --git a/apps/api/HRIS.Api/Features/Attendance/DTOs/UpdateDailyReportRequest.cs b/apps/api/HRIS.Api/Features/Attendance/DTOs/UpdateDailyReportRequest.cs new file mode 100644 index 0000000..1a8d2f5 --- /dev/null +++ b/apps/api/HRIS.Api/Features/Attendance/DTOs/UpdateDailyReportRequest.cs @@ -0,0 +1,24 @@ +namespace HRIS.Api.Features.Attendance.DTOs; + +public class UpdateDailyReportRequest +{ + // Section 4 – End-of-Day Summary + public string? KeyAccomplishments { get; set; } + public string? BlockersIssues { get; set; } + public string? RisksEarlyWarnings { get; set; } + public string? PlanForTomorrow { get; set; } + public string? SupportEscalationNeeded { get; set; } + + // Section 5 – End-of-Day Checklist + public bool CodeCommitted { get; set; } + public bool TicketsUpdated { get; set; } + public bool PullRequestCreated { get; set; } + public bool DocumentationUpdated { get; set; } + public bool TestsPassing { get; set; } + public bool ReportSubmittedOnTime { get; set; } + + // Section 6 – Tomorrow's Plan + public string? WorkArrangementTomorrow { get; set; } + public TimeOnly? ExpectedTimeIn { get; set; } + public string? LeaveAbsenceNotice { get; set; } +} \ No newline at end of file diff --git a/apps/api/HRIS.Api/Features/Attendance/Services/DailyReportsService.cs b/apps/api/HRIS.Api/Features/Attendance/Services/DailyReportsService.cs new file mode 100644 index 0000000..875e24a --- /dev/null +++ b/apps/api/HRIS.Api/Features/Attendance/Services/DailyReportsService.cs @@ -0,0 +1,271 @@ +using System.Security.Claims; +using HRIS.Api.Data; +using HRIS.Api.Features.Attendance.DTOs; +using HRIS.Api.Features.Common.Exceptions; +using HRIS.Api.Models; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; + +namespace HRIS.Api.Features.Attendance.Services; + +public class DailyReportsService : IDailyReportsService +{ + private readonly AppDbContext _db; + + public DailyReportsService(AppDbContext db) => _db = db; + + public async Task CreateAsync(ClaimsPrincipal user, CreateDailyReportRequest request) + { + var userIdRaw = + user.FindFirstValue(ClaimTypes.NameIdentifier) ?? + user.FindFirstValue("sub"); + + if (!long.TryParse(userIdRaw, out var userId)) + throw new ApiException("Invalid user.", StatusCodes.Status401Unauthorized); + + var employee = await _db.Employees + .FirstOrDefaultAsync(x => x.UserId == userId); + + if (employee == null) + throw new ApiException("Employee not found.", StatusCodes.Status404NotFound); + + var exists = await _db.DailyReports + .AnyAsync(r => r.EmployeeId == employee.Id && r.ReportDate == request.ReportDate); + + if (exists) + throw new ApiException("A report for this date already exists.", 409); + + var report = new DailyReport + { + EmployeeId = employee.Id, + ReportDate = request.ReportDate, + WorkArrangement = request.WorkArrangement, + SubmissionTime = DateTime.UtcNow, + Project = request.Project, + SprintIteration = request.SprintIteration, + TeamUnit = request.TeamUnit, + SubmittedToUserId = request.SubmittedToUserId, + TimeIn = request.TimeIn, + TimeOut = request.TimeOut, + BreakDurationMinutes = request.BreakDurationMinutes, + AttendedStandup = request.AttendedStandup, + ReachableViaComms = request.ReachableViaComms, + AvgResponseTime = request.AvgResponseTime, + ConnectivityIssues = request.ConnectivityIssues, + CollaborationLog = request.CollaborationLog, + Tasks = request.Tasks.Select(t => new DailyReportTask + { + TaskNumber = t.TaskNumber, + IsCarryOver = t.IsCarryOver, + Priority = t.Priority, + TaskType = t.TaskType, + TicketRefNo = t.TicketRefNo, + Description = t.Description, + Module = t.Module, + Status = t.Status, + PercentDone = t.PercentDone, + EstimatedHours = t.EstimatedHours, + ActualHours = t.ActualHours, + OutputDeliverable = t.OutputDeliverable, + CommitPrLink = t.CommitPrLink, + BlockedByRemarks = t.BlockedByRemarks + }).ToList() + }; + + _db.DailyReports.Add(report); + await _db.SaveChangesAsync(); + + return await GetByIdAsync(report.Id) ?? throw new InvalidOperationException(); + } + + public async Task UpdateAsync(int id, ClaimsPrincipal user, UpdateDailyReportRequest request) + { + var userIdRaw = + user.FindFirstValue(ClaimTypes.NameIdentifier) ?? + user.FindFirstValue("sub"); + + if (!long.TryParse(userIdRaw, out var userId)) + throw new ApiException("Invalid user.", StatusCodes.Status401Unauthorized); + + var employee = await _db.Employees + .FirstOrDefaultAsync(x => x.UserId == userId); + + if (employee == null) + throw new ApiException("Employee not found.", StatusCodes.Status404NotFound); + + var report = await _db.DailyReports + .FirstOrDefaultAsync(r => r.Id == id); + + if (report == null) + throw new ApiException("Report not found.", StatusCodes.Status404NotFound); + + if (report.EmployeeId != employee.Id) + throw new ApiException("You can only update your own report.", StatusCodes.Status403Forbidden); + + // Section 4 + report.KeyAccomplishments = request.KeyAccomplishments; + report.BlockersIssues = request.BlockersIssues; + report.RisksEarlyWarnings = request.RisksEarlyWarnings; + report.PlanForTomorrow = request.PlanForTomorrow; + report.SupportEscalationNeeded = request.SupportEscalationNeeded; + + // Section 5 + report.CodeCommitted = request.CodeCommitted; + report.TicketsUpdated = request.TicketsUpdated; + report.PullRequestCreated = request.PullRequestCreated; + report.DocumentationUpdated = request.DocumentationUpdated; + report.TestsPassing = request.TestsPassing; + report.ReportSubmittedOnTime = request.ReportSubmittedOnTime; + + // Section 6 + report.WorkArrangementTomorrow = request.WorkArrangementTomorrow; + report.ExpectedTimeIn = request.ExpectedTimeIn; + report.LeaveAbsenceNotice = request.LeaveAbsenceNotice; + + await _db.SaveChangesAsync(); + + return await GetByIdAsync(id) ?? throw new InvalidOperationException(); + } + + public async Task AddSupervisorRemarksAsync(int id, SupervisorRemarksRequest request) + { + var report = await _db.DailyReports + .FirstOrDefaultAsync(r => r.Id == id); + + if (report == null) + throw new ApiException("Report not found.", StatusCodes.Status404NotFound); + + // Section 7 + report.SupervisorNotes = request.SupervisorNotes; + report.PerformanceRating = request.PerformanceRating; + report.FollowUpRequired = request.FollowUpRequired; + report.ReviewDate = request.ReviewDate; + report.ManagerActionItems = request.ManagerActionItems; + + // Section 8 + report.ReviewedBy = request.ReviewedBy; + report.DateReviewed = request.DateReviewed; + + await _db.SaveChangesAsync(); + + return await GetByIdAsync(id) ?? throw new InvalidOperationException(); + } + + public async Task GetByIdAsync(int id) + { + var report = await _db.DailyReports + .Include(r => r.Employee) + .Include(r => r.Tasks) + .FirstOrDefaultAsync(r => r.Id == id); + + return report is null ? null : MapToDto(report); + } + + public async Task GetByEmployeeAndDateAsync(Guid employeeId, DateOnly date) + { + var report = await _db.DailyReports + .Include(r => r.Employee) + .Include(r => r.Tasks) + .FirstOrDefaultAsync(r => r.EmployeeId == employeeId && r.ReportDate == date); + + return report is null ? null : MapToDto(report); + } + + public async Task> GetAllAsync(GetDailyReportsQuery query) + { + var q = _db.DailyReports + .Include(r => r.Employee) + .Include(r => r.Tasks) + .AsQueryable(); + + if (query.EmployeeId.HasValue) + q = q.Where(r => r.EmployeeId == query.EmployeeId); + + if (query.Date.HasValue) + q = q.Where(r => r.ReportDate == query.Date); + + var reports = await q + .OrderByDescending(r => r.ReportDate) + .Skip((query.Page - 1) * query.PageSize) + .Take(query.PageSize) + .ToListAsync(); + + return reports.Select(MapToDto).ToList(); + } + + private static DailyReportDto MapToDto(DailyReport report) => new() + { + Id = report.Id, + EmployeeId = report.EmployeeId, + EmployeeName = $"{report.Employee.FirstName} {report.Employee.LastName}", + ReportDate = report.ReportDate, + WorkArrangement = report.WorkArrangement, + SubmissionTime = report.SubmissionTime, + Project = report.Project, + SprintIteration = report.SprintIteration, + TeamUnit = report.TeamUnit, + SubmittedToUserId = report.SubmittedToUserId, + TimeIn = report.TimeIn, + TimeOut = report.TimeOut, + BreakDurationMinutes = report.BreakDurationMinutes, + AttendedStandup = report.AttendedStandup, + ReachableViaComms = report.ReachableViaComms, + AvgResponseTime = report.AvgResponseTime, + ConnectivityIssues = report.ConnectivityIssues, + CollaborationLog = report.CollaborationLog, + + // Section 4 + KeyAccomplishments = report.KeyAccomplishments, + BlockersIssues = report.BlockersIssues, + RisksEarlyWarnings = report.RisksEarlyWarnings, + PlanForTomorrow = report.PlanForTomorrow, + SupportEscalationNeeded = report.SupportEscalationNeeded, + + // Section 5 + CodeCommitted = report.CodeCommitted, + TicketsUpdated = report.TicketsUpdated, + PullRequestCreated = report.PullRequestCreated, + DocumentationUpdated = report.DocumentationUpdated, + TestsPassing = report.TestsPassing, + ReportSubmittedOnTime = report.ReportSubmittedOnTime, + ChecklistCompletedCount = new[] { + report.CodeCommitted, report.TicketsUpdated, report.PullRequestCreated, + report.DocumentationUpdated, report.TestsPassing, report.ReportSubmittedOnTime + }.Count(x => x), + + // Section 6 + WorkArrangementTomorrow = report.WorkArrangementTomorrow, + ExpectedTimeIn = report.ExpectedTimeIn, + LeaveAbsenceNotice = report.LeaveAbsenceNotice, + + // Section 7 + SupervisorNotes = report.SupervisorNotes, + PerformanceRating = report.PerformanceRating, + FollowUpRequired = report.FollowUpRequired, + ReviewDate = report.ReviewDate, + ManagerActionItems = report.ManagerActionItems, + + // Section 8 + ReviewedBy = report.ReviewedBy, + DateReviewed = report.DateReviewed, + + Tasks = report.Tasks.Select(t => new DailyReportTaskDto + { + Id = t.Id, + TaskNumber = t.TaskNumber, + IsCarryOver = t.IsCarryOver, + Priority = t.Priority, + TaskType = t.TaskType, + TicketRefNo = t.TicketRefNo, + Description = t.Description, + Module = t.Module, + Status = t.Status, + PercentDone = t.PercentDone, + EstimatedHours = t.EstimatedHours, + ActualHours = t.ActualHours, + OutputDeliverable = t.OutputDeliverable, + CommitPrLink = t.CommitPrLink, + BlockedByRemarks = t.BlockedByRemarks + }).ToList() + }; +} \ No newline at end of file diff --git a/apps/api/HRIS.Api/Features/Attendance/Services/IDailyReportsService.cs b/apps/api/HRIS.Api/Features/Attendance/Services/IDailyReportsService.cs new file mode 100644 index 0000000..4b05e24 --- /dev/null +++ b/apps/api/HRIS.Api/Features/Attendance/Services/IDailyReportsService.cs @@ -0,0 +1,14 @@ +using System.Security.Claims; +using HRIS.Api.Features.Attendance.DTOs; + +namespace HRIS.Api.Features.Attendance.Services; + +public interface IDailyReportsService +{ + Task CreateAsync(ClaimsPrincipal user, CreateDailyReportRequest request); + Task GetByIdAsync(int id); + Task GetByEmployeeAndDateAsync(Guid employeeId, DateOnly date); + Task> GetAllAsync(GetDailyReportsQuery query); + Task UpdateAsync(int id, ClaimsPrincipal user, UpdateDailyReportRequest request); + Task AddSupervisorRemarksAsync(int id, SupervisorRemarksRequest request); +} \ No newline at end of file diff --git a/apps/api/HRIS.Api/Migrations/20260601170756_AddDailyReports.Designer.cs b/apps/api/HRIS.Api/Migrations/20260601170756_AddDailyReports.Designer.cs new file mode 100644 index 0000000..a120cc7 --- /dev/null +++ b/apps/api/HRIS.Api/Migrations/20260601170756_AddDailyReports.Designer.cs @@ -0,0 +1,1270 @@ +// +using System; +using HRIS.Api.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace HRIS.Api.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20260601170756_AddDailyReports")] + partial class AddDailyReports + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("HRIS.Api.Models.ActivityLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ActorEmail") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ActorRole") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ActorUserId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IpAddress") + .HasMaxLength(45) + .HasColumnType("varchar(45)"); + + b.Property("MetadataJson") + .HasColumnType("longtext"); + + b.Property("Module") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Summary") + .HasColumnType("longtext"); + + b.Property("TargetId") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("TargetType") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UserAgent") + .HasMaxLength(512) + .HasColumnType("varchar(512)"); + + b.HasKey("Id"); + + b.HasIndex("ActorUserId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("Module", "CreatedAt"); + + b.ToTable("activity_logs", (string)null); + }); + + modelBuilder.Entity("HRIS.Api.Models.AttendanceLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Accomplished") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("IsPresent") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("LateMinutes") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("OvertimeMinutes") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("RenderedMinutes") + .HasColumnType("int"); + + b.Property("Task") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("TimeIn") + .HasColumnType("time(6)"); + + b.Property("TimeOut") + .HasColumnType("time(6)"); + + b.Property("UndertimeMinutes") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId", "Date") + .IsUnique(); + + b.ToTable("attendance_logs", (string)null); + }); + + modelBuilder.Entity("HRIS.Api.Models.DailyReport", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AttendedStandup") + .HasColumnType("tinyint(1)"); + + b.Property("AvgResponseTime") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BreakDurationMinutes") + .HasColumnType("int"); + + b.Property("CollaborationLog") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("ConnectivityIssues") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("Project") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ReachableViaComms") + .HasColumnType("tinyint(1)"); + + b.Property("ReportDate") + .HasColumnType("date"); + + b.Property("SprintIteration") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SubmissionTime") + .HasColumnType("datetime(6)"); + + b.Property("SubmittedToId") + .HasColumnType("bigint"); + + b.Property("SubmittedToUserId") + .HasColumnType("int"); + + b.Property("TeamUnit") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TimeIn") + .HasColumnType("time(6)"); + + b.Property("TimeOut") + .HasColumnType("time(6)"); + + b.Property("WorkArrangement") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("SubmittedToId"); + + b.HasIndex("EmployeeId", "ReportDate") + .IsUnique(); + + b.ToTable("daily_reports", (string)null); + }); + + modelBuilder.Entity("HRIS.Api.Models.DailyReportTask", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ActualHours") + .HasPrecision(4, 2) + .HasColumnType("decimal(4,2)"); + + b.Property("BlockedByRemarks") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("CommitPrLink") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("DailyReportId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("EstimatedHours") + .HasPrecision(4, 2) + .HasColumnType("decimal(4,2)"); + + b.Property("IsCarryOver") + .HasColumnType("tinyint(1)"); + + b.Property("Module") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("OutputDeliverable") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("PercentDone") + .HasColumnType("int"); + + b.Property("Priority") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("TaskNumber") + .HasColumnType("int"); + + b.Property("TaskType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("TicketRefNo") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("DailyReportId"); + + b.ToTable("daily_report_tasks", (string)null); + }); + + modelBuilder.Entity("HRIS.Api.Models.Employee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AddressLine1") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("AddressLine2") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("BirthDate") + .HasColumnType("date"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("CivilStatus") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ContactNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("DateHired") + .HasColumnType("date"); + + b.Property("Department") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Email") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("EmployeeNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("EmploymentType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MiddleName") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("PagIbigNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("PhilHealthNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Position") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Province") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Sex") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("SssNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TinNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("ZipCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeNumber") + .IsUnique(); + + b.HasIndex("PagIbigNumber") + .IsUnique(); + + b.HasIndex("PhilHealthNumber") + .IsUnique(); + + b.HasIndex("SssNumber") + .IsUnique(); + + b.HasIndex("TinNumber") + .IsUnique(); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Employees"); + }); + + modelBuilder.Entity("HRIS.Api.Models.EmployeeDocument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("DocumentType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("OriginalFileName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("StoragePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("StoredFileName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("UploadedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("DocumentType"); + + b.HasIndex("EmployeeId"); + + b.ToTable("employee_documents", (string)null); + }); + + modelBuilder.Entity("HRIS.Api.Models.EmployeeShiftAssignment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveFrom") + .HasColumnType("date"); + + b.Property("EffectiveTo") + .HasColumnType("date"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("ShiftId") + .HasColumnType("int"); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("ShiftId"); + + b.HasIndex("EmployeeId", "IsActive"); + + b.ToTable("employee_shift_assignments", (string)null); + }); + + modelBuilder.Entity("HRIS.Api.Models.OvertimeRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("DateFrom") + .HasColumnType("date"); + + b.Property("DateTo") + .HasColumnType("date"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ReviewRemarks") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ReviewedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("ReviewedByUserId") + .HasColumnType("bigint"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("ReviewedByUserId"); + + b.HasIndex("Status"); + + b.HasIndex("EmployeeId", "DateFrom", "DateTo"); + + b.ToTable("overtime_requests", (string)null); + }); + + modelBuilder.Entity("HRIS.Api.Models.OvertimeRequestItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AttendanceLogId") + .HasColumnType("int"); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("OvertimeRequestId") + .HasColumnType("int"); + + b.Property("RequestedMinutes") + .HasColumnType("int"); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("AttendanceLogId"); + + b.HasIndex("Date"); + + b.HasIndex("OvertimeRequestId"); + + b.HasIndex("OvertimeRequestId", "Date") + .IsUnique(); + + b.ToTable("overtime_request_items", (string)null); + }); + + modelBuilder.Entity("HRIS.Api.Models.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CanArchive") + .HasColumnType("tinyint(1)"); + + b.Property("CanCreate") + .HasColumnType("tinyint(1)"); + + b.Property("CanUpdate") + .HasColumnType("tinyint(1)"); + + b.Property("CanView") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Module") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId", "Module") + .IsUnique(); + + b.ToTable("permissions", (string)null); + + b.HasData( + new + { + Id = 1, + CanArchive = true, + CanCreate = true, + CanUpdate = true, + CanView = true, + CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Module = "IAM", + RoleId = 1 + }, + new + { + Id = 6, + CanArchive = true, + CanCreate = true, + CanUpdate = true, + CanView = true, + CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Module = "EMPLOYEES", + RoleId = 1 + }, + new + { + Id = 9, + CanArchive = true, + CanCreate = true, + CanUpdate = true, + CanView = true, + CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Module = "ATTENDANCE", + RoleId = 1 + }, + new + { + Id = 3, + CanArchive = true, + CanCreate = true, + CanUpdate = true, + CanView = true, + CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Module = "IAM", + RoleId = 2 + }, + new + { + Id = 7, + CanArchive = true, + CanCreate = true, + CanUpdate = true, + CanView = true, + CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Module = "EMPLOYEES", + RoleId = 2 + }, + new + { + Id = 10, + CanArchive = true, + CanCreate = true, + CanUpdate = true, + CanView = true, + CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Module = "ATTENDANCE", + RoleId = 2 + }, + new + { + Id = 8, + CanArchive = false, + CanCreate = false, + CanUpdate = false, + CanView = false, + CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Module = "EMPLOYEES", + RoleId = 3 + }, + new + { + Id = 11, + CanArchive = false, + CanCreate = false, + CanUpdate = false, + CanView = false, + CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Module = "ATTENDANCE", + RoleId = 3 + }); + }); + + modelBuilder.Entity("HRIS.Api.Models.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique(); + + b.ToTable("roles", (string)null); + + b.HasData( + new + { + Id = 1, + IsSystem = true, + Name = "Super Admin", + NormalizedName = "SUPER_ADMIN" + }, + new + { + Id = 2, + IsSystem = true, + Name = "Admin", + NormalizedName = "ADMIN" + }, + new + { + Id = 3, + IsSystem = true, + Name = "User", + NormalizedName = "USER" + }); + }); + + modelBuilder.Entity("HRIS.Api.Models.Shift", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("IsFlexible") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("LateGraceMinutes") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("shifts", (string)null); + + b.HasData( + new + { + Id = 1001, + Code = "STD-0830-1730", + CreatedAtUtc = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Default weekday office shift", + IsActive = true, + IsFlexible = false, + LateGraceMinutes = 5, + Name = "Standard Office Shift" + }); + }); + + modelBuilder.Entity("HRIS.Api.Models.ShiftDay", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("BreakEndTime") + .HasColumnType("time(6)"); + + b.Property("BreakStartTime") + .HasColumnType("time(6)"); + + b.Property("DayOfWeek") + .HasColumnType("int"); + + b.Property("EndTime") + .HasColumnType("time(6)"); + + b.Property("IsWorkingDay") + .HasColumnType("tinyint(1)"); + + b.Property("ShiftId") + .HasColumnType("int"); + + b.Property("StartTime") + .HasColumnType("time(6)"); + + b.HasKey("Id"); + + b.HasIndex("ShiftId", "DayOfWeek") + .IsUnique(); + + b.ToTable("shift_days", (string)null); + + b.HasData( + new + { + Id = 2001, + BreakEndTime = new TimeOnly(13, 0, 0), + BreakStartTime = new TimeOnly(12, 0, 0), + DayOfWeek = 1, + EndTime = new TimeOnly(17, 30, 0), + IsWorkingDay = true, + ShiftId = 1001, + StartTime = new TimeOnly(8, 30, 0) + }, + new + { + Id = 2002, + BreakEndTime = new TimeOnly(13, 0, 0), + BreakStartTime = new TimeOnly(12, 0, 0), + DayOfWeek = 2, + EndTime = new TimeOnly(17, 30, 0), + IsWorkingDay = true, + ShiftId = 1001, + StartTime = new TimeOnly(8, 30, 0) + }, + new + { + Id = 2003, + BreakEndTime = new TimeOnly(13, 0, 0), + BreakStartTime = new TimeOnly(12, 0, 0), + DayOfWeek = 3, + EndTime = new TimeOnly(17, 30, 0), + IsWorkingDay = true, + ShiftId = 1001, + StartTime = new TimeOnly(8, 30, 0) + }, + new + { + Id = 2004, + BreakEndTime = new TimeOnly(13, 0, 0), + BreakStartTime = new TimeOnly(12, 0, 0), + DayOfWeek = 4, + EndTime = new TimeOnly(17, 30, 0), + IsWorkingDay = true, + ShiftId = 1001, + StartTime = new TimeOnly(8, 30, 0) + }, + new + { + Id = 2005, + BreakEndTime = new TimeOnly(13, 0, 0), + BreakStartTime = new TimeOnly(12, 0, 0), + DayOfWeek = 5, + EndTime = new TimeOnly(17, 30, 0), + IsWorkingDay = true, + ShiftId = 1001, + StartTime = new TimeOnly(8, 30, 0) + }, + new + { + Id = 2006, + DayOfWeek = 6, + IsWorkingDay = false, + ShiftId = 1001 + }, + new + { + Id = 2007, + DayOfWeek = 0, + IsWorkingDay = false, + ShiftId = 1001 + }); + }); + + modelBuilder.Entity("HRIS.Api.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("MiddleName") + .HasColumnType("longtext"); + + b.Property("NormalizedEmail") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("PasswordHash") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("PasswordResetToken") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("PasswordResetTokenExpiresAt") + .HasColumnType("datetime(6)"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("Suffix") + .HasColumnType("longtext"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .IsUnique(); + + b.HasIndex("PasswordResetToken"); + + b.HasIndex("RoleId"); + + b.ToTable("users", (string)null); + + b.HasData( + new + { + Id = 101L, + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Email = "superadmin@simplevia.com", + FullName = "Super Admin", + IsActive = true, + NormalizedEmail = "SUPERADMIN@SIMPLEVIA.COM", + PasswordHash = "$2a$11$K4TnWy1Wt/NB5n3e2FxEk.dwwOLwp5j0/ChgeOeookyl8ApuV8yim", + RoleId = 1 + }, + new + { + Id = 102L, + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Email = "admin@simplevia.com", + FullName = "Admin User", + IsActive = true, + NormalizedEmail = "ADMIN@SIMPLEVIA.COM", + PasswordHash = "$2a$11$4.lJCnxOfMgrWWJ//6bRCOvH.5XGyyExoyx.bPOsEdRcXCTm6rCi2", + RoleId = 2 + }, + new + { + Id = 103L, + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Email = "user@simplevia.com", + FullName = "Regular User", + IsActive = true, + NormalizedEmail = "USER@SIMPLEVIA.COM", + PasswordHash = "$2a$11$3w9FJ6ypCA1HkYL0J.z2AeoSJLavuSJRbXE3N3IZhD3pSZ4r86RsG", + RoleId = 3 + }); + }); + + modelBuilder.Entity("HRIS.Api.Models.AttendanceLog", b => + { + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("HRIS.Api.Models.DailyReport", b => + { + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("HRIS.Api.Models.User", "SubmittedTo") + .WithMany() + .HasForeignKey("SubmittedToId"); + + b.Navigation("Employee"); + + b.Navigation("SubmittedTo"); + }); + + modelBuilder.Entity("HRIS.Api.Models.DailyReportTask", b => + { + b.HasOne("HRIS.Api.Models.DailyReport", "DailyReport") + .WithMany("Tasks") + .HasForeignKey("DailyReportId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DailyReport"); + }); + + modelBuilder.Entity("HRIS.Api.Models.Employee", b => + { + b.HasOne("HRIS.Api.Models.User", "User") + .WithOne("Employee") + .HasForeignKey("HRIS.Api.Models.Employee", "UserId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("User"); + }); + + modelBuilder.Entity("HRIS.Api.Models.EmployeeDocument", b => + { + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany("Documents") + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("HRIS.Api.Models.EmployeeShiftAssignment", b => + { + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("HRIS.Api.Models.Shift", "Shift") + .WithMany() + .HasForeignKey("ShiftId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("Shift"); + }); + + modelBuilder.Entity("HRIS.Api.Models.OvertimeRequest", b => + { + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("HRIS.Api.Models.User", "ReviewedByUser") + .WithMany() + .HasForeignKey("ReviewedByUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Employee"); + + b.Navigation("ReviewedByUser"); + }); + + modelBuilder.Entity("HRIS.Api.Models.OvertimeRequestItem", b => + { + b.HasOne("HRIS.Api.Models.AttendanceLog", "AttendanceLog") + .WithMany() + .HasForeignKey("AttendanceLogId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("HRIS.Api.Models.OvertimeRequest", "OvertimeRequest") + .WithMany("Items") + .HasForeignKey("OvertimeRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AttendanceLog"); + + b.Navigation("OvertimeRequest"); + }); + + modelBuilder.Entity("HRIS.Api.Models.Permission", b => + { + b.HasOne("HRIS.Api.Models.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("HRIS.Api.Models.ShiftDay", b => + { + b.HasOne("HRIS.Api.Models.Shift", "Shift") + .WithMany("ShiftDays") + .HasForeignKey("ShiftId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Shift"); + }); + + modelBuilder.Entity("HRIS.Api.Models.User", b => + { + b.HasOne("HRIS.Api.Models.Role", "Role") + .WithMany("Users") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("HRIS.Api.Models.DailyReport", b => + { + b.Navigation("Tasks"); + }); + + modelBuilder.Entity("HRIS.Api.Models.Employee", b => + { + b.Navigation("Documents"); + }); + + modelBuilder.Entity("HRIS.Api.Models.OvertimeRequest", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("HRIS.Api.Models.Role", b => + { + b.Navigation("Users"); + }); + + modelBuilder.Entity("HRIS.Api.Models.Shift", b => + { + b.Navigation("ShiftDays"); + }); + + modelBuilder.Entity("HRIS.Api.Models.User", b => + { + b.Navigation("Employee"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/apps/api/HRIS.Api/Migrations/20260601170756_AddDailyReports.cs b/apps/api/HRIS.Api/Migrations/20260601170756_AddDailyReports.cs new file mode 100644 index 0000000..38d4360 --- /dev/null +++ b/apps/api/HRIS.Api/Migrations/20260601170756_AddDailyReports.cs @@ -0,0 +1,133 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace HRIS.Api.Migrations +{ + /// + public partial class AddDailyReports : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "daily_reports", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + EmployeeId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ReportDate = table.Column(type: "date", nullable: false), + WorkArrangement = table.Column(type: "varchar(50)", maxLength: 50, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + SubmissionTime = table.Column(type: "datetime(6)", nullable: false), + Project = table.Column(type: "varchar(200)", maxLength: 200, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + SprintIteration = table.Column(type: "varchar(100)", maxLength: 100, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + TeamUnit = table.Column(type: "varchar(100)", maxLength: 100, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SubmittedToUserId = table.Column(type: "int", nullable: true), + SubmittedToId = table.Column(type: "bigint", nullable: true), + TimeIn = table.Column(type: "time(6)", nullable: true), + TimeOut = table.Column(type: "time(6)", nullable: true), + BreakDurationMinutes = table.Column(type: "int", nullable: false), + AttendedStandup = table.Column(type: "tinyint(1)", nullable: false), + ReachableViaComms = table.Column(type: "tinyint(1)", nullable: false), + AvgResponseTime = table.Column(type: "varchar(50)", maxLength: 50, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConnectivityIssues = table.Column(type: "varchar(500)", maxLength: 500, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + CollaborationLog = table.Column(type: "varchar(1000)", maxLength: 1000, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_daily_reports", x => x.Id); + table.ForeignKey( + name: "FK_daily_reports_Employees_EmployeeId", + column: x => x.EmployeeId, + principalTable: "Employees", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_daily_reports_users_SubmittedToId", + column: x => x.SubmittedToId, + principalTable: "users", + principalColumn: "Id"); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "daily_report_tasks", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + DailyReportId = table.Column(type: "int", nullable: false), + TaskNumber = table.Column(type: "int", nullable: false), + IsCarryOver = table.Column(type: "tinyint(1)", nullable: false), + Priority = table.Column(type: "varchar(20)", maxLength: 20, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + TaskType = table.Column(type: "varchar(50)", maxLength: 50, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + TicketRefNo = table.Column(type: "varchar(100)", maxLength: 100, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Description = table.Column(type: "varchar(500)", maxLength: 500, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Module = table.Column(type: "varchar(100)", maxLength: 100, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Status = table.Column(type: "varchar(50)", maxLength: 50, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + PercentDone = table.Column(type: "int", nullable: false), + EstimatedHours = table.Column(type: "decimal(4,2)", precision: 4, scale: 2, nullable: false), + ActualHours = table.Column(type: "decimal(4,2)", precision: 4, scale: 2, nullable: false), + OutputDeliverable = table.Column(type: "varchar(500)", maxLength: 500, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + CommitPrLink = table.Column(type: "varchar(500)", maxLength: 500, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + BlockedByRemarks = table.Column(type: "varchar(500)", maxLength: 500, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_daily_report_tasks", x => x.Id); + table.ForeignKey( + name: "FK_daily_report_tasks_daily_reports_DailyReportId", + column: x => x.DailyReportId, + principalTable: "daily_reports", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_daily_report_tasks_DailyReportId", + table: "daily_report_tasks", + column: "DailyReportId"); + + migrationBuilder.CreateIndex( + name: "IX_daily_reports_EmployeeId_ReportDate", + table: "daily_reports", + columns: new[] { "EmployeeId", "ReportDate" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_daily_reports_SubmittedToId", + table: "daily_reports", + column: "SubmittedToId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "daily_report_tasks"); + + migrationBuilder.DropTable( + name: "daily_reports"); + } + } +} diff --git a/apps/api/HRIS.Api/Migrations/20260712212707_AddDailyReportSections4to8.Designer.cs b/apps/api/HRIS.Api/Migrations/20260712212707_AddDailyReportSections4to8.Designer.cs new file mode 100644 index 0000000..489608a --- /dev/null +++ b/apps/api/HRIS.Api/Migrations/20260712212707_AddDailyReportSections4to8.Designer.cs @@ -0,0 +1,1344 @@ +// +using System; +using HRIS.Api.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace HRIS.Api.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20260712212707_AddDailyReportSections4to8")] + partial class AddDailyReportSections4to8 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("HRIS.Api.Models.ActivityLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ActorEmail") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ActorRole") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ActorUserId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IpAddress") + .HasMaxLength(45) + .HasColumnType("varchar(45)"); + + b.Property("MetadataJson") + .HasColumnType("longtext"); + + b.Property("Module") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Summary") + .HasColumnType("longtext"); + + b.Property("TargetId") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("TargetType") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UserAgent") + .HasMaxLength(512) + .HasColumnType("varchar(512)"); + + b.HasKey("Id"); + + b.HasIndex("ActorUserId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("Module", "CreatedAt"); + + b.ToTable("activity_logs", (string)null); + }); + + modelBuilder.Entity("HRIS.Api.Models.AttendanceLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Accomplished") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("IsPresent") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("LateMinutes") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("OvertimeMinutes") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("RenderedMinutes") + .HasColumnType("int"); + + b.Property("Task") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("TimeIn") + .HasColumnType("time(6)"); + + b.Property("TimeOut") + .HasColumnType("time(6)"); + + b.Property("UndertimeMinutes") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId", "Date") + .IsUnique(); + + b.ToTable("attendance_logs", (string)null); + }); + + modelBuilder.Entity("HRIS.Api.Models.DailyReport", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AttendedStandup") + .HasColumnType("tinyint(1)"); + + b.Property("AvgResponseTime") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BlockersIssues") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("BreakDurationMinutes") + .HasColumnType("int"); + + b.Property("CodeCommitted") + .HasColumnType("tinyint(1)"); + + b.Property("CollaborationLog") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("ConnectivityIssues") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("DateReviewed") + .HasColumnType("date"); + + b.Property("DocumentationUpdated") + .HasColumnType("tinyint(1)"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("ExpectedTimeIn") + .HasColumnType("time(6)"); + + b.Property("FollowUpRequired") + .HasColumnType("tinyint(1)"); + + b.Property("KeyAccomplishments") + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("LeaveAbsenceNotice") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ManagerActionItems") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("PerformanceRating") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PlanForTomorrow") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("Project") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PullRequestCreated") + .HasColumnType("tinyint(1)"); + + b.Property("ReachableViaComms") + .HasColumnType("tinyint(1)"); + + b.Property("ReportDate") + .HasColumnType("date"); + + b.Property("ReportSubmittedOnTime") + .HasColumnType("tinyint(1)"); + + b.Property("ReviewDate") + .HasColumnType("date"); + + b.Property("ReviewedBy") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RisksEarlyWarnings") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("SprintIteration") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SubmissionTime") + .HasColumnType("datetime(6)"); + + b.Property("SubmittedToId") + .HasColumnType("bigint"); + + b.Property("SubmittedToUserId") + .HasColumnType("int"); + + b.Property("SupervisorNotes") + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("SupportEscalationNeeded") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("TeamUnit") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TestsPassing") + .HasColumnType("tinyint(1)"); + + b.Property("TicketsUpdated") + .HasColumnType("tinyint(1)"); + + b.Property("TimeIn") + .HasColumnType("time(6)"); + + b.Property("TimeOut") + .HasColumnType("time(6)"); + + b.Property("WorkArrangement") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("WorkArrangementTomorrow") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("SubmittedToId"); + + b.HasIndex("EmployeeId", "ReportDate") + .IsUnique(); + + b.ToTable("daily_reports", (string)null); + }); + + modelBuilder.Entity("HRIS.Api.Models.DailyReportTask", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ActualHours") + .HasPrecision(4, 2) + .HasColumnType("decimal(4,2)"); + + b.Property("BlockedByRemarks") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("CommitPrLink") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("DailyReportId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("EstimatedHours") + .HasPrecision(4, 2) + .HasColumnType("decimal(4,2)"); + + b.Property("IsCarryOver") + .HasColumnType("tinyint(1)"); + + b.Property("Module") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("OutputDeliverable") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("PercentDone") + .HasColumnType("int"); + + b.Property("Priority") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("TaskNumber") + .HasColumnType("int"); + + b.Property("TaskType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("TicketRefNo") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("DailyReportId"); + + b.ToTable("daily_report_tasks", (string)null); + }); + + modelBuilder.Entity("HRIS.Api.Models.Employee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AddressLine1") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("AddressLine2") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("BirthDate") + .HasColumnType("date"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("CivilStatus") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ContactNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("DateHired") + .HasColumnType("date"); + + b.Property("Department") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Email") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("EmployeeNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("EmploymentType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MiddleName") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("PagIbigNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("PhilHealthNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Position") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Province") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Sex") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("SssNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TinNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("ZipCode") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeNumber") + .IsUnique(); + + b.HasIndex("PagIbigNumber") + .IsUnique(); + + b.HasIndex("PhilHealthNumber") + .IsUnique(); + + b.HasIndex("SssNumber") + .IsUnique(); + + b.HasIndex("TinNumber") + .IsUnique(); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Employees"); + }); + + modelBuilder.Entity("HRIS.Api.Models.EmployeeDocument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("DocumentType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("OriginalFileName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("StoragePath") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("StoredFileName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("UploadedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("DocumentType"); + + b.HasIndex("EmployeeId"); + + b.ToTable("employee_documents", (string)null); + }); + + modelBuilder.Entity("HRIS.Api.Models.EmployeeShiftAssignment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveFrom") + .HasColumnType("date"); + + b.Property("EffectiveTo") + .HasColumnType("date"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("ShiftId") + .HasColumnType("int"); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("ShiftId"); + + b.HasIndex("EmployeeId", "IsActive"); + + b.ToTable("employee_shift_assignments", (string)null); + }); + + modelBuilder.Entity("HRIS.Api.Models.OvertimeRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("DateFrom") + .HasColumnType("date"); + + b.Property("DateTo") + .HasColumnType("date"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ReviewRemarks") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ReviewedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("ReviewedByUserId") + .HasColumnType("bigint"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("ReviewedByUserId"); + + b.HasIndex("Status"); + + b.HasIndex("EmployeeId", "DateFrom", "DateTo"); + + b.ToTable("overtime_requests", (string)null); + }); + + modelBuilder.Entity("HRIS.Api.Models.OvertimeRequestItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AttendanceLogId") + .HasColumnType("int"); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("OvertimeRequestId") + .HasColumnType("int"); + + b.Property("RequestedMinutes") + .HasColumnType("int"); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("AttendanceLogId"); + + b.HasIndex("Date"); + + b.HasIndex("OvertimeRequestId"); + + b.HasIndex("OvertimeRequestId", "Date") + .IsUnique(); + + b.ToTable("overtime_request_items", (string)null); + }); + + modelBuilder.Entity("HRIS.Api.Models.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CanArchive") + .HasColumnType("tinyint(1)"); + + b.Property("CanCreate") + .HasColumnType("tinyint(1)"); + + b.Property("CanUpdate") + .HasColumnType("tinyint(1)"); + + b.Property("CanView") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Module") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId", "Module") + .IsUnique(); + + b.ToTable("permissions", (string)null); + + b.HasData( + new + { + Id = 1, + CanArchive = true, + CanCreate = true, + CanUpdate = true, + CanView = true, + CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Module = "IAM", + RoleId = 1 + }, + new + { + Id = 6, + CanArchive = true, + CanCreate = true, + CanUpdate = true, + CanView = true, + CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Module = "EMPLOYEES", + RoleId = 1 + }, + new + { + Id = 9, + CanArchive = true, + CanCreate = true, + CanUpdate = true, + CanView = true, + CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Module = "ATTENDANCE", + RoleId = 1 + }, + new + { + Id = 3, + CanArchive = true, + CanCreate = true, + CanUpdate = true, + CanView = true, + CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Module = "IAM", + RoleId = 2 + }, + new + { + Id = 7, + CanArchive = true, + CanCreate = true, + CanUpdate = true, + CanView = true, + CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Module = "EMPLOYEES", + RoleId = 2 + }, + new + { + Id = 10, + CanArchive = true, + CanCreate = true, + CanUpdate = true, + CanView = true, + CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Module = "ATTENDANCE", + RoleId = 2 + }, + new + { + Id = 8, + CanArchive = false, + CanCreate = false, + CanUpdate = false, + CanView = false, + CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Module = "EMPLOYEES", + RoleId = 3 + }, + new + { + Id = 11, + CanArchive = false, + CanCreate = false, + CanUpdate = false, + CanView = false, + CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Module = "ATTENDANCE", + RoleId = 3 + }); + }); + + modelBuilder.Entity("HRIS.Api.Models.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique(); + + b.ToTable("roles", (string)null); + + b.HasData( + new + { + Id = 1, + IsSystem = true, + Name = "Super Admin", + NormalizedName = "SUPER_ADMIN" + }, + new + { + Id = 2, + IsSystem = true, + Name = "Admin", + NormalizedName = "ADMIN" + }, + new + { + Id = 3, + IsSystem = true, + Name = "User", + NormalizedName = "USER" + }); + }); + + modelBuilder.Entity("HRIS.Api.Models.Shift", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("IsFlexible") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b.Property("LateGraceMinutes") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("shifts", (string)null); + + b.HasData( + new + { + Id = 1001, + Code = "STD-0830-1730", + CreatedAtUtc = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Default weekday office shift", + IsActive = true, + IsFlexible = false, + LateGraceMinutes = 5, + Name = "Standard Office Shift" + }); + }); + + modelBuilder.Entity("HRIS.Api.Models.ShiftDay", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("BreakEndTime") + .HasColumnType("time(6)"); + + b.Property("BreakStartTime") + .HasColumnType("time(6)"); + + b.Property("DayOfWeek") + .HasColumnType("int"); + + b.Property("EndTime") + .HasColumnType("time(6)"); + + b.Property("IsWorkingDay") + .HasColumnType("tinyint(1)"); + + b.Property("ShiftId") + .HasColumnType("int"); + + b.Property("StartTime") + .HasColumnType("time(6)"); + + b.HasKey("Id"); + + b.HasIndex("ShiftId", "DayOfWeek") + .IsUnique(); + + b.ToTable("shift_days", (string)null); + + b.HasData( + new + { + Id = 2001, + BreakEndTime = new TimeOnly(13, 0, 0), + BreakStartTime = new TimeOnly(12, 0, 0), + DayOfWeek = 1, + EndTime = new TimeOnly(17, 30, 0), + IsWorkingDay = true, + ShiftId = 1001, + StartTime = new TimeOnly(8, 30, 0) + }, + new + { + Id = 2002, + BreakEndTime = new TimeOnly(13, 0, 0), + BreakStartTime = new TimeOnly(12, 0, 0), + DayOfWeek = 2, + EndTime = new TimeOnly(17, 30, 0), + IsWorkingDay = true, + ShiftId = 1001, + StartTime = new TimeOnly(8, 30, 0) + }, + new + { + Id = 2003, + BreakEndTime = new TimeOnly(13, 0, 0), + BreakStartTime = new TimeOnly(12, 0, 0), + DayOfWeek = 3, + EndTime = new TimeOnly(17, 30, 0), + IsWorkingDay = true, + ShiftId = 1001, + StartTime = new TimeOnly(8, 30, 0) + }, + new + { + Id = 2004, + BreakEndTime = new TimeOnly(13, 0, 0), + BreakStartTime = new TimeOnly(12, 0, 0), + DayOfWeek = 4, + EndTime = new TimeOnly(17, 30, 0), + IsWorkingDay = true, + ShiftId = 1001, + StartTime = new TimeOnly(8, 30, 0) + }, + new + { + Id = 2005, + BreakEndTime = new TimeOnly(13, 0, 0), + BreakStartTime = new TimeOnly(12, 0, 0), + DayOfWeek = 5, + EndTime = new TimeOnly(17, 30, 0), + IsWorkingDay = true, + ShiftId = 1001, + StartTime = new TimeOnly(8, 30, 0) + }, + new + { + Id = 2006, + DayOfWeek = 6, + IsWorkingDay = false, + ShiftId = 1001 + }, + new + { + Id = 2007, + DayOfWeek = 0, + IsWorkingDay = false, + ShiftId = 1001 + }); + }); + + modelBuilder.Entity("HRIS.Api.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("MiddleName") + .HasColumnType("longtext"); + + b.Property("NormalizedEmail") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("PasswordHash") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("PasswordResetToken") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("PasswordResetTokenExpiresAt") + .HasColumnType("datetime(6)"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("Suffix") + .HasColumnType("longtext"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .IsUnique(); + + b.HasIndex("PasswordResetToken"); + + b.HasIndex("RoleId"); + + b.ToTable("users", (string)null); + + b.HasData( + new + { + Id = 101L, + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Email = "superadmin@simplevia.com", + FullName = "Super Admin", + IsActive = true, + NormalizedEmail = "SUPERADMIN@SIMPLEVIA.COM", + PasswordHash = "$2a$11$K4TnWy1Wt/NB5n3e2FxEk.dwwOLwp5j0/ChgeOeookyl8ApuV8yim", + RoleId = 1 + }, + new + { + Id = 102L, + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Email = "admin@simplevia.com", + FullName = "Admin User", + IsActive = true, + NormalizedEmail = "ADMIN@SIMPLEVIA.COM", + PasswordHash = "$2a$11$4.lJCnxOfMgrWWJ//6bRCOvH.5XGyyExoyx.bPOsEdRcXCTm6rCi2", + RoleId = 2 + }, + new + { + Id = 103L, + CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Email = "user@simplevia.com", + FullName = "Regular User", + IsActive = true, + NormalizedEmail = "USER@SIMPLEVIA.COM", + PasswordHash = "$2a$11$3w9FJ6ypCA1HkYL0J.z2AeoSJLavuSJRbXE3N3IZhD3pSZ4r86RsG", + RoleId = 3 + }); + }); + + modelBuilder.Entity("HRIS.Api.Models.AttendanceLog", b => + { + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("HRIS.Api.Models.DailyReport", b => + { + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("HRIS.Api.Models.User", "SubmittedTo") + .WithMany() + .HasForeignKey("SubmittedToId"); + + b.Navigation("Employee"); + + b.Navigation("SubmittedTo"); + }); + + modelBuilder.Entity("HRIS.Api.Models.DailyReportTask", b => + { + b.HasOne("HRIS.Api.Models.DailyReport", "DailyReport") + .WithMany("Tasks") + .HasForeignKey("DailyReportId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DailyReport"); + }); + + modelBuilder.Entity("HRIS.Api.Models.Employee", b => + { + b.HasOne("HRIS.Api.Models.User", "User") + .WithOne("Employee") + .HasForeignKey("HRIS.Api.Models.Employee", "UserId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("User"); + }); + + modelBuilder.Entity("HRIS.Api.Models.EmployeeDocument", b => + { + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany("Documents") + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("HRIS.Api.Models.EmployeeShiftAssignment", b => + { + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("HRIS.Api.Models.Shift", "Shift") + .WithMany() + .HasForeignKey("ShiftId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("Shift"); + }); + + modelBuilder.Entity("HRIS.Api.Models.OvertimeRequest", b => + { + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("HRIS.Api.Models.User", "ReviewedByUser") + .WithMany() + .HasForeignKey("ReviewedByUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Employee"); + + b.Navigation("ReviewedByUser"); + }); + + modelBuilder.Entity("HRIS.Api.Models.OvertimeRequestItem", b => + { + b.HasOne("HRIS.Api.Models.AttendanceLog", "AttendanceLog") + .WithMany() + .HasForeignKey("AttendanceLogId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("HRIS.Api.Models.OvertimeRequest", "OvertimeRequest") + .WithMany("Items") + .HasForeignKey("OvertimeRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AttendanceLog"); + + b.Navigation("OvertimeRequest"); + }); + + modelBuilder.Entity("HRIS.Api.Models.Permission", b => + { + b.HasOne("HRIS.Api.Models.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("HRIS.Api.Models.ShiftDay", b => + { + b.HasOne("HRIS.Api.Models.Shift", "Shift") + .WithMany("ShiftDays") + .HasForeignKey("ShiftId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Shift"); + }); + + modelBuilder.Entity("HRIS.Api.Models.User", b => + { + b.HasOne("HRIS.Api.Models.Role", "Role") + .WithMany("Users") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("HRIS.Api.Models.DailyReport", b => + { + b.Navigation("Tasks"); + }); + + modelBuilder.Entity("HRIS.Api.Models.Employee", b => + { + b.Navigation("Documents"); + }); + + modelBuilder.Entity("HRIS.Api.Models.OvertimeRequest", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("HRIS.Api.Models.Role", b => + { + b.Navigation("Users"); + }); + + modelBuilder.Entity("HRIS.Api.Models.Shift", b => + { + b.Navigation("ShiftDays"); + }); + + modelBuilder.Entity("HRIS.Api.Models.User", b => + { + b.Navigation("Employee"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/apps/api/HRIS.Api/Migrations/20260712212707_AddDailyReportSections4to8.cs b/apps/api/HRIS.Api/Migrations/20260712212707_AddDailyReportSections4to8.cs new file mode 100644 index 0000000..266decb --- /dev/null +++ b/apps/api/HRIS.Api/Migrations/20260712212707_AddDailyReportSections4to8.cs @@ -0,0 +1,258 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace HRIS.Api.Migrations +{ + /// + public partial class AddDailyReportSections4to8 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "BlockersIssues", + table: "daily_reports", + type: "varchar(1000)", + maxLength: 1000, + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "CodeCommitted", + table: "daily_reports", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "DateReviewed", + table: "daily_reports", + type: "date", + nullable: true); + + migrationBuilder.AddColumn( + name: "DocumentationUpdated", + table: "daily_reports", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "ExpectedTimeIn", + table: "daily_reports", + type: "time(6)", + nullable: true); + + migrationBuilder.AddColumn( + name: "FollowUpRequired", + table: "daily_reports", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "KeyAccomplishments", + table: "daily_reports", + type: "varchar(2000)", + maxLength: 2000, + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "LeaveAbsenceNotice", + table: "daily_reports", + type: "varchar(500)", + maxLength: 500, + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "ManagerActionItems", + table: "daily_reports", + type: "varchar(1000)", + maxLength: 1000, + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "PerformanceRating", + table: "daily_reports", + type: "varchar(50)", + maxLength: 50, + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "PlanForTomorrow", + table: "daily_reports", + type: "varchar(1000)", + maxLength: 1000, + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "PullRequestCreated", + table: "daily_reports", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "ReportSubmittedOnTime", + table: "daily_reports", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "ReviewDate", + table: "daily_reports", + type: "date", + nullable: true); + + migrationBuilder.AddColumn( + name: "ReviewedBy", + table: "daily_reports", + type: "varchar(200)", + maxLength: 200, + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "RisksEarlyWarnings", + table: "daily_reports", + type: "varchar(1000)", + maxLength: 1000, + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "SupervisorNotes", + table: "daily_reports", + type: "varchar(2000)", + maxLength: 2000, + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "SupportEscalationNeeded", + table: "daily_reports", + type: "varchar(500)", + maxLength: 500, + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "TestsPassing", + table: "daily_reports", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "TicketsUpdated", + table: "daily_reports", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "WorkArrangementTomorrow", + table: "daily_reports", + type: "varchar(50)", + maxLength: 50, + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "BlockersIssues", + table: "daily_reports"); + + migrationBuilder.DropColumn( + name: "CodeCommitted", + table: "daily_reports"); + + migrationBuilder.DropColumn( + name: "DateReviewed", + table: "daily_reports"); + + migrationBuilder.DropColumn( + name: "DocumentationUpdated", + table: "daily_reports"); + + migrationBuilder.DropColumn( + name: "ExpectedTimeIn", + table: "daily_reports"); + + migrationBuilder.DropColumn( + name: "FollowUpRequired", + table: "daily_reports"); + + migrationBuilder.DropColumn( + name: "KeyAccomplishments", + table: "daily_reports"); + + migrationBuilder.DropColumn( + name: "LeaveAbsenceNotice", + table: "daily_reports"); + + migrationBuilder.DropColumn( + name: "ManagerActionItems", + table: "daily_reports"); + + migrationBuilder.DropColumn( + name: "PerformanceRating", + table: "daily_reports"); + + migrationBuilder.DropColumn( + name: "PlanForTomorrow", + table: "daily_reports"); + + migrationBuilder.DropColumn( + name: "PullRequestCreated", + table: "daily_reports"); + + migrationBuilder.DropColumn( + name: "ReportSubmittedOnTime", + table: "daily_reports"); + + migrationBuilder.DropColumn( + name: "ReviewDate", + table: "daily_reports"); + + migrationBuilder.DropColumn( + name: "ReviewedBy", + table: "daily_reports"); + + migrationBuilder.DropColumn( + name: "RisksEarlyWarnings", + table: "daily_reports"); + + migrationBuilder.DropColumn( + name: "SupervisorNotes", + table: "daily_reports"); + + migrationBuilder.DropColumn( + name: "SupportEscalationNeeded", + table: "daily_reports"); + + migrationBuilder.DropColumn( + name: "TestsPassing", + table: "daily_reports"); + + migrationBuilder.DropColumn( + name: "TicketsUpdated", + table: "daily_reports"); + + migrationBuilder.DropColumn( + name: "WorkArrangementTomorrow", + table: "daily_reports"); + } + } +} diff --git a/apps/api/HRIS.Api/Migrations/AppDbContextModelSnapshot.cs b/apps/api/HRIS.Api/Migrations/AppDbContextModelSnapshot.cs index 164e219..d90d268 100644 --- a/apps/api/HRIS.Api/Migrations/AppDbContextModelSnapshot.cs +++ b/apps/api/HRIS.Api/Migrations/AppDbContextModelSnapshot.cs @@ -435,6 +435,233 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("attendance_logs", (string)null); }); + modelBuilder.Entity("HRIS.Api.Models.DailyReport", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AttendedStandup") + .HasColumnType("tinyint(1)"); + + b.Property("AvgResponseTime") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BlockersIssues") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("BreakDurationMinutes") + .HasColumnType("int"); + + b.Property("CodeCommitted") + .HasColumnType("tinyint(1)"); + + b.Property("CollaborationLog") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("ConnectivityIssues") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("DateReviewed") + .HasColumnType("date"); + + b.Property("DocumentationUpdated") + .HasColumnType("tinyint(1)"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("ExpectedTimeIn") + .HasColumnType("time(6)"); + + b.Property("FollowUpRequired") + .HasColumnType("tinyint(1)"); + + b.Property("KeyAccomplishments") + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("LeaveAbsenceNotice") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ManagerActionItems") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("PerformanceRating") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PlanForTomorrow") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("Project") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PullRequestCreated") + .HasColumnType("tinyint(1)"); + + b.Property("ReachableViaComms") + .HasColumnType("tinyint(1)"); + + b.Property("ReportDate") + .HasColumnType("date"); + + b.Property("ReportSubmittedOnTime") + .HasColumnType("tinyint(1)"); + + b.Property("ReviewDate") + .HasColumnType("date"); + + b.Property("ReviewedBy") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RisksEarlyWarnings") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("SprintIteration") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SubmissionTime") + .HasColumnType("datetime(6)"); + + b.Property("SubmittedToId") + .HasColumnType("bigint"); + + b.Property("SubmittedToUserId") + .HasColumnType("int"); + + b.Property("SupervisorNotes") + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("SupportEscalationNeeded") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("TeamUnit") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TestsPassing") + .HasColumnType("tinyint(1)"); + + b.Property("TicketsUpdated") + .HasColumnType("tinyint(1)"); + + b.Property("TimeIn") + .HasColumnType("time(6)"); + + b.Property("TimeOut") + .HasColumnType("time(6)"); + + b.Property("WorkArrangement") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("WorkArrangementTomorrow") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("SubmittedToId"); + + b.HasIndex("EmployeeId", "ReportDate") + .IsUnique(); + + b.ToTable("daily_reports", (string)null); + }); + + modelBuilder.Entity("HRIS.Api.Models.DailyReportTask", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ActualHours") + .HasPrecision(4, 2) + .HasColumnType("decimal(4,2)"); + + b.Property("BlockedByRemarks") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("CommitPrLink") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("DailyReportId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("EstimatedHours") + .HasPrecision(4, 2) + .HasColumnType("decimal(4,2)"); + + b.Property("IsCarryOver") + .HasColumnType("tinyint(1)"); + + b.Property("Module") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("OutputDeliverable") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("PercentDone") + .HasColumnType("int"); + + b.Property("Priority") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("TaskNumber") + .HasColumnType("int"); + + b.Property("TaskType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("TicketRefNo") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("DailyReportId"); + + b.ToTable("daily_report_tasks", (string)null); + }); + modelBuilder.Entity("HRIS.Api.Models.Employee", b => { b.Property("Id") @@ -1964,6 +2191,34 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Employee"); }); + modelBuilder.Entity("HRIS.Api.Models.DailyReport", b => + { + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("HRIS.Api.Models.User", "SubmittedTo") + .WithMany() + .HasForeignKey("SubmittedToId"); + + b.Navigation("Employee"); + + b.Navigation("SubmittedTo"); + }); + + modelBuilder.Entity("HRIS.Api.Models.DailyReportTask", b => + { + b.HasOne("HRIS.Api.Models.DailyReport", "DailyReport") + .WithMany("Tasks") + .HasForeignKey("DailyReportId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DailyReport"); + }); + modelBuilder.Entity("HRIS.Api.Models.Employee", b => { b.HasOne("HRIS.Api.Models.User", "User") @@ -2225,6 +2480,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Returns"); }); + modelBuilder.Entity("HRIS.Api.Models.DailyReport", b => + { + b.Navigation("Tasks"); + }); + modelBuilder.Entity("HRIS.Api.Models.Employee", b => { b.Navigation("Documents"); diff --git a/apps/api/HRIS.Api/Models/DailyAccomplishmentReport.cs b/apps/api/HRIS.Api/Models/DailyAccomplishmentReport.cs new file mode 100644 index 0000000..2bcce91 --- /dev/null +++ b/apps/api/HRIS.Api/Models/DailyAccomplishmentReport.cs @@ -0,0 +1,46 @@ +namespace HRIS.Api.Models; + +public class DailyAccomplishmentReport +{ + public int Id { get; set; } + + // ─── Section 1: Developer Information ──────────────────────────────────── + public int EmployeeId { get; set; } + public Employee Employee { get; set; } = null!; + + public DateOnly Date { get; set; } + public string WorkArrangement { get; set; } = string.Empty; // "On-site" | "Remote" | "Hybrid" + public TimeOnly SubmissionTime { get; set; } + + public string ProjectSystem { get; set; } = string.Empty; + public string? SprintIteration { get; set; } + public string? TeamUnit { get; set; } + public string SubmittedTo { get; set; } = string.Empty; + + public TimeOnly TimeIn { get; set; } + public TimeOnly TimeOut { get; set; } + public int BreakDurationMinutes { get; set; } = 60; + + // Stored for record integrity even though they're computable + public decimal GrossDurationHours { get; set; } + public decimal NetProductiveHours { get; set; } + + // ─── Section 2: Availability & Connectivity ─────────────────────────────── + public string StandupAttended { get; set; } = string.Empty; // "Yes" | "No" | "N/A" + public string Reachable { get; set; } = string.Empty; // "Yes" | "Partial" | "No" + public string? AvgResponseTime { get; set; } + public string? ConnectivityIssues { get; set; } + public string? CollaborationLog { get; set; } + + // ─── Section 3: Time Breakdown ──────────────────────────────────────────── + public decimal? DevHours { get; set; } + public decimal? MeetingHours { get; set; } + public decimal? IdleHours { get; set; } + + // ─── Navigation ────────────────────────────────────────────────────────── + public ICollection Tasks { get; set; } = new List(); + + // ─── Audit ─────────────────────────────────────────────────────────────── + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + public DateTime? UpdatedAt { get; set; } +} diff --git a/apps/api/HRIS.Api/Models/DailyReport.cs b/apps/api/HRIS.Api/Models/DailyReport.cs new file mode 100644 index 0000000..d7f0a7d --- /dev/null +++ b/apps/api/HRIS.Api/Models/DailyReport.cs @@ -0,0 +1,62 @@ +namespace HRIS.Api.Models; + +public class DailyReport +{ + public int Id { get; set; } + + // Section 1 – Developer Information + public Guid EmployeeId { get; set; } + public Employee Employee { get; set; } = null!; + public DateOnly ReportDate { get; set; } + public string WorkArrangement { get; set; } = string.Empty; // Onsite / WFH / Hybrid + public DateTime SubmissionTime { get; set; } + public string Project { get; set; } = string.Empty; + public string? SprintIteration { get; set; } + public string? TeamUnit { get; set; } + public int? SubmittedToUserId { get; set; } + public User? SubmittedTo { get; set; } + public TimeOnly? TimeIn { get; set; } + public TimeOnly? TimeOut { get; set; } + public int BreakDurationMinutes { get; set; } = 60; + + // Section 2 – Availability & Connectivity + public bool AttendedStandup { get; set; } + public bool ReachableViaComms { get; set; } + public string? AvgResponseTime { get; set; } + public string? ConnectivityIssues { get; set; } + public string? CollaborationLog { get; set; } + + // Section 4 – End-of-Day Summary + public string? KeyAccomplishments { get; set; } + public string? BlockersIssues { get; set; } + public string? RisksEarlyWarnings { get; set; } + public string? PlanForTomorrow { get; set; } + public string? SupportEscalationNeeded { get; set; } + + // Section 5 – End-of-Day Checklist + public bool CodeCommitted { get; set; } + public bool TicketsUpdated { get; set; } + public bool PullRequestCreated { get; set; } + public bool DocumentationUpdated { get; set; } + public bool TestsPassing { get; set; } + public bool ReportSubmittedOnTime { get; set; } + + // Section 6 – Tomorrow's Plan + public string? WorkArrangementTomorrow { get; set; } + public TimeOnly? ExpectedTimeIn { get; set; } + public string? LeaveAbsenceNotice { get; set; } + + // Section 7 – Supervisor Remarks + public string? SupervisorNotes { get; set; } + public string? PerformanceRating { get; set; } + public bool FollowUpRequired { get; set; } + public DateOnly? ReviewDate { get; set; } + public string? ManagerActionItems { get; set; } + + // Section 8 – Acknowledgment + public string? ReviewedBy { get; set; } + public DateOnly? DateReviewed { get; set; } + + // Navigation + public ICollection Tasks { get; set; } = new List(); +} \ No newline at end of file diff --git a/apps/api/HRIS.Api/Models/DailyReportTask.cs b/apps/api/HRIS.Api/Models/DailyReportTask.cs new file mode 100644 index 0000000..5251b43 --- /dev/null +++ b/apps/api/HRIS.Api/Models/DailyReportTask.cs @@ -0,0 +1,24 @@ +namespace HRIS.Api.Models; + +public class DailyReportTask +{ + public int Id { get; set; } + public int DailyReportId { get; set; } + public DailyReport DailyReport { get; set; } = null!; + + // Section 3 – Task row fields + public int TaskNumber { get; set; } // 1–10 + public bool IsCarryOver { get; set; } + public string Priority { get; set; } = string.Empty; // High / Med / Low + public string TaskType { get; set; } = string.Empty; // Dev / Review / Meeting / etc. + public string? TicketRefNo { get; set; } + public string Description { get; set; } = string.Empty; + public string? Module { get; set; } + public string Status { get; set; } = string.Empty; // Done / In Progress / Blocked + public int PercentDone { get; set; } + public decimal EstimatedHours { get; set; } + public decimal ActualHours { get; set; } + public string? OutputDeliverable { get; set; } + public string? CommitPrLink { get; set; } + public string? BlockedByRemarks { get; set; } +} \ No newline at end of file diff --git a/apps/api/HRIS.Api/Models/DarTask.cs b/apps/api/HRIS.Api/Models/DarTask.cs new file mode 100644 index 0000000..95dd859 --- /dev/null +++ b/apps/api/HRIS.Api/Models/DarTask.cs @@ -0,0 +1,26 @@ +namespace HRIS.Api.Models; + +public class DarTask +{ + public int Id { get; set; } + + public int DailyAccomplishmentReportId { get; set; } + public DailyAccomplishmentReport DailyAccomplishmentReport { get; set; } = null!; + + public int RowNumber { get; set; } // preserves the order shown in the table + + // Matches the 14 columns in the frontend table exactly + public string? CarryOver { get; set; } // "" | "Yes" | "No" + public string? Priority { get; set; } // "" | "High" | "Medium" | "Low" + public string? TaskType { get; set; } // "" | "Development" | "Bug Fix" | "Testing" | "Review" | "Documentation" | "Meeting" | "Research" + public string? TicketRef { get; set; } + public string? Description { get; set; } + public string? Module { get; set; } + public string? Status { get; set; } // "" | "done" | "ip" | "blocked" | "todo" + public int? PercentDone { get; set; } + public decimal? EstimatedHours { get; set; } + public decimal? ActualHours { get; set; } + public string? Output { get; set; } + public string? CommitLink { get; set; } + public string? Remarks { get; set; } +} diff --git a/apps/api/HRIS.Api/Program.cs b/apps/api/HRIS.Api/Program.cs index 65acf0d..c65d89a 100644 --- a/apps/api/HRIS.Api/Program.cs +++ b/apps/api/HRIS.Api/Program.cs @@ -114,6 +114,12 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); +// Daily Reports +builder.Services.AddScoped(); + +// ===================== +// Dashboard Services +// ===================== builder.Services.AddScoped(); From 4d743e732300a4d704d299471a968b54e1aa70a8 Mon Sep 17 00:00:00 2001 From: makyluizp Date: Tue, 14 Jul 2026 03:14:23 +0800 Subject: [PATCH 2/5] fix: address CodeRabbit review comments --- .../Controllers/DailyReportsController.cs | 1 + .../DTOs/CreateDailyReportRequest.cs | 14 +- .../DTOs/SupervisorRemarksRequest.cs | 5 +- .../DTOs/UpdateDailyReportRequest.cs | 18 +-- .../Services/DailyReportsService.cs | 126 ++++++++++-------- 5 files changed, 93 insertions(+), 71 deletions(-) diff --git a/apps/api/HRIS.Api/Features/Attendance/Controllers/DailyReportsController.cs b/apps/api/HRIS.Api/Features/Attendance/Controllers/DailyReportsController.cs index 3b35af3..7f3f90f 100644 --- a/apps/api/HRIS.Api/Features/Attendance/Controllers/DailyReportsController.cs +++ b/apps/api/HRIS.Api/Features/Attendance/Controllers/DailyReportsController.cs @@ -38,6 +38,7 @@ public async Task AddSupervisorRemarks(int id, [FromBody] Supervi } [HttpGet("{id}")] + [PermissionAuthorize("ATTENDANCE", "View")] public async Task GetById(int id) { var result = await _service.GetByIdAsync(id); diff --git a/apps/api/HRIS.Api/Features/Attendance/DTOs/CreateDailyReportRequest.cs b/apps/api/HRIS.Api/Features/Attendance/DTOs/CreateDailyReportRequest.cs index af46f8d..3f1c3f0 100644 --- a/apps/api/HRIS.Api/Features/Attendance/DTOs/CreateDailyReportRequest.cs +++ b/apps/api/HRIS.Api/Features/Attendance/DTOs/CreateDailyReportRequest.cs @@ -1,6 +1,7 @@ +namespace HRIS.Api.Features.Attendance.DTOs; + public class CreateDailyReportRequest { - // Section 1 public DateOnly ReportDate { get; set; } public string WorkArrangement { get; set; } = string.Empty; public string Project { get; set; } = string.Empty; @@ -10,15 +11,11 @@ public class CreateDailyReportRequest public TimeOnly? TimeIn { get; set; } public TimeOnly? TimeOut { get; set; } public int BreakDurationMinutes { get; set; } = 60; - - // Section 2 public bool AttendedStandup { get; set; } public bool ReachableViaComms { get; set; } public string? AvgResponseTime { get; set; } public string? ConnectivityIssues { get; set; } public string? CollaborationLog { get; set; } - - // Section 3 public List Tasks { get; set; } = new(); } @@ -32,9 +29,16 @@ public class CreateDailyReportTaskRequest public string Description { get; set; } = string.Empty; public string? Module { get; set; } public string Status { get; set; } = string.Empty; + + [System.ComponentModel.DataAnnotations.Range(0, 100)] public int PercentDone { get; set; } + + [System.ComponentModel.DataAnnotations.Range(0, double.MaxValue)] public decimal EstimatedHours { get; set; } + + [System.ComponentModel.DataAnnotations.Range(0, double.MaxValue)] public decimal ActualHours { get; set; } + public string? OutputDeliverable { get; set; } public string? CommitPrLink { get; set; } public string? BlockedByRemarks { get; set; } diff --git a/apps/api/HRIS.Api/Features/Attendance/DTOs/SupervisorRemarksRequest.cs b/apps/api/HRIS.Api/Features/Attendance/DTOs/SupervisorRemarksRequest.cs index 860ec84..a4d5649 100644 --- a/apps/api/HRIS.Api/Features/Attendance/DTOs/SupervisorRemarksRequest.cs +++ b/apps/api/HRIS.Api/Features/Attendance/DTOs/SupervisorRemarksRequest.cs @@ -2,14 +2,11 @@ namespace HRIS.Api.Features.Attendance.DTOs; public class SupervisorRemarksRequest { - // Section 7 – Supervisor Remarks public string? SupervisorNotes { get; set; } public string? PerformanceRating { get; set; } - public bool FollowUpRequired { get; set; } + public bool? FollowUpRequired { get; set; } public DateOnly? ReviewDate { get; set; } public string? ManagerActionItems { get; set; } - - // Section 8 – Acknowledgment public string? ReviewedBy { get; set; } public DateOnly? DateReviewed { get; set; } } \ No newline at end of file diff --git a/apps/api/HRIS.Api/Features/Attendance/DTOs/UpdateDailyReportRequest.cs b/apps/api/HRIS.Api/Features/Attendance/DTOs/UpdateDailyReportRequest.cs index 1a8d2f5..e1da7dd 100644 --- a/apps/api/HRIS.Api/Features/Attendance/DTOs/UpdateDailyReportRequest.cs +++ b/apps/api/HRIS.Api/Features/Attendance/DTOs/UpdateDailyReportRequest.cs @@ -2,22 +2,22 @@ namespace HRIS.Api.Features.Attendance.DTOs; public class UpdateDailyReportRequest { - // Section 4 – End-of-Day Summary + // Section 4 public string? KeyAccomplishments { get; set; } public string? BlockersIssues { get; set; } public string? RisksEarlyWarnings { get; set; } public string? PlanForTomorrow { get; set; } public string? SupportEscalationNeeded { get; set; } - // Section 5 – End-of-Day Checklist - public bool CodeCommitted { get; set; } - public bool TicketsUpdated { get; set; } - public bool PullRequestCreated { get; set; } - public bool DocumentationUpdated { get; set; } - public bool TestsPassing { get; set; } - public bool ReportSubmittedOnTime { get; set; } + // Section 5 + public bool? CodeCommitted { get; set; } + public bool? TicketsUpdated { get; set; } + public bool? PullRequestCreated { get; set; } + public bool? DocumentationUpdated { get; set; } + public bool? TestsPassing { get; set; } + public bool? ReportSubmittedOnTime { get; set; } - // Section 6 – Tomorrow's Plan + // Section 6 public string? WorkArrangementTomorrow { get; set; } public TimeOnly? ExpectedTimeIn { get; set; } public string? LeaveAbsenceNotice { get; set; } diff --git a/apps/api/HRIS.Api/Features/Attendance/Services/DailyReportsService.cs b/apps/api/HRIS.Api/Features/Attendance/Services/DailyReportsService.cs index 875e24a..c2095d3 100644 --- a/apps/api/HRIS.Api/Features/Attendance/Services/DailyReportsService.cs +++ b/apps/api/HRIS.Api/Features/Attendance/Services/DailyReportsService.cs @@ -11,10 +11,11 @@ namespace HRIS.Api.Features.Attendance.Services; public class DailyReportsService : IDailyReportsService { private readonly AppDbContext _db; + private const int MaxPageSize = 100; public DailyReportsService(AppDbContext db) => _db = db; - public async Task CreateAsync(ClaimsPrincipal user, CreateDailyReportRequest request) + private async Task GetCurrentEmployeeAsync(ClaimsPrincipal user) { var userIdRaw = user.FindFirstValue(ClaimTypes.NameIdentifier) ?? @@ -29,6 +30,23 @@ public async Task CreateAsync(ClaimsPrincipal user, CreateDailyR if (employee == null) throw new ApiException("Employee not found.", StatusCodes.Status404NotFound); + return employee; + } + + public async Task CreateAsync(ClaimsPrincipal user, CreateDailyReportRequest request) + { + var employee = await GetCurrentEmployeeAsync(user); + + // Validate tasks + if (request.Tasks.Count > 10) + throw new ApiException("A report cannot have more than 10 tasks.", StatusCodes.Status400BadRequest); + + if (request.Tasks.Any(t => t.TaskNumber < 1 || t.TaskNumber > 10)) + throw new ApiException("TaskNumber must be between 1 and 10.", StatusCodes.Status400BadRequest); + + if (request.Tasks.GroupBy(t => t.TaskNumber).Any(g => g.Count() > 1)) + throw new ApiException("Duplicate TaskNumber values are not allowed.", StatusCodes.Status400BadRequest); + var exists = await _db.DailyReports .AnyAsync(r => r.EmployeeId == employee.Id && r.ReportDate == request.ReportDate); @@ -80,18 +98,7 @@ public async Task CreateAsync(ClaimsPrincipal user, CreateDailyR public async Task UpdateAsync(int id, ClaimsPrincipal user, UpdateDailyReportRequest request) { - var userIdRaw = - user.FindFirstValue(ClaimTypes.NameIdentifier) ?? - user.FindFirstValue("sub"); - - if (!long.TryParse(userIdRaw, out var userId)) - throw new ApiException("Invalid user.", StatusCodes.Status401Unauthorized); - - var employee = await _db.Employees - .FirstOrDefaultAsync(x => x.UserId == userId); - - if (employee == null) - throw new ApiException("Employee not found.", StatusCodes.Status404NotFound); + var employee = await GetCurrentEmployeeAsync(user); var report = await _db.DailyReports .FirstOrDefaultAsync(r => r.Id == id); @@ -102,25 +109,39 @@ public async Task UpdateAsync(int id, ClaimsPrincipal user, Upda if (report.EmployeeId != employee.Id) throw new ApiException("You can only update your own report.", StatusCodes.Status403Forbidden); - // Section 4 - report.KeyAccomplishments = request.KeyAccomplishments; - report.BlockersIssues = request.BlockersIssues; - report.RisksEarlyWarnings = request.RisksEarlyWarnings; - report.PlanForTomorrow = request.PlanForTomorrow; - report.SupportEscalationNeeded = request.SupportEscalationNeeded; - - // Section 5 - report.CodeCommitted = request.CodeCommitted; - report.TicketsUpdated = request.TicketsUpdated; - report.PullRequestCreated = request.PullRequestCreated; - report.DocumentationUpdated = request.DocumentationUpdated; - report.TestsPassing = request.TestsPassing; - report.ReportSubmittedOnTime = request.ReportSubmittedOnTime; - - // Section 6 - report.WorkArrangementTomorrow = request.WorkArrangementTomorrow; - report.ExpectedTimeIn = request.ExpectedTimeIn; - report.LeaveAbsenceNotice = request.LeaveAbsenceNotice; + // Section 4 — only update if provided + if (request.KeyAccomplishments != null) + report.KeyAccomplishments = request.KeyAccomplishments; + if (request.BlockersIssues != null) + report.BlockersIssues = request.BlockersIssues; + if (request.RisksEarlyWarnings != null) + report.RisksEarlyWarnings = request.RisksEarlyWarnings; + if (request.PlanForTomorrow != null) + report.PlanForTomorrow = request.PlanForTomorrow; + if (request.SupportEscalationNeeded != null) + report.SupportEscalationNeeded = request.SupportEscalationNeeded; + + // Section 5 — only update if provided + if (request.CodeCommitted.HasValue) + report.CodeCommitted = request.CodeCommitted.Value; + if (request.TicketsUpdated.HasValue) + report.TicketsUpdated = request.TicketsUpdated.Value; + if (request.PullRequestCreated.HasValue) + report.PullRequestCreated = request.PullRequestCreated.Value; + if (request.DocumentationUpdated.HasValue) + report.DocumentationUpdated = request.DocumentationUpdated.Value; + if (request.TestsPassing.HasValue) + report.TestsPassing = request.TestsPassing.Value; + if (request.ReportSubmittedOnTime.HasValue) + report.ReportSubmittedOnTime = request.ReportSubmittedOnTime.Value; + + // Section 6 — only update if provided + if (request.WorkArrangementTomorrow != null) + report.WorkArrangementTomorrow = request.WorkArrangementTomorrow; + if (request.ExpectedTimeIn.HasValue) + report.ExpectedTimeIn = request.ExpectedTimeIn.Value; + if (request.LeaveAbsenceNotice != null) + report.LeaveAbsenceNotice = request.LeaveAbsenceNotice; await _db.SaveChangesAsync(); @@ -135,16 +156,23 @@ public async Task AddSupervisorRemarksAsync(int id, SupervisorRe if (report == null) throw new ApiException("Report not found.", StatusCodes.Status404NotFound); - // Section 7 - report.SupervisorNotes = request.SupervisorNotes; - report.PerformanceRating = request.PerformanceRating; - report.FollowUpRequired = request.FollowUpRequired; - report.ReviewDate = request.ReviewDate; - report.ManagerActionItems = request.ManagerActionItems; + // Section 7 — only update if provided + if (request.SupervisorNotes != null) + report.SupervisorNotes = request.SupervisorNotes; + if (request.PerformanceRating != null) + report.PerformanceRating = request.PerformanceRating; + if (request.FollowUpRequired.HasValue) + report.FollowUpRequired = request.FollowUpRequired.Value; + if (request.ReviewDate.HasValue) + report.ReviewDate = request.ReviewDate.Value; + if (request.ManagerActionItems != null) + report.ManagerActionItems = request.ManagerActionItems; // Section 8 - report.ReviewedBy = request.ReviewedBy; - report.DateReviewed = request.DateReviewed; + if (request.ReviewedBy != null) + report.ReviewedBy = request.ReviewedBy; + if (request.DateReviewed.HasValue) + report.DateReviewed = request.DateReviewed.Value; await _db.SaveChangesAsync(); @@ -173,6 +201,9 @@ public async Task AddSupervisorRemarksAsync(int id, SupervisorRe public async Task> GetAllAsync(GetDailyReportsQuery query) { + var page = Math.Max(1, query.Page); + var pageSize = Math.Clamp(query.PageSize, 1, MaxPageSize); + var q = _db.DailyReports .Include(r => r.Employee) .Include(r => r.Tasks) @@ -186,8 +217,8 @@ public async Task> GetAllAsync(GetDailyReportsQuery query) var reports = await q .OrderByDescending(r => r.ReportDate) - .Skip((query.Page - 1) * query.PageSize) - .Take(query.PageSize) + .Skip((page - 1) * pageSize) + .Take(pageSize) .ToListAsync(); return reports.Select(MapToDto).ToList(); @@ -213,15 +244,11 @@ public async Task> GetAllAsync(GetDailyReportsQuery query) AvgResponseTime = report.AvgResponseTime, ConnectivityIssues = report.ConnectivityIssues, CollaborationLog = report.CollaborationLog, - - // Section 4 KeyAccomplishments = report.KeyAccomplishments, BlockersIssues = report.BlockersIssues, RisksEarlyWarnings = report.RisksEarlyWarnings, PlanForTomorrow = report.PlanForTomorrow, SupportEscalationNeeded = report.SupportEscalationNeeded, - - // Section 5 CodeCommitted = report.CodeCommitted, TicketsUpdated = report.TicketsUpdated, PullRequestCreated = report.PullRequestCreated, @@ -232,23 +259,16 @@ public async Task> GetAllAsync(GetDailyReportsQuery query) report.CodeCommitted, report.TicketsUpdated, report.PullRequestCreated, report.DocumentationUpdated, report.TestsPassing, report.ReportSubmittedOnTime }.Count(x => x), - - // Section 6 WorkArrangementTomorrow = report.WorkArrangementTomorrow, ExpectedTimeIn = report.ExpectedTimeIn, LeaveAbsenceNotice = report.LeaveAbsenceNotice, - - // Section 7 SupervisorNotes = report.SupervisorNotes, PerformanceRating = report.PerformanceRating, FollowUpRequired = report.FollowUpRequired, ReviewDate = report.ReviewDate, ManagerActionItems = report.ManagerActionItems, - - // Section 8 ReviewedBy = report.ReviewedBy, DateReviewed = report.DateReviewed, - Tasks = report.Tasks.Select(t => new DailyReportTaskDto { Id = t.Id, From 9142f2b849f380b2227faca392051835b50a477b Mon Sep 17 00:00:00 2001 From: Zekiroh Date: Fri, 17 Jul 2026 09:38:45 +0800 Subject: [PATCH 3/5] refactor(dar): remove obsolete report models --- .../Models/DailyAccomplishmentReport.cs | 46 ------------------- apps/api/HRIS.Api/Models/DarTask.cs | 26 ----------- 2 files changed, 72 deletions(-) delete mode 100644 apps/api/HRIS.Api/Models/DailyAccomplishmentReport.cs delete mode 100644 apps/api/HRIS.Api/Models/DarTask.cs diff --git a/apps/api/HRIS.Api/Models/DailyAccomplishmentReport.cs b/apps/api/HRIS.Api/Models/DailyAccomplishmentReport.cs deleted file mode 100644 index 2bcce91..0000000 --- a/apps/api/HRIS.Api/Models/DailyAccomplishmentReport.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace HRIS.Api.Models; - -public class DailyAccomplishmentReport -{ - public int Id { get; set; } - - // ─── Section 1: Developer Information ──────────────────────────────────── - public int EmployeeId { get; set; } - public Employee Employee { get; set; } = null!; - - public DateOnly Date { get; set; } - public string WorkArrangement { get; set; } = string.Empty; // "On-site" | "Remote" | "Hybrid" - public TimeOnly SubmissionTime { get; set; } - - public string ProjectSystem { get; set; } = string.Empty; - public string? SprintIteration { get; set; } - public string? TeamUnit { get; set; } - public string SubmittedTo { get; set; } = string.Empty; - - public TimeOnly TimeIn { get; set; } - public TimeOnly TimeOut { get; set; } - public int BreakDurationMinutes { get; set; } = 60; - - // Stored for record integrity even though they're computable - public decimal GrossDurationHours { get; set; } - public decimal NetProductiveHours { get; set; } - - // ─── Section 2: Availability & Connectivity ─────────────────────────────── - public string StandupAttended { get; set; } = string.Empty; // "Yes" | "No" | "N/A" - public string Reachable { get; set; } = string.Empty; // "Yes" | "Partial" | "No" - public string? AvgResponseTime { get; set; } - public string? ConnectivityIssues { get; set; } - public string? CollaborationLog { get; set; } - - // ─── Section 3: Time Breakdown ──────────────────────────────────────────── - public decimal? DevHours { get; set; } - public decimal? MeetingHours { get; set; } - public decimal? IdleHours { get; set; } - - // ─── Navigation ────────────────────────────────────────────────────────── - public ICollection Tasks { get; set; } = new List(); - - // ─── Audit ─────────────────────────────────────────────────────────────── - public DateTime CreatedAt { get; set; } = DateTime.UtcNow; - public DateTime? UpdatedAt { get; set; } -} diff --git a/apps/api/HRIS.Api/Models/DarTask.cs b/apps/api/HRIS.Api/Models/DarTask.cs deleted file mode 100644 index 95dd859..0000000 --- a/apps/api/HRIS.Api/Models/DarTask.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace HRIS.Api.Models; - -public class DarTask -{ - public int Id { get; set; } - - public int DailyAccomplishmentReportId { get; set; } - public DailyAccomplishmentReport DailyAccomplishmentReport { get; set; } = null!; - - public int RowNumber { get; set; } // preserves the order shown in the table - - // Matches the 14 columns in the frontend table exactly - public string? CarryOver { get; set; } // "" | "Yes" | "No" - public string? Priority { get; set; } // "" | "High" | "Medium" | "Low" - public string? TaskType { get; set; } // "" | "Development" | "Bug Fix" | "Testing" | "Review" | "Documentation" | "Meeting" | "Research" - public string? TicketRef { get; set; } - public string? Description { get; set; } - public string? Module { get; set; } - public string? Status { get; set; } // "" | "done" | "ip" | "blocked" | "todo" - public int? PercentDone { get; set; } - public decimal? EstimatedHours { get; set; } - public decimal? ActualHours { get; set; } - public string? Output { get; set; } - public string? CommitLink { get; set; } - public string? Remarks { get; set; } -} From bba037970b46650faf814814aad18a5a58c1bd95 Mon Sep 17 00:00:00 2001 From: Zekiroh Date: Fri, 17 Jul 2026 09:51:57 +0800 Subject: [PATCH 4/5] fix(dar): add self-service report retrieval --- .../Controllers/DailyReportsController.cs | 9 ++++++- .../Services/DailyReportsService.cs | 26 ++++++++++++++++++- .../Services/IDailyReportsService.cs | 3 ++- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/apps/api/HRIS.Api/Features/Attendance/Controllers/DailyReportsController.cs b/apps/api/HRIS.Api/Features/Attendance/Controllers/DailyReportsController.cs index 7f3f90f..e6873aa 100644 --- a/apps/api/HRIS.Api/Features/Attendance/Controllers/DailyReportsController.cs +++ b/apps/api/HRIS.Api/Features/Attendance/Controllers/DailyReportsController.cs @@ -37,6 +37,13 @@ public async Task AddSupervisorRemarks(int id, [FromBody] Supervi return Ok(result); } + [HttpGet("me")] + public async Task GetMine([FromQuery] GetDailyReportsQuery query) + { + var result = await _service.GetMineAsync(User, query); + return Ok(result); + } + [HttpGet("{id}")] [PermissionAuthorize("ATTENDANCE", "View")] public async Task GetById(int id) @@ -52,4 +59,4 @@ public async Task GetAll([FromQuery] GetDailyReportsQuery query) var result = await _service.GetAllAsync(query); return Ok(result); } -} \ No newline at end of file +} diff --git a/apps/api/HRIS.Api/Features/Attendance/Services/DailyReportsService.cs b/apps/api/HRIS.Api/Features/Attendance/Services/DailyReportsService.cs index c2095d3..9453bdb 100644 --- a/apps/api/HRIS.Api/Features/Attendance/Services/DailyReportsService.cs +++ b/apps/api/HRIS.Api/Features/Attendance/Services/DailyReportsService.cs @@ -224,6 +224,30 @@ public async Task> GetAllAsync(GetDailyReportsQuery query) return reports.Select(MapToDto).ToList(); } + public async Task> GetMineAsync(ClaimsPrincipal user, GetDailyReportsQuery query) + { + var employee = await GetCurrentEmployeeAsync(user); + var page = Math.Max(1, query.Page); + var pageSize = Math.Clamp(query.PageSize, 1, MaxPageSize); + + var q = _db.DailyReports + .Include(r => r.Employee) + .Include(r => r.Tasks) + .Where(r => r.EmployeeId == employee.Id) + .AsQueryable(); + + if (query.Date.HasValue) + q = q.Where(r => r.ReportDate == query.Date); + + var reports = await q + .OrderByDescending(r => r.ReportDate) + .Skip((page - 1) * pageSize) + .Take(pageSize) + .ToListAsync(); + + return reports.Select(MapToDto).ToList(); + } + private static DailyReportDto MapToDto(DailyReport report) => new() { Id = report.Id, @@ -288,4 +312,4 @@ public async Task> GetAllAsync(GetDailyReportsQuery query) BlockedByRemarks = t.BlockedByRemarks }).ToList() }; -} \ No newline at end of file +} diff --git a/apps/api/HRIS.Api/Features/Attendance/Services/IDailyReportsService.cs b/apps/api/HRIS.Api/Features/Attendance/Services/IDailyReportsService.cs index 4b05e24..ba2fd45 100644 --- a/apps/api/HRIS.Api/Features/Attendance/Services/IDailyReportsService.cs +++ b/apps/api/HRIS.Api/Features/Attendance/Services/IDailyReportsService.cs @@ -9,6 +9,7 @@ public interface IDailyReportsService Task GetByIdAsync(int id); Task GetByEmployeeAndDateAsync(Guid employeeId, DateOnly date); Task> GetAllAsync(GetDailyReportsQuery query); + Task> GetMineAsync(ClaimsPrincipal user, GetDailyReportsQuery query); Task UpdateAsync(int id, ClaimsPrincipal user, UpdateDailyReportRequest request); Task AddSupervisorRemarksAsync(int id, SupervisorRemarksRequest request); -} \ No newline at end of file +} From 71bfa1c46f6cecde39dd46b0f72216a6524ad51a Mon Sep 17 00:00:00 2001 From: Zekiroh Date: Fri, 17 Jul 2026 10:55:24 +0800 Subject: [PATCH 5/5] fix(dar): address backend review findings --- .../DailyReportConfiguration.cs | 7 +- .../DailyReportTaskConfiguration.cs | 3 +- .../Controllers/DailyReportsController.cs | 4 +- .../DTOs/CreateDailyReportRequest.cs | 5 +- .../Attendance/DTOs/DailyReportDto.cs | 4 +- .../DTOs/SupervisorRemarksRequest.cs | 4 +- .../Services/DailyReportsService.cs | 45 +- .../Services/IDailyReportsService.cs | 2 +- ...20260601170756_AddDailyReports.Designer.cs | 1270 ----------------- ...260712212707_AddDailyReportSections4to8.cs | 258 ---- ...AddDailyAccomplishmentReports.Designer.cs} | 1237 +++++++++++++++- ...17024910_AddDailyAccomplishmentReports.cs} | 55 +- .../Migrations/AppDbContextModelSnapshot.cs | 13 +- apps/api/HRIS.Api/Models/DailyReport.cs | 4 +- 14 files changed, 1322 insertions(+), 1589 deletions(-) delete mode 100644 apps/api/HRIS.Api/Migrations/20260601170756_AddDailyReports.Designer.cs delete mode 100644 apps/api/HRIS.Api/Migrations/20260712212707_AddDailyReportSections4to8.cs rename apps/api/HRIS.Api/Migrations/{20260712212707_AddDailyReportSections4to8.Designer.cs => 20260717024910_AddDailyAccomplishmentReports.Designer.cs} (52%) rename apps/api/HRIS.Api/Migrations/{20260601170756_AddDailyReports.cs => 20260717024910_AddDailyAccomplishmentReports.cs} (65%) diff --git a/apps/api/HRIS.Api/Data/Configurations/DailyReportConfiguration.cs b/apps/api/HRIS.Api/Data/Configurations/DailyReportConfiguration.cs index ac0f799..30b6abc 100644 --- a/apps/api/HRIS.Api/Data/Configurations/DailyReportConfiguration.cs +++ b/apps/api/HRIS.Api/Data/Configurations/DailyReportConfiguration.cs @@ -35,6 +35,11 @@ public void Configure(EntityTypeBuilder builder) .HasForeignKey(x => x.EmployeeId) .OnDelete(DeleteBehavior.Restrict); + builder.HasOne(x => x.SubmittedTo) + .WithMany() + .HasForeignKey(x => x.SubmittedToUserId) + .OnDelete(DeleteBehavior.Restrict); + builder.HasMany(x => x.Tasks) .WithOne(x => x.DailyReport) .HasForeignKey(x => x.DailyReportId) @@ -42,4 +47,4 @@ public void Configure(EntityTypeBuilder builder) builder.HasIndex(x => new { x.EmployeeId, x.ReportDate }).IsUnique(); } -} \ No newline at end of file +} diff --git a/apps/api/HRIS.Api/Data/Configurations/DailyReportTaskConfiguration.cs b/apps/api/HRIS.Api/Data/Configurations/DailyReportTaskConfiguration.cs index b06e358..d1d3d89 100644 --- a/apps/api/HRIS.Api/Data/Configurations/DailyReportTaskConfiguration.cs +++ b/apps/api/HRIS.Api/Data/Configurations/DailyReportTaskConfiguration.cs @@ -21,5 +21,6 @@ public void Configure(EntityTypeBuilder builder) builder.Property(x => x.OutputDeliverable).HasMaxLength(500); builder.Property(x => x.CommitPrLink).HasMaxLength(500); builder.Property(x => x.BlockedByRemarks).HasMaxLength(500); + builder.HasIndex(x => new { x.DailyReportId, x.TaskNumber }).IsUnique(); } -} \ No newline at end of file +} diff --git a/apps/api/HRIS.Api/Features/Attendance/Controllers/DailyReportsController.cs b/apps/api/HRIS.Api/Features/Attendance/Controllers/DailyReportsController.cs index e6873aa..4620f06 100644 --- a/apps/api/HRIS.Api/Features/Attendance/Controllers/DailyReportsController.cs +++ b/apps/api/HRIS.Api/Features/Attendance/Controllers/DailyReportsController.cs @@ -19,7 +19,7 @@ public class DailyReportsController : ControllerBase public async Task Create([FromBody] CreateDailyReportRequest request) { var result = await _service.CreateAsync(User, request); - return CreatedAtAction(nameof(GetById), new { id = result.Id }, result); + return StatusCode(StatusCodes.Status201Created, result); } [HttpPatch("{id}")] @@ -33,7 +33,7 @@ public async Task Update(int id, [FromBody] UpdateDailyReportRequ [PermissionAuthorize("ATTENDANCE", "Update")] public async Task AddSupervisorRemarks(int id, [FromBody] SupervisorRemarksRequest request) { - var result = await _service.AddSupervisorRemarksAsync(id, request); + var result = await _service.AddSupervisorRemarksAsync(id, User, request); return Ok(result); } diff --git a/apps/api/HRIS.Api/Features/Attendance/DTOs/CreateDailyReportRequest.cs b/apps/api/HRIS.Api/Features/Attendance/DTOs/CreateDailyReportRequest.cs index 3f1c3f0..f024051 100644 --- a/apps/api/HRIS.Api/Features/Attendance/DTOs/CreateDailyReportRequest.cs +++ b/apps/api/HRIS.Api/Features/Attendance/DTOs/CreateDailyReportRequest.cs @@ -7,9 +7,10 @@ public class CreateDailyReportRequest public string Project { get; set; } = string.Empty; public string? SprintIteration { get; set; } public string? TeamUnit { get; set; } - public int? SubmittedToUserId { get; set; } + public long? SubmittedToUserId { get; set; } public TimeOnly? TimeIn { get; set; } public TimeOnly? TimeOut { get; set; } + [System.ComponentModel.DataAnnotations.Range(0, 1440)] public int BreakDurationMinutes { get; set; } = 60; public bool AttendedStandup { get; set; } public bool ReachableViaComms { get; set; } @@ -42,4 +43,4 @@ public class CreateDailyReportTaskRequest public string? OutputDeliverable { get; set; } public string? CommitPrLink { get; set; } public string? BlockedByRemarks { get; set; } -} \ No newline at end of file +} diff --git a/apps/api/HRIS.Api/Features/Attendance/DTOs/DailyReportDto.cs b/apps/api/HRIS.Api/Features/Attendance/DTOs/DailyReportDto.cs index 5e946d9..09de594 100644 --- a/apps/api/HRIS.Api/Features/Attendance/DTOs/DailyReportDto.cs +++ b/apps/api/HRIS.Api/Features/Attendance/DTOs/DailyReportDto.cs @@ -11,7 +11,7 @@ public class DailyReportDto public string Project { get; set; } = string.Empty; public string? SprintIteration { get; set; } public string? TeamUnit { get; set; } - public int? SubmittedToUserId { get; set; } + public long? SubmittedToUserId { get; set; } public TimeOnly? TimeIn { get; set; } public TimeOnly? TimeOut { get; set; } public int BreakDurationMinutes { get; set; } @@ -73,4 +73,4 @@ public class DailyReportTaskDto public string? OutputDeliverable { get; set; } public string? CommitPrLink { get; set; } public string? BlockedByRemarks { get; set; } -} \ No newline at end of file +} diff --git a/apps/api/HRIS.Api/Features/Attendance/DTOs/SupervisorRemarksRequest.cs b/apps/api/HRIS.Api/Features/Attendance/DTOs/SupervisorRemarksRequest.cs index a4d5649..4650ff4 100644 --- a/apps/api/HRIS.Api/Features/Attendance/DTOs/SupervisorRemarksRequest.cs +++ b/apps/api/HRIS.Api/Features/Attendance/DTOs/SupervisorRemarksRequest.cs @@ -7,6 +7,4 @@ public class SupervisorRemarksRequest public bool? FollowUpRequired { get; set; } public DateOnly? ReviewDate { get; set; } public string? ManagerActionItems { get; set; } - public string? ReviewedBy { get; set; } - public DateOnly? DateReviewed { get; set; } -} \ No newline at end of file +} diff --git a/apps/api/HRIS.Api/Features/Attendance/Services/DailyReportsService.cs b/apps/api/HRIS.Api/Features/Attendance/Services/DailyReportsService.cs index 9453bdb..9398af6 100644 --- a/apps/api/HRIS.Api/Features/Attendance/Services/DailyReportsService.cs +++ b/apps/api/HRIS.Api/Features/Attendance/Services/DailyReportsService.cs @@ -5,6 +5,7 @@ using HRIS.Api.Models; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; +using MySqlConnector; namespace HRIS.Api.Features.Attendance.Services; @@ -15,6 +16,29 @@ public class DailyReportsService : IDailyReportsService public DailyReportsService(AppDbContext db) => _db = db; + private static string GetUserDisplayName(ClaimsPrincipal user) + { + var displayName = + user.FindFirstValue(ClaimTypes.Name) ?? + user.FindFirstValue("name") ?? + user.FindFirstValue("fullName") ?? + user.FindFirstValue(ClaimTypes.Email) ?? + user.FindFirstValue("email") ?? + user.FindFirstValue(ClaimTypes.NameIdentifier) ?? + user.FindFirstValue("sub"); + + if (string.IsNullOrWhiteSpace(displayName)) + throw new ApiException("Invalid user.", StatusCodes.Status401Unauthorized); + + return displayName; + } + + private static bool IsDuplicateDailyReportException(DbUpdateException ex) + { + return ex.InnerException is MySqlException { Number: 1062 } mysqlException && + mysqlException.Message.Contains("IX_daily_reports_EmployeeId_ReportDate", StringComparison.Ordinal); + } + private async Task GetCurrentEmployeeAsync(ClaimsPrincipal user) { var userIdRaw = @@ -91,7 +115,14 @@ public async Task CreateAsync(ClaimsPrincipal user, CreateDailyR }; _db.DailyReports.Add(report); - await _db.SaveChangesAsync(); + try + { + await _db.SaveChangesAsync(); + } + catch (DbUpdateException ex) when (IsDuplicateDailyReportException(ex)) + { + throw new ApiException("A report for this date already exists.", StatusCodes.Status409Conflict); + } return await GetByIdAsync(report.Id) ?? throw new InvalidOperationException(); } @@ -148,7 +179,7 @@ public async Task UpdateAsync(int id, ClaimsPrincipal user, Upda return await GetByIdAsync(id) ?? throw new InvalidOperationException(); } - public async Task AddSupervisorRemarksAsync(int id, SupervisorRemarksRequest request) + public async Task AddSupervisorRemarksAsync(int id, ClaimsPrincipal user, SupervisorRemarksRequest request) { var report = await _db.DailyReports .FirstOrDefaultAsync(r => r.Id == id); @@ -169,10 +200,8 @@ public async Task AddSupervisorRemarksAsync(int id, SupervisorRe report.ManagerActionItems = request.ManagerActionItems; // Section 8 - if (request.ReviewedBy != null) - report.ReviewedBy = request.ReviewedBy; - if (request.DateReviewed.HasValue) - report.DateReviewed = request.DateReviewed.Value; + report.ReviewedBy = GetUserDisplayName(user); + report.DateReviewed = DateOnly.FromDateTime(DateTime.UtcNow); await _db.SaveChangesAsync(); @@ -217,6 +246,7 @@ public async Task> GetAllAsync(GetDailyReportsQuery query) var reports = await q .OrderByDescending(r => r.ReportDate) + .ThenByDescending(r => r.Id) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); @@ -241,6 +271,7 @@ public async Task> GetMineAsync(ClaimsPrincipal user, GetDa var reports = await q .OrderByDescending(r => r.ReportDate) + .ThenByDescending(r => r.Id) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); @@ -293,7 +324,7 @@ public async Task> GetMineAsync(ClaimsPrincipal user, GetDa ManagerActionItems = report.ManagerActionItems, ReviewedBy = report.ReviewedBy, DateReviewed = report.DateReviewed, - Tasks = report.Tasks.Select(t => new DailyReportTaskDto + Tasks = report.Tasks.OrderBy(t => t.TaskNumber).Select(t => new DailyReportTaskDto { Id = t.Id, TaskNumber = t.TaskNumber, diff --git a/apps/api/HRIS.Api/Features/Attendance/Services/IDailyReportsService.cs b/apps/api/HRIS.Api/Features/Attendance/Services/IDailyReportsService.cs index ba2fd45..ac3e8e6 100644 --- a/apps/api/HRIS.Api/Features/Attendance/Services/IDailyReportsService.cs +++ b/apps/api/HRIS.Api/Features/Attendance/Services/IDailyReportsService.cs @@ -11,5 +11,5 @@ public interface IDailyReportsService Task> GetAllAsync(GetDailyReportsQuery query); Task> GetMineAsync(ClaimsPrincipal user, GetDailyReportsQuery query); Task UpdateAsync(int id, ClaimsPrincipal user, UpdateDailyReportRequest request); - Task AddSupervisorRemarksAsync(int id, SupervisorRemarksRequest request); + Task AddSupervisorRemarksAsync(int id, ClaimsPrincipal user, SupervisorRemarksRequest request); } diff --git a/apps/api/HRIS.Api/Migrations/20260601170756_AddDailyReports.Designer.cs b/apps/api/HRIS.Api/Migrations/20260601170756_AddDailyReports.Designer.cs deleted file mode 100644 index a120cc7..0000000 --- a/apps/api/HRIS.Api/Migrations/20260601170756_AddDailyReports.Designer.cs +++ /dev/null @@ -1,1270 +0,0 @@ -// -using System; -using HRIS.Api.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace HRIS.Api.Migrations -{ - [DbContext(typeof(AppDbContext))] - [Migration("20260601170756_AddDailyReports")] - partial class AddDailyReports - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.3") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("HRIS.Api.Models.ActivityLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("Action") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("ActorEmail") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("varchar(255)"); - - b.Property("ActorRole") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("ActorUserId") - .HasColumnType("int"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IpAddress") - .HasMaxLength(45) - .HasColumnType("varchar(45)"); - - b.Property("MetadataJson") - .HasColumnType("longtext"); - - b.Property("Module") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("Summary") - .HasColumnType("longtext"); - - b.Property("TargetId") - .HasMaxLength(64) - .HasColumnType("varchar(64)"); - - b.Property("TargetType") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("UserAgent") - .HasMaxLength(512) - .HasColumnType("varchar(512)"); - - b.HasKey("Id"); - - b.HasIndex("ActorUserId"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("Module", "CreatedAt"); - - b.ToTable("activity_logs", (string)null); - }); - - modelBuilder.Entity("HRIS.Api.Models.AttendanceLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("Accomplished") - .HasMaxLength(1000) - .HasColumnType("varchar(1000)"); - - b.Property("CreatedAtUtc") - .HasColumnType("datetime(6)"); - - b.Property("Date") - .HasColumnType("date"); - - b.Property("EmployeeId") - .HasColumnType("char(36)"); - - b.Property("IsPresent") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(false); - - b.Property("LateMinutes") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("OvertimeMinutes") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("RenderedMinutes") - .HasColumnType("int"); - - b.Property("Task") - .HasMaxLength(1000) - .HasColumnType("varchar(1000)"); - - b.Property("TimeIn") - .HasColumnType("time(6)"); - - b.Property("TimeOut") - .HasColumnType("time(6)"); - - b.Property("UndertimeMinutes") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("UpdatedAtUtc") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("EmployeeId", "Date") - .IsUnique(); - - b.ToTable("attendance_logs", (string)null); - }); - - modelBuilder.Entity("HRIS.Api.Models.DailyReport", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("AttendedStandup") - .HasColumnType("tinyint(1)"); - - b.Property("AvgResponseTime") - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("BreakDurationMinutes") - .HasColumnType("int"); - - b.Property("CollaborationLog") - .HasMaxLength(1000) - .HasColumnType("varchar(1000)"); - - b.Property("ConnectivityIssues") - .HasMaxLength(500) - .HasColumnType("varchar(500)"); - - b.Property("EmployeeId") - .HasColumnType("char(36)"); - - b.Property("Project") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar(200)"); - - b.Property("ReachableViaComms") - .HasColumnType("tinyint(1)"); - - b.Property("ReportDate") - .HasColumnType("date"); - - b.Property("SprintIteration") - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("SubmissionTime") - .HasColumnType("datetime(6)"); - - b.Property("SubmittedToId") - .HasColumnType("bigint"); - - b.Property("SubmittedToUserId") - .HasColumnType("int"); - - b.Property("TeamUnit") - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("TimeIn") - .HasColumnType("time(6)"); - - b.Property("TimeOut") - .HasColumnType("time(6)"); - - b.Property("WorkArrangement") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.HasKey("Id"); - - b.HasIndex("SubmittedToId"); - - b.HasIndex("EmployeeId", "ReportDate") - .IsUnique(); - - b.ToTable("daily_reports", (string)null); - }); - - modelBuilder.Entity("HRIS.Api.Models.DailyReportTask", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ActualHours") - .HasPrecision(4, 2) - .HasColumnType("decimal(4,2)"); - - b.Property("BlockedByRemarks") - .HasMaxLength(500) - .HasColumnType("varchar(500)"); - - b.Property("CommitPrLink") - .HasMaxLength(500) - .HasColumnType("varchar(500)"); - - b.Property("DailyReportId") - .HasColumnType("int"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("varchar(500)"); - - b.Property("EstimatedHours") - .HasPrecision(4, 2) - .HasColumnType("decimal(4,2)"); - - b.Property("IsCarryOver") - .HasColumnType("tinyint(1)"); - - b.Property("Module") - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("OutputDeliverable") - .HasMaxLength(500) - .HasColumnType("varchar(500)"); - - b.Property("PercentDone") - .HasColumnType("int"); - - b.Property("Priority") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("TaskNumber") - .HasColumnType("int"); - - b.Property("TaskType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("TicketRefNo") - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.HasKey("Id"); - - b.HasIndex("DailyReportId"); - - b.ToTable("daily_report_tasks", (string)null); - }); - - modelBuilder.Entity("HRIS.Api.Models.Employee", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AddressLine1") - .HasMaxLength(150) - .HasColumnType("varchar(150)"); - - b.Property("AddressLine2") - .HasMaxLength(150) - .HasColumnType("varchar(150)"); - - b.Property("BirthDate") - .HasColumnType("date"); - - b.Property("City") - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("CivilStatus") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.Property("ContactNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.Property("CreatedAtUtc") - .HasColumnType("datetime(6)"); - - b.Property("DateHired") - .HasColumnType("date"); - - b.Property("Department") - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("Email") - .HasMaxLength(150) - .HasColumnType("varchar(150)"); - - b.Property("EmployeeNumber") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("EmploymentType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("FirstName") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("IsActive") - .HasColumnType("tinyint(1)"); - - b.Property("LastName") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("MiddleName") - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("PagIbigNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.Property("PhilHealthNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.Property("Position") - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("Province") - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("Sex") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.Property("SssNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.Property("TinNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.Property("UpdatedAtUtc") - .HasColumnType("datetime(6)"); - - b.Property("UserId") - .HasColumnType("bigint"); - - b.Property("ZipCode") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("EmployeeNumber") - .IsUnique(); - - b.HasIndex("PagIbigNumber") - .IsUnique(); - - b.HasIndex("PhilHealthNumber") - .IsUnique(); - - b.HasIndex("SssNumber") - .IsUnique(); - - b.HasIndex("TinNumber") - .IsUnique(); - - b.HasIndex("UserId") - .IsUnique(); - - b.ToTable("Employees"); - }); - - modelBuilder.Entity("HRIS.Api.Models.EmployeeDocument", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ContentType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("DocumentType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("EmployeeId") - .HasColumnType("char(36)"); - - b.Property("FileSize") - .HasColumnType("bigint"); - - b.Property("OriginalFileName") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("varchar(255)"); - - b.Property("StoragePath") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("varchar(500)"); - - b.Property("StoredFileName") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("varchar(255)"); - - b.Property("UploadedAtUtc") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("DocumentType"); - - b.HasIndex("EmployeeId"); - - b.ToTable("employee_documents", (string)null); - }); - - modelBuilder.Entity("HRIS.Api.Models.EmployeeShiftAssignment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("CreatedAtUtc") - .HasColumnType("datetime(6)"); - - b.Property("EffectiveFrom") - .HasColumnType("date"); - - b.Property("EffectiveTo") - .HasColumnType("date"); - - b.Property("EmployeeId") - .HasColumnType("char(36)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("ShiftId") - .HasColumnType("int"); - - b.Property("UpdatedAtUtc") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("ShiftId"); - - b.HasIndex("EmployeeId", "IsActive"); - - b.ToTable("employee_shift_assignments", (string)null); - }); - - modelBuilder.Entity("HRIS.Api.Models.OvertimeRequest", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("CreatedAtUtc") - .HasColumnType("datetime(6)"); - - b.Property("DateFrom") - .HasColumnType("date"); - - b.Property("DateTo") - .HasColumnType("date"); - - b.Property("EmployeeId") - .HasColumnType("char(36)"); - - b.Property("Reason") - .HasMaxLength(500) - .HasColumnType("varchar(500)"); - - b.Property("ReviewRemarks") - .HasMaxLength(500) - .HasColumnType("varchar(500)"); - - b.Property("ReviewedAtUtc") - .HasColumnType("datetime(6)"); - - b.Property("ReviewedByUserId") - .HasColumnType("bigint"); - - b.Property("Status") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.Property("UpdatedAtUtc") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("EmployeeId"); - - b.HasIndex("ReviewedByUserId"); - - b.HasIndex("Status"); - - b.HasIndex("EmployeeId", "DateFrom", "DateTo"); - - b.ToTable("overtime_requests", (string)null); - }); - - modelBuilder.Entity("HRIS.Api.Models.OvertimeRequestItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("AttendanceLogId") - .HasColumnType("int"); - - b.Property("CreatedAtUtc") - .HasColumnType("datetime(6)"); - - b.Property("Date") - .HasColumnType("date"); - - b.Property("OvertimeRequestId") - .HasColumnType("int"); - - b.Property("RequestedMinutes") - .HasColumnType("int"); - - b.Property("UpdatedAtUtc") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("AttendanceLogId"); - - b.HasIndex("Date"); - - b.HasIndex("OvertimeRequestId"); - - b.HasIndex("OvertimeRequestId", "Date") - .IsUnique(); - - b.ToTable("overtime_request_items", (string)null); - }); - - modelBuilder.Entity("HRIS.Api.Models.Permission", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("CanArchive") - .HasColumnType("tinyint(1)"); - - b.Property("CanCreate") - .HasColumnType("tinyint(1)"); - - b.Property("CanUpdate") - .HasColumnType("tinyint(1)"); - - b.Property("CanView") - .HasColumnType("tinyint(1)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Module") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("RoleId") - .HasColumnType("int"); - - b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId", "Module") - .IsUnique(); - - b.ToTable("permissions", (string)null); - - b.HasData( - new - { - Id = 1, - CanArchive = true, - CanCreate = true, - CanUpdate = true, - CanView = true, - CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), - Module = "IAM", - RoleId = 1 - }, - new - { - Id = 6, - CanArchive = true, - CanCreate = true, - CanUpdate = true, - CanView = true, - CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), - Module = "EMPLOYEES", - RoleId = 1 - }, - new - { - Id = 9, - CanArchive = true, - CanCreate = true, - CanUpdate = true, - CanView = true, - CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), - Module = "ATTENDANCE", - RoleId = 1 - }, - new - { - Id = 3, - CanArchive = true, - CanCreate = true, - CanUpdate = true, - CanView = true, - CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), - Module = "IAM", - RoleId = 2 - }, - new - { - Id = 7, - CanArchive = true, - CanCreate = true, - CanUpdate = true, - CanView = true, - CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), - Module = "EMPLOYEES", - RoleId = 2 - }, - new - { - Id = 10, - CanArchive = true, - CanCreate = true, - CanUpdate = true, - CanView = true, - CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), - Module = "ATTENDANCE", - RoleId = 2 - }, - new - { - Id = 8, - CanArchive = false, - CanCreate = false, - CanUpdate = false, - CanView = false, - CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), - Module = "EMPLOYEES", - RoleId = 3 - }, - new - { - Id = 11, - CanArchive = false, - CanCreate = false, - CanUpdate = false, - CanView = false, - CreatedAt = new DateTime(2026, 2, 23, 0, 0, 0, 0, DateTimeKind.Utc), - Module = "ATTENDANCE", - RoleId = 3 - }); - }); - - modelBuilder.Entity("HRIS.Api.Models.Role", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("IsSystem") - .HasColumnType("tinyint(1)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("NormalizedName") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique(); - - b.ToTable("roles", (string)null); - - b.HasData( - new - { - Id = 1, - IsSystem = true, - Name = "Super Admin", - NormalizedName = "SUPER_ADMIN" - }, - new - { - Id = 2, - IsSystem = true, - Name = "Admin", - NormalizedName = "ADMIN" - }, - new - { - Id = 3, - IsSystem = true, - Name = "User", - NormalizedName = "USER" - }); - }); - - modelBuilder.Entity("HRIS.Api.Models.Shift", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("Code") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("varchar(50)"); - - b.Property("CreatedAtUtc") - .HasColumnType("datetime(6)"); - - b.Property("Description") - .HasMaxLength(255) - .HasColumnType("varchar(255)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("IsFlexible") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(false); - - b.Property("LateGraceMinutes") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("varchar(100)"); - - b.Property("UpdatedAtUtc") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("Code") - .IsUnique(); - - b.ToTable("shifts", (string)null); - - b.HasData( - new - { - Id = 1001, - Code = "STD-0830-1730", - CreatedAtUtc = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), - Description = "Default weekday office shift", - IsActive = true, - IsFlexible = false, - LateGraceMinutes = 5, - Name = "Standard Office Shift" - }); - }); - - modelBuilder.Entity("HRIS.Api.Models.ShiftDay", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("BreakEndTime") - .HasColumnType("time(6)"); - - b.Property("BreakStartTime") - .HasColumnType("time(6)"); - - b.Property("DayOfWeek") - .HasColumnType("int"); - - b.Property("EndTime") - .HasColumnType("time(6)"); - - b.Property("IsWorkingDay") - .HasColumnType("tinyint(1)"); - - b.Property("ShiftId") - .HasColumnType("int"); - - b.Property("StartTime") - .HasColumnType("time(6)"); - - b.HasKey("Id"); - - b.HasIndex("ShiftId", "DayOfWeek") - .IsUnique(); - - b.ToTable("shift_days", (string)null); - - b.HasData( - new - { - Id = 2001, - BreakEndTime = new TimeOnly(13, 0, 0), - BreakStartTime = new TimeOnly(12, 0, 0), - DayOfWeek = 1, - EndTime = new TimeOnly(17, 30, 0), - IsWorkingDay = true, - ShiftId = 1001, - StartTime = new TimeOnly(8, 30, 0) - }, - new - { - Id = 2002, - BreakEndTime = new TimeOnly(13, 0, 0), - BreakStartTime = new TimeOnly(12, 0, 0), - DayOfWeek = 2, - EndTime = new TimeOnly(17, 30, 0), - IsWorkingDay = true, - ShiftId = 1001, - StartTime = new TimeOnly(8, 30, 0) - }, - new - { - Id = 2003, - BreakEndTime = new TimeOnly(13, 0, 0), - BreakStartTime = new TimeOnly(12, 0, 0), - DayOfWeek = 3, - EndTime = new TimeOnly(17, 30, 0), - IsWorkingDay = true, - ShiftId = 1001, - StartTime = new TimeOnly(8, 30, 0) - }, - new - { - Id = 2004, - BreakEndTime = new TimeOnly(13, 0, 0), - BreakStartTime = new TimeOnly(12, 0, 0), - DayOfWeek = 4, - EndTime = new TimeOnly(17, 30, 0), - IsWorkingDay = true, - ShiftId = 1001, - StartTime = new TimeOnly(8, 30, 0) - }, - new - { - Id = 2005, - BreakEndTime = new TimeOnly(13, 0, 0), - BreakStartTime = new TimeOnly(12, 0, 0), - DayOfWeek = 5, - EndTime = new TimeOnly(17, 30, 0), - IsWorkingDay = true, - ShiftId = 1001, - StartTime = new TimeOnly(8, 30, 0) - }, - new - { - Id = 2006, - DayOfWeek = 6, - IsWorkingDay = false, - ShiftId = 1001 - }, - new - { - Id = 2007, - DayOfWeek = 0, - IsWorkingDay = false, - ShiftId = 1001 - }); - }); - - modelBuilder.Entity("HRIS.Api.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Email") - .IsRequired() - .HasMaxLength(120) - .HasColumnType("varchar(120)"); - - b.Property("FirstName") - .HasColumnType("longtext"); - - b.Property("FullName") - .IsRequired() - .HasMaxLength(120) - .HasColumnType("varchar(120)"); - - b.Property("IsActive") - .HasColumnType("tinyint(1)"); - - b.Property("LastName") - .HasColumnType("longtext"); - - b.Property("MiddleName") - .HasColumnType("longtext"); - - b.Property("NormalizedEmail") - .IsRequired() - .HasMaxLength(120) - .HasColumnType("varchar(120)"); - - b.Property("PasswordHash") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("varchar(255)"); - - b.Property("PasswordResetToken") - .HasMaxLength(255) - .HasColumnType("varchar(255)"); - - b.Property("PasswordResetTokenExpiresAt") - .HasColumnType("datetime(6)"); - - b.Property("RoleId") - .HasColumnType("int"); - - b.Property("Suffix") - .HasColumnType("longtext"); - - b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .IsUnique(); - - b.HasIndex("PasswordResetToken"); - - b.HasIndex("RoleId"); - - b.ToTable("users", (string)null); - - b.HasData( - new - { - Id = 101L, - CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), - Email = "superadmin@simplevia.com", - FullName = "Super Admin", - IsActive = true, - NormalizedEmail = "SUPERADMIN@SIMPLEVIA.COM", - PasswordHash = "$2a$11$K4TnWy1Wt/NB5n3e2FxEk.dwwOLwp5j0/ChgeOeookyl8ApuV8yim", - RoleId = 1 - }, - new - { - Id = 102L, - CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), - Email = "admin@simplevia.com", - FullName = "Admin User", - IsActive = true, - NormalizedEmail = "ADMIN@SIMPLEVIA.COM", - PasswordHash = "$2a$11$4.lJCnxOfMgrWWJ//6bRCOvH.5XGyyExoyx.bPOsEdRcXCTm6rCi2", - RoleId = 2 - }, - new - { - Id = 103L, - CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), - Email = "user@simplevia.com", - FullName = "Regular User", - IsActive = true, - NormalizedEmail = "USER@SIMPLEVIA.COM", - PasswordHash = "$2a$11$3w9FJ6ypCA1HkYL0J.z2AeoSJLavuSJRbXE3N3IZhD3pSZ4r86RsG", - RoleId = 3 - }); - }); - - modelBuilder.Entity("HRIS.Api.Models.AttendanceLog", b => - { - b.HasOne("HRIS.Api.Models.Employee", "Employee") - .WithMany() - .HasForeignKey("EmployeeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Employee"); - }); - - modelBuilder.Entity("HRIS.Api.Models.DailyReport", b => - { - b.HasOne("HRIS.Api.Models.Employee", "Employee") - .WithMany() - .HasForeignKey("EmployeeId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.HasOne("HRIS.Api.Models.User", "SubmittedTo") - .WithMany() - .HasForeignKey("SubmittedToId"); - - b.Navigation("Employee"); - - b.Navigation("SubmittedTo"); - }); - - modelBuilder.Entity("HRIS.Api.Models.DailyReportTask", b => - { - b.HasOne("HRIS.Api.Models.DailyReport", "DailyReport") - .WithMany("Tasks") - .HasForeignKey("DailyReportId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("DailyReport"); - }); - - modelBuilder.Entity("HRIS.Api.Models.Employee", b => - { - b.HasOne("HRIS.Api.Models.User", "User") - .WithOne("Employee") - .HasForeignKey("HRIS.Api.Models.Employee", "UserId") - .OnDelete(DeleteBehavior.Restrict); - - b.Navigation("User"); - }); - - modelBuilder.Entity("HRIS.Api.Models.EmployeeDocument", b => - { - b.HasOne("HRIS.Api.Models.Employee", "Employee") - .WithMany("Documents") - .HasForeignKey("EmployeeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Employee"); - }); - - modelBuilder.Entity("HRIS.Api.Models.EmployeeShiftAssignment", b => - { - b.HasOne("HRIS.Api.Models.Employee", "Employee") - .WithMany() - .HasForeignKey("EmployeeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("HRIS.Api.Models.Shift", "Shift") - .WithMany() - .HasForeignKey("ShiftId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Employee"); - - b.Navigation("Shift"); - }); - - modelBuilder.Entity("HRIS.Api.Models.OvertimeRequest", b => - { - b.HasOne("HRIS.Api.Models.Employee", "Employee") - .WithMany() - .HasForeignKey("EmployeeId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.HasOne("HRIS.Api.Models.User", "ReviewedByUser") - .WithMany() - .HasForeignKey("ReviewedByUserId") - .OnDelete(DeleteBehavior.Restrict); - - b.Navigation("Employee"); - - b.Navigation("ReviewedByUser"); - }); - - modelBuilder.Entity("HRIS.Api.Models.OvertimeRequestItem", b => - { - b.HasOne("HRIS.Api.Models.AttendanceLog", "AttendanceLog") - .WithMany() - .HasForeignKey("AttendanceLogId") - .OnDelete(DeleteBehavior.Restrict); - - b.HasOne("HRIS.Api.Models.OvertimeRequest", "OvertimeRequest") - .WithMany("Items") - .HasForeignKey("OvertimeRequestId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("AttendanceLog"); - - b.Navigation("OvertimeRequest"); - }); - - modelBuilder.Entity("HRIS.Api.Models.Permission", b => - { - b.HasOne("HRIS.Api.Models.Role", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("HRIS.Api.Models.ShiftDay", b => - { - b.HasOne("HRIS.Api.Models.Shift", "Shift") - .WithMany("ShiftDays") - .HasForeignKey("ShiftId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Shift"); - }); - - modelBuilder.Entity("HRIS.Api.Models.User", b => - { - b.HasOne("HRIS.Api.Models.Role", "Role") - .WithMany("Users") - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("HRIS.Api.Models.DailyReport", b => - { - b.Navigation("Tasks"); - }); - - modelBuilder.Entity("HRIS.Api.Models.Employee", b => - { - b.Navigation("Documents"); - }); - - modelBuilder.Entity("HRIS.Api.Models.OvertimeRequest", b => - { - b.Navigation("Items"); - }); - - modelBuilder.Entity("HRIS.Api.Models.Role", b => - { - b.Navigation("Users"); - }); - - modelBuilder.Entity("HRIS.Api.Models.Shift", b => - { - b.Navigation("ShiftDays"); - }); - - modelBuilder.Entity("HRIS.Api.Models.User", b => - { - b.Navigation("Employee"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/apps/api/HRIS.Api/Migrations/20260712212707_AddDailyReportSections4to8.cs b/apps/api/HRIS.Api/Migrations/20260712212707_AddDailyReportSections4to8.cs deleted file mode 100644 index 266decb..0000000 --- a/apps/api/HRIS.Api/Migrations/20260712212707_AddDailyReportSections4to8.cs +++ /dev/null @@ -1,258 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace HRIS.Api.Migrations -{ - /// - public partial class AddDailyReportSections4to8 : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "BlockersIssues", - table: "daily_reports", - type: "varchar(1000)", - maxLength: 1000, - nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AddColumn( - name: "CodeCommitted", - table: "daily_reports", - type: "tinyint(1)", - nullable: false, - defaultValue: false); - - migrationBuilder.AddColumn( - name: "DateReviewed", - table: "daily_reports", - type: "date", - nullable: true); - - migrationBuilder.AddColumn( - name: "DocumentationUpdated", - table: "daily_reports", - type: "tinyint(1)", - nullable: false, - defaultValue: false); - - migrationBuilder.AddColumn( - name: "ExpectedTimeIn", - table: "daily_reports", - type: "time(6)", - nullable: true); - - migrationBuilder.AddColumn( - name: "FollowUpRequired", - table: "daily_reports", - type: "tinyint(1)", - nullable: false, - defaultValue: false); - - migrationBuilder.AddColumn( - name: "KeyAccomplishments", - table: "daily_reports", - type: "varchar(2000)", - maxLength: 2000, - nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AddColumn( - name: "LeaveAbsenceNotice", - table: "daily_reports", - type: "varchar(500)", - maxLength: 500, - nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AddColumn( - name: "ManagerActionItems", - table: "daily_reports", - type: "varchar(1000)", - maxLength: 1000, - nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AddColumn( - name: "PerformanceRating", - table: "daily_reports", - type: "varchar(50)", - maxLength: 50, - nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AddColumn( - name: "PlanForTomorrow", - table: "daily_reports", - type: "varchar(1000)", - maxLength: 1000, - nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AddColumn( - name: "PullRequestCreated", - table: "daily_reports", - type: "tinyint(1)", - nullable: false, - defaultValue: false); - - migrationBuilder.AddColumn( - name: "ReportSubmittedOnTime", - table: "daily_reports", - type: "tinyint(1)", - nullable: false, - defaultValue: false); - - migrationBuilder.AddColumn( - name: "ReviewDate", - table: "daily_reports", - type: "date", - nullable: true); - - migrationBuilder.AddColumn( - name: "ReviewedBy", - table: "daily_reports", - type: "varchar(200)", - maxLength: 200, - nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AddColumn( - name: "RisksEarlyWarnings", - table: "daily_reports", - type: "varchar(1000)", - maxLength: 1000, - nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AddColumn( - name: "SupervisorNotes", - table: "daily_reports", - type: "varchar(2000)", - maxLength: 2000, - nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AddColumn( - name: "SupportEscalationNeeded", - table: "daily_reports", - type: "varchar(500)", - maxLength: 500, - nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AddColumn( - name: "TestsPassing", - table: "daily_reports", - type: "tinyint(1)", - nullable: false, - defaultValue: false); - - migrationBuilder.AddColumn( - name: "TicketsUpdated", - table: "daily_reports", - type: "tinyint(1)", - nullable: false, - defaultValue: false); - - migrationBuilder.AddColumn( - name: "WorkArrangementTomorrow", - table: "daily_reports", - type: "varchar(50)", - maxLength: 50, - nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "BlockersIssues", - table: "daily_reports"); - - migrationBuilder.DropColumn( - name: "CodeCommitted", - table: "daily_reports"); - - migrationBuilder.DropColumn( - name: "DateReviewed", - table: "daily_reports"); - - migrationBuilder.DropColumn( - name: "DocumentationUpdated", - table: "daily_reports"); - - migrationBuilder.DropColumn( - name: "ExpectedTimeIn", - table: "daily_reports"); - - migrationBuilder.DropColumn( - name: "FollowUpRequired", - table: "daily_reports"); - - migrationBuilder.DropColumn( - name: "KeyAccomplishments", - table: "daily_reports"); - - migrationBuilder.DropColumn( - name: "LeaveAbsenceNotice", - table: "daily_reports"); - - migrationBuilder.DropColumn( - name: "ManagerActionItems", - table: "daily_reports"); - - migrationBuilder.DropColumn( - name: "PerformanceRating", - table: "daily_reports"); - - migrationBuilder.DropColumn( - name: "PlanForTomorrow", - table: "daily_reports"); - - migrationBuilder.DropColumn( - name: "PullRequestCreated", - table: "daily_reports"); - - migrationBuilder.DropColumn( - name: "ReportSubmittedOnTime", - table: "daily_reports"); - - migrationBuilder.DropColumn( - name: "ReviewDate", - table: "daily_reports"); - - migrationBuilder.DropColumn( - name: "ReviewedBy", - table: "daily_reports"); - - migrationBuilder.DropColumn( - name: "RisksEarlyWarnings", - table: "daily_reports"); - - migrationBuilder.DropColumn( - name: "SupervisorNotes", - table: "daily_reports"); - - migrationBuilder.DropColumn( - name: "SupportEscalationNeeded", - table: "daily_reports"); - - migrationBuilder.DropColumn( - name: "TestsPassing", - table: "daily_reports"); - - migrationBuilder.DropColumn( - name: "TicketsUpdated", - table: "daily_reports"); - - migrationBuilder.DropColumn( - name: "WorkArrangementTomorrow", - table: "daily_reports"); - } - } -} diff --git a/apps/api/HRIS.Api/Migrations/20260712212707_AddDailyReportSections4to8.Designer.cs b/apps/api/HRIS.Api/Migrations/20260717024910_AddDailyAccomplishmentReports.Designer.cs similarity index 52% rename from apps/api/HRIS.Api/Migrations/20260712212707_AddDailyReportSections4to8.Designer.cs rename to apps/api/HRIS.Api/Migrations/20260717024910_AddDailyAccomplishmentReports.Designer.cs index 489608a..fa2888b 100644 --- a/apps/api/HRIS.Api/Migrations/20260712212707_AddDailyReportSections4to8.Designer.cs +++ b/apps/api/HRIS.Api/Migrations/20260717024910_AddDailyAccomplishmentReports.Designer.cs @@ -12,8 +12,8 @@ namespace HRIS.Api.Migrations { [DbContext(typeof(AppDbContext))] - [Migration("20260712212707_AddDailyReportSections4to8")] - partial class AddDailyReportSections4to8 + [Migration("20260717024910_AddDailyAccomplishmentReports")] + partial class AddDailyAccomplishmentReports { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -92,6 +92,287 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("activity_logs", (string)null); }); + modelBuilder.Entity("HRIS.Api.Models.Announcement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("varchar(4000)"); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("CreatedByUserId") + .HasColumnType("bigint"); + + b.Property("Priority") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PublishedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAtUtc"); + + b.HasIndex("CreatedByUserId"); + + b.HasIndex("Priority"); + + b.HasIndex("Status"); + + b.HasIndex("Status", "PublishedAtUtc"); + + b.ToTable("Announcements"); + }); + + modelBuilder.Entity("HRIS.Api.Models.AnnouncementRead", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AnnouncementId") + .HasColumnType("char(36)"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("ReadAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("AnnouncementId", "EmployeeId") + .IsUnique(); + + b.ToTable("AnnouncementReads"); + }); + + modelBuilder.Entity("HRIS.Api.Models.Asset", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AssetCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("AssetName") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("Brand") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("Model") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("PurchaseDate") + .HasColumnType("date"); + + b.Property("SerialNumber") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("AssetCode") + .IsUnique(); + + b.ToTable("Assets"); + }); + + modelBuilder.Entity("HRIS.Api.Models.AssetAssignment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AssetId") + .HasColumnType("int"); + + b.Property("AssignedByUserId") + .HasColumnType("bigint"); + + b.Property("AssignedDate") + .HasColumnType("date"); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Remarks") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("AssignedByUserId"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("AssetId", "IsActive"); + + b.ToTable("AssetAssignments"); + }); + + modelBuilder.Entity("HRIS.Api.Models.AssetReturn", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AssetAssignmentId") + .HasColumnType("int"); + + b.Property("Condition") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("ReceivedByUserId") + .HasColumnType("bigint"); + + b.Property("Remarks") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ReturnedDate") + .HasColumnType("date"); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("AssetAssignmentId") + .IsUnique(); + + b.HasIndex("ReceivedByUserId"); + + b.ToTable("AssetReturns"); + }); + + modelBuilder.Entity("HRIS.Api.Models.AssetReturnRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AssetAssignmentId") + .HasColumnType("int"); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("RequestedDate") + .HasColumnType("date"); + + b.Property("ReviewRemarks") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ReviewedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("ReviewedByUserId") + .HasColumnType("bigint"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("AssetAssignmentId"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("ReviewedByUserId"); + + b.HasIndex("Status"); + + b.ToTable("AssetReturnRequests"); + }); + modelBuilder.Entity("HRIS.Api.Models.AttendanceLog", b => { b.Property("Id") @@ -260,12 +541,9 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("SubmissionTime") .HasColumnType("datetime(6)"); - b.Property("SubmittedToId") + b.Property("SubmittedToUserId") .HasColumnType("bigint"); - b.Property("SubmittedToUserId") - .HasColumnType("int"); - b.Property("SupervisorNotes") .HasMaxLength(2000) .HasColumnType("varchar(2000)"); @@ -301,7 +579,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.HasIndex("SubmittedToId"); + b.HasIndex("SubmittedToUserId"); b.HasIndex("EmployeeId", "ReportDate") .IsUnique(); @@ -379,7 +657,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.HasIndex("DailyReportId"); + b.HasIndex("DailyReportId", "TaskNumber") + .IsUnique(); b.ToTable("daily_report_tasks", (string)null); }); @@ -515,27 +794,151 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("Employees"); }); - modelBuilder.Entity("HRIS.Api.Models.EmployeeDocument", b => + modelBuilder.Entity("HRIS.Api.Models.EmployeeClearance", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("int"); - b.Property("ContentType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("varchar(100)"); + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - b.Property("DocumentType") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("varchar(50)"); + b.Property("CompletedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("DepartmentApproved") + .HasColumnType("tinyint(1)"); b.Property("EmployeeId") .HasColumnType("char(36)"); - b.Property("FileSize") - .HasColumnType("bigint"); + b.Property("HrApproved") + .HasColumnType("tinyint(1)"); + + b.Property("LastWorkingDay") + .HasColumnType("date"); + + b.Property("Remarks") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("Status"); + + b.ToTable("EmployeeClearances"); + }); + + modelBuilder.Entity("HRIS.Api.Models.EmployeeClearanceActivity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ActorUserId") + .HasColumnType("bigint"); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeClearanceId") + .HasColumnType("int"); + + b.Property("Remarks") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.HasKey("Id"); + + b.HasIndex("ActorUserId"); + + b.HasIndex("EmployeeClearanceId"); + + b.ToTable("EmployeeClearanceActivities"); + }); + + modelBuilder.Entity("HRIS.Api.Models.EmployeeCompensation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("BaseAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("CompensationType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveFrom") + .HasColumnType("date"); + + b.Property("EffectiveTo") + .HasColumnType("date"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId", "IsActive"); + + b.ToTable("EmployeeCompensations"); + }); + + modelBuilder.Entity("HRIS.Api.Models.EmployeeDocument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("DocumentType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("FileSize") + .HasColumnType("bigint"); b.Property("OriginalFileName") .IsRequired() @@ -604,6 +1007,171 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("employee_shift_assignments", (string)null); }); + modelBuilder.Entity("HRIS.Api.Models.LeaveBalance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("LeaveType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("RemainingCredits") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("TotalCredits") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("UsedCredits") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("EmployeeId", "LeaveType") + .IsUnique(); + + b.ToTable("LeaveBalances"); + }); + + modelBuilder.Entity("HRIS.Api.Models.LeaveBalanceTransaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("CreatedByUserId") + .HasColumnType("bigint"); + + b.Property("Days") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("LeaveBalanceId") + .HasColumnType("int"); + + b.Property("LeaveType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("Remarks") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("TransactionType") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAtUtc"); + + b.HasIndex("CreatedByUserId"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("LeaveBalanceId"); + + b.HasIndex("LeaveType"); + + b.HasIndex("TransactionType"); + + b.ToTable("LeaveBalanceTransactions"); + }); + + modelBuilder.Entity("HRIS.Api.Models.LeaveRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("DaysRequested") + .HasPrecision(6, 2) + .HasColumnType("decimal(6,2)"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("LeaveType") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ReviewRemarks") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("ReviewedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("ReviewedByUserId") + .HasColumnType("bigint"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("LeaveType"); + + b.HasIndex("ReviewedByUserId"); + + b.HasIndex("Status"); + + b.HasIndex("EmployeeId", "StartDate", "EndDate"); + + b.ToTable("LeaveRequests"); + }); + modelBuilder.Entity("HRIS.Api.Models.OvertimeRequest", b => { b.Property("Id") @@ -699,6 +1267,216 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("overtime_request_items", (string)null); }); + modelBuilder.Entity("HRIS.Api.Models.PagIbigContributionRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("EffectiveFrom") + .HasColumnType("date"); + + b.Property("EffectiveTo") + .HasColumnType("date"); + + b.Property("EmployeeRate") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("EmployerRate") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("MaximumContribution") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("MinimumContribution") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("EffectiveFrom", "IsActive"); + + b.ToTable("PagIbigContributionRules", t => + { + t.HasCheckConstraint("CK_PagIbigContributionRule_AmountRange", "`EmployeeRate` >= 0 AND `EmployerRate` >= 0 AND `MinimumContribution` >= 0 AND `MaximumContribution` >= `MinimumContribution`"); + + t.HasCheckConstraint("CK_PagIbigContributionRule_EffectiveRange", "`EffectiveTo` IS NULL OR `EffectiveTo` >= `EffectiveFrom`"); + }); + }); + + modelBuilder.Entity("HRIS.Api.Models.PayrollPeriod", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("ProcessedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("ReleasedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("StartDate", "EndDate") + .IsUnique(); + + b.ToTable("PayrollPeriods"); + }); + + modelBuilder.Entity("HRIS.Api.Models.PayrollRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("GrossPay") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("NetPay") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PayrollPeriodId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TotalDeductions") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("PayrollPeriodId", "EmployeeId") + .IsUnique(); + + b.ToTable("PayrollRecords"); + }); + + modelBuilder.Entity("HRIS.Api.Models.PayrollRecordItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PayrollRecordId") + .HasColumnType("int"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("PayrollRecordId"); + + b.ToTable("PayrollRecordItems"); + }); + + modelBuilder.Entity("HRIS.Api.Models.PerformanceEvaluation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime(6)"); + + b.Property("EmployeeId") + .HasColumnType("char(36)"); + + b.Property("Rating") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Remarks") + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("ReviewPeriod") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ReviewerUserId") + .HasColumnType("bigint"); + + b.Property("Score") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("ReviewPeriod"); + + b.HasIndex("ReviewerUserId"); + + b.HasIndex("EmployeeId", "ReviewPeriod") + .IsUnique(); + + b.ToTable("PerformanceEvaluations", null, t => + { + t.HasCheckConstraint("CK_PerformanceEvaluations_ScoreRange", "`Score` >= 0 AND `Score` <= 5"); + }); + }); + modelBuilder.Entity("HRIS.Api.Models.Permission", b => { b.Property("Id") @@ -831,6 +1609,55 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) }); }); + modelBuilder.Entity("HRIS.Api.Models.PhilHealthContributionRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ContributionRate") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("EffectiveFrom") + .HasColumnType("date"); + + b.Property("EffectiveTo") + .HasColumnType("date"); + + b.Property("EmployeeSharePercent") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("EmployerSharePercent") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("MaximumContribution") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("MinimumContribution") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("EffectiveFrom", "IsActive"); + + b.ToTable("PhilHealthContributionRules", t => + { + t.HasCheckConstraint("CK_PhilHealthContributionRule_AmountRange", "`ContributionRate` >= 0 AND `MinimumContribution` >= 0 AND `MaximumContribution` >= `MinimumContribution` AND `EmployeeSharePercent` >= 0 AND `EmployeeSharePercent` <= 1 AND `EmployerSharePercent` >= 0 AND `EmployerSharePercent` <= 1"); + + t.HasCheckConstraint("CK_PhilHealthContributionRule_EffectiveRange", "`EffectiveTo` IS NULL OR `EffectiveTo` >= `EffectiveFrom`"); + }); + }); + modelBuilder.Entity("HRIS.Api.Models.Role", b => { b.Property("Id") @@ -1055,6 +1882,51 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) }); }); + modelBuilder.Entity("HRIS.Api.Models.SssContributionBracket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("EffectiveFrom") + .HasColumnType("date"); + + b.Property("EffectiveTo") + .HasColumnType("date"); + + b.Property("EmployeeShare") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EmployerShare") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("SalaryFrom") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("SalaryTo") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("EffectiveFrom", "IsActive"); + + b.ToTable("SssContributionBrackets", t => + { + t.HasCheckConstraint("CK_SssContributionBracket_AmountRange", "`SalaryFrom` >= 0 AND `EmployeeShare` >= 0 AND `EmployerShare` >= 0 AND (`SalaryTo` IS NULL OR `SalaryTo` >= `SalaryFrom`)"); + + t.HasCheckConstraint("CK_SssContributionBracket_EffectiveRange", "`EffectiveTo` IS NULL OR `EffectiveTo` >= `EffectiveFrom`"); + }); + }); + modelBuilder.Entity("HRIS.Api.Models.User", b => { b.Property("Id") @@ -1161,6 +2033,154 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) }); }); + modelBuilder.Entity("HRIS.Api.Models.WithholdingTaxBracket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("BaseTax") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("CompensationFrom") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("CompensationTo") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EffectiveFrom") + .HasColumnType("date"); + + b.Property("EffectiveTo") + .HasColumnType("date"); + + b.Property("ExcessOver") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("TaxRate") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.HasKey("Id"); + + b.HasIndex("EffectiveFrom", "IsActive"); + + b.ToTable("WithholdingTaxBrackets", t => + { + t.HasCheckConstraint("CK_WithholdingTaxBracket_AmountRange", "`CompensationFrom` >= 0 AND (`CompensationTo` IS NULL OR `CompensationTo` >= `CompensationFrom`) AND `BaseTax` >= 0 AND `ExcessOver` >= 0 AND `TaxRate` >= 0"); + + t.HasCheckConstraint("CK_WithholdingTaxBracket_EffectiveRange", "`EffectiveTo` IS NULL OR `EffectiveTo` >= `EffectiveFrom`"); + }); + }); + + modelBuilder.Entity("HRIS.Api.Models.Announcement", b => + { + b.HasOne("HRIS.Api.Models.User", "CreatedByUser") + .WithMany() + .HasForeignKey("CreatedByUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("CreatedByUser"); + }); + + modelBuilder.Entity("HRIS.Api.Models.AnnouncementRead", b => + { + b.HasOne("HRIS.Api.Models.Announcement", "Announcement") + .WithMany() + .HasForeignKey("AnnouncementId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Announcement"); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("HRIS.Api.Models.AssetAssignment", b => + { + b.HasOne("HRIS.Api.Models.Asset", "Asset") + .WithMany("Assignments") + .HasForeignKey("AssetId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("HRIS.Api.Models.User", "AssignedByUser") + .WithMany() + .HasForeignKey("AssignedByUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Asset"); + + b.Navigation("AssignedByUser"); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("HRIS.Api.Models.AssetReturn", b => + { + b.HasOne("HRIS.Api.Models.AssetAssignment", "AssetAssignment") + .WithMany("Returns") + .HasForeignKey("AssetAssignmentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("HRIS.Api.Models.User", "ReceivedByUser") + .WithMany() + .HasForeignKey("ReceivedByUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("AssetAssignment"); + + b.Navigation("ReceivedByUser"); + }); + + modelBuilder.Entity("HRIS.Api.Models.AssetReturnRequest", b => + { + b.HasOne("HRIS.Api.Models.AssetAssignment", "AssetAssignment") + .WithMany() + .HasForeignKey("AssetAssignmentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("HRIS.Api.Models.User", "ReviewedByUser") + .WithMany() + .HasForeignKey("ReviewedByUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("AssetAssignment"); + + b.Navigation("Employee"); + + b.Navigation("ReviewedByUser"); + }); + modelBuilder.Entity("HRIS.Api.Models.AttendanceLog", b => { b.HasOne("HRIS.Api.Models.Employee", "Employee") @@ -1182,7 +2202,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasOne("HRIS.Api.Models.User", "SubmittedTo") .WithMany() - .HasForeignKey("SubmittedToId"); + .HasForeignKey("SubmittedToUserId") + .OnDelete(DeleteBehavior.Restrict); b.Navigation("Employee"); @@ -1210,6 +2231,46 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Navigation("User"); }); + modelBuilder.Entity("HRIS.Api.Models.EmployeeClearance", b => + { + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("HRIS.Api.Models.EmployeeClearanceActivity", b => + { + b.HasOne("HRIS.Api.Models.User", "ActorUser") + .WithMany() + .HasForeignKey("ActorUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("HRIS.Api.Models.EmployeeClearance", "EmployeeClearance") + .WithMany("Activities") + .HasForeignKey("EmployeeClearanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ActorUser"); + + b.Navigation("EmployeeClearance"); + }); + + modelBuilder.Entity("HRIS.Api.Models.EmployeeCompensation", b => + { + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + modelBuilder.Entity("HRIS.Api.Models.EmployeeDocument", b => { b.HasOne("HRIS.Api.Models.Employee", "Employee") @@ -1240,6 +2301,60 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Navigation("Shift"); }); + modelBuilder.Entity("HRIS.Api.Models.LeaveBalance", b => + { + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("HRIS.Api.Models.LeaveBalanceTransaction", b => + { + b.HasOne("HRIS.Api.Models.User", "CreatedByUser") + .WithMany() + .HasForeignKey("CreatedByUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("HRIS.Api.Models.LeaveBalance", "LeaveBalance") + .WithMany("Transactions") + .HasForeignKey("LeaveBalanceId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("CreatedByUser"); + + b.Navigation("Employee"); + + b.Navigation("LeaveBalance"); + }); + + modelBuilder.Entity("HRIS.Api.Models.LeaveRequest", b => + { + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("HRIS.Api.Models.User", "ReviewedByUser") + .WithMany() + .HasForeignKey("ReviewedByUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Employee"); + + b.Navigation("ReviewedByUser"); + }); + modelBuilder.Entity("HRIS.Api.Models.OvertimeRequest", b => { b.HasOne("HRIS.Api.Models.Employee", "Employee") @@ -1276,6 +2391,54 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Navigation("OvertimeRequest"); }); + modelBuilder.Entity("HRIS.Api.Models.PayrollRecord", b => + { + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("HRIS.Api.Models.PayrollPeriod", "PayrollPeriod") + .WithMany("PayrollRecords") + .HasForeignKey("PayrollPeriodId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("PayrollPeriod"); + }); + + modelBuilder.Entity("HRIS.Api.Models.PayrollRecordItem", b => + { + b.HasOne("HRIS.Api.Models.PayrollRecord", "PayrollRecord") + .WithMany("Items") + .HasForeignKey("PayrollRecordId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PayrollRecord"); + }); + + modelBuilder.Entity("HRIS.Api.Models.PerformanceEvaluation", b => + { + b.HasOne("HRIS.Api.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("HRIS.Api.Models.User", "ReviewerUser") + .WithMany() + .HasForeignKey("ReviewerUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Employee"); + + b.Navigation("ReviewerUser"); + }); + modelBuilder.Entity("HRIS.Api.Models.Permission", b => { b.HasOne("HRIS.Api.Models.Role", "Role") @@ -1309,6 +2472,16 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Navigation("Role"); }); + modelBuilder.Entity("HRIS.Api.Models.Asset", b => + { + b.Navigation("Assignments"); + }); + + modelBuilder.Entity("HRIS.Api.Models.AssetAssignment", b => + { + b.Navigation("Returns"); + }); + modelBuilder.Entity("HRIS.Api.Models.DailyReport", b => { b.Navigation("Tasks"); @@ -1319,11 +2492,31 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Navigation("Documents"); }); + modelBuilder.Entity("HRIS.Api.Models.EmployeeClearance", b => + { + b.Navigation("Activities"); + }); + + modelBuilder.Entity("HRIS.Api.Models.LeaveBalance", b => + { + b.Navigation("Transactions"); + }); + modelBuilder.Entity("HRIS.Api.Models.OvertimeRequest", b => { b.Navigation("Items"); }); + modelBuilder.Entity("HRIS.Api.Models.PayrollPeriod", b => + { + b.Navigation("PayrollRecords"); + }); + + modelBuilder.Entity("HRIS.Api.Models.PayrollRecord", b => + { + b.Navigation("Items"); + }); + modelBuilder.Entity("HRIS.Api.Models.Role", b => { b.Navigation("Users"); diff --git a/apps/api/HRIS.Api/Migrations/20260601170756_AddDailyReports.cs b/apps/api/HRIS.Api/Migrations/20260717024910_AddDailyAccomplishmentReports.cs similarity index 65% rename from apps/api/HRIS.Api/Migrations/20260601170756_AddDailyReports.cs rename to apps/api/HRIS.Api/Migrations/20260717024910_AddDailyAccomplishmentReports.cs index 38d4360..f45574e 100644 --- a/apps/api/HRIS.Api/Migrations/20260601170756_AddDailyReports.cs +++ b/apps/api/HRIS.Api/Migrations/20260717024910_AddDailyAccomplishmentReports.cs @@ -7,7 +7,7 @@ namespace HRIS.Api.Migrations { /// - public partial class AddDailyReports : Migration + public partial class AddDailyAccomplishmentReports : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) @@ -29,8 +29,7 @@ protected override void Up(MigrationBuilder migrationBuilder) .Annotation("MySql:CharSet", "utf8mb4"), TeamUnit = table.Column(type: "varchar(100)", maxLength: 100, nullable: true) .Annotation("MySql:CharSet", "utf8mb4"), - SubmittedToUserId = table.Column(type: "int", nullable: true), - SubmittedToId = table.Column(type: "bigint", nullable: true), + SubmittedToUserId = table.Column(type: "bigint", nullable: true), TimeIn = table.Column(type: "time(6)", nullable: true), TimeOut = table.Column(type: "time(6)", nullable: true), BreakDurationMinutes = table.Column(type: "int", nullable: false), @@ -41,7 +40,39 @@ protected override void Up(MigrationBuilder migrationBuilder) ConnectivityIssues = table.Column(type: "varchar(500)", maxLength: 500, nullable: true) .Annotation("MySql:CharSet", "utf8mb4"), CollaborationLog = table.Column(type: "varchar(1000)", maxLength: 1000, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") + .Annotation("MySql:CharSet", "utf8mb4"), + KeyAccomplishments = table.Column(type: "varchar(2000)", maxLength: 2000, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + BlockersIssues = table.Column(type: "varchar(1000)", maxLength: 1000, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + RisksEarlyWarnings = table.Column(type: "varchar(1000)", maxLength: 1000, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PlanForTomorrow = table.Column(type: "varchar(1000)", maxLength: 1000, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SupportEscalationNeeded = table.Column(type: "varchar(500)", maxLength: 500, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + CodeCommitted = table.Column(type: "tinyint(1)", nullable: false), + TicketsUpdated = table.Column(type: "tinyint(1)", nullable: false), + PullRequestCreated = table.Column(type: "tinyint(1)", nullable: false), + DocumentationUpdated = table.Column(type: "tinyint(1)", nullable: false), + TestsPassing = table.Column(type: "tinyint(1)", nullable: false), + ReportSubmittedOnTime = table.Column(type: "tinyint(1)", nullable: false), + WorkArrangementTomorrow = table.Column(type: "varchar(50)", maxLength: 50, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpectedTimeIn = table.Column(type: "time(6)", nullable: true), + LeaveAbsenceNotice = table.Column(type: "varchar(500)", maxLength: 500, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SupervisorNotes = table.Column(type: "varchar(2000)", maxLength: 2000, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PerformanceRating = table.Column(type: "varchar(50)", maxLength: 50, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + FollowUpRequired = table.Column(type: "tinyint(1)", nullable: false), + ReviewDate = table.Column(type: "date", nullable: true), + ManagerActionItems = table.Column(type: "varchar(1000)", maxLength: 1000, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ReviewedBy = table.Column(type: "varchar(200)", maxLength: 200, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + DateReviewed = table.Column(type: "date", nullable: true) }, constraints: table => { @@ -53,10 +84,11 @@ protected override void Up(MigrationBuilder migrationBuilder) principalColumn: "Id", onDelete: ReferentialAction.Restrict); table.ForeignKey( - name: "FK_daily_reports_users_SubmittedToId", - column: x => x.SubmittedToId, + name: "FK_daily_reports_users_SubmittedToUserId", + column: x => x.SubmittedToUserId, principalTable: "users", - principalColumn: "Id"); + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); }) .Annotation("MySql:CharSet", "utf8mb4"); @@ -104,9 +136,10 @@ protected override void Up(MigrationBuilder migrationBuilder) .Annotation("MySql:CharSet", "utf8mb4"); migrationBuilder.CreateIndex( - name: "IX_daily_report_tasks_DailyReportId", + name: "IX_daily_report_tasks_DailyReportId_TaskNumber", table: "daily_report_tasks", - column: "DailyReportId"); + columns: new[] { "DailyReportId", "TaskNumber" }, + unique: true); migrationBuilder.CreateIndex( name: "IX_daily_reports_EmployeeId_ReportDate", @@ -115,9 +148,9 @@ protected override void Up(MigrationBuilder migrationBuilder) unique: true); migrationBuilder.CreateIndex( - name: "IX_daily_reports_SubmittedToId", + name: "IX_daily_reports_SubmittedToUserId", table: "daily_reports", - column: "SubmittedToId"); + column: "SubmittedToUserId"); } /// diff --git a/apps/api/HRIS.Api/Migrations/AppDbContextModelSnapshot.cs b/apps/api/HRIS.Api/Migrations/AppDbContextModelSnapshot.cs index d90d268..469ba40 100644 --- a/apps/api/HRIS.Api/Migrations/AppDbContextModelSnapshot.cs +++ b/apps/api/HRIS.Api/Migrations/AppDbContextModelSnapshot.cs @@ -538,12 +538,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("SubmissionTime") .HasColumnType("datetime(6)"); - b.Property("SubmittedToId") + b.Property("SubmittedToUserId") .HasColumnType("bigint"); - b.Property("SubmittedToUserId") - .HasColumnType("int"); - b.Property("SupervisorNotes") .HasMaxLength(2000) .HasColumnType("varchar(2000)"); @@ -579,7 +576,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.HasIndex("SubmittedToId"); + b.HasIndex("SubmittedToUserId"); b.HasIndex("EmployeeId", "ReportDate") .IsUnique(); @@ -657,7 +654,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.HasIndex("DailyReportId"); + b.HasIndex("DailyReportId", "TaskNumber") + .IsUnique(); b.ToTable("daily_report_tasks", (string)null); }); @@ -2201,7 +2199,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("HRIS.Api.Models.User", "SubmittedTo") .WithMany() - .HasForeignKey("SubmittedToId"); + .HasForeignKey("SubmittedToUserId") + .OnDelete(DeleteBehavior.Restrict); b.Navigation("Employee"); diff --git a/apps/api/HRIS.Api/Models/DailyReport.cs b/apps/api/HRIS.Api/Models/DailyReport.cs index d7f0a7d..a6b726a 100644 --- a/apps/api/HRIS.Api/Models/DailyReport.cs +++ b/apps/api/HRIS.Api/Models/DailyReport.cs @@ -13,7 +13,7 @@ public class DailyReport public string Project { get; set; } = string.Empty; public string? SprintIteration { get; set; } public string? TeamUnit { get; set; } - public int? SubmittedToUserId { get; set; } + public long? SubmittedToUserId { get; set; } public User? SubmittedTo { get; set; } public TimeOnly? TimeIn { get; set; } public TimeOnly? TimeOut { get; set; } @@ -59,4 +59,4 @@ public class DailyReport // Navigation public ICollection Tasks { get; set; } = new List(); -} \ No newline at end of file +}