mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 10:43:13 +00:00
GET /board/search accepts ids=1,2,3 (positive ints, deduplicated, at most 200). When present the result is exactly those work orders inside the caller's tenant and base scope; date, status, dispatcher, facet and text filters are ignored so none of them can hide a listed work order. Malformed or oversized lists are a 400.
92 lines
5.2 KiB
C#
92 lines
5.2 KiB
C#
using Data.SeaHavenIndustries;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using SeaHaven.DataServices.Helpers;
|
|
using SeaHaven.DataServices.Interfaces;
|
|
|
|
namespace SeaHaven.DataServices.Implementation
|
|
{
|
|
public class WorkOrderAdvancedSearchDataService : IWorkOrderAdvancedSearchDataService
|
|
{
|
|
private readonly ApplicationDbContext _context;
|
|
|
|
public WorkOrderAdvancedSearchDataService(ApplicationDbContext context)
|
|
{
|
|
_context = context;
|
|
}
|
|
|
|
public async Task<WorkOrderAdvancedSearchResult> SearchAsync(WorkOrderAdvancedSearchQuery query)
|
|
{
|
|
var baseQuery = WorkOrderBoardQueryFilters.ApplyBaseScope(_context.workOrders.AsNoTracking());
|
|
if (query.AccountId.HasValue)
|
|
baseQuery = WorkOrderBoardQueryFilters.ApplyAccountScope(baseQuery, query.AccountId.Value);
|
|
|
|
// An exact id set (a link from an alert or a linked-work-orders list) shows those work
|
|
// orders whatever their date, status or owner; only the base and tenant scope still apply.
|
|
baseQuery = query.Ids is { Count: > 0 } ids
|
|
? baseQuery.Where(w => ids.Contains(w.Id))
|
|
: ApplyFilters(baseQuery, query);
|
|
|
|
var totalCount = await baseQuery.CountAsync();
|
|
|
|
var sorted = ApplySort(baseQuery, query.SortBy, query.SortDir);
|
|
var paged = sorted
|
|
.Skip(query.Page * query.PageSize)
|
|
.Take(query.PageSize);
|
|
|
|
var rows = await WorkOrderBoardProjection.ProjectRowsAsync(_context, paged, isUnscheduled: false);
|
|
return new WorkOrderAdvancedSearchResult(rows, totalCount);
|
|
}
|
|
|
|
private IQueryable<WorkOrder> ApplyFilters(IQueryable<WorkOrder> baseQuery, WorkOrderAdvancedSearchQuery query)
|
|
{
|
|
if (query.UnscheduledOnly)
|
|
baseQuery = WorkOrderBoardQueryFilters.ApplyUnscheduledOnlyFilter(baseQuery);
|
|
else
|
|
baseQuery = WorkOrderBoardQueryFilters.ApplyDateRangeFilter(
|
|
baseQuery, query.DateFrom, query.DateTo, query.IncludeDateless);
|
|
baseQuery = WorkOrderBoardQueryFilters.ApplySiteFilter(baseQuery, query.Sites);
|
|
baseQuery = WorkOrderBoardQueryFilters.ApplyRegionFilter(baseQuery, query.Regions);
|
|
baseQuery = WorkOrderBoardQueryFilters.ApplyTypeFilter(baseQuery, query.Types, query.Overdue);
|
|
baseQuery = WorkOrderBoardQueryFilters.ApplyDispatcherFilter(
|
|
baseQuery, query.Dispatchers, query.MyWorkOrders, query.CurrentUserId);
|
|
baseQuery = WorkOrderBoardQueryFilters.ApplyStatusFilter(baseQuery, query.Statuses);
|
|
baseQuery = WorkOrderBoardQueryFilters.ApplyPmTypeFilter(baseQuery, query.PmTypes);
|
|
baseQuery = WorkOrderBoardQueryFilters.ApplyVendorFilter(baseQuery, query.VendorIds);
|
|
baseQuery = WorkOrderBoardQueryFilters.ApplyDocStatusFilter(baseQuery, query.DocStatuses);
|
|
baseQuery = WorkOrderBoardSearchFacetFilters.ApplySeverityFilter(baseQuery, query.Severities);
|
|
baseQuery = WorkOrderBoardSearchFacetFilters.ApplyRescheduledFilter(baseQuery, query.Rescheduled);
|
|
baseQuery = WorkOrderBoardSearchFacetFilters.ApplyCarriedOverFilter(baseQuery, query.CarriedOver);
|
|
baseQuery = WorkOrderBoardSearchFacetFilters.ApplyAddOnFilter(baseQuery, query.AddOn);
|
|
baseQuery = WorkOrderBoardSearchFacetFilters.ApplyAvetaOnlyFilter(baseQuery, query.AvetaOnly);
|
|
baseQuery = WorkOrderBoardSearchFacetFilters.ApplyFlagColorFilter(baseQuery, query.FlagColors);
|
|
baseQuery = WorkOrderBoardSearchFacetFilters.ApplyInternalOnlyFilter(baseQuery, query.InternalOnly);
|
|
baseQuery = WorkOrderBoardSearchFacetFilters.ApplyUpliftFilter(
|
|
baseQuery, _context, query.HasUplift, query.UpliftStatuses);
|
|
|
|
var normalizedSearch = WorkOrderBoardSearchFilter.NormalizeSearch(query.Search);
|
|
if (normalizedSearch != null)
|
|
baseQuery = WorkOrderBoardSearchFilter.Apply(baseQuery, normalizedSearch);
|
|
|
|
return baseQuery;
|
|
}
|
|
|
|
private static IQueryable<WorkOrder> ApplySort(IQueryable<WorkOrder> query, string sortBy, string sortDir)
|
|
{
|
|
var descending = string.Equals(sortDir, "desc", StringComparison.OrdinalIgnoreCase);
|
|
var field = (sortBy ?? "scheduledDate").Trim().ToLowerInvariant();
|
|
|
|
return field switch
|
|
{
|
|
"wonumber" or "woNumber" => descending
|
|
? query.OrderByDescending(w => w.InternalWONumber).ThenBy(w => w.Id)
|
|
: query.OrderBy(w => w.InternalWONumber).ThenBy(w => w.Id),
|
|
"duedate" or "dueDate" => descending
|
|
? query.OrderByDescending(w => w.DueDate).ThenBy(w => w.InternalWONumber).ThenBy(w => w.Id)
|
|
: query.OrderBy(w => w.DueDate).ThenBy(w => w.InternalWONumber).ThenBy(w => w.Id),
|
|
_ => descending
|
|
? query.OrderByDescending(w => w.ScheduledDate).ThenByDescending(w => w.InternalWONumber).ThenBy(w => w.Id)
|
|
: query.OrderBy(w => w.ScheduledDate).ThenBy(w => w.InternalWONumber).ThenBy(w => w.Id)
|
|
};
|
|
}
|
|
}
|
|
}
|