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:
@@ -990,7 +990,7 @@ public class CompanySettingsController : Controller
|
||||
// Add job counts
|
||||
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);
|
||||
@@ -1023,7 +1023,7 @@ public class CompanySettingsController : Controller
|
||||
|
||||
// Check if status code already exists for this company
|
||||
var exists = await _unitOfWork.JobStatusLookups
|
||||
.AnyAsync(s => s.StatusCode == dto.StatusCode);
|
||||
.AnyAsync(s => s.CompanyId == companyId.Value && s.StatusCode == dto.StatusCode);
|
||||
if (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" });
|
||||
|
||||
// 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)
|
||||
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
|
||||
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);
|
||||
@@ -1216,7 +1216,7 @@ public class CompanySettingsController : Controller
|
||||
|
||||
// Check if priority code already exists for this company
|
||||
var exists = await _unitOfWork.JobPriorityLookups
|
||||
.AnyAsync(p => p.PriorityCode == dto.PriorityCode);
|
||||
.AnyAsync(p => p.CompanyId == companyId.Value && p.PriorityCode == dto.PriorityCode);
|
||||
if (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" });
|
||||
|
||||
// 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)
|
||||
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
|
||||
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);
|
||||
@@ -1403,7 +1403,7 @@ public class CompanySettingsController : Controller
|
||||
|
||||
// Check if status code already exists for this company
|
||||
var exists = await _unitOfWork.QuoteStatusLookups
|
||||
.AnyAsync(s => s.StatusCode == dto.StatusCode);
|
||||
.AnyAsync(s => s.CompanyId == companyId.Value && s.StatusCode == dto.StatusCode);
|
||||
if (exists)
|
||||
return Json(new { success = false, message = "Status code already exists" });
|
||||
|
||||
@@ -1411,7 +1411,7 @@ public class CompanySettingsController : Controller
|
||||
if (dto.IsApprovedStatus)
|
||||
{
|
||||
var hasApproved = await _unitOfWork.QuoteStatusLookups
|
||||
.AnyAsync(s => s.IsApprovedStatus);
|
||||
.AnyAsync(s => s.CompanyId == companyId.Value && s.IsApprovedStatus);
|
||||
if (hasApproved)
|
||||
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)
|
||||
{
|
||||
var hasConverted = await _unitOfWork.QuoteStatusLookups
|
||||
.AnyAsync(s => s.IsConvertedStatus);
|
||||
.AnyAsync(s => s.CompanyId == companyId.Value && s.IsConvertedStatus);
|
||||
if (hasConverted)
|
||||
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)
|
||||
{
|
||||
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)
|
||||
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)
|
||||
{
|
||||
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)
|
||||
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" });
|
||||
|
||||
// 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)
|
||||
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
|
||||
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);
|
||||
@@ -1941,7 +1941,7 @@ public class CompanySettingsController : Controller
|
||||
|
||||
// Check if type code already exists for this company
|
||||
var exists = await _unitOfWork.AppointmentTypeLookups
|
||||
.AnyAsync(t => t.TypeCode == dto.TypeCode);
|
||||
.AnyAsync(t => t.CompanyId == companyId.Value && t.TypeCode == dto.TypeCode);
|
||||
if (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" });
|
||||
|
||||
// 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)
|
||||
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
|
||||
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);
|
||||
@@ -2127,7 +2127,7 @@ public class CompanySettingsController : Controller
|
||||
|
||||
// Check if category code already exists for this company
|
||||
var exists = await _unitOfWork.InventoryCategoryLookups
|
||||
.AnyAsync(c => c.CategoryCode == dto.CategoryCode);
|
||||
.AnyAsync(c => c.CompanyId == companyId.Value && c.CategoryCode == dto.CategoryCode);
|
||||
if (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" });
|
||||
|
||||
// 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)
|
||||
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." });
|
||||
|
||||
// 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)
|
||||
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;
|
||||
|
||||
// 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
|
||||
var hasCustomizedLookups = await _unitOfWork.JobStatusLookups.AnyAsync(
|
||||
j => j.CompanyId == companyId && j.UpdatedAt != null,
|
||||
|
||||
@@ -241,11 +241,12 @@ public class InvoicesController : Controller
|
||||
ViewBag.SortDirection = gridRequest.SortDirection;
|
||||
|
||||
// Pill badge counts — always global (not scoped to current filter/page)
|
||||
ViewBag.UnpaidCount = await _unitOfWork.Invoices.CountAsync(i =>
|
||||
i.Status == InvoiceStatus.Draft || i.Status == InvoiceStatus.Sent || i.Status == InvoiceStatus.Overdue);
|
||||
ViewBag.PartialCount = await _unitOfWork.Invoices.CountAsync(i => i.Status == InvoiceStatus.PartiallyPaid);
|
||||
ViewBag.PaidCount = await _unitOfWork.Invoices.CountAsync(i => i.Status == InvoiceStatus.Paid);
|
||||
ViewBag.AllCount = await _unitOfWork.Invoices.CountAsync();
|
||||
var companyId = _tenantContext.GetCurrentCompanyId() ?? 0;
|
||||
ViewBag.UnpaidCount = await _unitOfWork.Invoices.CountAsync(i => i.CompanyId == companyId &&
|
||||
(i.Status == InvoiceStatus.Draft || i.Status == InvoiceStatus.Sent || i.Status == InvoiceStatus.Overdue));
|
||||
ViewBag.PartialCount = await _unitOfWork.Invoices.CountAsync(i => i.CompanyId == companyId && i.Status == InvoiceStatus.PartiallyPaid);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -213,24 +213,29 @@ public class JobsController : Controller
|
||||
|
||||
// Pill badge counts — always global (not scoped to current filter/page)
|
||||
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 =>
|
||||
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.Delivered
|
||||
&& j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Cancelled);
|
||||
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.ReadyForPickup
|
||||
&& j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Delivered
|
||||
&& j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Cancelled);
|
||||
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.Delivered);
|
||||
|| j.JobStatus.StatusCode == AppConstants.StatusCodes.Job.Delivered));
|
||||
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
|
||||
ViewBag.SearchTerm = searchTerm;
|
||||
@@ -2171,10 +2176,12 @@ public class JobsController : Controller
|
||||
try
|
||||
{
|
||||
var today = date?.Date ?? DateTime.Today;
|
||||
var companyId = _tenantContext.GetCurrentCompanyId() ?? 0;
|
||||
|
||||
// Load all non-terminal statuses for the progress strip (excluding nav/hold/cancel)
|
||||
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.Pending && s.StatusCode != AppConstants.StatusCodes.Job.Approved);
|
||||
var allStatuses = allStatusesEnum.OrderBy(s => s.DisplayOrder).ToList();
|
||||
@@ -2184,7 +2191,7 @@ public class JobsController : Controller
|
||||
|
||||
// Get existing priority records for today
|
||||
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);
|
||||
|
||||
@@ -2281,7 +2288,8 @@ public class JobsController : Controller
|
||||
if (!companyId.HasValue) return RedirectToAction(nameof(Index));
|
||||
|
||||
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.Delivered);
|
||||
var allStatuses = allStatusesEnum.OrderBy(s => s.DisplayOrder).ToList();
|
||||
|
||||
@@ -57,13 +57,14 @@ public class JobsPriorityController : Controller
|
||||
public async Task<IActionResult> Index(DateTime? date)
|
||||
{
|
||||
var today = date?.Date ?? DateTime.Today;
|
||||
var companyId = _tenantContext.GetCurrentCompanyId() ?? 0;
|
||||
|
||||
// Get all jobs scheduled for today with related data
|
||||
var jobs = await _unitOfWork.Jobs.GetScheduledJobsForDateAsync(today);
|
||||
|
||||
// Get existing priority records for today
|
||||
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);
|
||||
|
||||
@@ -90,7 +91,6 @@ public class JobsPriorityController : Controller
|
||||
.ToList();
|
||||
|
||||
// Get priorities and workers for modal options
|
||||
var companyId = _tenantContext.GetCurrentCompanyId() ?? 0;
|
||||
var priorities = await _unitOfWork.JobPriorityLookups.FindAsync(p => p.CompanyId == companyId);
|
||||
var workers = await _userManager.Users
|
||||
.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)
|
||||
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),
|
||||
false,
|
||||
m => m.Equipment, m => m.AssignedUser))
|
||||
@@ -169,10 +169,11 @@ public class JobsPriorityController : Controller
|
||||
}
|
||||
|
||||
var today = DateTime.Today;
|
||||
var cid = _tenantContext.GetCurrentCompanyId() ?? 0;
|
||||
|
||||
// Get all existing priority records for today
|
||||
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);
|
||||
|
||||
|
||||
@@ -127,14 +127,14 @@ public class OvenSchedulerController : Controller
|
||||
|
||||
// Determine which coats are already scheduled — filter out removed/cancelled at database level
|
||||
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,
|
||||
i => i.Batch))
|
||||
.Select(i => i.JobItemCoatId)
|
||||
.ToHashSet();
|
||||
|
||||
// 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;
|
||||
|
||||
// Build the view model
|
||||
|
||||
@@ -590,7 +590,8 @@ public class ReportsController : Controller
|
||||
|
||||
// === POWDER USAGE ANALYTICS ===
|
||||
var powderTransactions = (await _unitOfWork.InventoryTransactions
|
||||
.FindAsync(t => t.TransactionType == InventoryTransactionType.JobUsage
|
||||
.FindAsync(t => t.CompanyId == companyId
|
||||
&& t.TransactionType == InventoryTransactionType.JobUsage
|
||||
&& t.TransactionDate >= startDate,
|
||||
false,
|
||||
t => t.InventoryItem))
|
||||
|
||||
Reference in New Issue
Block a user