mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-10-01 07:33:14 +00:00
175 lines
5.5 KiB
Markdown
175 lines
5.5 KiB
Markdown
|
|
# 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.
|