shoc-backend/SeaHaven.Services/DTOs/VendorCompanyRosterDTOs.cs
Alexandre Brandizzi 8515676f4d feat(vendors): add admin-assigned vendor company Area
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.
2026-09-16 11:34:45 -03:00

142 lines
5 KiB
C#

using System.Text.Json.Serialization;
namespace SeaHaven.Services.DTOs
{
public class VendorRosterDTO
{
public int CompanyId { get; set; }
public string Name { get; set; } = string.Empty;
public string? CompanyPhone { get; set; }
public string? Email { get; set; }
public string? Address { get; set; }
public string? City { get; set; }
public string? State { get; set; }
public string? Zip { get; set; }
public string? GoogleMapsUrl { get; set; }
public string? Notes { get; set; }
public int? AreaId { get; set; }
public string? AreaName { get; set; }
public string? RowVersion { get; set; }
public List<VendorRosterTechnicianDTO> Technicians { get; set; } = new();
}
public class VendorRosterTechnicianDTO
{
public int? Id { get; set; }
public string? ContactName { get; set; }
public string? Phone { get; set; }
public string? Email { get; set; }
public string? PreferredContact { get; set; }
public string? TradeSpecialties { get; set; }
public bool IsActive { get; set; }
public int TotalJobs { get; set; }
}
public class RosterTechnicianInputDTO
{
public int? Id { get; set; }
public string? ContactName { get; set; }
public string? Phone { get; set; }
public string? Email { get; set; }
public string? PreferredContact { get; set; }
public string? TradeSpecialties { get; set; }
public bool? IsActive { get; set; }
}
public class CreateVendorRosterDTO
{
public required string Name { get; set; }
public string? CompanyPhone { get; set; }
public string? Email { get; set; }
public string? Address { get; set; }
public string? City { get; set; }
public string? State { get; set; }
public string? Zip { get; set; }
public string? GoogleMapsUrl { get; set; }
public string? Notes { get; set; }
// SH-278: optional Area id; null leaves the new company Unassigned. Assigning
// an Area requires the Admin role.
public int? AreaId { get; set; }
public List<RosterTechnicianInputDTO> Technicians { get; set; } = new();
}
public class ReconcileVendorRosterDTO
{
public required string RowVersion { get; set; }
public required string Name { get; set; }
public string? CompanyPhone { get; set; }
public string? Email { get; set; }
public string? Address { get; set; }
public string? City { get; set; }
public string? State { get; set; }
public string? Zip { get; set; }
public string? GoogleMapsUrl { get; set; }
public string? Notes { get; set; }
// SH-278: an omitted areaId leaves the stored Area unchanged, so clients that
// predate Area never clear it. An explicit null clears it to Unassigned.
private int? _areaId;
public int? AreaId
{
get => _areaId;
set
{
_areaId = value;
AreaIdProvided = true;
}
}
[JsonIgnore]
public bool AreaIdProvided { get; private set; }
public List<RosterTechnicianInputDTO> Technicians { get; set; } = new();
}
// Partial, purely additive roster update (SH-250): technicians listed here are
// inserted; any technician absent from the payload is never removed, soft-deleted
// or deactivated. Company fields are optional; a null field means "unchanged".
public class AddTechniciansVendorRosterDTO
{
public required string RowVersion { get; set; }
public List<RosterTechnicianInputDTO> AddTechnicians { get; set; } = new();
public VendorRosterCompanyFieldsDTO? CompanyFields { get; set; }
}
public class VendorRosterCompanyFieldsDTO
{
public string? Name { get; set; }
public string? CompanyPhone { get; set; }
public string? Email { get; set; }
public string? Address { get; set; }
public string? City { get; set; }
public string? State { get; set; }
public string? Zip { get; set; }
public string? GoogleMapsUrl { get; set; }
public string? Notes { get; set; }
// SH-278: same presence semantics as ReconcileVendorRosterDTO.AreaId.
private int? _areaId;
public int? AreaId
{
get => _areaId;
set
{
_areaId = value;
AreaIdProvided = true;
}
}
[JsonIgnore]
public bool AreaIdProvided { get; private set; }
}
// SH-278: raised when a caller without the Admin role tries to change a vendor
// company's Area. Mapped to a stable 403 by the API.
public sealed class VendorAreaAssignmentForbiddenException : Exception
{
public VendorAreaAssignmentForbiddenException()
: base("Only administrators can assign a vendor company Area.")
{
}
}
}