mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 08:23:12 +00:00
154 lines
5.4 KiB
Markdown
154 lines
5.4 KiB
Markdown
# 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](../phase-0/README.md), [Fase 1](../phase-1/README.md)
|
||
|
||
---
|
||
|
||
## 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:**
|
||
|
||
```json
|
||
{
|
||
"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:**
|
||
|
||
```bash
|
||
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
|
||
|
||
- [x] `PATCH /api/workorders/{id}/board` para todos os campos da matriz
|
||
- [x] Dual RowVersion validado; 409 com `currentState`
|
||
- [x] Auto-schedule Incomplete→Scheduled
|
||
- [x] RescheduleCount++ em reagendamento
|
||
- [x] Bloqueio status quando PastDue; reagendar limpa flag on-read
|
||
- [x] WO cancelado/closed rejeita edição (422)
|
||
- [x] WO# normalizado 11 dígitos + unicidade
|
||
- [x] Vendor/appt muta Dispatch; cria primário se ausente
|
||
- [x] 1 audit event por campo; field lock criado
|
||
- [x] 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.
|