shoc-backend/docs/work-orders/phase-2
2026-06-30 10:09:48 -03:00
..
README.md wip: work orders phases 1-7 (isolated from phase 0 foundation) 2026-06-30 10:09:48 -03:00

Fase 2 — Inline Edit + Concorrência

Programa: Work Orders Board
Objetivo: Edição spreadsheet-style célula a célula no board SHOC, com dual RowVersion, audit granular e regras de domínio no backend.

Depende de: Fase 0, Fase 1


Endpoints

PATCH /api/workorders/{id}/board

Atualiza um único campo do board com validação otimista de concorrência.

Autenticação: Bearer JWT ([Authorize])

Request body:

{
  "field": "scheduledDate",
  "value": "2026-06-25",
  "workOrderVersion": "<base64 RowVersion>",
  "dispatchVersion": "<base64 RowVersion | null para campos WO-only>",
  "primaryDispatchId": 123
}
Campo Obrigatório Descrição
field Sim Nome canônico do campo (case-insensitive)
value Sim* Valor serializado como string (*pode ser vazio para limpar datas)
workOrderVersion Sim RowVersion atual da WO (Base64)
dispatchVersion Condicional Obrigatório para campos Dispatch quando dispatch já existe
primaryDispatchId Não Valida que o dispatch pertence à WO

Responses:

HTTP Body Quando
200 WorkOrderBoardRowDto Sucesso — row completo com versões atualizadas
409 WorkOrderBoardConflictDto + currentState Conflito de versão
422 WorkOrderBoardValidationErrorDto Regra de domínio violada
400 Response Erro genérico / argumento inválido

Exemplo:

curl -X PATCH -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"field":"siteCode","value":"BK5","workOrderVersion":"AQAAAAAAAAA="}' \
  "https://localhost:5001/api/workorders/1/board"

Campos editáveis

field (API) Agregado Coluna board Audit FieldName
woNumber WorkOrder WO# InternalWONumber
workOrderType WorkOrder Type WorkOrderType
siteCode WorkOrder Site SiteCode
lifecycleStatus WorkOrder Status LifecycleStatus
assignTo WorkOrder Assignee AssignTo
dueDate WorkOrder Due DueDate
scheduledDate WorkOrder Scheduled ScheduledDate
targetWeek WorkOrder Scheduled (week-only) TargetWeek
scheduleWeekOnly WorkOrder Scheduled ScheduleWeekOnly
vendorId Dispatch Vendor VendorId
apptDate Dispatch Appt ApptDate
apptTime Dispatch/WO Appt ApptTime
docStatus WorkOrder Doc DocStatus

Read-only: isPastDue, carriedOver, rescheduleCount (badge derivado; count incrementado por regra).


Dual RowVersion

Agregado Quando exigir versão
WorkOrder Sempre (workOrderVersion)
Dispatch Campos vendor/appt quando dispatch primário já existe (dispatchVersion)

O GET board retorna rowVersion, dispatchRowVersion e primaryDispatchId em cada row.

409 currentState: inclui row recarregado do DB para refresh imediato no FE.


Regras de domínio

Regra Comportamento
Auto-schedule Incomplete + scheduledDate + assignTo → Scheduled
Reschedule Mudança de scheduledDate com data anterior → rescheduleCount++
Set-once tracking Primeira scheduledDate → OriginalDate / OriginalWeek
Past Due block Não permite mudar lifecycleStatus quando isPastDue
Cancel read-only Status terminal (Canceled, Closed, Complete) bloqueia PATCH
WO# 11 dígitos Normalização numérica + unicidade
Vendor ADR Mutação em Dispatch; cria dispatch primário se ausente

Audit

  • 1 evento por campo alterado (FieldChanged, StatusChanged, ou AssignmentChanged)
  • Side effects (auto-schedule, rescheduleCount) geram eventos adicionais
  • Field lock (WorkOrderFieldLocks) criado automaticamente em edições manuais
  • Eventos Dispatch incluem dispatchId

Código entregue

Camada Arquivo
DTOs SeaHaven.Services/DTOs/WorkOrderBoardDTOs.cs
Helpers WorkOrderBoardMutationRules, WorkOrderNumberNormalizer, WorkOrderBoardFieldNames, WorkOrderBoardApptTimeParser
Update service SeaHaven.Services/Implementation/WorkOrderBoardUpdateService.cs
Audit IWorkOrderAuditService.StageFieldChanged, LogFieldChangedAsync
API WorkOrderController — PATCH {id}/board
Testes WorkOrderBoardMutationRulesTests, WorkOrderNumberNormalizerTests, WorkOrderBoardUpdateServiceTests, WorkOrderBoardConcurrencyTests

Gates de aceite

Desenvolvimento

  • PATCH /api/workorders/{id}/board para todos os campos da matriz
  • Dual RowVersion validado; 409 com currentState
  • Auto-schedule Incomplete→Scheduled
  • RescheduleCount++ em reagendamento
  • Bloqueio status quando PastDue; reagendar limpa flag on-read
  • WO cancelado/closed rejeita edição (422)
  • WO# normalizado 11 dígitos + unicidade
  • Vendor/appt muta Dispatch; cria primário se ausente
  • 1 audit event por campo; field lock criado
  • 51 testes unitários total (25 Fase 2 + 26 Fases 0–1)

Staging (pendente)

  • Smoke Sync durante edição SHOC
  • UAT dispatcher: editar células reais no board
  • Latência PATCH aceitável (<200ms p95 Tier S)

Próximo passo

Fase 3 — Criação wizard/inline, soft cancel dedicado, ManualEditFlag na criação.