shoc-backend/docs/auditoria-work-orders-api-vs-frontend.md

300 lines
16 KiB
Markdown
Raw Normal View History

# Auditoria — API Work Orders vs Frontend (WorkOrders.tsx)
**Data:** 2026-06-19
**Fonte da verdade (FE):** `seahaven.desing/src/pages/WorkOrders.tsx`
**Backend auditado:** `seaheven.api`
---
## Resumo executivo
| Métrica | Valor |
|---------|-------|
| Compatibility Score | **24/100** |
| Backend Readiness | **Not Ready** |
| Confidence | **High** |
A API atual foi construída para **CRUD legado** (listagem paginada, detalhe rico, dispatch). O frontend `WorkOrders.tsx` define um **Schedule Board operacional** (visão semanal, 14 colunas, edição inline, advanced search, completion doc WO-level). Os contratos são fundamentalmente diferentes.
**O que funciona hoje:** id, dueDate, location (parcial), CRUD básico, detalhe rico, comments, audit field-based, mídia WO-level, reatribuição, dispatch.
**O que falta:** listagem window-based, 14 colunas board, PATCH inline, campos derivados, advanced search, completion doc WO-level, jobs Past Due/Carried Over, status 10 labels, WO# 11 dígitos.
---
## Endpoints que existem hoje
| Endpoint | O que entrega |
|----------|----------------|
| `GET GetWorkOrderList` | Lista **paginada** (default 12): id, número, título, location, priority, status, dueDate, assignedTo, lastUpdated |
| `GET Getworkorders` / `GetworkordersDD` | Lista legada para admin/dropdown |
| `GET GetFilteredWorkorder` / `2` | Filtros legados (assignee, location, priority, status, due date buckets) — carrega tudo em memória |
| `GET GetWorkorderById` / `{id}` | Detalhe: título, descrição, datas, status, problem, trade, POC, attachments, comments, audit, dispatches |
| `POST AddWorkorder` | Criação via form: título, descrição, assignee, dueDate, location, priority, mídia, contacts/categories |
| `POST/PUT EditWorkorder` | Update parcial via form (subset mapeado ao service) |
| `POST ChangeStatus` | Muda status (string livre) + audit |
| `POST ChangeAssignment` | Reatribui dispatcher + audit |
| `POST AddComment` / `AddCommentJson` | Comentário + upload opcional |
| `GET GetComments` / `GetCommentsByWorkorderId` | Lista comentários |
| `DELETE DeleteWorkorder` | **Delete físico** (não soft cancel) |
| Dispatch endpoints | Vendor, checklist, signoff, verify — **nível dispatch**, não board |
**Lookups relacionados (outros controllers):** `Common/GetUsers`, `Location/GetLocationList`, `Vendor/GetVendorList` — paginados, contrato diferente do FE.
**Documentado mas não implementado no código:** `GET GetWorkOrdersBoard`, `IWorkOrderBoardMapper`, `IWorkOrderStatusMapper` (ver `docs/adr/work-orders-board-api.md`).
---
## 1. Listagem da tela principal (visão semanal)
| Frontend espera | API entrega hoje | Gap |
|-----------------|------------------|-----|
| 1 call por semana (`weekStart` / `weekEnd`) | Paginação `page` / `pageSize` sem janela temporal | Sem fetch window-based |
| WOs agrupados por dia (Mon–Fri) | Lista flat paginada | Sem `scheduledDate` na projeção de listagem |
| Seção **Unscheduled** sempre visível no topo | Só WOs da página atual | Sem WOs sem data retornados independente da semana |
| `targetWeek` (scheduling week-only) | Campo inexistente na entidade/API | Não implementado |
| Contador `X of Y` da semana ativa | `totalCount` global paginado | Contagem inadequada para o board |
| Skeleton Mon–Fri vazios | N/A (FE-only) | OK — responsabilidade do FE |
**Evidência BE:** `WorkOrderDataService.GetWorkOrderListPagedAsync` — projeção L163-183, sem `ScheduledDate`, `SiteCode`, vendor, type.
---
## 2. Colunas da tabela (`COLS`)
Definidas no FE em `WorkOrders.tsx` L4234-4249.
| Coluna FE | Campo(s) FE | API hoje | Gap |
|-----------|-------------|----------|-----|
| Grip | drag reorder | Ausente | FE local-only; BE não persiste ordem |
| Flag | flag pessoal | Ausente | FE session-only |
| **SITE** | `site`, `pocName`, `pocPhone`, `pocNotes` | List: `location` (name); Detail: POC via `WorkOrderContacts` | Sem `siteCode` na listagem; sem POC na lista |
| **WO** | `woNumber`, `rescheduleCount`, `carriedOver` | `internalWONumber` / `workerOrderNumber` | Formato ≠ 11 dígitos; sem badges ↻ ↷N |
| **TYPE OF WO** | `type` (PM / Reactive / Emergency / Add-On / Overdue) | Não exposto (`WorkOrderType` em `db.txt`, não mapeado em EF) | Coluna inoperante |
| **ASSIGNED TO** | `dispatcherId` + avatar/cor | `assignedTo` (nome completo); `AssignTo` = GUID Identity | Sem id/initials/color para avatar |
| **SCHEDULE ON** | `scheduledOn`, `targetWeek` | Detail: `scheduledDate`; **list: ausente** | Listagem sem data de agendamento |
| **DUE DATE** | `dueDate` | `dueDate` | **OK** na listagem |
| **SERVICE** | `pm` | Detail: `trade` / `problem`; list: ausente | Coluna vazia na lista |
| **VENDOR** | `company`, `tech`, `techPhone` | Detail: `dispatches` → vendor; list: ausente | Coluna vazia na lista |
| **APPT TIME** | `apptTime` (ex: "07:00 – 09:00") | Entidade: `ScheduledStart`; sem `ScheduledEnd` | Janela de horário incompleta |
| **STATUS** | 10 labels + overlay Past Due | String legada DB + enum 5 valores API | Sem mapping para status operacionais do FE |
| **COMP DOC** | `docStatus` (Yes / No / NN) | Ausente no WO | Completion existe só no **dispatch** (`VerifyDispatch`) |
| Actions | View / Edit | `GetWorkorderById` separado | Detalhe existe; contrato diferente |
---
## 3. Filtros e busca
### Barra principal (FE L5083-5099)
| Frontend | API hoje | Gap |
|----------|----------|-----|
| **Dispatcher** multi-select + `__unassigned__` + default usuário logado | `assignee` single string; `__unassigned` suportado em `GetWorkOrderList` | Multi-select e default "My WOs" não cobertos |
| **Semana ativa** | Sem filtro `ScheduledDate` | Filtro semanal inexistente |
| **Tipo** (All / PM / Reactive / Emergency / Add-On / Overdue) | Sem filtro por work order type | Inexistente |
| **Busca contextual** na semana (site, wo#, dispatcher, location, pm, company, tech, status) | Search em 5 campos (`InternalWONumber`, `WorkerOrderNumber`, `Title`, `Location`), escopo global paginado | Campos e escopo incompatíveis |
### Advanced Search (FE L4255-4324)
| Frontend | API hoje | Gap |
|----------|----------|-----|
| Lista plana cross-week | Ausente | Sem endpoint dedicado |
| Date range (this-week, last-week, this-month, last-3-months, next-week, next-month, custom) | `GetFilteredWorkorder` com due date buckets legados | Semântica diferente |
| Filtros: sites[], types[], dispatchers[], statuses[], pmTypes[], vendorTechs[], docs[] | Parcial em endpoints legados | Cobertura incompleta |
---
## 4. Edição inline (spreadsheet-style)
| Frontend | API hoje | Gap |
|----------|----------|-----|
| PATCH por campo (site, wo#, type, dispatcher, schedule, due, service, vendor, appt, status…) | `EditWorkorder` (form multipart) + `ChangeStatus` + `ChangeAssignment` separados | Sem update granular por campo |
| Auto-schedule: `Incomplete` → `Scheduled` quando data + dispatcher | Ausente no BE | Regra `maybeAutoSchedule` só no FE |
| `rescheduleCount++` ao mudar `scheduledOn` | Campo inexistente | Badge ↻ impossível |
| Limpar `isPastDue` ao reagendar para data futura | Campo derivado inexistente | Past Due não limpa via API |
| Bloquear mudança de status se `isPastDue` | `ChangeStatus` aceita qualquer string | Sem validação 422 |
| WO# único 11 dígitos normalizado | Gerador `WO-{yyyyMMdd}-{random}` ou sync sequencial `10000001` | Formato e unicidade incompatíveis |
| WO cancelado read-only | Sem enforcement no BE | Só lógica no FE (`WOSlideOver` L2380) |
**Evidência BE:** `WorkOrderController.Editworkorder` L182-192 mapeia subset para `UpdateWorkOrderDTO` — ignora `Problem`, `Trade`, `ScheduledDate`, `Source` apesar de existirem em `EditWorkorder_DTO`.
---
## 5. Criação de WO
| Frontend | API hoje | Gap |
|----------|----------|-----|
| Wizard `NewWOWizard` + inline `InlineRow` | `POST AddWorkorder` | Campos do wizard majoritariamente ausentes |
| Campos: site, type, scheduledOn, targetWeek, pm, company, tech, appt, POC | `CreateWorkOrderDTO`: title, description, assignTo, dueDate, location | Contrato incompleto |
| Status inicial `Incomplete` | Cria com `WorkOrderStatus.Open` | Status inicial diferente |
| `scheduleWeekOnly` / `targetWeek` | Ausente | Week-only scheduling impossível |
| Navegação automática para semana do WO criado | Depende de listagem semanal | Listagem incompatível |
---
## 6. Cancelamento
| Frontend | API hoje | Gap |
|----------|----------|-----|
| Soft cancel → status `Canceled` | `POST ChangeStatus(id, "Canceled")` manual | Sem endpoint `POST cancel` dedicado |
| WO cancelado não editável | Sem bloqueio no BE | Enforcement só no FE |
| Delete permanente (Admin) | `DELETE DeleteWorkorder` — delete físico | Comportamento diverge do soft cancel FE |
---
## 7. Slide-over (`WOSlideOver`)
| Tab FE | API hoje | Gap |
|--------|----------|-----|
| **Info** | `GET GetWorkorderById` | Omite `siteCode`; sem campos derivados FE (`isPastDue`, `rescheduleCount`, etc.) |
| **Comments** | `AddComment` + `GetCommentsByWorkorderId` | Formato FE `{authorId, text, time}` vs BE `{Commenttext, FirstName, Documents}` |
| **Audit Log** | `WorkOrderAuditLog` retornado no detalhe | Schema `{fieldName, oldValue, newValue, action}` vs FE `{type: manual\|system, dispatcherId?, action, time}` |
| **Completion Doc** | `SignOffName` / `SignOffAttachment` no WO; `VerifyDispatch` no dispatch | Sem template por service type, PDF, `docStatus` WO-level |
| **Extra Docs / Media** | `BeforPhoto`, `AfterPhoto`, `workOrderAttachments` | Sem `MediaFile.category`; sem API extra docs dedicada |
---
## 8. Regras de sistema (background jobs)
| Frontend assume | API hoje | Gap |
|-----------------|----------|-----|
| Job Past Due: `scheduledOn < hoje` + não terminal → `isPastDue=true` | Ausente (zero `BackgroundService` / Hangfire / Quartz no repo) | Flag nunca setada automaticamente |
| Job Carried Over: virada de semana → `carriedOver++` | Ausente | Contador ↷N nunca incrementado |
| Type Overdue promotion | Ausente | Filtro/tipo Overdue não automático |
| Sync APM | `SyncController` POST manual (DynamoDB → SQL) | On-demand, não integrado à UX do board |
---
## 9. Lookups (catálogos)
| FE usa (mock estático) | API relacionada | Gap |
|------------------------|-----------------|-----|
| `DISPATCHERS` (id, name, initials, color) | `Common/GetUsers` | Sem initials/color; paginado; ids são GUIDs |
| `SITE_OPTIONS` (code → city, state) | `Location/GetLocationList` | Sem lookup por site code; sem mapeamento BK5 → Dallas |
| `PM_TYPES` | Ausente | Sem catálogo de service types |
| `TECHNICIANS` (name, company, phone) | `Vendor/GetVendorList` | Sem endpoint technicians |
| `WEEK_RANGES` (Mon–Fri + flag LIVE) | Ausente | Sem endpoint weeks |
---
## Contrato de dados — campo a campo
| Campo FE (`WorkOrder`) | Campo BE | Status |
|------------------------|----------|--------|
| `id` | `WorkOrder.Id` | SUPPORTED |
| `woNumber` | `InternalWONumber` / `WorkerOrderNumber` | PARTIALLY_SUPPORTED — formatos conflitantes |
| `site` | `SiteCode` | PARTIALLY_SUPPORTED — entidade tem; API omite na listagem |
| `type` | — (`WorkOrderType` só em SQL) | NOT_SUPPORTED |
| `dispatcherId` | `AssignTo` (GUID) | PARTIALLY_SUPPORTED |
| `scheduledOn` | `ScheduledDate` | PARTIALLY_SUPPORTED — list omite |
| `dayGroup` / `dayLabel` | — | NOT_SUPPORTED (derivado) |
| `location` | `Locations.Name` | SUPPORTED (via join) |
| `pm` | `Trade` / `Problem` | PARTIALLY_SUPPORTED |
| `company` / `tech` | `Dispatches` → `Vendor` | PARTIALLY_SUPPORTED — não inline na lista |
| `techPhone` | — | NOT_SUPPORTED |
| `apptTime` | `ScheduledStart` (sem end) | PARTIALLY_SUPPORTED |
| `status` | `Status` (string livre) | PARTIALLY_SUPPORTED — 10 vs 5+ formatos |
| `docStatus` | — | NOT_SUPPORTED |
| `pocName` / `pocPhone` / `pocNotes` | `WorkOrderContacts` | PARTIALLY_SUPPORTED — notes ausente |
| `dueDate` | `DueDate` | SUPPORTED |
| `rescheduleCount` | — | NOT_SUPPORTED |
| `carriedOver` | — | NOT_SUPPORTED |
| `originalWeek` / `originalDate` | — | NOT_SUPPORTED |
| `isPastDue` | — | NOT_SUPPORTED (derivado) |
| `targetWeek` | — | NOT_SUPPORTED |
---
## Regras de negócio — FE vs BE
| Regra FE | Evidência FE | BE hoje | Status |
|----------|--------------|---------|--------|
| Auto-schedule Incomplete → Scheduled | `maybeAutoSchedule` L500-506 | Ausente | NOT_SUPPORTED |
| Reschedule incrementa contador | `updateScheduledOn` L4856 | Ausente | NOT_SUPPORTED |
| Reschedule limpa Past Due | L4851-4857 | Ausente | NOT_SUPPORTED |
| Status bloqueado em Past Due | `StatusCell` L1120-1159 | `ChangeStatus` sem validação | NOT_SUPPORTED |
| WO# único 11 dígitos | `findDuplicateWONumber` L4897 | Gerador random; `InternalNumberExistsAsync` sem normalização | NOT_SUPPORTED |
| Cancel → Canceled, read-only | `cancelWO` L4946; `WOSlideOver` L2380 | `ChangeStatus` manual | PARTIALLY_SUPPORTED |
| Carried over job semanal | Audit derivado L2414-2416 | Sem job | NOT_SUPPORTED |
| Completion doc por tipo de serviço | `CompDocDialog` L3902+ | Dispatch verify only | NOT_SUPPORTED (WO-level) |
| Emergency/Reactive → media flow | `isMediaWO` L332-334 | Mídia WO genérica | PARTIALLY_SUPPORTED |
---
## Diagrama — contrato atual vs esperado
```
HOJE (GetWorkOrderList) FRONTEND (WorkOrders.tsx)
───────────────────── ─────────────────────────
Paginação 12/page 1 call = semana inteira
Sem ScheduledDate Agrupamento Mon–Fri
Sem siteCode, type, vendor 14 colunas preenchidas
Sem unscheduled semantics Seção Unscheduled fixa
5 status legados 10 status + Past Due overlay
CRUD admin Schedule Board operacional
EditWorkorder (form) PATCH inline por célula
```
---
## Cobertura por área
| Área | Cobertura estimada |
|------|-------------------|
| Listagem board (visão semanal) | ~5% |
| Colunas da tabela (14) | ~15% |
| Filtros e busca | ~10% |
| Edição inline | ~0% |
| Criação (wizard/inline) | ~25% |
| Slide-over (detalhe) | ~40% |
| Completion doc WO-level | ~0% |
| Background jobs | ~0% |
| Lookups/catálogos | ~20% |
---
## Critical blockers
1. **Sem API window-based** para visão semanal + Unscheduled — tela principal não carrega.
2. **Contrato de listagem incompatível** com 14 colunas do board.
3. **Campos persistidos ausentes** — `rescheduleCount`, `carriedOver`, `targetWeek`, `docStatus`, `isPastDue`.
4. **Sem PATCH inline** — edição spreadsheet-style impossível.
5. **Advanced search cross-week inexistente.**
6. **Status 10 valores + Past Due** sem mapping layer implementado.
7. **Completion doc WO-level inexistente** — coluna COMP DOC inoperante.
8. **WO# 11 dígitos único** não suportado.
9. **Jobs Past Due / Carried Over ausentes.**
---
## Non-critical gaps
- Reorder intra-dia persistido (FE já local-only)
- Flags pessoais (FE session-only)
- Paginação 12/24/48/96 (FE não implementou)
- Bulk select (FE não implementou)
- `[Authorize]` comentado no `WorkOrderController` (risco de segurança)
---
## Referências
- Frontend: `seahaven.desing/src/pages/WorkOrders.tsx`
- Controller: `Api.SeaHavenIndustries/Controllers/WorkOrderController.cs`
- Data service: `SeaHaven.DataServices/Implementation/WorkOrderDataService.cs`
- Entidade: `Data.SeaHavenIndustries/Models/WorkerOrder.cs`
- ADR (proposto, não implementado): `docs/adr/work-orders-board-api.md`
- Spikes: `docs/spikes/consumer-audit-getworkorderlist.md`, `search-strategy.md`, `status-mapping.md`, `work-order-type.md`
---
## Classificação final
| Métrica | Valor |
|---------|-------|
| Compatibility Level | **Minimally Compatible** |
| Backend Readiness | **Not Ready** |
| Confidence | **High** |
Este documento reporta apenas **findings** — gaps entre o que a API entrega hoje e o que o frontend especifica. Não inclui propostas de implementação.