shoc-backend/docs/work-orders/phase-3/README.md

175 lines
5.5 KiB
Markdown
Raw Normal View History

# 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 <token>" \
-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 <token>" \
"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.