Finish FindAsync tenant sweep: CompanySettings, dashboards, lookups

Completes the multi-tenant defense-in-depth FindAsync/Count/Any pass.

- CompanySettings (~21): in-use Count checks, dup-code Any checks, and the
  Quote-status "single approved/converted" Any checks now filter by
  CompanyId. (Were scoped for normal users via the global filter; this
  hardens them against raw platform-admin sessions.)
- ReportsController.Analytics: powder-usage transactions scoped.
- Dashboard pill counts (Invoices, Jobs) + onboarding status-history check.
- JobsPriority / Jobs ShopDisplay+ShopMobile: today's priorities,
  maintenance, and status-lookup queries scoped.
- OvenScheduler: scheduled-coat set and CompanyOperatingCosts lookup
  (c => true) scoped — the latter could return another tenant's defaults
  under a raw platform-admin session.

Confirmed safe / left as-is: parent-FK child queries, by-PK fetches,
platform tables (PowderCatalog, SubscriptionPlanConfig), SuperAdmin-only
controllers (AuditLog/UserActivity/StripeEvents/SubscriptionManagement),
already-filtered IQueryables, and intentional IgnoreQueryFilters number
generators. Build clean; 293 unit tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-20 17:30:21 -04:00
parent c0d3a30176
commit f804906481
7 changed files with 53 additions and 42 deletions
@@ -990,7 +990,7 @@ public class CompanySettingsController : Controller
// Add job counts // Add job counts
foreach (var dto in dtos) foreach (var dto in dtos)
{ {
dto.JobCount = await _unitOfWork.Jobs.CountAsync(j => j.JobStatusId == dto.Id); dto.JobCount = await _unitOfWork.Jobs.CountAsync(j => j.CompanyId == companyId && j.JobStatusId == dto.Id);
} }
return Json(dtos); return Json(dtos);
@@ -1023,7 +1023,7 @@ public class CompanySettingsController : Controller
// Check if status code already exists for this company // Check if status code already exists for this company
var exists = await _unitOfWork.JobStatusLookups var exists = await _unitOfWork.JobStatusLookups
.AnyAsync(s => s.StatusCode == dto.StatusCode); .AnyAsync(s => s.CompanyId == companyId.Value && s.StatusCode == dto.StatusCode);
if (exists) if (exists)
return Json(new { success = false, message = "Status code already exists" }); return Json(new { success = false, message = "Status code already exists" });
@@ -1100,7 +1100,7 @@ public class CompanySettingsController : Controller
return Json(new { success = false, message = "Cannot delete system-defined status" }); return Json(new { success = false, message = "Cannot delete system-defined status" });
// Check if status is in use // Check if status is in use
var inUse = await _unitOfWork.Jobs.AnyAsync(j => j.JobStatusId == id); var inUse = await _unitOfWork.Jobs.AnyAsync(j => j.CompanyId == status.CompanyId && j.JobStatusId == id);
if (inUse) if (inUse)
return Json(new { success = false, message = "Status is in use and cannot be deleted" }); return Json(new { success = false, message = "Status is in use and cannot be deleted" });
@@ -1184,7 +1184,7 @@ public class CompanySettingsController : Controller
// Add job counts // Add job counts
foreach (var dto in dtos) foreach (var dto in dtos)
{ {
dto.JobCount = await _unitOfWork.Jobs.CountAsync(j => j.JobPriorityId == dto.Id); dto.JobCount = await _unitOfWork.Jobs.CountAsync(j => j.CompanyId == companyId && j.JobPriorityId == dto.Id);
} }
return Json(dtos); return Json(dtos);
@@ -1216,7 +1216,7 @@ public class CompanySettingsController : Controller
// Check if priority code already exists for this company // Check if priority code already exists for this company
var exists = await _unitOfWork.JobPriorityLookups var exists = await _unitOfWork.JobPriorityLookups
.AnyAsync(p => p.PriorityCode == dto.PriorityCode); .AnyAsync(p => p.CompanyId == companyId.Value && p.PriorityCode == dto.PriorityCode);
if (exists) if (exists)
return Json(new { success = false, message = "Priority code already exists" }); return Json(new { success = false, message = "Priority code already exists" });
@@ -1290,7 +1290,7 @@ public class CompanySettingsController : Controller
return Json(new { success = false, message = "Cannot delete system-defined priority" }); return Json(new { success = false, message = "Cannot delete system-defined priority" });
// Check if priority is in use // Check if priority is in use
var inUse = await _unitOfWork.Jobs.AnyAsync(j => j.JobPriorityId == id); var inUse = await _unitOfWork.Jobs.AnyAsync(j => j.CompanyId == priority.CompanyId && j.JobPriorityId == id);
if (inUse) if (inUse)
return Json(new { success = false, message = "Priority is in use and cannot be deleted" }); return Json(new { success = false, message = "Priority is in use and cannot be deleted" });
@@ -1370,7 +1370,7 @@ public class CompanySettingsController : Controller
// Add quote counts // Add quote counts
foreach (var dto in dtos) foreach (var dto in dtos)
{ {
dto.QuoteCount = await _unitOfWork.Quotes.CountAsync(q => q.QuoteStatusId == dto.Id); dto.QuoteCount = await _unitOfWork.Quotes.CountAsync(q => q.CompanyId == companyId && q.QuoteStatusId == dto.Id);
} }
return Json(dtos); return Json(dtos);
@@ -1403,7 +1403,7 @@ public class CompanySettingsController : Controller
// Check if status code already exists for this company // Check if status code already exists for this company
var exists = await _unitOfWork.QuoteStatusLookups var exists = await _unitOfWork.QuoteStatusLookups
.AnyAsync(s => s.StatusCode == dto.StatusCode); .AnyAsync(s => s.CompanyId == companyId.Value && s.StatusCode == dto.StatusCode);
if (exists) if (exists)
return Json(new { success = false, message = "Status code already exists" }); return Json(new { success = false, message = "Status code already exists" });
@@ -1411,7 +1411,7 @@ public class CompanySettingsController : Controller
if (dto.IsApprovedStatus) if (dto.IsApprovedStatus)
{ {
var hasApproved = await _unitOfWork.QuoteStatusLookups var hasApproved = await _unitOfWork.QuoteStatusLookups
.AnyAsync(s => s.IsApprovedStatus); .AnyAsync(s => s.CompanyId == companyId.Value && s.IsApprovedStatus);
if (hasApproved) if (hasApproved)
return Json(new { success = false, message = "Only one status can be marked as 'Approved'" }); return Json(new { success = false, message = "Only one status can be marked as 'Approved'" });
} }
@@ -1419,7 +1419,7 @@ public class CompanySettingsController : Controller
if (dto.IsConvertedStatus) if (dto.IsConvertedStatus)
{ {
var hasConverted = await _unitOfWork.QuoteStatusLookups var hasConverted = await _unitOfWork.QuoteStatusLookups
.AnyAsync(s => s.IsConvertedStatus); .AnyAsync(s => s.CompanyId == companyId.Value && s.IsConvertedStatus);
if (hasConverted) if (hasConverted)
return Json(new { success = false, message = "Only one status can be marked as 'Converted'" }); return Json(new { success = false, message = "Only one status can be marked as 'Converted'" });
} }
@@ -1466,7 +1466,7 @@ public class CompanySettingsController : Controller
if (dto.IsApprovedStatus && !status.IsApprovedStatus) if (dto.IsApprovedStatus && !status.IsApprovedStatus)
{ {
var hasApproved = await _unitOfWork.QuoteStatusLookups var hasApproved = await _unitOfWork.QuoteStatusLookups
.AnyAsync(s => s.Id != dto.Id && s.IsApprovedStatus); .AnyAsync(s => s.CompanyId == status.CompanyId && s.Id != dto.Id && s.IsApprovedStatus);
if (hasApproved) if (hasApproved)
return Json(new { success = false, message = "Only one status can be marked as 'Approved'" }); return Json(new { success = false, message = "Only one status can be marked as 'Approved'" });
} }
@@ -1474,7 +1474,7 @@ public class CompanySettingsController : Controller
if (dto.IsConvertedStatus && !status.IsConvertedStatus) if (dto.IsConvertedStatus && !status.IsConvertedStatus)
{ {
var hasConverted = await _unitOfWork.QuoteStatusLookups var hasConverted = await _unitOfWork.QuoteStatusLookups
.AnyAsync(s => s.Id != dto.Id && s.IsConvertedStatus); .AnyAsync(s => s.CompanyId == status.CompanyId && s.Id != dto.Id && s.IsConvertedStatus);
if (hasConverted) if (hasConverted)
return Json(new { success = false, message = "Only one status can be marked as 'Converted'" }); return Json(new { success = false, message = "Only one status can be marked as 'Converted'" });
} }
@@ -1512,7 +1512,7 @@ public class CompanySettingsController : Controller
return Json(new { success = false, message = "Cannot delete system-defined status" }); return Json(new { success = false, message = "Cannot delete system-defined status" });
// Check if status is in use // Check if status is in use
var inUse = await _unitOfWork.Quotes.AnyAsync(q => q.QuoteStatusId == id); var inUse = await _unitOfWork.Quotes.AnyAsync(q => q.CompanyId == status.CompanyId && q.QuoteStatusId == id);
if (inUse) if (inUse)
return Json(new { success = false, message = "Status is in use and cannot be deleted" }); return Json(new { success = false, message = "Status is in use and cannot be deleted" });
@@ -1909,7 +1909,7 @@ public class CompanySettingsController : Controller
// Add appointment counts // Add appointment counts
foreach (var dto in dtos) foreach (var dto in dtos)
{ {
dto.AppointmentCount = await _unitOfWork.Appointments.CountAsync(a => a.AppointmentTypeId == dto.Id); dto.AppointmentCount = await _unitOfWork.Appointments.CountAsync(a => a.CompanyId == companyId && a.AppointmentTypeId == dto.Id);
} }
return Json(dtos); return Json(dtos);
@@ -1941,7 +1941,7 @@ public class CompanySettingsController : Controller
// Check if type code already exists for this company // Check if type code already exists for this company
var exists = await _unitOfWork.AppointmentTypeLookups var exists = await _unitOfWork.AppointmentTypeLookups
.AnyAsync(t => t.TypeCode == dto.TypeCode); .AnyAsync(t => t.CompanyId == companyId.Value && t.TypeCode == dto.TypeCode);
if (exists) if (exists)
return Json(new { success = false, message = "Type code already exists" }); return Json(new { success = false, message = "Type code already exists" });
@@ -2015,7 +2015,7 @@ public class CompanySettingsController : Controller
return Json(new { success = false, message = "Cannot delete system-defined type" }); return Json(new { success = false, message = "Cannot delete system-defined type" });
// Check if type is in use // Check if type is in use
var inUse = await _unitOfWork.Appointments.AnyAsync(a => a.AppointmentTypeId == id); var inUse = await _unitOfWork.Appointments.AnyAsync(a => a.CompanyId == type.CompanyId && a.AppointmentTypeId == id);
if (inUse) if (inUse)
return Json(new { success = false, message = "Type is in use and cannot be deleted" }); return Json(new { success = false, message = "Type is in use and cannot be deleted" });
@@ -2095,7 +2095,7 @@ public class CompanySettingsController : Controller
// Add item counts // Add item counts
foreach (var dto in dtos) foreach (var dto in dtos)
{ {
dto.ItemCount = await _unitOfWork.InventoryItems.CountAsync(i => i.InventoryCategoryId == dto.Id); dto.ItemCount = await _unitOfWork.InventoryItems.CountAsync(i => i.CompanyId == companyId && i.InventoryCategoryId == dto.Id);
} }
return Json(dtos); return Json(dtos);
@@ -2127,7 +2127,7 @@ public class CompanySettingsController : Controller
// Check if category code already exists for this company // Check if category code already exists for this company
var exists = await _unitOfWork.InventoryCategoryLookups var exists = await _unitOfWork.InventoryCategoryLookups
.AnyAsync(c => c.CategoryCode == dto.CategoryCode); .AnyAsync(c => c.CompanyId == companyId.Value && c.CategoryCode == dto.CategoryCode);
if (exists) if (exists)
return Json(new { success = false, message = "Category code already exists" }); return Json(new { success = false, message = "Category code already exists" });
@@ -2193,7 +2193,7 @@ public class CompanySettingsController : Controller
return Json(new { success = false, message = "Category not found" }); return Json(new { success = false, message = "Category not found" });
// Check if category is in use // Check if category is in use
var inUse = await _unitOfWork.InventoryItems.AnyAsync(i => i.InventoryCategoryId == id); var inUse = await _unitOfWork.InventoryItems.AnyAsync(i => i.CompanyId == category.CompanyId && i.InventoryCategoryId == id);
if (inUse) if (inUse)
return Json(new { success = false, message = "Category is in use and cannot be deleted" }); return Json(new { success = false, message = "Category is in use and cannot be deleted" });
@@ -2404,7 +2404,7 @@ public class CompanySettingsController : Controller
return Json(new { success = false, message = "Oven not found." }); return Json(new { success = false, message = "Oven not found." });
// Check if any quotes reference this oven // Check if any quotes reference this oven
var usageCount = await _unitOfWork.Quotes.CountAsync(q => q.OvenCostId == id); var usageCount = await _unitOfWork.Quotes.CountAsync(q => q.CompanyId == companyId.Value && q.OvenCostId == id);
if (usageCount > 0) if (usageCount > 0)
return Json(new { success = false, message = $"Cannot delete: {usageCount} quote(s) reference this oven. Deactivate it instead." }); return Json(new { success = false, message = $"Cannot delete: {usageCount} quote(s) reference this oven. Deactivate it instead." });
@@ -499,7 +499,7 @@ public class DashboardController : Controller
return null; return null;
// These share the same scoped DbContext so must run sequentially // These share the same scoped DbContext so must run sequentially
var hasStatusHistory = await _unitOfWork.JobStatusHistory.AnyAsync(_ => true); var hasStatusHistory = await _unitOfWork.JobStatusHistory.AnyAsync(h => h.CompanyId == companyId);
// ignoreQueryFilters so soft-deleted lookups (UpdatedAt set on delete) are also visible // ignoreQueryFilters so soft-deleted lookups (UpdatedAt set on delete) are also visible
var hasCustomizedLookups = await _unitOfWork.JobStatusLookups.AnyAsync( var hasCustomizedLookups = await _unitOfWork.JobStatusLookups.AnyAsync(
j => j.CompanyId == companyId && j.UpdatedAt != null, j => j.CompanyId == companyId && j.UpdatedAt != null,
@@ -241,11 +241,12 @@ public class InvoicesController : Controller
ViewBag.SortDirection = gridRequest.SortDirection; ViewBag.SortDirection = gridRequest.SortDirection;
// Pill badge counts — always global (not scoped to current filter/page) // Pill badge counts — always global (not scoped to current filter/page)
ViewBag.UnpaidCount = await _unitOfWork.Invoices.CountAsync(i => var companyId = _tenantContext.GetCurrentCompanyId() ?? 0;
i.Status == InvoiceStatus.Draft || i.Status == InvoiceStatus.Sent || i.Status == InvoiceStatus.Overdue); ViewBag.UnpaidCount = await _unitOfWork.Invoices.CountAsync(i => i.CompanyId == companyId &&
ViewBag.PartialCount = await _unitOfWork.Invoices.CountAsync(i => i.Status == InvoiceStatus.PartiallyPaid); (i.Status == InvoiceStatus.Draft || i.Status == InvoiceStatus.Sent || i.Status == InvoiceStatus.Overdue));
ViewBag.PaidCount = await _unitOfWork.Invoices.CountAsync(i => i.Status == InvoiceStatus.Paid); ViewBag.PartialCount = await _unitOfWork.Invoices.CountAsync(i => i.CompanyId == companyId && i.Status == InvoiceStatus.PartiallyPaid);
ViewBag.AllCount = await _unitOfWork.Invoices.CountAsync(); ViewBag.PaidCount = await _unitOfWork.Invoices.CountAsync(i => i.CompanyId == companyId && i.Status == InvoiceStatus.Paid);
ViewBag.AllCount = await _unitOfWork.Invoices.CountAsync(i => i.CompanyId == companyId);
return View(pagedResult); return View(pagedResult);
} }
@@ -213,24 +213,29 @@ public class JobsController : Controller
// Pill badge counts — always global (not scoped to current filter/page) // Pill badge counts — always global (not scoped to current filter/page)
var today = DateTime.Today; var today = DateTime.Today;
ViewBag.AllJobCount = await _unitOfWork.Jobs.CountAsync(); var companyId = _tenantContext.GetCurrentCompanyId() ?? 0;
ViewBag.AllJobCount = await _unitOfWork.Jobs.CountAsync(j => j.CompanyId == companyId);
ViewBag.ActiveCount = await _unitOfWork.Jobs.CountAsync(j => ViewBag.ActiveCount = await _unitOfWork.Jobs.CountAsync(j =>
j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Completed j.CompanyId == companyId
&& j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Completed
&& j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.ReadyForPickup && j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.ReadyForPickup
&& j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Delivered && j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Delivered
&& j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Cancelled); && j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Cancelled);
ViewBag.OverdueCount = await _unitOfWork.Jobs.CountAsync(j => ViewBag.OverdueCount = await _unitOfWork.Jobs.CountAsync(j =>
j.DueDate < today j.CompanyId == companyId
&& j.DueDate < today
&& j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Completed && j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Completed
&& j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.ReadyForPickup && j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.ReadyForPickup
&& j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Delivered && j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Delivered
&& j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Cancelled); && j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Cancelled);
ViewBag.CompletedCount = await _unitOfWork.Jobs.CountAsync(j => ViewBag.CompletedCount = await _unitOfWork.Jobs.CountAsync(j =>
j.JobStatus.StatusCode == AppConstants.StatusCodes.Job.Completed j.CompanyId == companyId &&
(j.JobStatus.StatusCode == AppConstants.StatusCodes.Job.Completed
|| j.JobStatus.StatusCode == AppConstants.StatusCodes.Job.ReadyForPickup || j.JobStatus.StatusCode == AppConstants.StatusCodes.Job.ReadyForPickup
|| j.JobStatus.StatusCode == AppConstants.StatusCodes.Job.Delivered); || j.JobStatus.StatusCode == AppConstants.StatusCodes.Job.Delivered));
ViewBag.ReadyCount = await _unitOfWork.Jobs.CountAsync(j => ViewBag.ReadyCount = await _unitOfWork.Jobs.CountAsync(j =>
j.JobStatus.StatusCode == AppConstants.StatusCodes.Job.ReadyForPickup); j.CompanyId == companyId
&& j.JobStatus.StatusCode == AppConstants.StatusCodes.Job.ReadyForPickup);
// Set ViewBag for sorting // Set ViewBag for sorting
ViewBag.SearchTerm = searchTerm; ViewBag.SearchTerm = searchTerm;
@@ -2171,10 +2176,12 @@ public class JobsController : Controller
try try
{ {
var today = date?.Date ?? DateTime.Today; var today = date?.Date ?? DateTime.Today;
var companyId = _tenantContext.GetCurrentCompanyId() ?? 0;
// Load all non-terminal statuses for the progress strip (excluding nav/hold/cancel) // Load all non-terminal statuses for the progress strip (excluding nav/hold/cancel)
var allStatusesEnum = await _unitOfWork.JobStatusLookups.FindAsync(s => var allStatusesEnum = await _unitOfWork.JobStatusLookups.FindAsync(s =>
s.StatusCode != AppConstants.StatusCodes.Job.OnHold && s.StatusCode != AppConstants.StatusCodes.Job.Cancelled s.CompanyId == companyId
&& s.StatusCode != AppConstants.StatusCodes.Job.OnHold && s.StatusCode != AppConstants.StatusCodes.Job.Cancelled
&& s.StatusCode != AppConstants.StatusCodes.Job.Delivered && s.StatusCode != AppConstants.StatusCodes.Job.Quoted && s.StatusCode != AppConstants.StatusCodes.Job.Delivered && s.StatusCode != AppConstants.StatusCodes.Job.Quoted
&& s.StatusCode != AppConstants.StatusCodes.Job.Pending && s.StatusCode != AppConstants.StatusCodes.Job.Approved); && s.StatusCode != AppConstants.StatusCodes.Job.Pending && s.StatusCode != AppConstants.StatusCodes.Job.Approved);
var allStatuses = allStatusesEnum.OrderBy(s => s.DisplayOrder).ToList(); var allStatuses = allStatusesEnum.OrderBy(s => s.DisplayOrder).ToList();
@@ -2184,7 +2191,7 @@ public class JobsController : Controller
// Get existing priority records for today // Get existing priority records for today
var existingPriorities = await _unitOfWork.JobDailyPriorities var existingPriorities = await _unitOfWork.JobDailyPriorities
.FindAsync(p => p.ScheduledDate.Date == today); .FindAsync(p => p.CompanyId == companyId && p.ScheduledDate.Date == today);
var priorityDict = existingPriorities.ToDictionary(p => p.JobId); var priorityDict = existingPriorities.ToDictionary(p => p.JobId);
@@ -2281,7 +2288,8 @@ public class JobsController : Controller
if (!companyId.HasValue) return RedirectToAction(nameof(Index)); if (!companyId.HasValue) return RedirectToAction(nameof(Index));
var allStatusesEnum = await _unitOfWork.JobStatusLookups.FindAsync(s => var allStatusesEnum = await _unitOfWork.JobStatusLookups.FindAsync(s =>
!s.IsTerminalStatus s.CompanyId == companyId.Value
&& !s.IsTerminalStatus
&& s.StatusCode != AppConstants.StatusCodes.Job.OnHold && s.StatusCode != AppConstants.StatusCodes.Job.Cancelled && s.StatusCode != AppConstants.StatusCodes.Job.OnHold && s.StatusCode != AppConstants.StatusCodes.Job.Cancelled
&& s.StatusCode != AppConstants.StatusCodes.Job.Delivered); && s.StatusCode != AppConstants.StatusCodes.Job.Delivered);
var allStatuses = allStatusesEnum.OrderBy(s => s.DisplayOrder).ToList(); var allStatuses = allStatusesEnum.OrderBy(s => s.DisplayOrder).ToList();
@@ -57,13 +57,14 @@ public class JobsPriorityController : Controller
public async Task<IActionResult> Index(DateTime? date) public async Task<IActionResult> Index(DateTime? date)
{ {
var today = date?.Date ?? DateTime.Today; var today = date?.Date ?? DateTime.Today;
var companyId = _tenantContext.GetCurrentCompanyId() ?? 0;
// Get all jobs scheduled for today with related data // Get all jobs scheduled for today with related data
var jobs = await _unitOfWork.Jobs.GetScheduledJobsForDateAsync(today); var jobs = await _unitOfWork.Jobs.GetScheduledJobsForDateAsync(today);
// Get existing priority records for today // Get existing priority records for today
var existingPriorities = await _unitOfWork.JobDailyPriorities var existingPriorities = await _unitOfWork.JobDailyPriorities
.FindAsync(p => p.ScheduledDate.Date == today); .FindAsync(p => p.CompanyId == companyId && p.ScheduledDate.Date == today);
var priorityDict = existingPriorities.ToDictionary(p => p.JobId); var priorityDict = existingPriorities.ToDictionary(p => p.JobId);
@@ -90,7 +91,6 @@ public class JobsPriorityController : Controller
.ToList(); .ToList();
// Get priorities and workers for modal options // Get priorities and workers for modal options
var companyId = _tenantContext.GetCurrentCompanyId() ?? 0;
var priorities = await _unitOfWork.JobPriorityLookups.FindAsync(p => p.CompanyId == companyId); var priorities = await _unitOfWork.JobPriorityLookups.FindAsync(p => p.CompanyId == companyId);
var workers = await _userManager.Users var workers = await _userManager.Users
.Where(u => u.CompanyId == companyId && u.IsActive && u.CompanyRole != null) .Where(u => u.CompanyId == companyId && u.IsActive && u.CompanyRole != null)
@@ -99,7 +99,7 @@ public class JobsPriorityController : Controller
// Get maintenance records scheduled for today (Scheduled or InProgress) // Get maintenance records scheduled for today (Scheduled or InProgress)
var maintenanceItems = (await _unitOfWork.MaintenanceRecords.FindAsync( var maintenanceItems = (await _unitOfWork.MaintenanceRecords.FindAsync(
m => m.ScheduledDate.Date == today && m => m.CompanyId == companyId && m.ScheduledDate.Date == today &&
(m.Status == MaintenanceStatus.Scheduled || m.Status == MaintenanceStatus.InProgress), (m.Status == MaintenanceStatus.Scheduled || m.Status == MaintenanceStatus.InProgress),
false, false,
m => m.Equipment, m => m.AssignedUser)) m => m.Equipment, m => m.AssignedUser))
@@ -169,10 +169,11 @@ public class JobsPriorityController : Controller
} }
var today = DateTime.Today; var today = DateTime.Today;
var cid = _tenantContext.GetCurrentCompanyId() ?? 0;
// Get all existing priority records for today // Get all existing priority records for today
var existingPriorities = await _unitOfWork.JobDailyPriorities var existingPriorities = await _unitOfWork.JobDailyPriorities
.FindAsync(p => p.ScheduledDate.Date == today); .FindAsync(p => p.CompanyId == cid && p.ScheduledDate.Date == today);
var priorityDict = existingPriorities.ToDictionary(p => p.JobId); var priorityDict = existingPriorities.ToDictionary(p => p.JobId);
@@ -127,14 +127,14 @@ public class OvenSchedulerController : Controller
// Determine which coats are already scheduled — filter out removed/cancelled at database level // Determine which coats are already scheduled — filter out removed/cancelled at database level
var scheduledCoatIds = (await _unitOfWork.OvenBatchItems.FindAsync( var scheduledCoatIds = (await _unitOfWork.OvenBatchItems.FindAsync(
i => i.Status != OvenBatchItemStatus.Removed && i.Batch.Status != OvenBatchStatus.Cancelled, i => i.CompanyId == companyId && i.Status != OvenBatchItemStatus.Removed && i.Batch.Status != OvenBatchStatus.Cancelled,
false, false,
i => i.Batch)) i => i.Batch))
.Select(i => i.JobItemCoatId) .Select(i => i.JobItemCoatId)
.ToHashSet(); .ToHashSet();
// Get company defaults // Get company defaults
var companyCosts = await _unitOfWork.CompanyOperatingCosts.FirstOrDefaultAsync(c => true); var companyCosts = await _unitOfWork.CompanyOperatingCosts.FirstOrDefaultAsync(c => c.CompanyId == companyId);
var defaultCycleMinutes = companyCosts?.DefaultOvenCycleMinutes ?? 45; var defaultCycleMinutes = companyCosts?.DefaultOvenCycleMinutes ?? 45;
// Build the view model // Build the view model
@@ -590,7 +590,8 @@ public class ReportsController : Controller
// === POWDER USAGE ANALYTICS === // === POWDER USAGE ANALYTICS ===
var powderTransactions = (await _unitOfWork.InventoryTransactions var powderTransactions = (await _unitOfWork.InventoryTransactions
.FindAsync(t => t.TransactionType == InventoryTransactionType.JobUsage .FindAsync(t => t.CompanyId == companyId
&& t.TransactionType == InventoryTransactionType.JobUsage
&& t.TransactionDate >= startDate, && t.TransactionDate >= startDate,
false, false,
t => t.InventoryItem)) t => t.InventoryItem))