5.4 KiB
ADR: Work Orders Board API (Fase 1 read-only)
Status: Proposed — spikes G0, G1, G3 concluídas
Data: 2026-06-19
Deciders: Backend + FE (aprovação Fase 1 pending)
Contexto
O Schedule Board (FE) organiza work orders por ScheduledDate em visão semanal. O backend atual expõe GetWorkOrderList — listagem CRUD paginada sem janela temporal, sem siteCode, sem vendor inline, sem ScheduledDate na projeção.
Spikes de referência:
Decisão
1. Endpoint dedicado (sem projection=)
Criar endpoint separado para o Schedule Board. Não alterar GetWorkOrderList.
GET /api/workorders/GetWorkOrdersBoard
?scheduledFrom=2026-06-01
&scheduledTo=2026-06-07
&assignee=guid1&assignee=guid2
&search=BK5
&status=Open&status=InProgress
&locationId=42
Rota alias: GET /api/WorkOrder/GetWorkOrdersBoard (consistente com controller existente).
Rejeitado:
GET /api/workorders/tableGET /GetWorkOrderList?projection=table
2. Window-based fetch (sem paginação Fase 1)
| Abordagem | Fase 1 |
|---|---|
scheduledFrom + scheduledTo |
Obrigatório |
page / pageSize |
Ausente |
| Limite janela | Max 90 dias (validação server-side) |
Response envelope:
{
"scheduledFrom": "2026-06-01",
"scheduledTo": "2026-06-07",
"totalCount": 42,
"items": [
{
"row": { /* WorkOrderBoardRowDto */ },
"presentation": { /* BoardRowPresentation */ }
}
]
}
3. DTO — domínio vs presentation
WorkOrderBoardRowDto — dados persistidos / joináveis:
public record WorkOrderBoardRowDto(
int Id,
string? WoNumber,
string? SiteCode,
string? LocationLabel,
DateOnly? ScheduledDate,
TimeOnly? ScheduledStart,
DateOnly? DueDate,
string? WorkOrderType,
string? ServiceType,
string? AssigneeId,
string? AssigneeName,
string? VendorCompany,
string? VendorTechnician,
string Status,
string? PocName,
string? PocPhone
);
BoardRowPresentation — calculado no mapper backend:
public record BoardRowPresentation(
string StatusDisplay,
bool IsPastDue,
string? WorkOrderTypeDisplay
);
Mapper: IWorkOrderBoardMapper centraliza Past Due e type display.
4. Query EF (Fase 1)
- Filtro base:
istemplate != true - Janela:
ScheduledDate >= from && ScheduledDate < to.AddDays(1) - Includes:
Locations,AssignToUser,Dispatches→Vendor(1 vendor por WO — first active dispatch) - POC:
WorkOrderContacts(primary) - Service type:
Tradeou category join (mínimo:Trade) - Não incluir subquery
lastUpdated(Comments/AuditLog)
5. Search (conforme spike)
- Search scoped à janela temporal
- Servidor:
StartsWithemInternalWONumber,SiteCode - Count ≤ 300: retornar semana inteira; FE filtra title/location/serviceType
- Count > 300: exigir termo indexável ou retornar 400
- Assignee:
assigneeId[]multi — não filtrar por nome
Índices (migration separada pós-aprovação):
IX_workOrders_ScheduledDate(P1)IX_workOrders_InternalWONumber,IX_workOrders_SiteCode(P2, após fix tamanho coluna)
6. WorkOrderType
- Mapear coluna legada
WorkOrderType varchar(50)na entidade EF - Expor em
WorkOrderBoardRowDto IsPastDueem presentation — não confundir com tipo- Enum C# adiado até amostragem prod
7. Status mapping
- API retorna status canônico (
Open,InProgress,Completed,Cancelled,OnHold) StatusDisplayviaIWorkOrderStatusMapper(workshop G2 separado)- Sem migration de enum DB
Consequências
Positivas
- 1 HTTP call por mudança de semana (FE)
- Contrato estável para board sem acoplar CRUD legado
- Performance previsível (janela + threshold 300)
- Blazor legado inalterado (EF local)
Negativas / trade-offs
- Duplicação parcial de lógica de listagem vs
GetWorkOrderList(aceitável — bounded contexts distintos) - Vendor technician pode ser null Fase 1 se dispatch spike incompleto
- Setup DB local necessário para validação integrada
Compliance com spikes
| Spike | Gate | Resultado |
|---|---|---|
| Consumer Audit | G0 | GetWorkOrderList órfão no repo — endpoint dedicado aprovado |
| WorkOrderType | G1 | Mapear coluna EF; Overdue = presentation |
| Search Strategy | G3 | Window-first, threshold 300, StartsWith identificadores |
Fora de escopo Fase 1
- PATCH inline edit
- Completion doc, media, flags, reorder
- Search vendor/tech server-side
- Paginação cross-week
- Alteração de
GetWorkOrderList
Próximos passos (implementação)
- Aprovação explícita Fase 1 only
- Migration:
WorkOrderType+ índiceScheduledDate GetWorkOrdersBoardemWorkOrderController+WorkOrderDataService- DTOs +
IWorkOrderBoardMapper - FE:
useWorkOrdersBoard({ from, to })substituindoRAW_ORDERS
Referências
- Gap analysis v3:
work_orders_api_gap_analysis_ce735946.plan.md WorkOrderController.csWorkOrderDataService.csDispatchController.List— padrão janela temporal