Remove ShopWorker entity and migrate worker identity to ApplicationUser
Removes the ShopWorker and ShopWorkerRoleCost entities, all related DTOs, mappings, controllers, views, and import/export paths. Worker identity is now handled entirely through ApplicationUser with per-user LaborCostPerHour. ShopWorkerRoleCosts table remains in production pending manual data migration. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -756,7 +756,6 @@ public class CompanySettingsController : Controller
|
||||
var costs = company.OperatingCosts;
|
||||
|
||||
var ovens = (await _unitOfWork.OvenCosts.FindAsync(o => o.IsActive)).OrderBy(o => o.DisplayOrder).ToList();
|
||||
var workers = (await _unitOfWork.ShopWorkers.FindAsync(w => w.IsActive)).ToList();
|
||||
var coatingCategories = (await _unitOfWork.InventoryCategoryLookups.FindAsync(c => c.IsCoating)).ToList();
|
||||
|
||||
var sb = new System.Text.StringBuilder();
|
||||
@@ -783,8 +782,7 @@ public class CompanySettingsController : Controller
|
||||
ShopCapabilityTier.Large => "high-volume",
|
||||
_ => "small"
|
||||
};
|
||||
sb.AppendLine($"We are a {tierLabel} operation" +
|
||||
(workers.Count > 0 ? $" with {workers.Count} active shop worker{(workers.Count == 1 ? "" : "s")}." : "."));
|
||||
sb.AppendLine($"We are a {tierLabel} operation.");
|
||||
}
|
||||
|
||||
// Ovens
|
||||
@@ -827,32 +825,6 @@ public class CompanySettingsController : Controller
|
||||
sb.AppendLine($"Powder categories we stock: {string.Join(", ", catNames)}.");
|
||||
}
|
||||
|
||||
// Worker roles
|
||||
if (workers.Any())
|
||||
{
|
||||
var roles = workers
|
||||
.Select(w => w.Role)
|
||||
.Distinct()
|
||||
.Select(r => r switch
|
||||
{
|
||||
ShopWorkerRole.Sandblaster => "sandblasting",
|
||||
ShopWorkerRole.Coater => "powder coating",
|
||||
ShopWorkerRole.Masker => "masking",
|
||||
ShopWorkerRole.QualityControl => "quality control",
|
||||
ShopWorkerRole.OvenOperator => "oven operation",
|
||||
ShopWorkerRole.Supervisor => "supervision",
|
||||
ShopWorkerRole.Maintenance => "equipment maintenance",
|
||||
_ => "general labor"
|
||||
})
|
||||
.Distinct()
|
||||
.ToList();
|
||||
if (roles.Count > 1)
|
||||
{
|
||||
sb.AppendLine();
|
||||
sb.AppendLine($"Staff specialties on hand: {string.Join(", ", roles)}.");
|
||||
}
|
||||
}
|
||||
|
||||
// Rates hint
|
||||
if (costs != null && costs.StandardLaborRate > 0)
|
||||
{
|
||||
@@ -2719,79 +2691,6 @@ public class CompanySettingsController : Controller
|
||||
|
||||
// ── Role-Based Labor Rates ────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Returns the per-role hourly labor rates configured for the current company, keyed by
|
||||
/// <see cref="PowderCoating.Core.Enums.ShopWorkerRole"/> integer value. An empty list is returned
|
||||
/// (rather than a 404) when no rates have been configured yet, so the UI can render the rate grid
|
||||
/// without special-casing an empty state. The global multi-tenant filter on
|
||||
/// <c>ShopWorkerRoleCosts</c> ensures only this company's rates are returned.
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetRoleCosts()
|
||||
{
|
||||
var companyId = _tenantContext.GetCurrentCompanyId();
|
||||
if (companyId == null) return Json(new List<object>());
|
||||
|
||||
var rates = await _unitOfWork.ShopWorkerRoleCosts.FindAsync(r => r.CompanyId == companyId.Value);
|
||||
var result = rates.Select(r => new { role = (int)r.Role, hourlyRate = r.HourlyRate }).ToList();
|
||||
return Json(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Upserts the per-role hourly labor rates for the current company. The operation handles three cases
|
||||
/// per rate in a single pass: (1) rate cleared (≤ 0) — soft-delete the existing record; (2) rate set
|
||||
/// but no existing record — insert new; (3) rate changed — update existing. This avoids full
|
||||
/// table replace semantics that could cause audit log noise or trigger unintended EF change-tracking.
|
||||
/// These rates are used by the pricing calculator when <c>UseRoleBasedLaborRates</c> is enabled in
|
||||
/// <c>CompanyOperatingCosts</c>.
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> SaveRoleCosts([FromBody] List<SaveRoleCostDto> rates)
|
||||
{
|
||||
try
|
||||
{
|
||||
var companyId = _tenantContext.GetCurrentCompanyId();
|
||||
if (companyId == null) return Json(new { success = false, message = "No company found." });
|
||||
|
||||
var existing = (await _unitOfWork.ShopWorkerRoleCosts.FindAsync(r => r.CompanyId == companyId.Value)).ToList();
|
||||
|
||||
foreach (var dto in rates)
|
||||
{
|
||||
var record = existing.FirstOrDefault(r => (int)r.Role == dto.Role);
|
||||
if (dto.HourlyRate <= 0)
|
||||
{
|
||||
// Remove rate if cleared
|
||||
if (record != null)
|
||||
await _unitOfWork.ShopWorkerRoleCosts.SoftDeleteAsync(record.Id);
|
||||
}
|
||||
else if (record == null)
|
||||
{
|
||||
await _unitOfWork.ShopWorkerRoleCosts.AddAsync(new PowderCoating.Core.Entities.ShopWorkerRoleCost
|
||||
{
|
||||
CompanyId = companyId.Value,
|
||||
Role = (PowderCoating.Core.Enums.ShopWorkerRole)dto.Role,
|
||||
HourlyRate = dto.HourlyRate,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
record.HourlyRate = dto.HourlyRate;
|
||||
record.UpdatedAt = DateTime.UtcNow;
|
||||
await _unitOfWork.ShopWorkerRoleCosts.UpdateAsync(record);
|
||||
}
|
||||
}
|
||||
|
||||
await _unitOfWork.CompleteAsync();
|
||||
return Json(new { success = true });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error saving role costs");
|
||||
return Json(new { success = false, message = "An error occurred saving role rates." });
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Stripe Connect ───────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
@@ -3055,7 +2954,6 @@ public class CompanySettingsController : Controller
|
||||
}
|
||||
|
||||
public record SaveTemplateJsonRequest(int Id, string? Subject, string? Body);
|
||||
public record SaveRoleCostDto(int Role, decimal HourlyRate);
|
||||
public record SaveOnlinePaymentSettingsDto(
|
||||
OnlinePaymentSurchargeType SurchargeType,
|
||||
decimal SurchargeValue,
|
||||
|
||||
Reference in New Issue
Block a user