shoc-backend/docs/work-orders/phase-0/manual-edit-flag-design.md

2.1 KiB

ManualEditFlag — Design

Fase: 0
Implementação: tabela satélite WorkOrderFieldLocks


1. Problema

Sync/Lambda faz upsert overwrite em campos que dispatchers editam no SHOC. Precisamos saber, por campo, se houve edição manual SHOC para rejeitar ingest conflitante.


2. Solução escolhida: tabela satélite

WorkOrderFieldLocks
  Id (PK)
  WorkOrderId (FK)
  FieldName (string, max 64)
  LockedAt (UTC)
  LockedByUserId (nullable — null = system/bootstrap)
  UNIQUE (WorkOrderId, FieldName)

Alternativa descartada: bitmask — difícil de auditar e estender.


3. Comportamento

Evento Ação
SHOC edita campo X pela 1ª vez INSERT lock (WorkOrderId, FieldName)
Sync tenta atualizar campo X com lock Skip update; audit SyncRejected
Sync atualiza campo Y sem lock Apply merge normal
WO novo (Sync create) Sem locks; full ingest
Blazor edita campo legado Não cria lock Fase 0 (gap conhecido; Blazor congelado)

4. Campos elegíveis a lock (SHOC writers)

Definidos em SyncFieldMergePolicy.ShocOwnedFields:

  • LifecycleStatus, AssignTo, ScheduledDate, ScheduledEnd, TargetWeek, ScheduleWeekOnly
  • DueDate, Description, WorkerOrderTitle, SiteCode, Building, LocationId
  • WorkOrderType, DocStatus, Trade, Problem, Priority, InternalWONumber

5. Audit em rejeição

{
  "eventType": "Sync",
  "action": "SyncRejected",
  "fieldName": "DueDate",
  "oldValue": "2026-06-01",
  "newValue": "2026-06-15",
  "actorType": "Sync"
}

6. API interna

interface IWorkOrderFieldLockService
{
    Task LockFieldAsync(int workOrderId, string fieldName, string? userId);
    Task<bool> IsLockedAsync(int workOrderId, string fieldName);
}

Implementação: WorkOrderFieldLockService em SeaHaven.Services.

Lock criado automaticamente por IWorkOrderAuditService em eventos Manual + FieldChanged / StatusChanged.


7. Critérios de aceite

  • Tabela criada na migration Phase0
  • Sync consulta locks antes de overwrite
  • SyncRejected aparece no audit log