mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-10-01 07:33:14 +00:00
Merge pull request #200 from Sea-Haven-Industries/research/ab/sh413-service-contract-thr_9fpik7tdmw
SH-413: preserve service history and filter registry names
This commit is contained in:
commit
d03c88d48f
5 changed files with 513 additions and 4 deletions
|
|
@ -8,6 +8,8 @@ namespace SeaHaven.DataServices.Helpers
|
|||
{
|
||||
public static class WorkOrderBoardProjection
|
||||
{
|
||||
private static readonly string[] CompletedLegacyStatuses = { "completed", "complete", "done", "closed" };
|
||||
|
||||
private static readonly JsonSerializerOptions SnapshotJsonOptions =
|
||||
new(JsonSerializerDefaults.Web);
|
||||
|
||||
|
|
@ -36,6 +38,17 @@ namespace SeaHaven.DataServices.Helpers
|
|||
w.WorkOrderType,
|
||||
w.ServiceId,
|
||||
w.ServiceNameSnapshot,
|
||||
ServiceDisplayName = w.ServiceId.HasValue
|
||||
? (w.LifecycleStatus == LifecycleStatus.Completed
|
||||
|| (w.LifecycleStatus == null
|
||||
&& CompletedLegacyStatuses.Contains(((w.LegacyStatus ?? w.Status) ?? "").Trim().ToLower()))
|
||||
? w.ServiceNameSnapshot ?? w.Trade ?? w.Problem
|
||||
: (w.ServiceDefinition != null
|
||||
&& w.ServiceDefinition.IsActive
|
||||
&& w.ServiceDefinition.IsDeleted != true
|
||||
? w.ServiceDefinition.Name
|
||||
: w.ServiceNameSnapshot ?? w.Trade ?? w.Problem))
|
||||
: w.Trade ?? w.Problem,
|
||||
w.SiteCode,
|
||||
LocationName = w.Locations != null ? w.Locations.Name : null,
|
||||
SiteNotes = w.Locations != null ? w.Locations.Notes : null,
|
||||
|
|
@ -180,7 +193,7 @@ namespace SeaHaven.DataServices.Helpers
|
|||
technicianAssigned == false ? null : frozenCompany?.ContactName ?? w.TechName,
|
||||
technicianAssigned == false ? null : frozenCompany?.Phone ?? techPhone,
|
||||
w.DispatchApptDate,
|
||||
w.Trade ?? w.Problem,
|
||||
w.ServiceDisplayName,
|
||||
w.Problem,
|
||||
w.ServiceNotes,
|
||||
w.ExtraServices,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ namespace SeaHaven.DataServices.Helpers
|
|||
{
|
||||
public static class WorkOrderBoardQueryFilters
|
||||
{
|
||||
private static readonly string[] CompletedLegacyStatuses = { "completed", "complete", "done", "closed" };
|
||||
|
||||
/// <summary>
|
||||
/// The window the Work Orders board searches when no date range is selected
|
||||
/// (client ADVANCED_SEARCH_ALL_WEEKS_FROM/TO), used with includeDateless.
|
||||
|
|
@ -263,9 +265,20 @@ namespace SeaHaven.DataServices.Helpers
|
|||
return query;
|
||||
|
||||
return query.Where(w =>
|
||||
normalized.Any(pm =>
|
||||
(w.Trade ?? "").ToLower().Contains(pm)
|
||||
|| (w.Problem ?? "").ToLower().Contains(pm)));
|
||||
w.ServiceId.HasValue
|
||||
? (w.ServiceDefinition != null
|
||||
&& w.ServiceDefinition.IsActive
|
||||
&& w.ServiceDefinition.IsDeleted != true
|
||||
&& !(w.LifecycleStatus == LifecycleStatus.Completed
|
||||
|| (w.LifecycleStatus == null
|
||||
&& CompletedLegacyStatuses.Contains(((w.LegacyStatus ?? w.Status) ?? "").Trim().ToLower())))
|
||||
? normalized.Contains(((w.ServiceDefinition.Name ?? "").Trim()).ToLower())
|
||||
: ((w.ServiceDefinition != null
|
||||
&& normalized.Contains(((w.ServiceDefinition.Name ?? "").Trim()).ToLower()))
|
||||
|| normalized.Contains(((w.ServiceNameSnapshot ?? w.Trade ?? w.Problem ?? "").Trim()).ToLower())))
|
||||
: normalized.Any(pm =>
|
||||
(w.Trade ?? "").ToLower().Contains(pm)
|
||||
|| (w.Problem ?? "").ToLower().Contains(pm)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ namespace SeaHaven.DataServices.Helpers
|
|||
|| (w.AssignToUser != null && ((w.AssignToUser.FirstName ?? "") + " " + (w.AssignToUser.LastName ?? "")).ToLower().Contains(s))
|
||||
|| (w.Trade ?? "").ToLower().Contains(s)
|
||||
|| (w.Problem ?? "").ToLower().Contains(s)
|
||||
|| (w.ServiceDefinition != null && (w.ServiceDefinition.Name ?? "").ToLower().Contains(s))
|
||||
|| (w.ServiceNameSnapshot ?? "").ToLower().Contains(s)
|
||||
|| (w.PrimaryDispatch != null && w.PrimaryDispatch.Vendor != null && (w.PrimaryDispatch.Vendor.CompanyName ?? "").ToLower().Contains(s))
|
||||
|| (w.TechnicianAssigned != false && w.PrimaryDispatch != null && w.PrimaryDispatch.Vendor != null && (w.PrimaryDispatch.Vendor.ContactName ?? "").ToLower().Contains(s))
|
||||
|| (w.Status ?? "").ToLower().Contains(s)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,271 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Data.SeaHavenIndustries.Enums;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Helpers;
|
||||
using SeaHaven.Services.Implementation;
|
||||
|
||||
namespace SeaHavenIndustries.Tests;
|
||||
|
||||
public sealed class WorkOrderServiceRegistryFilterRelationalTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task ServiceDisplayAndFilter_RespectCompletedSnapshotsAndKeepCurrentAliases()
|
||||
{
|
||||
await using var connection = new SqliteConnection("Data Source=:memory:;Foreign Keys=True");
|
||||
await connection.OpenAsync();
|
||||
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||
.UseSqlite(connection)
|
||||
.Options;
|
||||
|
||||
await using var context = new SqliteServiceRegistryTestDbContext(options);
|
||||
await context.Database.EnsureCreatedAsync();
|
||||
|
||||
context.Services.AddRange(
|
||||
new Service
|
||||
{
|
||||
Id = 10,
|
||||
Name = "Current Active Name",
|
||||
NormalizedName = "CURRENT ACTIVE NAME",
|
||||
Trade = "Electrical",
|
||||
IconKey = "electrical",
|
||||
IsActive = true
|
||||
},
|
||||
new Service
|
||||
{
|
||||
Id = 20,
|
||||
Name = "Original Inactive Name",
|
||||
NormalizedName = "ORIGINAL INACTIVE NAME",
|
||||
Trade = "Electrical",
|
||||
IconKey = "electrical",
|
||||
IsActive = true
|
||||
},
|
||||
new Service
|
||||
{
|
||||
Id = 30,
|
||||
Name = "QA Service",
|
||||
NormalizedName = "QA SERVICE",
|
||||
Trade = "Electrical",
|
||||
IconKey = "electrical",
|
||||
IsActive = true
|
||||
},
|
||||
new Service
|
||||
{
|
||||
Id = 31,
|
||||
Name = "QA Service Extended",
|
||||
NormalizedName = "QA SERVICE EXTENDED",
|
||||
Trade = "Electrical",
|
||||
IconKey = "electrical",
|
||||
IsActive = true
|
||||
});
|
||||
context.workOrders.AddRange(
|
||||
new WorkOrder
|
||||
{
|
||||
Id = 1,
|
||||
ServiceId = 10,
|
||||
ServiceNameSnapshot = "Original Active Name",
|
||||
Trade = "Original Active Name"
|
||||
},
|
||||
new WorkOrder
|
||||
{
|
||||
Id = 2,
|
||||
ServiceId = 20,
|
||||
ServiceNameSnapshot = "Historical Inactive Name",
|
||||
Trade = "Historical Inactive Name"
|
||||
},
|
||||
new WorkOrder
|
||||
{
|
||||
Id = 3,
|
||||
Trade = "Legacy Cooling",
|
||||
Problem = "Legacy Leak"
|
||||
},
|
||||
new WorkOrder
|
||||
{
|
||||
Id = 4,
|
||||
ServiceId = 10,
|
||||
ServiceNameSnapshot = "Completed Archive Alpha",
|
||||
Trade = "Completed Archive Alpha",
|
||||
LifecycleStatus = LifecycleStatus.Completed
|
||||
},
|
||||
new WorkOrder
|
||||
{
|
||||
Id = 5,
|
||||
ServiceId = 10,
|
||||
ServiceNameSnapshot = "Completed Archive Beta",
|
||||
Trade = "Completed Archive Beta",
|
||||
Status = "Done"
|
||||
},
|
||||
new WorkOrder
|
||||
{
|
||||
Id = 6,
|
||||
ServiceId = 30,
|
||||
ServiceNameSnapshot = "QA Service",
|
||||
Trade = "QA Service",
|
||||
LifecycleStatus = LifecycleStatus.Scheduled
|
||||
},
|
||||
new WorkOrder
|
||||
{
|
||||
Id = 7,
|
||||
ServiceId = 31,
|
||||
ServiceNameSnapshot = "QA Service Extended",
|
||||
Trade = "QA Service Extended",
|
||||
LifecycleStatus = LifecycleStatus.Scheduled
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
var renamedService = await context.Services.SingleAsync(service => service.Id == 20);
|
||||
renamedService.Name = "Current Inactive Name";
|
||||
renamedService.NormalizedName = "CURRENT INACTIVE NAME";
|
||||
await context.SaveChangesAsync();
|
||||
renamedService.IsActive = false;
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
var activeRows = await WorkOrderBoardProjection.ProjectRowsAsync(
|
||||
context,
|
||||
context.workOrders.AsNoTracking().Where(w => w.Id == 1),
|
||||
isUnscheduled: true);
|
||||
var activeDisplay = WorkOrderBoardService.MapRawRow(Assert.Single(activeRows), DateTime.UtcNow);
|
||||
|
||||
var inactiveRows = await WorkOrderBoardProjection.ProjectRowsAsync(
|
||||
context,
|
||||
context.workOrders.AsNoTracking().Where(w => w.Id == 2),
|
||||
isUnscheduled: true);
|
||||
var inactiveDisplay = WorkOrderBoardService.MapRawRow(Assert.Single(inactiveRows), DateTime.UtcNow);
|
||||
|
||||
var legacyRows = await WorkOrderBoardProjection.ProjectRowsAsync(
|
||||
context,
|
||||
context.workOrders.AsNoTracking().Where(w => w.Id == 3),
|
||||
isUnscheduled: true);
|
||||
var legacyDisplay = WorkOrderBoardService.MapRawRow(Assert.Single(legacyRows), DateTime.UtcNow);
|
||||
|
||||
var completedRows = await WorkOrderBoardProjection.ProjectRowsAsync(
|
||||
context,
|
||||
context.workOrders.AsNoTracking().Where(w => w.Id == 4),
|
||||
isUnscheduled: true);
|
||||
var completedDisplay = WorkOrderBoardService.MapRawRow(Assert.Single(completedRows), DateTime.UtcNow);
|
||||
|
||||
var legacyCompletedRows = await WorkOrderBoardProjection.ProjectRowsAsync(
|
||||
context,
|
||||
context.workOrders.AsNoTracking().Where(w => w.Id == 5),
|
||||
isUnscheduled: true);
|
||||
var legacyCompletedDisplay = WorkOrderBoardService.MapRawRow(Assert.Single(legacyCompletedRows), DateTime.UtcNow);
|
||||
|
||||
var currentActiveNameMatches = await WorkOrderBoardQueryFilters
|
||||
.ApplyPmTypeFilter(context.workOrders.AsNoTracking(), new[] { "Current Active Name" })
|
||||
.Select(w => w.Id)
|
||||
.ToListAsync();
|
||||
|
||||
var historicalInactiveNameMatches = await WorkOrderBoardQueryFilters
|
||||
.ApplyPmTypeFilter(context.workOrders.AsNoTracking(), new[] { "Historical Inactive Name" })
|
||||
.Select(w => w.Id)
|
||||
.ToListAsync();
|
||||
|
||||
var inactiveRegistryNameMatches = await WorkOrderBoardQueryFilters
|
||||
.ApplyPmTypeFilter(context.workOrders.AsNoTracking(), new[] { "Current Inactive Name" })
|
||||
.Select(w => w.Id)
|
||||
.ToListAsync();
|
||||
|
||||
var completedSnapshotMatches = await WorkOrderBoardQueryFilters
|
||||
.ApplyPmTypeFilter(context.workOrders.AsNoTracking(), new[] { "Completed Archive Alpha" })
|
||||
.Select(w => w.Id)
|
||||
.ToListAsync();
|
||||
|
||||
var legacyCompletedSnapshotMatches = await WorkOrderBoardQueryFilters
|
||||
.ApplyPmTypeFilter(context.workOrders.AsNoTracking(), new[] { "Completed Archive Beta" })
|
||||
.Select(w => w.Id)
|
||||
.ToListAsync();
|
||||
|
||||
var currentNameAliasMatches = await WorkOrderBoardQueryFilters
|
||||
.ApplyPmTypeFilter(context.workOrders.AsNoTracking(), new[] { "Current Active Name" })
|
||||
.Select(w => w.Id)
|
||||
.OrderBy(id => id)
|
||||
.ToListAsync();
|
||||
|
||||
var exactRegistryNameMatches = await WorkOrderBoardQueryFilters
|
||||
.ApplyPmTypeFilter(context.workOrders.AsNoTracking(), new[] { "QA Service" })
|
||||
.Select(w => w.Id)
|
||||
.OrderBy(id => id)
|
||||
.ToListAsync();
|
||||
|
||||
var multipleRegistryNameMatches = await WorkOrderBoardQueryFilters
|
||||
.ApplyPmTypeFilter(context.workOrders.AsNoTracking(), new[] { "QA Service", "QA Service Extended" })
|
||||
.Select(w => w.Id)
|
||||
.OrderBy(id => id)
|
||||
.ToListAsync();
|
||||
|
||||
var legacyTradeMatches = await WorkOrderBoardQueryFilters
|
||||
.ApplyPmTypeFilter(context.workOrders.AsNoTracking(), new[] { "Legacy Cooling" })
|
||||
.Select(w => w.Id)
|
||||
.ToListAsync();
|
||||
|
||||
var legacyProblemMatches = await WorkOrderBoardQueryFilters
|
||||
.ApplyPmTypeFilter(context.workOrders.AsNoTracking(), new[] { "Legacy Leak" })
|
||||
.Select(w => w.Id)
|
||||
.ToListAsync();
|
||||
|
||||
var actual = string.Join("\n", new[]
|
||||
{
|
||||
$"active-display={activeDisplay.Pm}",
|
||||
$"inactive-display={inactiveDisplay.Pm}",
|
||||
$"legacy-display={legacyDisplay.Pm}",
|
||||
$"completed-display={completedDisplay.Pm}",
|
||||
$"legacy-completed-display={legacyCompletedDisplay.Pm}",
|
||||
$"active-filter={string.Join(',', currentActiveNameMatches)}",
|
||||
$"inactive-snapshot-filter={string.Join(',', historicalInactiveNameMatches)}",
|
||||
$"inactive-current-name-filter={string.Join(',', inactiveRegistryNameMatches)}",
|
||||
$"completed-snapshot-filter={string.Join(',', completedSnapshotMatches)}",
|
||||
$"legacy-completed-snapshot-filter={string.Join(',', legacyCompletedSnapshotMatches)}",
|
||||
$"current-name-alias-filter={string.Join(',', currentNameAliasMatches)}",
|
||||
$"exact-registry-filter={string.Join(',', exactRegistryNameMatches)}",
|
||||
$"multiple-registry-filter={string.Join(',', multipleRegistryNameMatches)}",
|
||||
$"legacy-trade-filter={string.Join(',', legacyTradeMatches)}",
|
||||
$"legacy-problem-filter={string.Join(',', legacyProblemMatches)}"
|
||||
});
|
||||
var expected = string.Join("\n", new[]
|
||||
{
|
||||
"active-display=Current Active Name",
|
||||
"inactive-display=Historical Inactive Name",
|
||||
"legacy-display=Legacy Cooling",
|
||||
"completed-display=Completed Archive Alpha",
|
||||
"legacy-completed-display=Completed Archive Beta",
|
||||
"active-filter=1,4,5",
|
||||
"inactive-snapshot-filter=2",
|
||||
"inactive-current-name-filter=2",
|
||||
"completed-snapshot-filter=4",
|
||||
"legacy-completed-snapshot-filter=5",
|
||||
"current-name-alias-filter=1,4,5",
|
||||
"exact-registry-filter=6",
|
||||
"multiple-registry-filter=6,7",
|
||||
"legacy-trade-filter=3",
|
||||
"legacy-problem-filter=3"
|
||||
});
|
||||
Assert.True(expected == actual, $"Expected:\n{expected}\nActual:\n{actual}");
|
||||
}
|
||||
|
||||
private sealed class SqliteServiceRegistryTestDbContext : ApplicationDbContext
|
||||
{
|
||||
public SqliteServiceRegistryTestDbContext(DbContextOptions<ApplicationDbContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
base.OnModelCreating(builder);
|
||||
|
||||
// SQL Server filtered indexes and rowversion semantics are not supported by SQLite.
|
||||
foreach (var index in builder.Model.GetEntityTypes().SelectMany(entity => entity.GetIndexes()))
|
||||
{
|
||||
if (index.GetFilter() != null)
|
||||
index.SetFilter(null);
|
||||
}
|
||||
|
||||
foreach (var entityType in new[] { typeof(WorkOrder), typeof(Dispatch) })
|
||||
{
|
||||
var property = builder.Entity(entityType).Property("RowVersion").Metadata;
|
||||
property.ValueGenerated = Microsoft.EntityFrameworkCore.Metadata.ValueGenerated.Never;
|
||||
property.IsConcurrencyToken = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Data.SeaHavenIndustries.Enums;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Helpers;
|
||||
using SeaHaven.DataServices.Implementation;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
|
||||
namespace SeaHavenIndustries.Tests;
|
||||
|
||||
public sealed class WorkOrderServiceRegistryKeywordSearchRelationalTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Search_MatchesCurrentAndHistoricalServiceLabelsAndPreservesExistingAliases()
|
||||
{
|
||||
await using var connection = new SqliteConnection("Data Source=:memory:;Foreign Keys=True");
|
||||
await connection.OpenAsync();
|
||||
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||
.UseSqlite(connection)
|
||||
.Options;
|
||||
|
||||
await using var context = new SqliteSearchTestDbContext(options);
|
||||
await context.Database.EnsureCreatedAsync();
|
||||
|
||||
context.Services.AddRange(
|
||||
new Service
|
||||
{
|
||||
Id = 10,
|
||||
Name = "Chiller Maintenance Original",
|
||||
NormalizedName = "CHILLER MAINTENANCE ORIGINAL",
|
||||
Trade = "HVAC",
|
||||
IconKey = "hvac",
|
||||
IsActive = true
|
||||
},
|
||||
new Service
|
||||
{
|
||||
Id = 20,
|
||||
Name = "Roof Maintenance Current",
|
||||
NormalizedName = "ROOF MAINTENANCE CURRENT",
|
||||
Trade = "Roofing",
|
||||
IconKey = "roofing",
|
||||
IsActive = true
|
||||
},
|
||||
new Service
|
||||
{
|
||||
Id = 30,
|
||||
Name = "Pump Service Current",
|
||||
NormalizedName = "PUMP SERVICE CURRENT",
|
||||
Trade = "Plumbing",
|
||||
IconKey = "plumbing",
|
||||
IsActive = false
|
||||
});
|
||||
context.Vendors.Add(new Vendor
|
||||
{
|
||||
Id = 40,
|
||||
CompanyName = "Acme Vendor Group",
|
||||
ContactName = "Morgan Technician"
|
||||
});
|
||||
context.Dispatches.Add(new Dispatch { Id = 50, VendorId = 40, Status = "Sent" });
|
||||
context.workOrders.AddRange(
|
||||
new WorkOrder
|
||||
{
|
||||
Id = 1,
|
||||
ServiceId = 10,
|
||||
ServiceNameSnapshot = "Legacy Chiller Label",
|
||||
Trade = "HVAC",
|
||||
Problem = "Cooling noise",
|
||||
LifecycleStatus = LifecycleStatus.Scheduled,
|
||||
WorkOrderType = WorkOrderType.PM,
|
||||
ScheduledDate = new DateTime(2026, 9, 15),
|
||||
PrimaryDispatchId = 50,
|
||||
TechnicianAssigned = true
|
||||
},
|
||||
new WorkOrder
|
||||
{
|
||||
Id = 2,
|
||||
ServiceId = 20,
|
||||
ServiceNameSnapshot = "Frozen Roof Label",
|
||||
Trade = "Roofing",
|
||||
Problem = "Roof leak",
|
||||
LifecycleStatus = LifecycleStatus.Completed,
|
||||
WorkOrderType = WorkOrderType.PM,
|
||||
ScheduledDate = new DateTime(2026, 9, 15)
|
||||
},
|
||||
new WorkOrder
|
||||
{
|
||||
Id = 3,
|
||||
ServiceId = 30,
|
||||
ServiceNameSnapshot = "Historical Pump Label",
|
||||
Trade = "Plumbing",
|
||||
Problem = "Pump noise",
|
||||
LifecycleStatus = LifecycleStatus.Scheduled,
|
||||
WorkOrderType = WorkOrderType.PM,
|
||||
ScheduledDate = new DateTime(2026, 9, 15)
|
||||
},
|
||||
new WorkOrder
|
||||
{
|
||||
Id = 4,
|
||||
Trade = "Legacy Boiler Service",
|
||||
Problem = "Oil leak",
|
||||
LifecycleStatus = LifecycleStatus.Scheduled,
|
||||
WorkOrderType = WorkOrderType.Reactive,
|
||||
ScheduledDate = new DateTime(2026, 9, 15)
|
||||
},
|
||||
new WorkOrder
|
||||
{
|
||||
Id = 5,
|
||||
Trade = "Unlinked vendor WO",
|
||||
LifecycleStatus = LifecycleStatus.Scheduled,
|
||||
WorkOrderType = WorkOrderType.Reactive,
|
||||
ScheduledDate = new DateTime(2026, 9, 15),
|
||||
PrimaryDispatchId = 50,
|
||||
TechnicianAssigned = false
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
var renamedService = await context.Services.SingleAsync(service => service.Id == 10);
|
||||
renamedService.Name = "Northstar Chiller Service";
|
||||
renamedService.NormalizedName = "NORTHSTAR CHILLER SERVICE";
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
async Task<int[]> SearchIds(string search)
|
||||
=> await WorkOrderBoardSearchFilter
|
||||
.Apply(context.workOrders.AsNoTracking(), search)
|
||||
.Select(workOrder => workOrder.Id)
|
||||
.OrderBy(id => id)
|
||||
.ToArrayAsync();
|
||||
|
||||
Assert.Equal(new[] { 1 }, await SearchIds("Northstar Chiller Service"));
|
||||
Assert.Equal(new[] { 1 }, await SearchIds("Legacy Chiller Label"));
|
||||
Assert.Equal(new[] { 2 }, await SearchIds("Frozen Roof Label"));
|
||||
Assert.Equal(new[] { 2 }, await SearchIds("Roof Maintenance Current"));
|
||||
Assert.Equal(new[] { 3 }, await SearchIds("Historical Pump Label"));
|
||||
Assert.Equal(new[] { 3 }, await SearchIds("Pump Service Current"));
|
||||
Assert.Equal(new[] { 4 }, await SearchIds("Legacy Boiler Service"));
|
||||
Assert.Equal(new[] { 4 }, await SearchIds("Oil leak"));
|
||||
Assert.Equal(new[] { 1, 5 }, await SearchIds("Acme Vendor Group"));
|
||||
Assert.Equal(new[] { 1 }, await SearchIds("Morgan Technician"));
|
||||
|
||||
var advancedSearch = new WorkOrderAdvancedSearchDataService(context);
|
||||
var matchingFacet = await advancedSearch.SearchAsync(CreateQuery(
|
||||
search: "Northstar Chiller Service",
|
||||
pmTypes: new[] { "Northstar Chiller Service" }));
|
||||
Assert.Equal(new[] { 1 }, matchingFacet.Rows.Select(row => row.Id));
|
||||
Assert.Equal("Northstar Chiller Service", Assert.Single(matchingFacet.Rows).Trade);
|
||||
|
||||
var conflictingFacet = await advancedSearch.SearchAsync(CreateQuery(
|
||||
search: "Northstar Chiller Service",
|
||||
pmTypes: new[] { "Roof Maintenance Current" }));
|
||||
Assert.Empty(conflictingFacet.Rows);
|
||||
Assert.Equal(0, conflictingFacet.TotalCount);
|
||||
}
|
||||
|
||||
private static WorkOrderAdvancedSearchQuery CreateQuery(string search, IReadOnlyList<string>? pmTypes = null)
|
||||
=> new(
|
||||
Search: search,
|
||||
DateFrom: new DateOnly(2026, 9, 1),
|
||||
DateTo: new DateOnly(2026, 9, 30),
|
||||
UnscheduledOnly: false,
|
||||
Sites: null,
|
||||
Regions: null,
|
||||
Types: null,
|
||||
Overdue: false,
|
||||
Dispatchers: null,
|
||||
Statuses: null,
|
||||
PmTypes: pmTypes,
|
||||
VendorIds: null,
|
||||
DocStatuses: null,
|
||||
Severities: null,
|
||||
Rescheduled: false,
|
||||
CarriedOver: false,
|
||||
AddOn: false,
|
||||
AvetaOnly: false,
|
||||
FlagColors: null,
|
||||
InternalOnly: false,
|
||||
HasUplift: false,
|
||||
UpliftStatuses: null,
|
||||
MyWorkOrders: false,
|
||||
CurrentUserId: null,
|
||||
Page: 0,
|
||||
PageSize: 100,
|
||||
SortBy: "scheduledDate",
|
||||
SortDir: "asc");
|
||||
|
||||
private sealed class SqliteSearchTestDbContext : ApplicationDbContext
|
||||
{
|
||||
public SqliteSearchTestDbContext(DbContextOptions<ApplicationDbContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
base.OnModelCreating(builder);
|
||||
|
||||
foreach (var index in builder.Model.GetEntityTypes().SelectMany(entity => entity.GetIndexes()))
|
||||
{
|
||||
if (index.GetFilter() != null)
|
||||
index.SetFilter(null);
|
||||
}
|
||||
|
||||
foreach (var entityType in new[] { typeof(WorkOrder), typeof(Dispatch) })
|
||||
{
|
||||
var property = builder.Entity(entityType).Property("RowVersion").Metadata;
|
||||
property.ValueGenerated = Microsoft.EntityFrameworkCore.Metadata.ValueGenerated.Never;
|
||||
property.IsConcurrencyToken = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue