using Data.SeaHavenIndustries; namespace SeaHaven.DataServices.Models { public sealed class VendorCompanyRosterReadModel { 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 byte[]? RowVersion { get; set; } public List Technicians { get; set; } = new(); } public sealed class VendorRosterTechnicianReadModel { 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 sealed class VendorCompanyRosterWriteModel { public int CompanyId { get; set; } public byte[]? RowVersion { 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; } // SH-278: applied only when AreaIdProvided; null AreaId means Unassigned. public bool AreaIdProvided { get; set; } public int? AreaId { get; set; } public string? ActorUserId { get; set; } public List Technicians { get; set; } = new(); } public sealed class RosterTechnicianWriteModel { 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; } = true; } // Additive roster update (SH-250): AddTechnicians rows are inserted and nothing // is ever removed. CompanyFields carries optional company-level updates where a // null property means "leave unchanged". public sealed class VendorCompanyRosterAddWriteModel { public int CompanyId { get; set; } public byte[]? RowVersion { get; set; } public string? ActorUserId { get; set; } public VendorRosterCompanyFieldsWriteModel? CompanyFields { get; set; } public List AddTechnicians { get; set; } = new(); } public sealed class VendorRosterCompanyFieldsWriteModel { 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: applied only when AreaIdProvided; null AreaId means Unassigned. public bool AreaIdProvided { get; set; } public int? AreaId { get; set; } } // Raised by the roster data service when a technician removed from the snapshot // still has open linked work orders, so the whole reconcile must fail. Carries the // blocking work orders so the API can surface a stable 409 without leaking internals. /// /// Raised when a company rename collides with the unique NormalizedName index. /// Distinct from , which reports open /// linked work orders, so callers can return a stable client conflict instead of /// letting the raw /// surface as a 500. /// public sealed class VendorRosterDuplicateNameException : Exception { public VendorRosterDuplicateNameException(string message, Exception inner) : base(message, inner) { } } public sealed class VendorRosterConflictException : Exception { public IReadOnlyList BlockedWorkOrders { get; } public VendorRosterConflictException(string message, IReadOnlyList blockedWorkOrders) : base(message) { BlockedWorkOrders = blockedWorkOrders; } } }