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:
@@ -73,6 +73,9 @@ public class ApplicationUser : IdentityUser
|
||||
// Passkey enrollment prompt
|
||||
public bool PasskeyPromptDismissed { get; set; } = false;
|
||||
|
||||
/// <summary>BCrypt hash of the employee's 4-digit kiosk PIN. Null means kiosk timeclock is disabled for this user.</summary>
|
||||
public string? KioskPin { get; set; }
|
||||
|
||||
// Ban
|
||||
public bool IsBanned { get; set; } = false;
|
||||
public DateTime? BannedAt { get; set; }
|
||||
|
||||
@@ -133,6 +133,12 @@ public class Company : BaseEntity
|
||||
/// </summary>
|
||||
public string? KioskActivationToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Device activation token for the Timeclock kiosk tablet.
|
||||
/// Null = timeclock kiosk not activated on any device.
|
||||
/// </summary>
|
||||
public string? TimeclockKioskToken { get; set; }
|
||||
|
||||
// Navigation Properties
|
||||
public virtual ICollection<ApplicationUser> Users { get; set; } = new List<ApplicationUser>();
|
||||
public virtual ICollection<Customer> Customers { get; set; } = new List<Customer>();
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace PowderCoating.Core.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Facility-level clock-in/clock-out record for an employee.
|
||||
/// Tracks when an employee arrives and leaves the facility — separate from JobTimeEntry which tracks
|
||||
/// hours against a specific job. Multiple entries per day are fully supported (lunch breaks, etc.).
|
||||
/// The only enforced constraint: a user may not have more than one open entry (ClockOutTime == null) at a time.
|
||||
/// </summary>
|
||||
public class EmployeeClockEntry : BaseEntity
|
||||
{
|
||||
public string UserId { get; set; } = string.Empty;
|
||||
|
||||
public DateTime ClockInTime { get; set; }
|
||||
|
||||
/// <summary>Null means the employee is currently clocked in.</summary>
|
||||
public DateTime? ClockOutTime { get; set; }
|
||||
|
||||
/// <summary>Stored at clock-out time: (ClockOutTime - ClockInTime) in hours, rounded to 2 decimal places.</summary>
|
||||
public decimal? HoursWorked { get; set; }
|
||||
|
||||
public string? Notes { get; set; }
|
||||
|
||||
public virtual ApplicationUser User { get; set; } = null!;
|
||||
}
|
||||
@@ -52,6 +52,9 @@ public class SubscriptionPlanConfig : BaseEntity
|
||||
/// <summary>When true, companies on this plan can send SMS notifications to customers (subject to platform kill-switch and per-company opt-in).</summary>
|
||||
public bool AllowSms { get; set; } = false;
|
||||
|
||||
/// <summary>When true, companies on this plan can create and use Custom Formula Item Templates in quotes and jobs.</summary>
|
||||
public bool AllowCustomFormulas { get; set; } = false;
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
public int SortOrder { get; set; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user