2026-09-18 12:40:33 -03:00
|
|
|
|
namespace SeaHaven.DataServices.Models;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Server-derived feed scope. <see cref="AccountId"/> null is org-wide;
|
|
|
|
|
|
/// <see cref="DispatcherId"/> null means every dispatcher's work orders.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public sealed record NotificationFeedScope(int? AccountId, string? DispatcherId);
|
|
|
|
|
|
|
|
|
|
|
|
public sealed class UnassignedWorkOrdersSummary
|
|
|
|
|
|
{
|
|
|
|
|
|
public int Count { get; init; }
|
|
|
|
|
|
public DateTime? LatestCreatedAt { get; init; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public sealed class NotificationWorkOrderCandidate
|
|
|
|
|
|
{
|
|
|
|
|
|
public int Id { get; init; }
|
|
|
|
|
|
public string? Number { get; init; }
|
|
|
|
|
|
public DateTime ScheduledDate { get; init; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>A bounded slice of candidates plus the unbounded total they were taken from.</summary>
|
|
|
|
|
|
public sealed class NotificationCandidatePage
|
|
|
|
|
|
{
|
|
|
|
|
|
public int Total { get; init; }
|
|
|
|
|
|
public IReadOnlyList<NotificationWorkOrderCandidate> Items { get; init; } = Array.Empty<NotificationWorkOrderCandidate>();
|
|
|
|
|
|
}
|
2026-09-18 13:15:33 -03:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Server-derived scope for items addressed to one person: <see cref="UserId"/> is the signed-in
|
|
|
|
|
|
/// user from claims and <see cref="AccountId"/> null is org-wide.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public sealed record NotificationPersonalScope(int? AccountId, string UserId);
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>An open work order assigned to the user, with when and by whom it was last assigned.</summary>
|
|
|
|
|
|
public sealed class NotificationAssignmentCandidate
|
|
|
|
|
|
{
|
|
|
|
|
|
public int WorkOrderId { get; init; }
|
|
|
|
|
|
public string? Number { get; init; }
|
|
|
|
|
|
public DateTime AssignedAt { get; init; }
|
|
|
|
|
|
public string? AssignedBy { get; init; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public sealed class NotificationCommentCandidate
|
|
|
|
|
|
{
|
|
|
|
|
|
public int CommentId { get; init; }
|
|
|
|
|
|
public int WorkOrderId { get; init; }
|
|
|
|
|
|
public string? WorkOrderNumber { get; init; }
|
|
|
|
|
|
public string? AuthorFirstName { get; init; }
|
|
|
|
|
|
public string? AuthorLastName { get; init; }
|
|
|
|
|
|
/// <summary>Free-text author for comments with no user (email sync, vendor portal).</summary>
|
|
|
|
|
|
public string? Commenter { get; init; }
|
|
|
|
|
|
public string? Text { get; init; }
|
|
|
|
|
|
public DateTime CreatedAt { get; init; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public sealed class NotificationUpliftDecisionCandidate
|
|
|
|
|
|
{
|
|
|
|
|
|
public int UpliftId { get; init; }
|
|
|
|
|
|
public int WorkOrderId { get; init; }
|
|
|
|
|
|
public string? WorkOrderNumber { get; init; }
|
|
|
|
|
|
public string Status { get; init; } = "";
|
|
|
|
|
|
public decimal Amount { get; init; }
|
|
|
|
|
|
public string? DecisionNote { get; init; }
|
|
|
|
|
|
public DateTime DecidedAt { get; init; }
|
|
|
|
|
|
}
|
2026-09-25 11:16:21 -03:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Per SEV level, the latest work-order creation time that has reached a response-window threshold.
|
|
|
|
|
|
/// The business service computes them; the query only compares creation times.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public sealed record NotificationSlaCutoffs(DateTime Sev1, DateTime Sev2, DateTime Sev3, DateTime Sev4, DateTime Sev5);
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The work-order audit entry that records an SLA breach acknowledgement: who (UserId), when (CreatedAt)
|
|
|
|
|
|
/// and the SEV level whose deadline was missed (NewValue). It is also what keeps the breach out of the feed.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static class SlaBreachAcknowledgementAudit
|
|
|
|
|
|
{
|
|
|
|
|
|
public const string FieldName = "SlaBreachAcknowledged";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>An open Reactive/Emergency work order with a SEV 1–5 level and a creation time.</summary>
|
|
|
|
|
|
public sealed class NotificationSlaCandidate
|
|
|
|
|
|
{
|
|
|
|
|
|
public int Id { get; init; }
|
|
|
|
|
|
public string? Number { get; init; }
|
|
|
|
|
|
public string Severity { get; init; } = "";
|
|
|
|
|
|
public DateTime CreatedAt { get; init; }
|
|
|
|
|
|
/// <summary>A breach of the work order's current severity has already been acknowledged.</summary>
|
|
|
|
|
|
public bool BreachAcknowledged { get; init; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public sealed class NotificationSlaCandidates
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>Every work order past half its window but not past the whole window.</summary>
|
|
|
|
|
|
public IReadOnlyList<NotificationSlaCandidate> AtRisk { get; init; } = Array.Empty<NotificationSlaCandidate>();
|
|
|
|
|
|
/// <summary>Unacknowledged breaches: the newest slice, plus the unbounded total.</summary>
|
|
|
|
|
|
public IReadOnlyList<NotificationSlaCandidate> Breached { get; init; } = Array.Empty<NotificationSlaCandidate>();
|
|
|
|
|
|
public int BreachedTotal { get; init; }
|
|
|
|
|
|
}
|