Files
PowderCoatingLogix/src/PowderCoating.Core/Entities/ApplicationUser.cs
T
spouliot 6c2fe6e1c4 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>
2026-05-26 19:53:13 -04:00

109 lines
4.3 KiB
C#

using Microsoft.AspNetCore.Identity;
using PowderCoating.Core.Enums;
using System.Linq;
namespace PowderCoating.Core.Entities;
public class ApplicationUser : IdentityUser
{
// Multi-tenancy
public int CompanyId { get; set; }
public virtual Company? Company { get; set; } // Nullable to avoid EF query filter issues
public string? CompanyRole { get; set; } // CompanyAdmin, Manager, Worker, Viewer
// Basic Information
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public string? EmployeeNumber { get; set; }
public DateTime HireDate { get; set; }
public DateTime? TerminationDate { get; set; }
public string? Address { get; set; }
public string? City { get; set; }
public string? State { get; set; }
public string? ZipCode { get; set; }
public string? Department { get; set; }
public string? Position { get; set; }
// User Preferences
public string? Theme { get; set; } = "light";
public string? DateFormat { get; set; } = "MM/dd/yyyy";
public string? TimeZone { get; set; } = "America/New_York";
public int? DashboardLayout { get; set; }
// Settings
public bool IsActive { get; set; } = true;
public bool CanViewShopFloor { get; set; } = true;
public bool CanManageJobs { get; set; } = false;
public bool CanManageInventory { get; set; } = false;
public bool CanManageCustomers { get; set; } = false;
public bool CanCreateQuotes { get; set; } = false;
public bool CanApproveQuotes { get; set; } = false;
public bool CanManageCalendar { get; set; } = false;
public bool CanViewCalendar { get; set; } = true;
public bool CanManageProducts { get; set; } = false;
public bool CanViewProducts { get; set; } = true;
public bool CanManageEquipment { get; set; } = false;
public bool CanManageVendors { get; set; } = false;
public bool CanManageMaintenance { get; set; } = false;
public bool CanManageInvoices { get; set; } = false;
public bool CanViewReports { get; set; } = false;
public bool CanManageBills { get; set; } = false;
public bool CanManageAccounting { get; set; } = false;
// Profile Photo (filesystem storage)
public string? ProfilePictureFilePath { get; set; } // Relative path from ContentRoot/media/ (e.g., "123/profile-photos/user-abc.jpg")
public string? SidebarColor { get; set; } = "ocean";
public string? Notes { get; set; }
/// <summary>
/// Per-worker labor cost per hour used for job costing profit/margin calculations.
/// Overrides the company-level LaborCostPerHour when set.
/// Leave null to use the company default.
/// </summary>
public decimal? LaborCostPerHour { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime? UpdatedAt { get; set; }
public DateTime? LastLoginDate { get; set; }
// 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; }
public string? BanReason { get; set; }
public string? BannedByUserId { get; set; }
// Relationships
public virtual ICollection<Quote> PreparedQuotes { get; set; } = new List<Quote>();
public virtual ICollection<MaintenanceRecord> PerformedMaintenances { get; set; } = new List<MaintenanceRecord>();
// Full name helper
public string FullName => $"{FirstName} {LastName}";
/// <summary>
/// Sets every <c>Can*</c> boolean permission property to <c>true</c>.
/// Use this when creating a first-time company admin so that any permission added
/// to this class in the future is automatically granted without needing to update
/// every account-creation path individually.
/// </summary>
public void GrantAllPermissions()
{
foreach (var prop in typeof(ApplicationUser).GetProperties()
.Where(p => p.Name.StartsWith("Can") &&
p.PropertyType == typeof(bool) &&
p.CanWrite))
{
prop.SetValue(this, true);
}
}
}