mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 06:03:12 +00:00
Seed an organization-wide Area catalogue (East, Central, West, California) with stable ids, add a nullable AreaId to VendorCompany, allow only Admins to change it through the roster endpoints, expose areas facet metadata and an areas[n] company-directory filter with the __unassigned__ sentinel.
147 lines
6.6 KiB
C#
147 lines
6.6 KiB
C#
namespace SeaHaven.DataServices.Models
|
|
{
|
|
public sealed class VendorDirectoryRow
|
|
{
|
|
public int? VendorId { get; set; }
|
|
public int CompanyId { get; set; }
|
|
public string? CompanyName { get; set; }
|
|
public string? ContactName { get; set; }
|
|
public string? Email { get; set; }
|
|
public string? Phone { get; set; }
|
|
public string? CompanyPhone { get; set; }
|
|
public string? PreferredContact { get; set; }
|
|
public string? Address { get; set; }
|
|
public string? City { get; set; }
|
|
public string? State { get; set; }
|
|
public string? Zip { get; set; }
|
|
public string? TradeSpecialties { get; set; }
|
|
public string? GoogleMapsUrl { get; set; }
|
|
public string? Notes { get; set; }
|
|
public int? AreaId { get; set; }
|
|
public bool IsActive { get; set; }
|
|
public int TotalJobs { get; set; }
|
|
}
|
|
|
|
// SH-278: Area facet filter for the company directory. AreaIds are already
|
|
// resolved against the active catalogue; IncludeUnassigned selects groups with no
|
|
// Area (including unlinked legacy vendors). An empty filter matches nobody.
|
|
public sealed class VendorAreaFilter
|
|
{
|
|
public IReadOnlyCollection<int> AreaIds { get; init; } = Array.Empty<int>();
|
|
public bool IncludeUnassigned { get; init; }
|
|
}
|
|
|
|
// SH-281 company directory: per-row source for the grouped key query. Carries the
|
|
// company group key parts (CompanyId 0 = unlinked; NameKey groups unlinked legacy
|
|
// vendors by trimmed case-insensitive CompanyName; VendorKey isolates vendors with
|
|
// a missing name by vendor identity) plus one 0/1 match flag per facet filter so a
|
|
// group can be selected when any status-eligible row matches that facet.
|
|
internal sealed class VendorDirectoryGroupSourceRow
|
|
{
|
|
public int? VendorId { get; set; }
|
|
public int CompanyId { get; set; }
|
|
public string NameKey { get; set; } = string.Empty;
|
|
public int VendorKey { get; set; }
|
|
public int TotalJobs { get; set; }
|
|
public int SearchFlag { get; set; }
|
|
public int CompanyFlag { get; set; }
|
|
public int TradeFlag { get; set; }
|
|
public int LocationFlag { get; set; }
|
|
public int AreaFlag { get; set; }
|
|
}
|
|
|
|
// SH-281: one row per company group after SQL grouping — the bounded key page.
|
|
internal sealed class VendorCompanyGroupKeyRow
|
|
{
|
|
public int CompanyId { get; set; }
|
|
public string NameKey { get; set; } = string.Empty;
|
|
public int VendorKey { get; set; }
|
|
public int? MaxVendorId { get; set; }
|
|
public int TotalJobs { get; set; }
|
|
public int SearchMatch { get; set; }
|
|
public int CompanyMatch { get; set; }
|
|
public int TradeMatch { get; set; }
|
|
public int LocationMatch { get; set; }
|
|
public int AreaMatch { get; set; }
|
|
}
|
|
|
|
// SH-281: full status-eligible roster row (phase 2) carrying both the vendor's own
|
|
// technician fields and the linked VendorCompany canonical fields, so group
|
|
// assembly can apply the existing company-canonical projections in memory without
|
|
// reapplying search/trade/location filters.
|
|
internal sealed class VendorDirectoryRosterRow
|
|
{
|
|
public int VendorId { get; set; }
|
|
public int? CompanyId { get; set; }
|
|
public string? CompanyName { get; set; }
|
|
public string? CanonicalCompanyName { get; set; }
|
|
public string? CompanyEmail { get; set; }
|
|
public string? CompanyPhone { get; set; }
|
|
public string? CompanyAddress { get; set; }
|
|
public string? CompanyCity { get; set; }
|
|
public string? CompanyState { get; set; }
|
|
public string? CompanyNotes { get; set; }
|
|
public int? CompanyAreaId { get; set; }
|
|
public string? CompanyAreaName { get; set; }
|
|
public string? ContactName { get; set; }
|
|
public string? Email { get; set; }
|
|
public string? Phone { get; set; }
|
|
public string? PreferredContact { get; set; }
|
|
public string? VendorCompanyPhone { get; set; }
|
|
public string? Address { get; set; }
|
|
public string? City { get; set; }
|
|
public string? State { get; set; }
|
|
public string? Zip { get; set; }
|
|
public string? TradeSpecialties { get; set; }
|
|
public string? GoogleMapsUrl { get; set; }
|
|
public bool IsActive { get; set; }
|
|
public int TotalJobs { get; set; }
|
|
public string NameKey { get; set; } = string.Empty;
|
|
public int VendorKey { get; set; }
|
|
}
|
|
|
|
// SH-281: one technician inside a company group.
|
|
public sealed class VendorCompanyGroupTechnician
|
|
{
|
|
public int Id { get; set; }
|
|
public string? ContactName { get; set; }
|
|
public string? Email { get; set; }
|
|
public string? Phone { get; set; }
|
|
public string? PreferredContact { get; set; }
|
|
public string? TradeSpecialties { get; set; }
|
|
public bool IsActive { get; set; }
|
|
public int TotalJobs { get; set; }
|
|
}
|
|
|
|
// SH-281 company directory group: linked vendors collapse by real CompanyId;
|
|
// unlinked legacy vendors group by trimmed case-insensitive CompanyName (vendors
|
|
// with a missing name stay isolated by vendor identity); companies with zero
|
|
// non-deleted technicians remain as company-only groups. Top-level technician
|
|
// fields come from the primary technician (newest VendorId, promoted when a
|
|
// nonblank search matches a non-primary technician); company fields are
|
|
// company-canonical as the existing projections intend; TotalJobs is the sum of
|
|
// every status-eligible technician's distinct work-order count.
|
|
public sealed class VendorCompanyGroup
|
|
{
|
|
public int? Id { get; set; }
|
|
public int? CompanyId { get; set; }
|
|
public string? CompanyName { get; set; }
|
|
public string? ContactName { get; set; }
|
|
public string? Email { get; set; }
|
|
public string? Phone { get; set; }
|
|
public string? CompanyPhone { get; set; }
|
|
public string? PreferredContact { get; set; }
|
|
public string? Address { get; set; }
|
|
public string? City { get; set; }
|
|
public string? State { get; set; }
|
|
public string? Zip { get; set; }
|
|
public string? TradeSpecialties { get; set; }
|
|
public string? GoogleMapsUrl { get; set; }
|
|
public string? Notes { get; set; }
|
|
public int? AreaId { get; set; }
|
|
public string? AreaName { get; set; }
|
|
public bool IsActive { get; set; }
|
|
public int TotalJobs { get; set; }
|
|
public IReadOnlyList<VendorCompanyGroupTechnician> Technicians { get; set; } = Array.Empty<VendorCompanyGroupTechnician>();
|
|
}
|
|
}
|