Add Employee Timeclock feature with kiosk, attendance report, and payroll CSV export
- New EmployeeClockEntry entity (facility-level attendance, separate from job time entries) - KioskPin added to ApplicationUser; TimeclockKioskToken added to Company - TimeclockController: clock in/out, who's in, 14-day history, manager edit/delete, tablet kiosk with device-cookie auth, PIN management via Users edit page - Kiosk UI: employee tile grid + 4-digit PIN pad + auto-detect clock-in vs clock-out - Attendance report at /Reports/Attendance with weekly subtotal rows - Payroll CSV export at /Reports/AttendanceCsv (flat, one row per segment) - AllowCustomFormulas wired through PlatformSubscriptionController + subscription views - Fix soft-delete bug on CustomItemTemplate (missing HasQueryFilter in OnModelCreating) - Help article (Help/Timeclock.cshtml) and AI knowledge base updated - Migrations: AddEmployeeTimeclock, AddTimeclockKioskToken Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -378,6 +378,10 @@ public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IDataPro
|
||||
/// <summary>Per-company reusable NCalc pricing formula templates; tenant-filtered with soft delete.</summary>
|
||||
public DbSet<CustomItemTemplate> CustomItemTemplates { get; set; }
|
||||
|
||||
// Employee Timeclock
|
||||
/// <summary>Facility-level clock-in/clock-out entries per employee; tenant-filtered with soft delete. Multiple entries per day are supported (lunch breaks, etc.).</summary>
|
||||
public DbSet<EmployeeClockEntry> EmployeeClockEntries { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Platform-wide audit log capturing who changed what and when, across all tenants.
|
||||
/// No global query filter — SuperAdmin controllers query this directly.
|
||||
@@ -771,6 +775,24 @@ modelBuilder.Entity<ReworkRecord>().HasQueryFilter(e =>
|
||||
.HasForeignKey(k => k.LinkedJobId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
// Custom Formula Templates — tenant-filtered + soft delete
|
||||
modelBuilder.Entity<CustomItemTemplate>().HasQueryFilter(e =>
|
||||
!e.IsDeleted && (IsPlatformAdmin || e.CompanyId == CurrentCompanyId));
|
||||
|
||||
// Employee Timeclock — tenant-filtered + soft delete
|
||||
modelBuilder.Entity<EmployeeClockEntry>().HasQueryFilter(e =>
|
||||
!e.IsDeleted && (IsPlatformAdmin || e.CompanyId == CurrentCompanyId));
|
||||
// FK to ApplicationUser: Restrict delete so removing a user doesn't erase attendance history.
|
||||
// Use DeleteBehavior.Restrict rather than NoAction to surface a cleaner error in EF.
|
||||
modelBuilder.Entity<EmployeeClockEntry>()
|
||||
.HasOne(c => c.User)
|
||||
.WithMany()
|
||||
.HasForeignKey(c => c.UserId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
// Composite index for "who's clocked in today" and date-range attendance reports
|
||||
modelBuilder.Entity<EmployeeClockEntry>()
|
||||
.HasIndex(c => new { c.CompanyId, c.ClockInTime });
|
||||
|
||||
// Account self-referencing hierarchy
|
||||
modelBuilder.Entity<Account>()
|
||||
.HasOne(a => a.ParentAccount)
|
||||
|
||||
Reference in New Issue
Block a user