shoc-backend/SeaHaven.Services/DTOs/WorkOrderBoardRequestDTOs.cs
Alexandre Brandizzi cbecc7b4ea fix(workorders): persist manual POC override with audit and site-follow (SH-379)
Editing a work order's POC never reached the backend: no update path wrote
PocName/PocPhone/PocNotes, so the optimistic UI edit was lost on refetch and
the completion freeze captured the Site contact instead of the manual value,
and nothing was audited.

- Add tenant-scoped PATCH api/workorders/{id}/poc via new WorkOrderPocService
  + WorkOrderPocDataService: persists the override, stages FieldChanged audit
  entries (which also write field locks so sync never overwrites a manual POC),
  and enforces row-version concurrency and terminal-status read-only rules.
- Lock semantics (SH-190): a manual POC away from the Site's live contacts is
  stored WO-level; an edit equal to a live Site contact (or blanking name+phone)
  stores nothing so the WO follows the Site. PocCustomized exposes the state.
- Board projection, completion freeze and create now share one Site-contact
  fallback (first non-deleted contact by SiteContactOrder) so a never-overridden
  WO keeps following the Site, including at create when the wizard prefills it.
- Route contract baseline gains PATCH {id:int}/poc.
2026-09-18 14:30:54 -03:00

114 lines
5.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Data.SeaHavenIndustries.Enums;
using SeaHaven.Services.Helpers;
namespace SeaHaven.Services.DTOs
{
/// <summary>
/// Payload for creating a board work order (SHOC create wizard).
/// FE follow-up: extend WorkOrderBoardCreatePayload + mapper with PrimaryService,
/// ExtraServices, ServiceNotes, PocName, PocPhone, VendorNotes, TechPhone; stop embedding
/// POC/vendor/service notes in description (pocNotes already exists). Prefer structured
/// fields; keep Trade+Description temporarily for legacy clients.
/// </summary>
public class WorkOrderBoardCreateRequestDto
{
public string? WoNumber { get; set; }
public WorkOrderType WorkOrderType { get; set; }
/// <summary>
/// Manual Add-On override when unscheduled. Ignored when ScheduledDate or TargetWeek is set
/// (server computes from cutoff).
/// </summary>
public bool? IsAddOn { get; set; }
public string? SiteCode { get; set; }
/// <summary>
/// Optional display customer name. Ignored when stamping AccountId
/// (board create resolves from Location.AccountId).
/// </summary>
public string? Customer { get; set; }
public string? Description { get; set; }
/// <summary>Legacy alias for primary service; prefer <see cref="PrimaryService"/>.</summary>
public string? Trade { get; set; }
/// <summary>Primary service label; persisted as Trade / board pm.</summary>
public string? PrimaryService { get; set; }
public int? ServiceId { get; set; }
public List<string>? ExtraServices { get; set; }
public string? ServiceNotes { get; set; }
/// <summary>Per-WO Aveta compliance. Omit or false = not required.</summary>
public bool? AvetaRequired { get; set; }
public int? LocationId { get; set; }
public DateTime? DueDate { get; set; }
public string? AssignTo { get; set; }
public DateTime? ScheduledDate { get; set; }
public DateOnly? TargetWeek { get; set; }
public bool? ScheduleWeekOnly { get; set; }
public DocStatus? DocStatus { get; set; }
/// <summary>Optional create-time lifecycle. When omitted, server defaults to Incomplete.</summary>
public LifecycleStatus? LifecycleStatus { get; set; }
public int? VendorId { get; set; }
public DateTime? ApptDate { get; set; }
public string? ApptTime { get; set; }
public int? PocContactId { get; set; }
public string? PocName { get; set; }
public string? PocPhone { get; set; }
public string? PocNotes { get; set; }
public List<WorkOrderAdditionalContactDto>? AdditionalContacts { get; set; }
public string? TechPhone { get; set; }
public string? VendorNotes { get; set; }
/// <summary>Severity 1–5 for Emergency/Reactive. Ignored for other types.</summary>
public int? Severity { get; set; }
}
/// <summary>
/// SH-379: replaces the work-order-level POC override (PocName/PocPhone/PocNotes).
/// Blank name and phone clear the override so the work order follows the Site's
/// live contact again. An edit equal to a live Site contact does not lock (SH-190).
/// </summary>
public class WorkOrderPocUpdateRequestDto
{
public string? PocName { get; set; }
public string? PocPhone { get; set; }
public string? PocNotes { get; set; }
/// <summary>Base64 row version, same concurrency token as the board patch.</summary>
public string? WorkOrderVersion { get; set; }
}
/// <summary>Advanced search / filter query for the board.</summary>
public class WorkOrderAdvancedSearchQueryDto
{
public int Page { get; set; } = 0;
public int PageSize { get; set; } = 50;
public string? Search { get; set; }
public WorkOrderAdvancedSearchDatePreset? DatePreset { get; set; }
public DateOnly? DateFrom { get; set; }
public DateOnly? DateTo { get; set; }
/// <summary>
/// When true, only unscheduled rows (ScheduledDate is null) are returned.
/// Takes precedence over DatePreset/DateFrom/DateTo for the scheduled-date
/// filter; every other facet still applies.
/// </summary>
public bool UnscheduledOnly { get; set; }
public List<string>? Sites { get; set; }
public List<string>? Regions { get; set; }
public List<WorkOrderType>? Types { get; set; }
/// <summary>When true, include past-due (isPastDue) rows. Combined with Types via OR.</summary>
public bool Overdue { get; set; }
public List<string>? Dispatchers { get; set; }
public List<LifecycleStatus>? Statuses { get; set; }
public List<string>? PmTypes { get; set; }
public List<int>? VendorIds { get; set; }
public List<DocStatus>? DocStatuses { get; set; }
public List<int>? Severities { get; set; }
public bool Rescheduled { get; set; }
public bool CarriedOver { get; set; }
public bool AddOn { get; set; }
/// <summary>When true, only rows with AvetaRequired.</summary>
public bool AvetaOnly { get; set; }
public List<string>? FlagColors { get; set; }
public bool InternalOnly { get; set; }
public bool HasUplift { get; set; }
public List<string>? UpliftStatuses { get; set; }
public bool MyWorkOrders { get; set; }
public string? SortBy { get; set; }
public string? SortDir { get; set; }
}
}