mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-10-03 15:03:29 +00:00
fix(work-orders): complete Phase 1 board indexes, Service DTO and helper wiring
Add AssignTo+ScheduledDate and WorkOrderType indexes, expose service on board rows, and route data access through shared projection/filter helpers.
This commit is contained in:
parent
ff53cb9fa6
commit
8efe4460bb
8 changed files with 69 additions and 165 deletions
|
|
@ -18,6 +18,22 @@ namespace Data.SeaHavenIndustries.Migrations
|
||||||
BEGIN
|
BEGIN
|
||||||
CREATE INDEX IX_workOrders_ScheduledDate ON workOrders (ScheduledDate);
|
CREATE INDEX IX_workOrders_ScheduledDate ON workOrders (ScheduledDate);
|
||||||
END
|
END
|
||||||
|
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM sys.indexes
|
||||||
|
WHERE name = 'IX_workOrders_AssignTo_ScheduledDate'
|
||||||
|
AND object_id = OBJECT_ID('workOrders'))
|
||||||
|
BEGIN
|
||||||
|
CREATE INDEX IX_workOrders_AssignTo_ScheduledDate ON workOrders (AssignTo, ScheduledDate);
|
||||||
|
END
|
||||||
|
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM sys.indexes
|
||||||
|
WHERE name = 'IX_workOrders_WorkOrderType'
|
||||||
|
AND object_id = OBJECT_ID('workOrders'))
|
||||||
|
BEGIN
|
||||||
|
CREATE INDEX IX_workOrders_WorkOrderType ON workOrders (WorkOrderType);
|
||||||
|
END
|
||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -25,6 +41,18 @@ namespace Data.SeaHavenIndustries.Migrations
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.Sql("""
|
migrationBuilder.Sql("""
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1 FROM sys.indexes
|
||||||
|
WHERE name = 'IX_workOrders_WorkOrderType'
|
||||||
|
AND object_id = OBJECT_ID('workOrders'))
|
||||||
|
DROP INDEX IX_workOrders_WorkOrderType ON workOrders;
|
||||||
|
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1 FROM sys.indexes
|
||||||
|
WHERE name = 'IX_workOrders_AssignTo_ScheduledDate'
|
||||||
|
AND object_id = OBJECT_ID('workOrders'))
|
||||||
|
DROP INDEX IX_workOrders_AssignTo_ScheduledDate ON workOrders;
|
||||||
|
|
||||||
IF EXISTS (
|
IF EXISTS (
|
||||||
SELECT 1 FROM sys.indexes
|
SELECT 1 FROM sys.indexes
|
||||||
WHERE name = 'IX_workOrders_ScheduledDate'
|
WHERE name = 'IX_workOrders_ScheduledDate'
|
||||||
|
|
|
||||||
|
|
@ -2031,12 +2031,16 @@ namespace Data.SeaHavenIndustries.Migrations
|
||||||
|
|
||||||
b.HasIndex("AssignTo");
|
b.HasIndex("AssignTo");
|
||||||
|
|
||||||
|
b.HasIndex("AssignTo", "ScheduledDate");
|
||||||
|
|
||||||
b.HasIndex("LocationId");
|
b.HasIndex("LocationId");
|
||||||
|
|
||||||
b.HasIndex("PrimaryDispatchId");
|
b.HasIndex("PrimaryDispatchId");
|
||||||
|
|
||||||
b.HasIndex("ScheduledDate");
|
b.HasIndex("ScheduledDate");
|
||||||
|
|
||||||
|
b.HasIndex("WorkOrderType");
|
||||||
|
|
||||||
b.ToTable("workOrders");
|
b.ToTable("workOrders");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ namespace SeaHaven.DataServices.Helpers
|
||||||
? w.PrimaryDispatch.Vendor.Phone
|
? w.PrimaryDispatch.Vendor.Phone
|
||||||
: null,
|
: null,
|
||||||
DispatchApptDate = w.PrimaryDispatch != null ? w.PrimaryDispatch.ScheduledDate : null,
|
DispatchApptDate = w.PrimaryDispatch != null ? w.PrimaryDispatch.ScheduledDate : null,
|
||||||
|
w.Service,
|
||||||
w.Trade,
|
w.Trade,
|
||||||
w.Problem,
|
w.Problem,
|
||||||
w.DocStatus,
|
w.DocStatus,
|
||||||
|
|
@ -92,8 +93,7 @@ namespace SeaHaven.DataServices.Helpers
|
||||||
w.TechName,
|
w.TechName,
|
||||||
w.TechPhone,
|
w.TechPhone,
|
||||||
w.DispatchApptDate,
|
w.DispatchApptDate,
|
||||||
w.Trade ?? w.Problem,
|
w.Service ?? w.Trade ?? w.Problem,
|
||||||
w.Problem,
|
|
||||||
w.DocStatus,
|
w.DocStatus,
|
||||||
w.RowVersion,
|
w.RowVersion,
|
||||||
isUnscheduled)).ToList();
|
isUnscheduled)).ToList();
|
||||||
|
|
|
||||||
|
|
@ -134,5 +134,24 @@ namespace SeaHaven.DataServices.Helpers
|
||||||
&& w.TargetWeek >= dateFrom
|
&& w.TargetWeek >= dateFrom
|
||||||
&& w.TargetWeek <= dateTo));
|
&& w.TargetWeek <= dateTo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IQueryable<WorkOrder> ApplySearchFilter(IQueryable<WorkOrder> query, string? search)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(search))
|
||||||
|
return query;
|
||||||
|
|
||||||
|
var s = search.Trim().ToLower();
|
||||||
|
return query.Where(w =>
|
||||||
|
(w.SiteCode ?? "").ToLower().Contains(s)
|
||||||
|
|| (w.InternalWONumber ?? "").ToLower().Contains(s)
|
||||||
|
|| (w.WorkerOrderNumber ?? "").ToLower().Contains(s)
|
||||||
|
|| (w.Locations != null && (w.Locations.Name ?? "").ToLower().Contains(s))
|
||||||
|
|| (w.AssignToUser != null && ((w.AssignToUser.FirstName ?? "") + " " + (w.AssignToUser.LastName ?? "")).ToLower().Contains(s))
|
||||||
|
|| (w.Service ?? "").ToLower().Contains(s)
|
||||||
|
|| (w.Trade ?? "").ToLower().Contains(s)
|
||||||
|
|| (w.Problem ?? "").ToLower().Contains(s)
|
||||||
|
|| (w.PrimaryDispatch != null && w.PrimaryDispatch.Vendor != null && (w.PrimaryDispatch.Vendor.CompanyName ?? "").ToLower().Contains(s))
|
||||||
|
|| (w.Status ?? "").ToLower().Contains(s));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using Data.SeaHavenIndustries;
|
using Data.SeaHavenIndustries;
|
||||||
using Data.SeaHavenIndustries.Enums;
|
using Data.SeaHavenIndustries.Enums;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using SeaHaven.DataServices.Helpers;
|
||||||
using SeaHaven.DataServices.Interfaces;
|
using SeaHaven.DataServices.Interfaces;
|
||||||
|
|
||||||
namespace SeaHaven.DataServices.Implementation
|
namespace SeaHaven.DataServices.Implementation
|
||||||
|
|
@ -19,12 +20,10 @@ namespace SeaHaven.DataServices.Implementation
|
||||||
var weekStartDate = query.WeekStart.ToDateTime(TimeOnly.MinValue);
|
var weekStartDate = query.WeekStart.ToDateTime(TimeOnly.MinValue);
|
||||||
var weekEndDate = query.WeekEnd.ToDateTime(TimeOnly.MinValue);
|
var weekEndDate = query.WeekEnd.ToDateTime(TimeOnly.MinValue);
|
||||||
|
|
||||||
var baseQuery = _context.workOrders
|
var baseQuery = WorkOrderBoardQueryFilters.ApplyBaseScope(_context.workOrders.AsNoTracking());
|
||||||
.AsNoTracking()
|
baseQuery = WorkOrderBoardQueryFilters.ApplyDispatcherFilter(
|
||||||
.Where(w => w.istemplate != true && (w.IsDeleted != true || w.IsDeleted == null));
|
baseQuery, query.Dispatchers, query.MyWorkOrders, query.CurrentUserId);
|
||||||
|
baseQuery = WorkOrderBoardQueryFilters.ApplyTypeFilter(baseQuery, query.Types);
|
||||||
baseQuery = ApplyDispatcherFilter(baseQuery, query);
|
|
||||||
baseQuery = ApplyTypeFilter(baseQuery, query);
|
|
||||||
|
|
||||||
var scheduledBase = baseQuery.Where(w =>
|
var scheduledBase = baseQuery.Where(w =>
|
||||||
(w.ScheduledDate != null
|
(w.ScheduledDate != null
|
||||||
|
|
@ -41,12 +40,16 @@ namespace SeaHaven.DataServices.Implementation
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(query.Search))
|
if (!string.IsNullOrWhiteSpace(query.Search))
|
||||||
{
|
{
|
||||||
scheduledBase = ApplySearchFilter(scheduledBase, query.Search);
|
scheduledBase = WorkOrderBoardQueryFilters.ApplySearchFilter(scheduledBase, query.Search);
|
||||||
unscheduledBase = ApplySearchFilter(unscheduledBase, query.Search);
|
unscheduledBase = WorkOrderBoardQueryFilters.ApplySearchFilter(unscheduledBase, query.Search);
|
||||||
}
|
}
|
||||||
|
|
||||||
var scheduledRows = await ProjectRows(scheduledBase, isUnscheduled: false);
|
var scheduledRows = await WorkOrderBoardProjection.ProjectRowsAsync(
|
||||||
var unscheduledRows = await ProjectRows(unscheduledBase, isUnscheduled: true);
|
scheduledBase.OrderBy(w => w.ScheduledDate).ThenBy(w => w.InternalWONumber),
|
||||||
|
isUnscheduled: false);
|
||||||
|
var unscheduledRows = await WorkOrderBoardProjection.ProjectRowsAsync(
|
||||||
|
unscheduledBase.OrderBy(w => w.InternalWONumber),
|
||||||
|
isUnscheduled: true);
|
||||||
|
|
||||||
return new WorkOrderBoardQueryResult(scheduledRows, unscheduledRows, scheduledTotal);
|
return new WorkOrderBoardQueryResult(scheduledRows, unscheduledRows, scheduledTotal);
|
||||||
}
|
}
|
||||||
|
|
@ -66,154 +69,5 @@ namespace SeaHaven.DataServices.Implementation
|
||||||
u.Color))
|
u.Color))
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IQueryable<WorkOrder> ApplyDispatcherFilter(IQueryable<WorkOrder> query, WorkOrderBoardQuery boardQuery)
|
|
||||||
{
|
|
||||||
if (boardQuery.MyWorkOrders && !string.IsNullOrEmpty(boardQuery.CurrentUserId))
|
|
||||||
return query.Where(w => w.AssignTo == boardQuery.CurrentUserId);
|
|
||||||
|
|
||||||
if (boardQuery.Dispatchers == null || boardQuery.Dispatchers.Count == 0)
|
|
||||||
return query;
|
|
||||||
|
|
||||||
var ids = boardQuery.Dispatchers
|
|
||||||
.Where(d => d != "__unassigned__")
|
|
||||||
.ToList();
|
|
||||||
var includeUnassigned = boardQuery.Dispatchers.Contains("__unassigned__");
|
|
||||||
|
|
||||||
if (includeUnassigned && ids.Count == 0)
|
|
||||||
return query.Where(w => w.AssignTo == null || w.AssignTo == "");
|
|
||||||
|
|
||||||
if (includeUnassigned && ids.Count > 0)
|
|
||||||
{
|
|
||||||
return query.Where(w =>
|
|
||||||
w.AssignTo == null || w.AssignTo == "" || ids.Contains(w.AssignTo));
|
|
||||||
}
|
|
||||||
|
|
||||||
return query.Where(w => w.AssignTo != null && ids.Contains(w.AssignTo));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IQueryable<WorkOrder> ApplyTypeFilter(IQueryable<WorkOrder> query, WorkOrderBoardQuery boardQuery)
|
|
||||||
{
|
|
||||||
if (boardQuery.Types == null || boardQuery.Types.Count == 0)
|
|
||||||
return query;
|
|
||||||
|
|
||||||
return query.Where(w => w.WorkOrderType != null && boardQuery.Types.Contains(w.WorkOrderType.Value));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IQueryable<WorkOrder> ApplySearchFilter(IQueryable<WorkOrder> query, string search)
|
|
||||||
{
|
|
||||||
var s = search.Trim().ToLower();
|
|
||||||
return query.Where(w =>
|
|
||||||
(w.SiteCode ?? "").ToLower().Contains(s)
|
|
||||||
|| (w.InternalWONumber ?? "").ToLower().Contains(s)
|
|
||||||
|| (w.WorkerOrderNumber ?? "").ToLower().Contains(s)
|
|
||||||
|| (w.Locations != null && (w.Locations.Name ?? "").ToLower().Contains(s))
|
|
||||||
|| (w.AssignToUser != null && ((w.AssignToUser.FirstName ?? "") + " " + (w.AssignToUser.LastName ?? "")).ToLower().Contains(s))
|
|
||||||
|| (w.Trade ?? "").ToLower().Contains(s)
|
|
||||||
|| (w.Problem ?? "").ToLower().Contains(s)
|
|
||||||
|| (w.PrimaryDispatch != null && w.PrimaryDispatch.Vendor != null && (w.PrimaryDispatch.Vendor.CompanyName ?? "").ToLower().Contains(s))
|
|
||||||
|| (w.Status ?? "").ToLower().Contains(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<IReadOnlyList<WorkOrderBoardRawRow>> ProjectRows(
|
|
||||||
IQueryable<WorkOrder> query,
|
|
||||||
bool isUnscheduled)
|
|
||||||
{
|
|
||||||
var rows = await query
|
|
||||||
.Include(w => w.AssignToUser)
|
|
||||||
.Include(w => w.Locations)
|
|
||||||
.Include(w => w.PrimaryDispatch!)
|
|
||||||
.ThenInclude(d => d!.Vendor)
|
|
||||||
.Include(w => w.WorkOrderContacts!)
|
|
||||||
.ThenInclude(c => c.POC)
|
|
||||||
.OrderBy(w => w.ScheduledDate)
|
|
||||||
.ThenBy(w => w.InternalWONumber)
|
|
||||||
.Select(w => new
|
|
||||||
{
|
|
||||||
w.Id,
|
|
||||||
w.InternalWONumber,
|
|
||||||
w.RescheduleCount,
|
|
||||||
w.CarriedOver,
|
|
||||||
w.WorkOrderType,
|
|
||||||
w.SiteCode,
|
|
||||||
LocationName = w.Locations != null ? w.Locations.Name : null,
|
|
||||||
Poc = w.WorkOrderContacts!
|
|
||||||
.OrderBy(c => c.Id)
|
|
||||||
.Select(c => new
|
|
||||||
{
|
|
||||||
Name = c.POC != null
|
|
||||||
? ((c.POC.FirstName ?? "") + " " + (c.POC.LastName ?? "")).Trim()
|
|
||||||
: null,
|
|
||||||
c.POC!.PhoneNumber,
|
|
||||||
c.Notes
|
|
||||||
})
|
|
||||||
.FirstOrDefault(),
|
|
||||||
w.LifecycleStatus,
|
|
||||||
w.LegacyStatus,
|
|
||||||
w.Status,
|
|
||||||
w.AssignTo,
|
|
||||||
w.AssignToUser!.FirstName,
|
|
||||||
w.AssignToUser!.LastName,
|
|
||||||
w.AssignToUser!.Initials,
|
|
||||||
w.AssignToUser!.Color,
|
|
||||||
w.DueDate,
|
|
||||||
w.ScheduledDate,
|
|
||||||
w.ScheduledStart,
|
|
||||||
w.ScheduledEnd,
|
|
||||||
w.TargetWeek,
|
|
||||||
w.ScheduleWeekOnly,
|
|
||||||
VendorId = w.PrimaryDispatch != null ? (int?)w.PrimaryDispatch.VendorId : null,
|
|
||||||
VendorName = w.PrimaryDispatch != null && w.PrimaryDispatch.Vendor != null
|
|
||||||
? w.PrimaryDispatch.Vendor.CompanyName
|
|
||||||
: null,
|
|
||||||
TechName = w.PrimaryDispatch != null && w.PrimaryDispatch.Vendor != null
|
|
||||||
? w.PrimaryDispatch.Vendor.ContactName
|
|
||||||
: null,
|
|
||||||
TechPhone = w.PrimaryDispatch != null && w.PrimaryDispatch.Vendor != null
|
|
||||||
? w.PrimaryDispatch.Vendor.Phone
|
|
||||||
: null,
|
|
||||||
DispatchApptDate = w.PrimaryDispatch != null ? w.PrimaryDispatch.ScheduledDate : null,
|
|
||||||
w.Trade,
|
|
||||||
w.Problem,
|
|
||||||
w.DocStatus,
|
|
||||||
w.RowVersion
|
|
||||||
})
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
return rows.Select(w => new WorkOrderBoardRawRow(
|
|
||||||
w.Id,
|
|
||||||
w.InternalWONumber,
|
|
||||||
w.RescheduleCount,
|
|
||||||
w.CarriedOver,
|
|
||||||
w.WorkOrderType,
|
|
||||||
w.SiteCode,
|
|
||||||
w.LocationName,
|
|
||||||
w.Poc?.Name,
|
|
||||||
w.Poc?.PhoneNumber,
|
|
||||||
w.Poc?.Notes,
|
|
||||||
w.LifecycleStatus,
|
|
||||||
w.LegacyStatus ?? w.Status,
|
|
||||||
w.AssignTo,
|
|
||||||
w.FirstName,
|
|
||||||
w.LastName,
|
|
||||||
w.Initials,
|
|
||||||
w.Color,
|
|
||||||
w.DueDate,
|
|
||||||
w.ScheduledDate,
|
|
||||||
w.ScheduledStart,
|
|
||||||
w.ScheduledEnd,
|
|
||||||
w.TargetWeek,
|
|
||||||
w.ScheduleWeekOnly,
|
|
||||||
w.VendorId,
|
|
||||||
w.VendorName,
|
|
||||||
w.TechName,
|
|
||||||
w.TechPhone,
|
|
||||||
w.DispatchApptDate,
|
|
||||||
w.Trade ?? w.Problem,
|
|
||||||
w.Problem,
|
|
||||||
w.DocStatus,
|
|
||||||
w.RowVersion,
|
|
||||||
isUnscheduled)).ToList();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,7 @@ namespace SeaHaven.DataServices.Interfaces
|
||||||
string? TechName,
|
string? TechName,
|
||||||
string? TechPhone,
|
string? TechPhone,
|
||||||
DateTime? DispatchApptDate,
|
DateTime? DispatchApptDate,
|
||||||
string? Trade,
|
string? Service,
|
||||||
string? Problem,
|
|
||||||
DocStatus? DocStatus,
|
DocStatus? DocStatus,
|
||||||
byte[]? RowVersion,
|
byte[]? RowVersion,
|
||||||
bool IsUnscheduled);
|
bool IsUnscheduled);
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ namespace SeaHaven.Services.DTOs
|
||||||
public string? TechPhone { get; set; }
|
public string? TechPhone { get; set; }
|
||||||
public DateTime? ApptDate { get; set; }
|
public DateTime? ApptDate { get; set; }
|
||||||
public string? ApptTime { get; set; }
|
public string? ApptTime { get; set; }
|
||||||
public string? Pm { get; set; }
|
public string? Service { get; set; }
|
||||||
public DocStatus? DocStatus { get; set; }
|
public DocStatus? DocStatus { get; set; }
|
||||||
public byte[]? RowVersion { get; set; }
|
public byte[]? RowVersion { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ namespace SeaHaven.Services.Implementation
|
||||||
TechPhone = row.TechPhone,
|
TechPhone = row.TechPhone,
|
||||||
ApptDate = row.DispatchApptDate ?? row.ScheduledDate,
|
ApptDate = row.DispatchApptDate ?? row.ScheduledDate,
|
||||||
ApptTime = WorkOrderDerivedFields.FormatApptTime(apptStart, apptEnd),
|
ApptTime = WorkOrderDerivedFields.FormatApptTime(apptStart, apptEnd),
|
||||||
Pm = row.Trade ?? row.Problem,
|
Service = row.Service,
|
||||||
DocStatus = row.DocStatus,
|
DocStatus = row.DocStatus,
|
||||||
RowVersion = row.RowVersion
|
RowVersion = row.RowVersion
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue