Demo seed Phase 2: workers, time entries, maintenance records
- 5 named shop workers seeded as ApplicationUser (Employee role): Mike Sanders (Coater), Jake Wilson (Sandblaster), Sarah Brooks (Inspector), Tyler Green (General), Chris Mason (Lead) — @pcldemo.com fingerprint domain - Job time entries seeded for all in-progress and completed jobs; Worker Productivity report will have data from day one - Maintenance history seeded per equipment: 2 completed records + 1 upcoming scheduled + 1 overdue record on Pressure Pot for overdue alert demo - Equipment renamed to spec names: Main Batch Oven, Small Batch Oven, Powder Coating Booth, Blast Cabinet, Pressure Pot Blaster, Air Compressor, Wash Station, Forklift (replaced Overhead Conveyor which wasn't in spec) - RemoveSeedDataOptions.Workers added; Remove.cs cleans up workers + time entries on Demo Reset; SeedDataController resets workers in ResetDemoCompany Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using PowderCoating.Core.Entities;
|
||||
using PowderCoating.Core.Enums;
|
||||
|
||||
namespace PowderCoating.Infrastructure.Services;
|
||||
|
||||
public partial class SeedDataService
|
||||
{
|
||||
/// <summary>
|
||||
/// Seeds maintenance records for all seeded equipment: historical completed records,
|
||||
/// upcoming scheduled records, and one overdue record for the Pressure Pot so the
|
||||
/// Equipment Maintenance report always has meaningful data.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Each piece of equipment gets 2-3 completed historical maintenance records (up to
|
||||
/// 12 months back) plus one upcoming scheduled record. The Pressure Pot additionally
|
||||
/// has one overdue record (past due date, still Scheduled) to populate the overdue
|
||||
/// indicator on the Equipment page.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Labor and parts costs are realistic for shop equipment maintenance, giving the
|
||||
/// Equipment Maintenance Cost report non-trivial totals from day one.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Idempotency: bails early if any maintenance records already exist for this company's equipment.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
private async Task<int> SeedMaintenanceRecordsAsync(Company company)
|
||||
{
|
||||
var equipmentIds = await _context.Set<Equipment>()
|
||||
.IgnoreQueryFilters()
|
||||
.Where(e => e.CompanyId == company.Id && !e.IsDeleted && SeededEquipmentSerials.Contains(e.SerialNumber))
|
||||
.Select(e => e.Id)
|
||||
.ToListAsync();
|
||||
|
||||
if (equipmentIds.Count == 0) return 0;
|
||||
|
||||
var existingCount = await _context.Set<MaintenanceRecord>()
|
||||
.IgnoreQueryFilters()
|
||||
.CountAsync(m => equipmentIds.Contains(m.EquipmentId));
|
||||
if (existingCount > 0) return 0;
|
||||
|
||||
var equipment = await _context.Set<Equipment>()
|
||||
.IgnoreQueryFilters()
|
||||
.Where(e => equipmentIds.Contains(e.Id))
|
||||
.ToListAsync();
|
||||
|
||||
// Try to grab a worker user to assign as performed-by
|
||||
var worker = await _userManager.Users
|
||||
.Where(u => u.CompanyId == company.Id && u.IsActive)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var records = new List<MaintenanceRecord>();
|
||||
|
||||
// Per-equipment maintenance spec: (type, daysAgoFirst, intervalDays, laborCost, partsCost, notes)
|
||||
// Each equipment gets 2 completed records + 1 scheduled upcoming.
|
||||
// The Pressure Pot also gets 1 overdue record.
|
||||
static (string type, decimal labor, decimal parts, string desc, string work)
|
||||
MaintSpec(int i) => (i % 5) switch
|
||||
{
|
||||
0 => ("Preventive", 120m, 45m, "Quarterly preventive maintenance", "Inspected elements, cleaned contacts, checked gaskets"),
|
||||
1 => ("Inspection", 80m, 0m, "Monthly operational inspection", "Checked all systems, calibrated temperature probes"),
|
||||
2 => ("Repair", 200m, 185m, "Filter and seal replacement", "Replaced intake filters and worn door seals"),
|
||||
3 => ("Preventive", 140m, 60m, "Semi-annual preventive maintenance", "Lubricated moving parts, replaced wear items"),
|
||||
_ => ("Inspection", 60m, 0m, "Pre-season inspection and cleaning", "Full operational test, cleaned all surfaces"),
|
||||
};
|
||||
|
||||
int idx = 0;
|
||||
foreach (var eq in equipment)
|
||||
{
|
||||
var isPressurePot = eq.SerialNumber == "CLM101223456"; // Media Blast Room / Pressure Pot
|
||||
|
||||
for (int r = 0; r < 2; r++)
|
||||
{
|
||||
var (mtype, labor, parts, desc, work) = MaintSpec(idx + r);
|
||||
var daysAgo = 180 - r * 60 - (idx % 4) * 15;
|
||||
var scheduled = now.AddDays(-daysAgo);
|
||||
var total = labor + parts;
|
||||
|
||||
records.Add(new MaintenanceRecord
|
||||
{
|
||||
EquipmentId = eq.Id,
|
||||
MaintenanceType = mtype,
|
||||
Status = MaintenanceStatus.Completed,
|
||||
Priority = MaintenancePriority.Normal,
|
||||
ScheduledDate = scheduled,
|
||||
CompletedDate = scheduled.AddDays(1),
|
||||
PerformedById = worker?.Id,
|
||||
AssignedUserId = worker?.Id,
|
||||
Description = desc,
|
||||
WorkPerformed = work,
|
||||
LaborCost = labor,
|
||||
PartsCost = parts,
|
||||
TotalCost = total,
|
||||
DowntimeHours = 2m + r,
|
||||
CompanyId = company.Id,
|
||||
CreatedAt = scheduled.AddDays(-7)
|
||||
});
|
||||
}
|
||||
|
||||
// One upcoming scheduled record
|
||||
var upcomingDays = 15 + (idx % 30);
|
||||
records.Add(new MaintenanceRecord
|
||||
{
|
||||
EquipmentId = eq.Id,
|
||||
MaintenanceType = "Preventive",
|
||||
Status = MaintenanceStatus.Scheduled,
|
||||
Priority = MaintenancePriority.Normal,
|
||||
ScheduledDate = now.AddDays(upcomingDays),
|
||||
AssignedUserId = worker?.Id,
|
||||
Description = "Scheduled preventive maintenance",
|
||||
LaborCost = 0m, PartsCost = 0m, TotalCost = 0m,
|
||||
CompanyId = company.Id,
|
||||
CreatedAt = now.AddDays(-7)
|
||||
});
|
||||
|
||||
// Overdue record for the pressure pot only
|
||||
if (isPressurePot)
|
||||
{
|
||||
records.Add(new MaintenanceRecord
|
||||
{
|
||||
EquipmentId = eq.Id,
|
||||
MaintenanceType = "Repair",
|
||||
Status = MaintenanceStatus.Scheduled,
|
||||
Priority = MaintenancePriority.High,
|
||||
ScheduledDate = now.AddDays(-20), // overdue
|
||||
AssignedUserId = worker?.Id,
|
||||
Description = "Filter replacement — OVERDUE",
|
||||
Notes = "Media filter became clogged ahead of schedule. Shop is running reduced blast capacity until repaired.",
|
||||
LaborCost = 0m, PartsCost = 0m, TotalCost = 0m,
|
||||
CompanyId = company.Id,
|
||||
CreatedAt = now.AddDays(-30)
|
||||
});
|
||||
}
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
await _context.Set<MaintenanceRecord>().AddRangeAsync(records);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return records.Count;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user