Add explicit CompanyId to tenant-scoped FindAsync queries (partial sweep)
Multi-tenant defense-in-depth sweep, FindAsync/FirstOrDefaultAsync vector. Adds explicit CompanyId predicates to list/index/validation queries that previously relied only on the global tenant filter (exposure: raw platform-admin sessions where the filter is bypassed). Done this pass: - Financial: Budgets, CreditMemos, FixedAssets, GiftCertificates, TaxRates, PricingTiers, VendorCredits, Accounts (year-end close), Invoices (tax-rate default, merchandise). - Operational: Inventory (bin/sample-panels/vendors/usage-edit), OvenScheduler (ovens/batches/queue), Customers (pricing tiers), InAppNotifications (mark-all-read), CatalogItems (by-category / merchandise / price-check lists). - AI: AiQuickQuote and Quotes (powder cost, predictions, walk-in customer, benchmark), Reports (budgets, 1099 vendors). Child-by-parent-FK and by-PK queries were left as-is (already scoped via the verified parent). Builds clean; 293 unit tests pass. REMAINING (next session): ReportsController.Analytics powder-usage query (line ~593) and the ~20 CompanySettings delete-protection Count/Any + dup-code checks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3428,7 +3428,7 @@ public class QuotesController : Controller
|
||||
try
|
||||
{
|
||||
var powders = await _unitOfWork.InventoryItems.FindAsync(i =>
|
||||
i.Category != null && i.Category.ToLower().Contains("powder") && i.UnitCost > 0);
|
||||
i.CompanyId == companyId && i.Category != null && i.Category.ToLower().Contains("powder") && i.UnitCost > 0);
|
||||
avgPowderCost = powders.Any() ? powders.Average(p => p.UnitCost) : 8m;
|
||||
}
|
||||
catch
|
||||
@@ -3522,7 +3522,7 @@ public class QuotesController : Controller
|
||||
try
|
||||
{
|
||||
var powders = await _unitOfWork.InventoryItems.FindAsync(i =>
|
||||
i.Category != null && i.Category.ToLower().Contains("powder") && i.UnitCost > 0);
|
||||
i.CompanyId == companyId && i.Category != null && i.Category.ToLower().Contains("powder") && i.UnitCost > 0);
|
||||
avgPowderCost = powders.Any() ? powders.Average(p => p.UnitCost) : 8m;
|
||||
}
|
||||
catch { avgPowderCost = 8m; }
|
||||
@@ -3617,7 +3617,7 @@ public class QuotesController : Controller
|
||||
|
||||
// Pull recent accepted predictions (user didn't override) as few-shot calibration examples
|
||||
var allPredictions = await _unitOfWork.AiItemPredictions.FindAsync(
|
||||
p => !p.UserOverrodeEstimate && p.PredictedSurfaceAreaSqFt > 0 && p.PredictedUnitPrice > 0);
|
||||
p => p.CompanyId == companyId && !p.UserOverrodeEstimate && p.PredictedSurfaceAreaSqFt > 0 && p.PredictedUnitPrice > 0);
|
||||
|
||||
context.AcceptedExamples = allPredictions
|
||||
.OrderByDescending(p => p.CreatedAt)
|
||||
@@ -3660,9 +3660,11 @@ public class QuotesController : Controller
|
||||
{
|
||||
var sqFtMin = sqFt * 0.4m;
|
||||
var sqFtMax = sqFt * 2.5m;
|
||||
var companyId = (await _userManager.GetUserAsync(User))?.CompanyId ?? 0;
|
||||
|
||||
var matches = await _unitOfWork.JobItems.FindAsync(
|
||||
ji => ji.Complexity == complexity
|
||||
ji => ji.CompanyId == companyId
|
||||
&& ji.Complexity == complexity
|
||||
&& ji.SurfaceAreaSqFt >= sqFtMin
|
||||
&& ji.SurfaceAreaSqFt <= sqFtMax
|
||||
&& ji.UnitPrice > 0
|
||||
@@ -3670,7 +3672,7 @@ public class QuotesController : Controller
|
||||
|
||||
var jobIds = matches.Select(ji => ji.JobId).Distinct().ToList();
|
||||
var completedStatusIds = (await _unitOfWork.JobStatusLookups.FindAsync(
|
||||
s => s.StatusCode == AppConstants.StatusCodes.Job.Completed || s.StatusCode == AppConstants.StatusCodes.Job.Delivered))
|
||||
s => s.CompanyId == companyId && (s.StatusCode == AppConstants.StatusCodes.Job.Completed || s.StatusCode == AppConstants.StatusCodes.Job.Delivered)))
|
||||
.Select(s => s.Id).ToHashSet();
|
||||
var completedJobs = await _unitOfWork.Jobs.FindAsync(
|
||||
j => jobIds.Contains(j.Id) && completedStatusIds.Contains(j.JobStatusId));
|
||||
|
||||
Reference in New Issue
Block a user