# Fase 3 — Criação e Cancelamento **Programa:** Work Orders Board **Objetivo:** Criação wizard/inline via contrato do board SHOC, cancelamento soft dedicado, ManualEditFlag na criação, e delete físico restrito a Admin. **Depende de:** [Fase 0](../phase-0/README.md), [Fase 1](../phase-1/README.md), [Fase 2](../phase-2/README.md) --- ## Endpoints ### `POST /api/workorders/board` Cria uma WO com contrato alinhado ao board. Suporta wizard (payload completo) e inline row (subset mínimo). **Autenticação:** Bearer JWT (`[Authorize]`) **Request body:** ```json { "woNumber": "12345", "workOrderType": "PM", "siteCode": "BK5", "assignTo": "dispatcher-guid", "dueDate": "2026-07-01", "scheduledDate": "2026-06-25", "targetWeek": "2026-06-22", "scheduleWeekOnly": false, "vendorId": 5, "apptDate": "2026-06-26", "apptTime": "09:00 – 11:00", "docStatus": "No", "description": "Leak in break room", "trade": "HVAC PM", "locationId": 12, "pocContactId": 3, "pocNotes": "Call before arrival" } ``` | Campo | Obrigatório | Descrição | |-------|-------------|-----------| | `workOrderType` | Sim | Enum `WorkOrderType` | | `siteCode` | Sim | Código do site | | `woNumber` | Não | Se omitido, auto-gera sequencial normalizado 11 dígitos | | `scheduleWeekOnly` | Não | Se `true`, `targetWeek` é obrigatório | | `vendorId` | Condicional | Obrigatório quando `apptDate` ou `apptTime` informados | **Responses:** | HTTP | Body | Quando | |------|------|--------| | 200 | `WorkOrderBoardRowDto` | Sucesso — row pronto para inserir no board | | 400 | `Response` | Validação FluentValidation | | 409 | `WorkOrderBoardValidationErrorDto` | WO# duplicado | | 422 | `WorkOrderBoardValidationErrorDto` | Regra de domínio | **Exemplo (inline mínimo):** ```bash curl -X POST -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"workOrderType":"PM","siteCode":"BK5"}' \ "https://localhost:5001/api/workorders/board" ``` --- ### `POST /api/workorders/{id}/cancel` Soft cancel — transição para `LifecycleStatus.Canceled` com WO read-only. **Autenticação:** Bearer JWT **Responses:** | HTTP | Body | Quando | |------|------|--------| | 200 | `WorkOrderBoardRowDto` | Cancelado (ou já estava cancelado — idempotente) | | 422 | `WorkOrderBoardValidationErrorDto` | WO `Complete` ou `Closed` | **Exemplo:** ```bash curl -X POST -H "Authorization: Bearer " \ "https://localhost:5001/api/workorders/42/cancel" ``` --- ## Regras de domínio na criação | Regra | Comportamento | |-------|---------------| | Status inicial | `Incomplete` | | Auto-schedule | `scheduledDate` + `assignTo` → `Scheduled` | | Set-once tracking | Primeira `scheduledDate` → `OriginalDate` / `OriginalWeek` | | Week-only | `scheduleWeekOnly=true` + `targetWeek` → aparece na semana no GET board | | WO# | Manual normalizado 11 dígitos; auto-gerado se omitido | | Vendor | Cria dispatch primário quando `vendorId` informado | | ManualEditFlag | Field locks criados para cada campo SHOC preenchido | --- ## Soft cancel vs hard delete | Operação | Endpoint | Quem | Efeito | |----------|----------|------|--------| | Soft cancel | `POST /{id}/cancel` | Dispatcher | Status `Canceled`, WO permanece, PATCH bloqueado (422) | | Hard delete | `DELETE DeleteWorkorder` | **Admin only** | Remove registro, anexos e contatos | O legado `POST AddWorkorder` (form/Blazor) permanece inalterado. --- ## ManualEditFlag na criação Campos preenchidos na criação SHOC recebem lock em `WorkOrderFieldLocks` via audit `FieldChanged`. Sync subsequente em campo lockado gera `SyncRejected` (ver [manual-edit-flag-design.md](../phase-0/manual-edit-flag-design.md)). WO criada via Sync/Lambda continua sem locks até edição SHOC. --- ## Audit | Evento | Action | |--------|--------| | Criação | 1× `Create` + `FieldChanged` por campo preenchido | | Auto-schedule na criação | `StatusChanged` adicional | | Cancel | `StatusChanged` → `Canceled` | | Hard delete Admin | `Delete` | --- ## Código entregue | Camada | Arquivo | |--------|---------| | DTOs | `SeaHaven.Services/DTOs/WorkOrderBoardDTOs.cs` | | Validação | `SeaHaven.Services/Validation/WorkOrderBoardCreateValidation.cs` | | Scheduling compartilhado | `SeaHaven.Services/Helpers/WorkOrderBoardFieldMutations.cs` | | Create service | `SeaHaven.Services/Implementation/WorkOrderBoardCreateService.cs` | | Cancel service | `SeaHaven.Services/Implementation/WorkOrderBoardCancelService.cs` | | Audit | `IWorkOrderAuditService.StageCreated`, `LogCreatedAsync` | | API | `WorkOrderController` — `POST board`, `POST {id}/cancel`, `DELETE` Admin | | Testes | `WorkOrderBoardCreateServiceTests`, `WorkOrderBoardCancelServiceTests`, `WorkOrderBoardCreateSyncLockTests` | --- ## Gates de aceite ### Desenvolvimento - [x] `POST /api/workorders/board` — Incomplete / auto-schedule / week-only - [x] WO# auto ou manual 11 dígitos + unicidade (409) - [x] Vendor/dispatch primário na criação - [x] Field locks + SyncRejected pós-create SHOC - [x] `POST /{id}/cancel` idempotente; PATCH bloqueado após cancel - [x] `DELETE DeleteWorkorder` restrito a Admin - [x] Legado `AddWorkorder` inalterado - [x] 68 testes unitários total (17 Fase 3 + 51 Fases 0–2) ### Staging (pendente) - [ ] UAT dispatcher: wizard + inline row - [ ] Smoke Sync durante criação SHOC - [ ] Confirmar navegação FE para semana do WO criado --- ## Próximo passo **Fase 4** — Busca contextual na semana + advanced search cross-week conforme tier volume.