From 65c1cdc7e9fd4eba8e35f8405da3f3370bf74d72 Mon Sep 17 00:00:00 2001 From: Arthur Bassi Date: Mon, 29 Jun 2026 17:22:31 -0300 Subject: [PATCH] docs(work-orders): add Phase 0 governance documentation --- docs/auditoria-work-orders-api-vs-frontend.md | 299 ++++++ docs/roadmap-work-orders-board.md | 907 ++++++++++++++++++ docs/work-orders/phase-0/README.md | 32 + .../phase-0/adr-vendor-source-of-truth.md | 67 ++ .../phase-0/audit-event-contract.md | 106 ++ .../work-orders/phase-0/consumer-inventory.md | 85 ++ .../phase-0/dar-domain-architecture-review.md | 139 +++ .../dar-persisted-vs-derived-matrix.md | 54 ++ .../phase-0/data-ownership-model.md | 75 ++ .../phase-0/database-drift-report.md | 110 +++ .../phase-0/field-ownership-matrix.md | 86 ++ .../phase-0/manual-edit-flag-design.md | 87 ++ .../phase-0/migration-dry-run-report.md | 58 ++ .../phase-0/phase-0-gates-signoff.md | 46 + docs/work-orders/phase-0/rowversion-design.md | 36 + docs/work-orders/phase-0/smoke-checklist.md | 57 ++ .../phase-0/volume-discovery-report.md | 96 ++ 17 files changed, 2340 insertions(+) create mode 100644 docs/auditoria-work-orders-api-vs-frontend.md create mode 100644 docs/roadmap-work-orders-board.md create mode 100644 docs/work-orders/phase-0/README.md create mode 100644 docs/work-orders/phase-0/adr-vendor-source-of-truth.md create mode 100644 docs/work-orders/phase-0/audit-event-contract.md create mode 100644 docs/work-orders/phase-0/consumer-inventory.md create mode 100644 docs/work-orders/phase-0/dar-domain-architecture-review.md create mode 100644 docs/work-orders/phase-0/dar-persisted-vs-derived-matrix.md create mode 100644 docs/work-orders/phase-0/data-ownership-model.md create mode 100644 docs/work-orders/phase-0/database-drift-report.md create mode 100644 docs/work-orders/phase-0/field-ownership-matrix.md create mode 100644 docs/work-orders/phase-0/manual-edit-flag-design.md create mode 100644 docs/work-orders/phase-0/migration-dry-run-report.md create mode 100644 docs/work-orders/phase-0/phase-0-gates-signoff.md create mode 100644 docs/work-orders/phase-0/rowversion-design.md create mode 100644 docs/work-orders/phase-0/smoke-checklist.md create mode 100644 docs/work-orders/phase-0/volume-discovery-report.md diff --git a/docs/auditoria-work-orders-api-vs-frontend.md b/docs/auditoria-work-orders-api-vs-frontend.md new file mode 100644 index 0000000..b8a0ce6 --- /dev/null +++ b/docs/auditoria-work-orders-api-vs-frontend.md @@ -0,0 +1,299 @@ +# 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. diff --git a/docs/roadmap-work-orders-board.md b/docs/roadmap-work-orders-board.md new file mode 100644 index 0000000..4af57f4 --- /dev/null +++ b/docs/roadmap-work-orders-board.md @@ -0,0 +1,907 @@ +# Roadmap de Implementação — Work Orders API vs Frontend Board + +**Documento:** Relatório consolidado de arquitetura e entrega +**Fonte da verdade (frontend):** `seahaven.desing/src/pages/WorkOrders.tsx` +**Backend:** `seaheven.api` +**Auditoria base:** [auditoria-work-orders-api-vs-frontend.md](auditoria-work-orders-api-vs-frontend.md) +**Estado atual:** Compatibility Score **24/100** — backend **Not Ready** +**Nota técnica do programa:** **9.0/10** (meta kickoff enterprise **9.5+**) +**Escopo:** capacidades de negócio, domínio, governança, fases, riscos e validação — sem design de API, schema ou código + +--- + +## 1. Executive Summary + +### 1.1 Readiness geral + +O backend é **maduro para CRUD legado, dispatch, vendor portal, checklist/sign-off e audit**, mas foi construído para **listagem paginada administrativa**, não para o **Schedule Board operacional** que o frontend define. A tela principal do board **não pode funcionar** com os contratos atuais. + +| Dimensão | Avaliação | +|----------|-----------| +| Tela principal (visão semanal) | ~5% | +| 14 colunas do board | ~15% | +| Edição inline | ~0% | +| Completion doc WO-level | ~0% | +| Background jobs | ~0% | +| Detalhe / slide-over | ~40% | +| **Média ponderada** | **~24%** | + +### 1.2 Problemas arquiteturais principais + +1. **Paradigma de contrato incompatível** — paginação global vs. janela semanal com seção Unscheduled fixa. +2. **Projeção de listagem insuficiente** — 9 das 14 colunas do board não são alimentadas pela listagem atual. +3. **Ausência de camada de domínio operacional** — campos derivados, tipo de WO e status operacionais ausentes ou não expostos. +4. **Database drift** — `WorkOrderType` existe no SQL legado mas não no EF; possíveis outras colunas desalinhadas. +5. **Modelo de mutação inadequado** — form multipart vs. edição granular por célula. +6. **Sem automação de sistema** — zero infraestrutura de jobs; regras do FE nunca executam no servidor. +7. **Cinco consumidores paralelos** — SHOC, Blazor EF, Vendor Portal, Sync/Lambda, e-mails — sem governança de ownership. +8. **Duas trilhas paralelas** — Blazor acessa EF direto; API REST é integração do SHOC. + +### 1.3 Maior risco do programa + +**Governança de ownership de dados** — quem pode alterar cada campo entre SHOC, Vendor Portal, Sync, Blazor e Jobs. A arquitetura de domínio resolve fontes da verdade; a governança operacional é o gate final. + +### 1.4 Decisões de produto registradas + +- **Novas funcionalidades do board** → somente via API REST para o frontend React (SHOC). +- **Blazor (`SeaHavenIndustries`)** → não recebe board, inline edit, jobs, advanced search nem completion doc WO-level; manutenção mínima até sunset do módulo WO. +- **Vendor Portal** → permanece ativo; não-regressão obrigatória. +- **DynamoDB/Sync** → ponte temporária; aposentar após SHOC ingerir direto ([TODO.md](../TODO.md)). + +### 1.5 Complexidade e esforço + +| Métrica | Valor | +|---------|-------| +| Complexidade geral | **Alta** | +| Esforço total | **XL** (6–9 meses, 2 squads; ou 4–6 meses, 3 squads) | +| Fase 0 — Fundação domínio + governança | L | +| Fase 1 — Board semanal | L | +| Fase 2 — Edição inline + concorrência | L | +| Fase 3 — Criação e cancelamento | M | +| Fase 4 — Busca avançada | M | +| Fase 5 — Domain events (jobs) | M | +| Fase 6 — Completion doc e slide-over | M | +| Fase 7 — Rollout e produção | M | + +### 1.6 Gates bloqueantes da Fase 0 + +Nenhuma implementação de board inicia sem: + +1. Domain Architecture Review (DAR) aprovado +2. **Data Ownership Model** assinado +3. **Field Ownership Matrix** assinada +4. **Audit Event Contract** aprovado +5. Volume Discovery Report (sem premissa de volume) +6. Database drift report (SQL vs EF) +7. Spike vendor/dispatch +8. Data migration dry-run +9. RowVersion multi-agregado definido + +--- + +## 2. Capability Gap Assessment + +### 2.1 Weekly Scheduling Board — **Critical** +- **Atual:** paginação sem janela temporal, sem Unscheduled. +- **Desejado:** 1 call/semana, Mon–Fri + Unscheduled, contador `X of Y`. + +### 2.2 Work Order Lifecycle — **High** +- **Atual:** form parcial, status `Open`, delete físico. +- **Desejado:** wizard/inline completo, `Incomplete`, soft cancel read-only. + +### 2.3 Inline Editing — **Critical** +- **Atual:** `EditWorkorder` multipart; endpoints separados. +- **Desejado:** update granular por célula com regras embutidas. + +### 2.4 Search & Filtering — **High** +- **Atual:** 5 campos, escopo global, assignee single-select. +- **Desejado:** busca contextual na semana + advanced search cross-week. + +### 2.5 Status Management — **Critical** +- **Atual:** enum 5 valores + string livre; sem Past Due. +- **Desejado:** 10 labels + flag Past Due separada; auto-schedule; bloqueios. + +### 2.6 Completion Documentation — **High** +- **Atual:** sign-off básico; verify só no dispatch. +- **Desejado:** `docStatus` WO-level (Yes/No/NN); templates por serviço. + +### 2.7 Vendor Management (board) — **High** +- **Atual:** vendor só no detalhe via dispatch. +- **Desejado:** colunas VENDOR/APPT na lista; edição via dispatch primário. + +### 2.8 Scheduling Intelligence — **Critical** +- **Atual:** `ScheduledDate` no detalhe; sem `targetWeek`, `ScheduledEnd`. +- **Desejado:** agendamento semântico completo; badges ↻ e ↷N. + +### 2.9 Audit & Tracking — **Medium** +- **Atual:** audit field-based legado. +- **Desejado:** `{type: manual|system, dispatcherId?, action, time}` + eventos de sistema. + +### 2.10 Background Automation — **Critical** (como otimização, não fonte da verdade) +- **Atual:** zero jobs. +- **Desejado:** WeekRolled, cache opcional PastDue; domínio recalcula on-read. + +### 2.11 Lookup Data — **High** +- **Atual:** lookups paginados incompatíveis; sem PM types, technicians. +- **Desejado:** dispatchers (initials/color), sites por code, PM types, technicians. + +### 2.12 Reporting / Contadores — **Medium** +- **Atual:** `totalCount` global. +- **Desejado:** `X of Y` semanal. + +### 2.13 Security — **High** +- **Atual:** `[Authorize]` comentado no `WorkOrderController`. +- **Desejado:** auth ativa; default "My WOs". + +--- + +## 3. Dependency Map + +```mermaid +flowchart TD + subgraph phase0 [Fase0_Fundacao] + DAR[DAR e 3 artefatos] + DOM[Agregados dominio] + VOL[Volume Discovery] + OWN[Data Ownership] + end + subgraph phase1 [Fase1_Board] + B1[Listagem semanal] + B2[14 colunas] + end + subgraph phase2 [Fase2_Mutacao] + M1[Inline edit] + M2[Concorrencia] + end + subgraph phase3 [Fase3_Lifecycle] + L1[Criacao wizard] + L2[Cancelamento] + end + subgraph phase45 [Fase4_5] + S1[Search] + E1[Domain events] + end + subgraph phase6 [Fase6_Completion] + C1[DocStatus slideover] + end + DAR --> DOM + DOM --> B1 + OWN --> M1 + VOL --> B1 + B1 --> B2 --> M1 + M1 --> L1 + B2 --> S1 + DOM --> E1 + B2 --> C1 +``` + +**Ordem obrigatória:** Fase 0 → Board → Inline → Lifecycle → Search/Events → Completion → Rollout. + +--- + +## 4. Domain Model + +### 4.1 Agregados e limites (anti God Entity) + +```mermaid +flowchart TB + subgraph root [Aggregate Root WorkOrder] + CORE[Core Lifecycle Assignment] + SCH[Scheduling slice] + TRK[Tracking slice] + COMP[Completion slice] + ANA[Analytics slice] + end + subgraph separate [Agregado separado] + DISP[Dispatch fonte vendor] + end + AUD[WorkOrderAuditLog append-only] + CORE --> SCH + CORE --> TRK + CORE --> ANA + CORE --> COMP + CORE --> DISP + CORE --> AUD +``` + +| Agregado | Responsabilidade | +|----------|------------------| +| **WorkOrder (core)** | Id, WoNumber, Type, LifecycleStatus, SiteCode, LocationId, DueDate, AssignTo, Title, RowVersion | +| **Scheduling slice** | ScheduledDate, ScheduledStart/End, TargetWeek, ScheduleWeekOnly | +| **Tracking slice** | OriginalWeek, OriginalDate (set-once) | +| **Analytics slice** | RescheduleCount, CarriedOver (contadores históricos) | +| **Completion slice** | DocStatus, refs attachments | +| **Dispatch** | Vendor, tech, status portal — **fonte da verdade vendor** | +| **Audit** | Rastreabilidade imutável | + +**Proibido:** `VendorId`, `TechName`, `TechPhone` canônicos na WO. + +### 4.2 Scheduling Aggregate Growth Watchlist + +Campos que **não** entram em Scheduling sem ARB review: `MoveReason`, `MoveUser`, `MoveCategory`, `MoveSource`. Metadados de movimentação → Audit. Gate: slice Scheduling > 8 campos operacionais → ARB obrigatório. + +### 4.3 Campos reaproveitados (sem nova coluna) + +`SiteCode`, `ScheduledDate`, `DueDate`, `AssignTo`, `Trade`/`Problem`, `LocationId`, POC via `WorkOrderContacts`. + +### 4.4 Database drift conhecido + +| Coluna SQL | Ação | +|------------|------| +| `WorkOrderType` | Mapear no Core | +| `AvettaTask` | Inventariar; mapear ou deprecar | +| `AssignDate` | Inventariar; mapear ou deprecar | + +Gate Fase 0: diff completo SQL vs `ApplicationDbContextModelSnapshot`. + +### 4.5 Entidades relacionadas + +| Entidade | Mudança | Fase | +|----------|---------|------| +| `WorkOrderContacts` | `Notes` (pocNotes) | 0 | +| `WorkOrderAuditLog` | EventType Manual/System/Vendor/Sync | 0 | +| `ApplicationUser` | Initials, Color | 0–1 | +| `Locations` | SiteCode | 0–1 | +| `WorkOrderAttachments` | Category | 2–6 | +| `WorkOrderEnums` | LifecycleStatus, WorkOrderType, DocStatus, OperationalFlags | 0 | +| `Dispatch` | Proteção regression | contínuo | + +Fora de escopo imediato: `FollowUps`, `Quotes`, `PMSchedules`, `Employee`, `WorkOrderCategories` no board. + +--- + +## 5. Domain Architecture Review (DAR) + +### 5.1 Fonte da verdade por conceito + +| Conceito | Fonte da verdade | Proibido | +|----------|------------------|----------| +| Status | `LifecycleStatus` (10 valores) | String livre; status composto | +| Past Due | `OperationalFlags.PastDue` | Misturar no enum lifecycle | +| Vendor/Tech | Dispatch primário | Duplicar na WO | +| Schedule | Scheduling slice | Duplicar no core | +| Completion | Completion.DocStatus | Só inferir de dispatch | +| Reschedule/Carried | Analytics + audit | Job blind overwrite | +| WO# | Campo canônico único | Dois números sem regra | +| POC | WorkOrderContacts | Só no detalhe | + +### 5.2 Matriz Persistido vs Derivado + +| Campo FE | Persistido | Derivado | Fonte / Regra | +|----------|------------|----------|---------------| +| `lifecycleStatus` | Sim | Não | Core | +| `isPastDue` | Não | **Sim** | `ScheduledDate < hoje` AND NOT terminal; job = cache opcional | +| `type` Overdue | Não | **Sim** | Regra type + schedule + status | +| `carriedOver` | Sim | Não | Métrica histórica; incremento via `WeekRolled` | +| `rescheduleCount` | Sim | Parcial | Domain event em mudança de `ScheduledDate` | +| `docStatus` | Sim | Não | Completion slice | +| `dayGroup`, `apptTime` | Não | Sim | Calculados na leitura | +| vendor/tech | Via Dispatch | Projeção | Join dispatch primário | + +**Regra de ouro:** Jobs nunca são a única fonte da verdade. Leitura sempre recalcula derivados. + +### 5.3 Lifecycle Status vs Operational Flags + +```text +LifecycleStatus → enum único (10 valores FE) +OperationalFlags → PastDue, (futuro: Escalated) +``` + +Filtro status = LifecycleStatus. Overlay Past Due = flag. Bloqueio de edição = validação sobre flag. + +### 5.4 CarriedOver — justificativa + +| | `isPastDue` | `carriedOver` | +|--|-------------|---------------| +| Natureza | Estado pontual | **Métrica histórica acumulada** | +| Persistir | Não (derivado) | **Sim** (contador) | +| Por quê | Calculável da data atual | Não recuperável só do schedule atual | + +Invariante: incremento **somente** via domain event `WeekRolled`. + +### 5.5 Vendor / Dispatch — spike obrigatório (Fase 0) + +1. WO mantém `PrimaryDispatchId` +2. Colunas VENDOR = projeção do dispatch primário +3. Edição inline vendor = mutação no Dispatch +4. WO sem dispatch → edição cria dispatch primário + +Entregável: ADR "Vendor Source of Truth". Critério: zero `Tech*` canônicos na WO. + +### 5.6 Jobs — domínio primeiro + +| Job | Papel | +|-----|-------| +| Past Due diário | Cache refresh opcional; on-read sempre correto | +| Carried Over semanal | Dispara `WeekRolled` → incrementa contador + audit system | +| Promoção Overdue | Derivado on-read | +| Audit | Síncrono em toda mutação desde Fase 0 | + +### 5.7 Database drift investigation + +Diff SQL vs EF; classificar: mapear, deprecar, mover para agregado satélite. Tabelas: `workOrders`, `Comments`, `Locations`, `Dispatch`. + +--- + +## 6. Data Ownership Model + +### 6.1 Ownership por ator + +| Ator | Pode alterar | Não pode alterar | +|------|--------------|------------------| +| **SHOC** | Lifecycle, Schedule, Assignment, Completion, vendor via Dispatch, POC, WO# | Checklist/signoff vendor | +| **Vendor Portal** | Dispatch status, checklist, signoff, comments | Schedule, lifecycle, assignment, DueDate | +| **Sync/Lambda** | Campos ingest (Description, ExternalId, etc.) — só owner Sync | Campos SHOC-owned após 1ª edição manual | +| **Blazor** | Campos legados até sunset | Campos novos board | +| **Jobs** | Incremento CarriedOver; audit system | Lifecycle, Schedule, Dispatch | + +### 6.2 Field Ownership Matrix + +| Campo | Agregado | Owner escritor | Sync sobrescreve? | Conflito SHOC vs Sync | +|-------|----------|----------------|-------------------|----------------------| +| LifecycleStatus | Core | SHOC | Não após 1ª edição SHOC | SHOC vence | +| AssignTo | Core | SHOC | Não após 1ª edição SHOC | SHOC vence | +| ScheduledDate / janela | Scheduling | SHOC | Não | SHOC vence | +| TargetWeek | Scheduling | SHOC | Não | SHOC vence | +| DueDate | Core | SHOC + Sync | Sim se nunca editado SHOC | SHOC se ManualEditFlag | +| Description | Core | SHOC + Sync | Merge / SHOC priority | SHOC se editado | +| SiteCode / Location | Core | SHOC + Sync | Sim na criação; não após SHOC | SHOC após flag | +| WorkOrderType | Core | SHOC | Sim na criação ingest | SHOC após flag | +| WoNumber | Core | SHOC | Não | SHOC only | +| Vendor / tech | Dispatch | SHOC + Vendor | Não | Domínios separados | +| Checklist/signoff | Dispatch | Vendor | Não | Vendor vence | +| DocStatus | Completion | SHOC | Não | SHOC only | +| RescheduleCount / CarriedOver | Analytics | Domain events | Não | N/A | +| ExternalWorkOrderId | Core | Sync | Sim (idempotente) | Sync only | + +**ManualEditFlag:** primeira edição SHOC marca campo; Sync subsequente skip + audit `SyncRejected`. + +### 6.3 Sync merge — cenários + +| Cenário | Comportamento | +|---------|---------------| +| Sync DueDate + SHOC ScheduledDate | Merge — campos distintos | +| Sync Status + SHOC Status | SHOC vence; sync skip | +| Sync AssignTo + SHOC AssignTo | SHOC vence | +| Sync Description + SHOC Description | SHOC vence se ManualEditFlag | +| Sync cria WO novo | Full ingest | +| Sync atualiza por ExternalWorkOrderId | Field-level merge por matriz | + +--- + +## 7. Concurrency Strategy + +### 7.1 Atores + +Dispatcher (SHOC), Vendor (Portal), Sync/Lambda, Blazor (congelado), Jobs. + +### 7.2 RowVersion multi-agregado + +| Agregado | RowVersion | Quando muda | +|----------|------------|-------------| +| **WorkOrder (root)** | No Core | Mutação Core, Scheduling, Completion, Analytics na mesma transação | +| **Dispatch** | Próprio | Mutação vendor/tech/checklist/status dispatch | +| **Projeção board** | N/A | FE envia `workOrderVersion` + `dispatchVersion` | + +**Propagação:** +- SHOC edita ScheduledDate → bump WO.RowVersion +- SHOC edita vendor → bump Dispatch.RowVersion +- Vendor edita checklist → bump Dispatch.RowVersion only +- Sync altera Description → bump WO se merge aplicado +- DocStatus → bump WO (Completion na mesma transação) + +Conflito → 409 com `currentState` para refresh. + +### 7.3 Concurrency Ownership Rules + +| Cenário | Resultado | +|---------|-----------| +| SHOC vs SHOC (mesmo campo) | 409; último RowVersion válido | +| SHOC vs Sync (mesmo campo) | Vence owner do campo | +| SHOC vs Sync (campos diferentes) | Merge | +| Vendor vs Sync | Domínios separados | +| SHOC vs Vendor | Merge se campos distintos; impossível mesmo conceito por design | +| Job WeekRolled vs SHOC reschedule | Idempotência WO+semana | +| Blazor vs SHOC | SHOC vence; Blazor congelado | + +### 7.4 Testes + +Fase 2: conflito paralelo (2 dispatchers, sync durante edição); dual RowVersion; audit count = fields changed. + +--- + +## 8. Audit Event Contract + +### 8.1 Princípios + +- Toda mutação gera audit **síncrono** desde Fase 0 +- Edição inline = **1 evento por campo** +- Audit para investigação operacional, não só compliance + +### 8.2 Schema por evento + +| Campo | Obrigatório | +|-------|-------------| +| WorkOrderId | Sim | +| EventType | Sim: Manual / System / Sync / Vendor | +| Action | Sim: FieldChanged, StatusChanged, WeekRolled, SyncRejected | +| FieldName | Sim para inline | +| OldValue, NewValue | Sim | +| ActorId, ActorType | Sim para Manual | +| Timestamp | Sim UTC | +| CorrelationId | Recomendado | +| DispatchId | Quando evento é no dispatch | + +### 8.3 Mutação → eventos + +| Mutação | Eventos | +|---------|---------| +| ChangeStatus | 1× StatusChanged | +| Inline cell | 1× FieldChanged por campo | +| Auto-schedule | StatusChanged + FieldChanged se aplicável | +| Reschedule | FieldChanged + RescheduleCount + audit analytics | +| WeekRolled | System WeekRolled + CarriedOver old/new | +| Sync skip | Sync SyncRejected | +| Vendor checklist | Vendor FieldChanged no Dispatch | + +### 8.4 Contrato FE (Audit tab) + +`{ type, dispatcherId?, action, fieldName?, oldValue, newValue, time }` + +--- + +## 9. Data Migration Plan + +### 9.1 Status + +| Legado | LifecycleStatus alvo | +|--------|------------------------| +| Open | Incomplete | +| InProgress | In Progress | +| Completed | Complete | +| Cancelled | Canceled | +| OnHold | On Hold | +| Desconhecido | Incomplete + NeedsReview | + +Processo: dry-run staging; 100% classificados; `LegacyStatus` read-only 1 release; rollback documentado. + +### 9.2 WO# + +| Formato legado | Estratégia | +|----------------|------------| +| Sequencial sync `10000001` | Normalizar 11 dígitos | +| `WO-{date}-{random}` | Coexistência; novos só 11 dígitos | +| Duplicatas | Resolução manual pré-go-live | + +### 9.3 WorkOrderType + +Mapear SQL existente → enum; NULL → default PO; Overdue = derivado. + +### 9.4 Rollback + +Migrations reversíveis; feature flag SHOC; snapshot pré-migration. + +--- + +## 10. Volume Discovery e Search Scalability + +### 10.1 Volume Discovery (Fase 0 — gate Fase 1) + +**Sem premissa de volume.** Métricas obrigatórias: + +| Métrica | Gate | +|---------|------| +| Total WOs produção/staging | Obrigatório | +| WOs/semana (p95) | Obrigatório | +| WOs Unscheduled | Obrigatório | +| Crescimento mensal | Desejável | + +### 10.2 Tiers e impacto + +| Tier | Total WOs | Listagem semanal | Advanced search | +|------|-----------|------------------|-----------------| +| **S** | < 25k | Índices simples | SQL filtros compostos | +| **M** | 25k–250k | Índices covering | Paginação obrigatória | +| **L** | 250k–1M | Read model candidato | Full-text | +| **XL** | > 1M | Materialized view | Search dedicado (ARB) | + +Fases 1–3: índices conservadores compatíveis com qualquer tier. Fase 4: implementação conforme tier + load test. + +--- + +## 11. Coexistência Multi-Consumidor + +### 11.1 Inventário + +| Consumidor | Conexão | Status | +|------------|---------|--------| +| **SHOC React** (`seahaven.desing`) | REST JWT | Em desenvolvimento — **único alvo novas features** | +| **Blazor** (`SeaHavenIndustries`) | EF direto, não REST | Ativo — manutenção mínima; sunset WO | +| **Vendor Portal UI** | REST `X-Vendor-Token` | API ativa; UI externa | +| **SyncController + Lambda** | DynamoDB → SQL | Ponte temporária | +| **E-mails SendGrid** | Deep links SHOC/Portal | Ativo | +| **Mobile** | — | Sem evidência — confirmar com PO | + +```mermaid +flowchart TB + SHOC[SHOC React] -->|REST JWT| API[Api REST] + VPortal[Vendor Portal] -->|X-Vendor-Token| API + Lambda[Lambda ingest] --> DynamoDB[(DynamoDB)] + DynamoDB --> Sync[SyncController] + Sync --> DB[(SQL Server)] + API --> DB + Blazor[Blazor EF] -->|legado| DB +``` + +**Nota:** DynamoDB é **ingestão externa**, não banco operacional. SQL Server é fonte para board, Blazor e Portal. + +### 11.2 Matriz de coexistência + +| Consumidor | Novas features board | Schema | Sunset | +|------------|---------------------|--------|--------| +| SHOC | Recebe tudo | Consome novos campos | Destino final | +| Blazor | Não recebe | Colunas nullable; backward compatible | Módulo WO congela | +| Vendor Portal | Não usa board | Dispatch intacto | Mantém | +| Sync/Lambda | Indireto | Defaults para novos campos | Após cutover API | +| E-mails | Links SHOC | FrontendBaseUrl prod | Atualizar templates | + +### 11.3 Trilhas de entrega + +- **Trilha A SHOC:** fases 0–7; feature flags; piloto dispatchers +- **Trilha B Blazor:** smoke EF pós-migration; sem board; bugfix crítico only +- **Trilha C Vendor Portal:** regression suite cada release que toca WO/Dispatch +- **Trilha D Lambda:** manter até SHOC produção; cutover Fase 7; critério: criação WO + auth serviço estáveis + +### 11.4 Endpoints legado vs board + +- Legado (`GetWorkOrderList`, etc.): manter durante coexistência; congelar contrato +- Board: novos contratos; não estender legado com hacks +- Deprecação: após 100% dispatchers SHOC + sunset Blazor WO + +### 11.5 Ordem de rollout + +1. Fase 0 staging — gates + migrations +2. Gate Blazor smoke EF +3. Fase 1 staging — SHOC board feature flag +4. Gate Vendor Portal regression +5. Fases 2–6 incrementais +6. Piloto 1–2 dispatchers +7. Rollout gradual +8. Sunset Blazor WO +9. Cutover Lambda → API +10. Deprecação legado + SyncController + +### 11.6 Monitoramento coexistência + +Métricas por consumidor; drift DynamoDB vs SQL; adoção SHOC vs Blazor; alertas job failure. + +### 11.7 Checklist pré-go-live PO + +- App mobile externo? +- Data sunset Blazor WO +- FrontendBaseUrl produção → SHOC +- Owner Lambda cutover + +--- + +## 12. Implementation Phases + +### Fase 0 — Domain Foundation + Governança + +**Objetivo:** Kickoff enterprise — domínio, ownership, volume, audit antes de board. + +**Escopo:** DAR; 3 artefatos (Ownership, Field Matrix, Audit Contract); Volume Discovery; drift SQL/EF; spike vendor; agregados Core+Scheduling+Tracking+Analytics+Completion; enums; RowVersion root+Dispatch; migration dry-run; ManualEditFlag design; audit baseline ChangeStatus/ChangeAssignment; smoke Blazor/Sync/Portal. + +**Não inicia:** Board API, inline edit, jobs. + +**Validação:** 9 gates assinados; audit granularidade testada; tier volume definido. + +--- + +### Fase 1 — Weekly Board (leitura) + +**Objetivo:** Tela principal carrega semana operacional. + +**Escopo:** Listagem window-based; Unscheduled; 14 colunas como projeção; `X of Y`; filtros (dispatcher multi, `__unassigned__`, My WOs, tipo, semana); `isPastDue` derivado on-read; vendor via dispatch primário; índices conforme tier volume. + +**Validação:** 1 call = semana + Unscheduled; 12/14 colunas mínimo; gate Vendor Portal regression. + +--- + +### Fase 2 — Inline Edit + Concorrência + +**Objetivo:** Edição spreadsheet com regras e conflitos tratados. + +**Escopo:** Update granular; dual RowVersion; audit 1 evento/campo; auto-schedule; rescheduleCount++; bloqueio PastDue flag; cancel read-only; WO# 11 dígitos; testes concorrência. + +--- + +### Fase 3 — Criação e Cancelamento + +**Escopo:** Wizard/inline completo; Incomplete inicial; targetWeek; soft cancel; ManualEditFlag na criação; delete admin documentado. + +--- + +### Fase 4 — Search + +**Escopo:** Busca contextual semana; advanced search cross-week conforme tier; load test; sem implementar advanced sem tier definido. + +--- + +### Fase 5 — Scheduled Domain Events + +**Escopo:** WeekRolled → CarriedOver++; cache opcional PastDue; idempotência; monitoramento falha; on-read sempre correto. + +--- + +### Fase 6 — Completion Doc e Slide-over + +**Escopo:** docStatus WO-level; templates por serviço; Comments/Audit/Media alinhados ao FE. + +--- + +### Fase 7 — Rollout e Produção + +**Escopo:** Piloto; rollout gradual; sunset Blazor WO; cutover Lambda; deprecação legado; UAT; zero P1 duas semanas; dashboards saúde. + +--- + +## 13. Critical Path Analysis + +### Must Have +1. Listagem window-based + Unscheduled +2. Projeção 14 colunas +3. Agregados domínio + enums +4. Update granular por campo +5. Status 10 labels + flag PastDue +6. Lookups +7. Criação wizard +8. WO# 11 dígitos +9. DAR + 3 artefatos Fase 0 + +### Should Have +10. Domain events CarriedOver +11. Advanced search +12. Regras mutação completas +13. Soft cancel enforcement +14. docStatus WO-level +15. Auth ativa + +### Nice to Have +16. Reorder intra-dia persistido (FE local) +17. Flags pessoais session-only +18. Paginação 12/24/48/96 +19. Bulk select + +--- + +## 14. Frontend Impact Assessment + +| Gap | UX | Severidade | Workaround | +|-----|-----|------------|------------| +| Sem listagem semanal | Tela inutilizável | Critical | Mock FE | +| 14 colunas incompletas | Board ilegível | Critical | Nenhum em prod | +| Sem inline edit | Core inoperante | Critical | Modal legado | +| Sem advanced search | WOs históricos invisíveis | High | Busca por ID | +| Status incompatíveis | Decisões erradas | Critical | Hardcode FE | +| Sem jobs (derivados) | Badges stale se só client | High | On-read BE resolve PastDue | +| Sem docStatus | COMP DOC vazia | High | Ocultar coluna | +| WO# formato errado | Criação bloqueada | Critical | — | +| Ownership Sync vs SHOC | Overwrite silencioso | Critical | Field Matrix Fase 0 | +| Auth desabilitada | Risco segurança | High | API gateway | + +--- + +## 15. Business Rules Assessment + +| Regra | Status | Impacto | +|-------|--------|---------| +| Auto-schedule Incomplete→Scheduled | Unsupported | Status incorreto | +| Reschedule → rescheduleCount++ | Unsupported | Badge ↻ ausente | +| Reschedule limpa PastDue (derivado) | Unsupported | Flag stale até re-read | +| Status bloqueado se PastDue | Unsupported | Bypass via API | +| WO# único 11 dígitos | Unsupported | Duplicatas | +| Cancel read-only | Partial | Edição pós-cancel | +| WeekRolled → carriedOver | Unsupported | Badge ↷N ausente | +| Completion doc por serviço | Unsupported | Workflow manual | +| Week-only scheduling | Unsupported | Wizard incompleto | + +--- + +## 16. Data Contract Readiness + +| Área | Readiness % | Gap principal | +|------|-------------|---------------| +| List Views board | 5% | 14 colunas, schedule, vendor, flags | +| Detail slide-over | 40% | Derivados, docStatus, schema | +| Search | 10% | Cross-week, date ranges | +| Lookups | 20% | initials, site code, PM, tech | +| Scheduling | 25% | targetWeek, ScheduledEnd | +| Status | 30% | 10 labels + flag | +| Completion | 0% | docStatus, templates | +| Comments | 60% | Schema | +| Audit | 50% | type system, granularidade | +| Media | 45% | category | + +**Média ponderada: ~28%** + +--- + +## 17. Testing Strategy + +### Functional +Board semanal, 14 colunas, filtros, inline por campo, regras negócio, criação/cancelamento, docStatus, status+flag, ownership rejection sync. + +### Integration +SHOC E2E por fase; Vendor Portal regression; Sync; auth; dual RowVersion. + +### Regression +Endpoints legados; Blazor smoke EF; Portal checklist/signoff. + +### UAT +Roteiros dispatchers reais; sign-off PO por fase. + +### Data validation +WO# migration; status mapping; derivados on-read vs contadores; referencial. + +### Migration validation +Dump produção espelho; antes/depois contagens; rollback por fase. + +### Fase-specific +- **0:** volume queries; audit granularidade; sync skip +- **2:** concurrency suite; 409 handling +- **4:** load test tier +- **5:** job failure injection; on-read correctness + +--- + +## 18. Risk Matrix + +| Risco | Prob. | Impacto | Mitigação | +|-------|-------|---------|-----------| +| Board inoperante em prod | Alta | Crítico | Fase 1 gate; E2E | +| Ownership ambíguo Sync vs SHOC | Alta | Crítico | Field Ownership Matrix | +| God Entity creep | Média | Alto | Growth Watchlist; ARB | +| Conflito cross-agregado | Média | Alto | RowVersion root + Dispatch | +| Job falha estado errado | Alta | Alto | Derivados on-read | +| Vendor drift Dispatch vs WO | Alta | Crítico | Spike; projeção only | +| Status migration incorreta | Média | Crítico | LegacyStatus; dry-run | +| Volume desconhecido → índices errados | Alta | Alto | Volume Discovery Fase 0 | +| Audit inútil investigação | Média | Médio | Event Contract | +| Regressão Vendor Portal | Média | Crítico | Suite dedicada | +| Blazor vs SHOC divergência | Alta | Alto | Congelar Blazor WO | +| Auth bloqueia integrações | Média | Alto | Inventário consumidores | +| CarriedOver recalculado errado | Baixa | Médio | Só via WeekRolled | +| Scheduling aggregate creep | Média | Alto | Watchlist §4.2 | + +--- + +## 19. Prioritized Backlog + +Ordenado por sequência de implementação. + +| # | Item | P | Fase | Dep | Complex. | Risco | +|---|------|---|------|-----|----------|-------| +| 1 | DAR completo + sign-off CTO | P0 | 0 | — | M | Alto | +| 2 | Data Ownership Model | P0 | 0 | — | S | Alto | +| 3 | Field Ownership Matrix + ManualEditFlag | P0 | 0 | 2 | M | Alto | +| 4 | Audit Event Contract + baseline | P0 | 0 | — | M | Médio | +| 5 | Volume Discovery Report | P0 | 0 | — | S | Alto | +| 6 | Database drift SQL vs EF | P0 | 0 | — | M | Médio | +| 7 | Spike vendor/dispatch source of truth | P0 | 0 | — | M | Alto | +| 8 | Agregados domínio (anti God Entity) | P0 | 0 | 1 | L | Alto | +| 9 | Matriz Persistido vs Derivado | P0 | 0 | 1 | S | Médio | +| 10 | RowVersion root + Dispatch | P0 | 0 | 8 | M | Alto | +| 11 | Enums Lifecycle + Flags + WorkOrderType | P0 | 0 | 8 | M | Médio | +| 12 | Data migration dry-run | P0 | 0 | 8 | M | Alto | +| 13 | Inventário consumidores + coexistência | P0 | 0 | — | S | Médio | +| 14 | Smoke Blazor EF + Sync + Portal | P0 | 0 | 8 | M | Alto | +| 15 | Normalização WO# 11 dígitos | P0 | 0 | 12 | M | Alto | +| 16 | Reativar auth endpoints WO | P0 | 0 | — | S | Médio | +| 17 | Lookups (dispatchers, sites, PM, tech) | P0 | 0–1 | — | M | Baixo | +| 18 | Listagem window-based + Unscheduled | P0 | 1 | 8,11,17 | L | Alto | +| 19 | Projeção 14 colunas | P0 | 1 | 18 | L | Alto | +| 20 | isPastDue derivado on-read | P0 | 1 | 19 | S | Baixo | +| 21 | Vendor via dispatch primário | P0 | 1 | 7,19 | M | Alto | +| 22 | Filtros principais + My WOs | P0 | 1 | 18 | M | Médio | +| 23 | Contador X of Y | P1 | 1 | 18 | S | Baixo | +| 24 | Sync merge por field ownership | P0 | 0–1 | 3 | M | Alto | +| 25 | Update granular inline | P0 | 2 | 19 | L | Alto | +| 26 | Audit 1 evento por campo | P0 | 2 | 4,25 | M | Médio | +| 27 | Concurrency integration tests | P0 | 2 | 10,25 | M | Alto | +| 28 | Auto-schedule Incomplete→Scheduled | P0 | 2 | 25 | M | Médio | +| 29 | RescheduleCount++ domain event | P0 | 2 | 25 | S | Baixo | +| 30 | Bloqueio status se PastDue flag | P0 | 2 | 25 | S | Baixo | +| 31 | Cancel read-only enforcement | P1 | 2 | 25 | S | Baixo | +| 32 | Criação wizard/inline completa | P0 | 3 | 25 | M | Médio | +| 33 | Week-only scheduling targetWeek | P1 | 3 | 32 | M | Médio | +| 34 | Soft cancel dedicado | P1 | 3 | 31 | S | Baixo | +| 35 | Busca contextual semana | P1 | 4 | 19 | M | Baixo | +| 36 | Advanced search cross-week | P1 | 4 | 5,19 | L | Médio | +| 37 | Load test search por tier | P0 | 4 | 5 | M | Alto | +| 38 | WeekRolled domain event + CarriedOver | P1 | 5 | 8 | M | Médio | +| 39 | Cache opcional PastDue | P2 | 5 | 20 | S | Baixo | +| 40 | docStatus WO-level | P1 | 6 | 8 | M | Médio | +| 41 | Completion templates por serviço | P2 | 6 | 40 | L | Médio | +| 42 | Slide-over Comments/Audit/Media | P2 | 6 | 4,40 | M | Baixo | +| 43 | Regression suite legado + Portal | P0 | cont. | — | M | Alto | +| 44 | UAT dispatchers por fase | P0 | cont. | — | S | Médio | +| 45 | Piloto + rollout gradual SHOC | P0 | 7 | 1–42 | M | Alto | +| 46 | Sunset Blazor WO | P1 | 7 | 45 | S | Médio | +| 47 | Cutover Lambda → API | P1 | 7 | 32 | M | Alto | +| 48 | Deprecação endpoints legado + Sync | P2 | 7 | 45 | M | Médio | +| 49 | Monitoramento coexistência | P1 | 7 | 45 | S | Baixo | + +--- + +## 20. Cronograma indicativo + +```mermaid +gantt + title Work Orders Board + dateFormat YYYY-MM-DD + section Fundacao + DAR_arteFatos_dominio :2026-07-01, 8w + section Board + Listagem_14colunas :2026-09-01, 8w + section Mutacao + Inline_concorrencia :2026-11-01, 8w + Criacao_cancelamento :2026-11-15, 6w + section Inteligencia + Search :2027-01-01, 6w + Domain_events :2027-01-15, 4w + section Completion + DocStatus_slideover :2027-02-15, 6w + section Producao + Piloto_rollout :2027-04-01, 8w +``` + +--- + +## 21. Stakeholders + +| Papel | Responsabilidade | +|-------|------------------| +| CEO / Sponsor | Investimento XL; prioridade SHOC sobre Blazor | +| Product Owner | Aceite por fase; semana operacional; regras ambíguas | +| CTO / Arquiteto | DAR; 3 artefatos; ownership; cutover Lambda | +| Backend | Fases 0–6 domínio e contratos | +| Frontend SHOC | Integração; feature flags; remover mocks | +| QA | Testes §17; UAT; concurrency | +| Documentação | Contratos; guia consumidores legados | +| Operações | Jobs; alertas; monitoramento coexistência | + +--- + +## 22. Critérios de prontidão para kickoff (9.5+) + +| Área | Critério | +|------|----------| +| Domain Design | DAR aprovado; agregados; Watchlist | +| Source of Truth | Field Ownership Matrix assinada | +| Data Governance | Data Ownership Model completo | +| Concurrency | RowVersion root + Dispatch; testes Fase 2 | +| Sync | Cenários explícitos; ManualEditFlag | +| Audit | Event Contract; 1 evento/campo inline | +| Migration | Dry-run; LegacyStatus rollback | +| Volume | Discovery Fase 0; tier definido | +| Jobs | Domain events; on-read correto se job falha | +| Coexistência | Inventário; trilhas; smoke Blazor/Portal/Sync | + +--- + +## 23. Conclusão + +Programa **viável e de grande porte (XL)**. O caminho crítico é: + +```text +Resolver domínio e governança (Fase 0) + → Board semanal (Fase 1) + → Inline edit + concorrência (Fase 2) + → Paridade funcional progressiva (Fases 3–6) + → Rollout seguro (Fase 7) +``` + +O documento integra: gap assessment da auditoria, modelo de domínio com agregados, governança de ownership, concorrência multi-agregado, migração de dados, coexistência SHOC/Blazor/Portal/Lambda, e backlog executável — pronto para Architecture Review Board e kickoff de implementação enterprise. + +**Próximo passo imediato:** executar Fase 0 — produzir e assinar os três artefatos (Data Ownership Model, Field Ownership Matrix, Audit Event Contract) antes de qualquer migration de schema. diff --git a/docs/work-orders/phase-0/README.md b/docs/work-orders/phase-0/README.md new file mode 100644 index 0000000..239469e --- /dev/null +++ b/docs/work-orders/phase-0/README.md @@ -0,0 +1,32 @@ +# Fase 0 — Work Orders Foundation + +Documentação e gates da Fase 0 (Domain Foundation + Governança). + +## Governança + +- [DAR — Domain Architecture Review](dar-domain-architecture-review.md) +- [Matriz Persistido vs Derivado](dar-persisted-vs-derived-matrix.md) +- [Data Ownership Model](data-ownership-model.md) +- [Field Ownership Matrix](field-ownership-matrix.md) +- [ManualEditFlag Design](manual-edit-flag-design.md) +- [Audit Event Contract](audit-event-contract.md) + +## Discovery + +- [Volume Discovery Report](volume-discovery-report.md) +- [Database Drift Report](database-drift-report.md) +- [Consumer Inventory](consumer-inventory.md) +- [ADR Vendor Source of Truth](adr-vendor-source-of-truth.md) + +## Validação + +- [RowVersion Design](rowversion-design.md) +- [Migration Dry-Run Report](migration-dry-run-report.md) +- [Smoke Checklist](smoke-checklist.md) +- [Gates Sign-Off](phase-0-gates-signoff.md) + +## Código entregue + +- Migration: `Data.SeaHavenIndustries/Migrations/20260624163145_Phase0_DomainFoundation.cs` +- Serviços: `IWorkOrderAuditService`, `IWorkOrderFieldLockService`, `ISyncFieldMergePolicy` +- Testes: `SeaHavenIndustries.Tests` (12 testes) diff --git a/docs/work-orders/phase-0/adr-vendor-source-of-truth.md b/docs/work-orders/phase-0/adr-vendor-source-of-truth.md new file mode 100644 index 0000000..6dbafa9 --- /dev/null +++ b/docs/work-orders/phase-0/adr-vendor-source-of-truth.md @@ -0,0 +1,67 @@ +# ADR — Vendor Source of Truth + +**Status:** Accepted (Fase 0 spike) +**Decisores:** Backend + Arquiteto + +--- + +## Contexto + +O board SHOC exibe colunas VENDOR e APPT na listagem semanal. Hoje vendor existe apenas no agregado Dispatch; a entidade WorkOrder não possui referência ao dispatch primário. + +--- + +## Decisão + +1. **WorkOrder.PrimaryDispatchId** (nullable FK → Dispatches) identifica o dispatch canônico para projeção board. +2. Colunas VENDOR/APPT = **projeção read-only** via join no dispatch primário (Vendor.Name, Dispatch.ScheduledDate). +3. Edição inline vendor (Fase 2) muta **Dispatch**, não WorkOrder. Bump `Dispatch.RowVersion`. +4. WO sem dispatch: primeira edição vendor cria dispatch primário e seta `PrimaryDispatchId`. +5. **Proibido** adicionar VendorId, TechName, TechPhone na tabela workOrders. + +--- + +## Query de projeção (spike) + +```sql +SELECT wo.Id, + wo.InternalWONumber, + d.Id AS DispatchId, + v.Name AS VendorName, + d.ScheduledDate AS ApptDate, + d.Status AS DispatchStatus +FROM workOrders wo +LEFT JOIN Dispatches d ON d.Id = wo.PrimaryDispatchId +LEFT JOIN Vendors v ON v.Id = d.VendorId +WHERE wo.IsDeleted IS NULL OR wo.IsDeleted = 0; +``` + +Implementação C# em `DispatchDataService.GetPrimaryDispatchProjectionAsync(workOrderId)` (Fase 1). + +--- + +## Seleção do dispatch primário + +| Cenário | Regra | +|---------|-------| +| PrimaryDispatchId setado | Usar esse dispatch | +| Null + exatamente 1 dispatch | Auto-set PrimaryDispatchId na migration backfill | +| Null + N dispatches | Usar dispatch mais recente por DispatchedAt; log para revisão manual | +| Zero dispatches | Vendor columns vazias no board | + +--- + +## Consequências + +- **Positivo:** Zero drift vendor WO vs Dispatch; Portal regression isolada. +- **Negativo:** Join extra na listagem board — mitigado por índice em PrimaryDispatchId. +- **Fase 0:** Coluna + FK + backfill script no dry-run; endpoint board na Fase 1. + +--- + +## Critérios de aceite spike + +- [x] ADR documentado +- [x] Query prototipada +- [ ] PrimaryDispatchId na migration Phase0 +- [ ] Backfill documentado no dry-run report diff --git a/docs/work-orders/phase-0/audit-event-contract.md b/docs/work-orders/phase-0/audit-event-contract.md new file mode 100644 index 0000000..bb773f4 --- /dev/null +++ b/docs/work-orders/phase-0/audit-event-contract.md @@ -0,0 +1,106 @@ +# Audit Event Contract — Work Orders + +**Fase:** 0 +**Status:** Aprovado para implementação baseline +**Schema EF:** `WorkOrderAuditLog` estendido + +--- + +## 1. Princípios + +- Toda mutação gera audit **síncrono** desde Fase 0. +- Edição inline (Fase 2) = **1 evento por campo**. +- Audit serve investigação operacional, não só compliance. + +--- + +## 2. Schema por evento + +| Campo | Tipo | Obrigatório | +|-------|------|-------------| +| Id | int | Sim (PK) | +| WorkOrderId | int | Sim | +| EventType | enum string | Sim: Manual, System, Sync, Vendor | +| Action | string | Sim: FieldChanged, StatusChanged, WeekRolled, SyncRejected, AssignmentChanged | +| FieldName | string | Sim para inline / field change | +| OldValue | string | Sim | +| NewValue | string | Sim | +| UserId / ActorId | string | Sim para Manual | +| ActorType | enum string | Sim: Dispatcher, Vendor, System, Sync | +| CreatedAt | DateTime UTC | Sim | +| CorrelationId | string | Recomendado | +| DispatchId | int? | Quando evento é no dispatch | + +**Compatibilidade:** colunas `Action` legada mapeada para novo `Action`; `UserId` = ActorId para Manual. + +--- + +## 3. Enums + +```csharp +AuditEventType: Manual | System | Sync | Vendor +AuditActorType: Dispatcher | Vendor | System | Sync +AuditActionType: FieldChanged | StatusChanged | AssignmentChanged | WeekRolled | SyncRejected | Create | Delete +``` + +--- + +## 4. Mutação → eventos + +| Mutação | Eventos | +|---------|---------| +| ChangeStatus | 1× StatusChanged (EventType=Manual) | +| ChangeAssignment | 1× AssignmentChanged (FieldName=AssignTo) | +| Inline cell (Fase 2) | 1× FieldChanged por campo | +| Auto-schedule (Fase 2) | StatusChanged + FieldChanged se aplicável | +| Reschedule (Fase 2) | FieldChanged + analytics | +| WeekRolled (Fase 5) | System WeekRolled + CarriedOver | +| Sync skip | Sync SyncRejected | +| Vendor checklist | Vendor FieldChanged + DispatchId | + +--- + +## 5. Contrato FE (Audit tab) + +```typescript +interface AuditEntry { + type: 'manual' | 'system' | 'sync' | 'vendor'; + dispatcherId?: string; + action: string; + fieldName?: string; + oldValue: string; + newValue: string; + time: string; // ISO UTC + dispatchId?: number; +} +``` + +Mapeamento API → FE: + +| API EventType | FE type | +|---------------|---------| +| Manual | manual | +| System | system | +| Sync | sync | +| Vendor | vendor | + +--- + +## 6. Baseline Fase 0 (endpoints) + +| Endpoint | Garantia | +|----------|----------| +| POST ChangeStatus | 1 audit StatusChanged; Old/New preenchidos | +| POST ChangeAssignment | 1 audit AssignmentChanged; nomes usuário em Old/New | + +Implementação via `IWorkOrderAuditService`. + +--- + +## 7. Critérios de aceite + +- [ ] Migration estende WorkOrderAuditLog +- [ ] ChangeStatus/ChangeAssignment usam serviço central +- [ ] Testes assertam 1 evento por mutação + +**Assinatura Backend Lead:** _________________ Data: _______ diff --git a/docs/work-orders/phase-0/consumer-inventory.md b/docs/work-orders/phase-0/consumer-inventory.md new file mode 100644 index 0000000..5d1e35d --- /dev/null +++ b/docs/work-orders/phase-0/consumer-inventory.md @@ -0,0 +1,85 @@ +# Consumer Inventory — Work Orders + +**Fase:** 0 +**Referência:** roadmap §11 + +--- + +## 1. Inventário de consumidores + +| Consumidor | Conexão | Código principal | Campos mutados | Audit hoje? | +|------------|---------|------------------|----------------|-------------| +| **SHOC React** | REST JWT | `Api.SeaHavenIndustries/Controllers/WorkOrderController.cs` | Status, AssignTo, CRUD parcial via Service | Parcial (ChangeStatus/Assignment) | +| **Blazor** | EF direto | `SeaHavenIndustries/Data/Services/WorkorderService.cs` | CRUD completo, comments, status | **Não** | +| **Vendor Portal** | REST token | `Api.SeaHavenIndustries/Controllers/VendorPortalController.cs` | Dispatch checklist, signoff, status | Sim (ad hoc) | +| **Sync/Lambda** | DynamoDB → SQL | `Api.SeaHavenIndustries/Controllers/SyncController.cs` | Upsert WO por ExternalWorkOrderId | **Não** | +| **Dispatch API** | REST | `DispatchController.cs` | Dispatch create/update/verify | Sim | +| **E-mail SendGrid** | Deep links | `Helper/SendMessage` | Nenhum (read-only links) | N/A | + +--- + +## 2. Diagrama + +```mermaid +flowchart TB + SHOC[SHOC_React] -->|REST_JWT| API[Api_REST] + VPortal[Vendor_Portal] -->|X_Vendor_Token| API + Lambda[Lambda_ingest] --> DynamoDB[(DynamoDB)] + DynamoDB --> Sync[SyncController] + Sync --> DB[(SQL_Server)] + API --> DB + Blazor[Blazor_EF] -->|legado| DB +``` + +--- + +## 3. Trilhas de entrega (coexistência) + +| Trilha | Escopo Fase 0+ | +|--------|----------------| +| **A — SHOC** | Fases 0–7; feature flags; piloto dispatchers | +| **B — Blazor** | Smoke EF pós-migration; bugfix crítico only | +| **C — Vendor Portal** | Regression checklist cada release WO/Dispatch | +| **D — Lambda/Sync** | Manter até SHOC produção; merge policy Fase 0 | + +--- + +## 4. Endpoints legado vs board + +| Tipo | Exemplos | Política | +|------|----------|----------| +| Legado | GetWorkOrderList, ChangeStatus | Congelar contrato | +| Board (Fase 1+) | GetWorkOrdersBoard | Novos contratos separados | + +--- + +## 5. Gaps Fase 0 endereçados + +| Gap | Mitigação | +|-----|-----------| +| Blazor sem audit | Congelar; não expandir | +| Sync overwrite | ISyncFieldMergePolicy + ManualEditFlag | +| Auth WO desabilitada | Reativar `[Authorize]` WorkOrderController | +| WO# inconsistente | Documentar normalização 11 dígitos (Fase 2 execução) | + +--- + +## 6. Checklist pré-go-live PO + +- [ ] App mobile externo confirmado? +- [ ] Data sunset Blazor WO definida? +- [ ] FrontendBaseUrl produção → SHOC? +- [ ] Owner Lambda cutover nomeado? + +--- + +## 7. Auth — consumidores e tokens + +| Consumidor | Auth | +|------------|------| +| SHOC | JWT Bearer | +| Sync | JWT Bearer (`[Authorize]` no SyncController) | +| Vendor Portal | X-Vendor-Token (rotas públicas dispatch) | +| Blazor | Cookie Identity (app separado) | + +Reativar auth em WorkOrderController exige SHOC enviar JWT em todas as chamadas WO. diff --git a/docs/work-orders/phase-0/dar-domain-architecture-review.md b/docs/work-orders/phase-0/dar-domain-architecture-review.md new file mode 100644 index 0000000..481a20e --- /dev/null +++ b/docs/work-orders/phase-0/dar-domain-architecture-review.md @@ -0,0 +1,139 @@ +# DAR — Domain Architecture Review (Work Orders) + +**Fase:** 0 +**Status:** Draft para sign-off CTO +**Referência:** [roadmap-work-orders-board.md](../../roadmap-work-orders-board.md) §4–5 + +--- + +## 1. Objetivo + +Formalizar o modelo de domínio operacional do Schedule Board antes de qualquer migration de schema board ou contrato de listagem semanal. + +--- + +## 2. Agregados e limites (anti God Entity) + +```mermaid +flowchart TB + subgraph root [AggregateRoot_WorkOrder] + CORE[Core_Lifecycle_Assignment] + SCH[Scheduling_slice] + TRK[Tracking_slice] + COMP[Completion_slice] + ANA[Analytics_slice] + end + subgraph separate [Agregado_separado] + DISP[Dispatch_fonte_vendor] + end + AUD[WorkOrderAuditLog_append_only] + CORE --> SCH + CORE --> TRK + CORE --> ANA + CORE --> COMP + CORE --> DISP + CORE --> AUD +``` + +| Agregado / Slice | Responsabilidade | Persistência Fase 0 | +|------------------|------------------|---------------------| +| **Core** | Id, WoNumber, Type, LifecycleStatus, SiteCode, LocationId, DueDate, AssignTo, Title, RowVersion, PrimaryDispatchId | Colunas em `workOrders` | +| **Scheduling** | ScheduledDate, ScheduledStart/End, TargetWeek, ScheduleWeekOnly | Colunas em `workOrders` | +| **Tracking** | OriginalWeek, OriginalDate (set-once) | Colunas em `workOrders` | +| **Analytics** | RescheduleCount, CarriedOver | Colunas em `workOrders` | +| **Completion** | DocStatus, refs attachments | Coluna DocStatus + attachments existentes | +| **Dispatch** | Vendor, tech, status portal | Tabela `Dispatches` + RowVersion | +| **Audit** | Rastreabilidade imutável | `WorkOrderAuditLogs` | + +**Proibido:** `VendorId`, `TechName`, `TechPhone` canônicos na entidade WorkOrder. Vendor sempre via Dispatch primário. + +--- + +## 3. Fonte da verdade por conceito + +| Conceito | Fonte da verdade | Proibido | +|----------|------------------|----------| +| Status operacional | `LifecycleStatus` (10 valores FE) | String livre `Status` como canônico | +| Past Due | Derivado on-read (`ScheduledDate < hoje` AND NOT terminal) | Misturar no enum lifecycle | +| Vendor / Tech | Dispatch primário (`PrimaryDispatchId`) | Duplicar na WO | +| Schedule | Scheduling slice | Duplicar no core sem slice lógico | +| Completion | `DocStatus` | Inferir só de dispatch signoff | +| Reschedule / CarriedOver | Analytics + domain events | Job blind overwrite | +| WO# | `InternalWONumber` / `WorkerOrderNumber` com regra 11 dígitos (Fase 2+) | Dois números sem regra | +| POC | `WorkOrderContacts` + Notes | Só no detalhe sem contato | + +--- + +## 4. Lifecycle Status vs Operational Flags + +```text +LifecycleStatus → enum único (10 valores alinhados ao frontend) +OperationalFlags → PastDue (derivado on-read; cache opcional Fase 5) +LegacyStatus → string original read-only (migration Fase 0) +``` + +Filtro de status no board = `LifecycleStatus`. Overlay Past Due = flag derivada. Bloqueio de edição = validação sobre flag (Fase 2). + +### Mapeamento legado → LifecycleStatus + +| Legado (`Status`) | LifecycleStatus | +|-------------------|-----------------| +| Open | Incomplete | +| InProgress / In Progress | InProgress | +| Completed / Complete | Complete | +| Cancelled / Canceled | Canceled | +| OnHold / On Hold | OnHold | +| Desconhecido | Incomplete + NeedsReview | + +--- + +## 5. CarriedOver — justificativa + +| | isPastDue | carriedOver | +|--|-----------|-------------| +| Natureza | Estado pontual | Métrica histórica acumulada | +| Persistir | Não (derivado) | Sim (contador) | +| Incremento | N/A | Somente via domain event `WeekRolled` (Fase 5) | + +--- + +## 6. Scheduling Aggregate Growth Watchlist + +Campos que **não** entram em Scheduling sem ARB review: + +- `MoveReason`, `MoveUser`, `MoveCategory`, `MoveSource` + +Metadados de movimentação → Audit Event Contract. + +**Gate:** slice Scheduling > 8 campos operacionais → ARB obrigatório. +Contagem Fase 0: ScheduledDate, ScheduledStart, ScheduledEnd, TargetWeek, ScheduleWeekOnly, OriginalWeek, OriginalDate (tracking separado) — dentro do limite. + +--- + +## 7. Jobs — domínio primeiro + +| Job | Papel | +|-----|-------| +| Past Due diário | Cache refresh opcional; on-read sempre correto | +| Carried Over semanal | Dispara `WeekRolled` → incrementa contador + audit system | +| Promoção Overdue type | Derivado on-read | +| Audit | Síncrono em toda mutação desde Fase 0 | + +**Regra de ouro:** Jobs nunca são a única fonte da verdade. + +--- + +## 8. Critérios de aceite (sign-off CTO) + +- [ ] Zero campos vendor canônicos na WO +- [ ] Agregados documentados e refletidos no schema Fase 0 +- [ ] Persistido vs derivado validado com PO (ver `dar-persisted-vs-derived-matrix.md`) +- [ ] Watchlist Scheduling registrada +- [ ] Blazor congelado — sem novos campos board via EF direto + +**Assinaturas** + +| Papel | Nome | Data | +|-------|------|------| +| CTO / Arquiteto | | | +| Product Owner | | | diff --git a/docs/work-orders/phase-0/dar-persisted-vs-derived-matrix.md b/docs/work-orders/phase-0/dar-persisted-vs-derived-matrix.md new file mode 100644 index 0000000..ceef146 --- /dev/null +++ b/docs/work-orders/phase-0/dar-persisted-vs-derived-matrix.md @@ -0,0 +1,54 @@ +# DAR — Matriz Persistido vs Derivado + +**Fase:** 0 +**Status:** Draft para validação PO + CTO + +--- + +## Matriz de campos (board + domínio) + +| Campo FE / conceito | Persistido | Derivado | Fonte / Regra | +|---------------------|------------|----------|---------------| +| lifecycleStatus | Sim | Não | Core — `LifecycleStatus` enum | +| isPastDue | Não | **Sim** | `ScheduledDate < UTC hoje` AND NOT status terminal | +| type (Overdue) | Parcial | **Sim** | Regra type + schedule + status | +| workOrderType | Sim | Não | Core — enum `WorkOrderType` | +| carriedOver | Sim | Não | Analytics — incremento via `WeekRolled` only | +| rescheduleCount | Sim | Parcial | Analytics — incremento em mudança ScheduledDate | +| docStatus | Sim | Não | Completion slice | +| dayGroup | Não | Sim | Calculado na leitura a partir de ScheduledDate | +| apptTime | Não | Sim | Projeção Dispatch primário ScheduledDate/Start | +| vendorName / tech | Não | Sim (projeção) | Join Dispatch primário | +| targetWeek | Sim | Não | Scheduling slice | +| scheduledEnd | Sim | Não | Scheduling slice | +| originalWeek / originalDate | Sim | Não | Tracking — set-once na primeira agenda | +| operationalFlags.pastDue | Não* | Sim | *Cache opcional Fase 5; on-read authoritative | +| legacyStatus | Sim (read-only) | Não | Valor string original pré-migration | + +--- + +## Regras de derivação (on-read) + +### isPastDue + +```text +isPastDue = ScheduledDate.HasValue + AND ScheduledDate.Value.Date < DateTime.UtcNow.Date + AND LifecycleStatus NOT IN (Complete, Canceled) +``` + +### type Overdue (derivado) + +WO com type != Overdue pode exibir badge Overdue quando isPastDue && regras de negócio PO confirmadas. + +--- + +## Validação PO + +| Pergunta | Resposta PO | +|----------|-------------| +| Past Due separado do lifecycle status? | Sim | +| carriedOver persiste mesmo após reschedule? | Sim | +| Vendor nunca duplicado na WO? | Sim | + +**Assinatura PO:** _________________ Data: _______ diff --git a/docs/work-orders/phase-0/data-ownership-model.md b/docs/work-orders/phase-0/data-ownership-model.md new file mode 100644 index 0000000..1c15bbf --- /dev/null +++ b/docs/work-orders/phase-0/data-ownership-model.md @@ -0,0 +1,75 @@ +# Data Ownership Model — Work Orders + +**Fase:** 0 +**Status:** Draft para sign-off CTO + +--- + +## 1. Princípios + +1. **SHOC (React)** é o único destino de novas features do board. +2. **SQL Server** é fonte operacional para board, Blazor e Portal. +3. **DynamoDB/Sync** é ponte temporária de ingestão externa. +4. Conflito entre atores resolve-se pela Field Ownership Matrix, não por last-write-wins global. + +--- + +## 2. Ownership por ator + +| Ator | Conexão | Pode alterar | Não pode alterar | +|------|---------|--------------|------------------| +| **SHOC** | REST JWT (`api/WorkOrder`) | Lifecycle, Schedule, Assignment, Completion, vendor via Dispatch, POC, WO# | Checklist/signoff vendor | +| **Vendor Portal** | REST `X-Vendor-Token` | Dispatch status, checklist, signoff, comments vendor | Schedule, lifecycle, assignment, DueDate | +| **Sync/Lambda** | `POST api/Sync/WorkOrders` | Campos ingest (Description, ExternalId, SiteCode na criação, etc.) | Campos SHOC-owned após ManualEditFlag | +| **Blazor** | EF direto (`WorkorderService`) | Campos legados existentes até sunset | Campos novos board; **congelado** | +| **Jobs** | Domain events (Fase 5+) | Incremento CarriedOver; audit System | Lifecycle, Schedule, Dispatch | + +--- + +## 3. Política Blazor (sunset) + +- Manutenção mínima: bugfix crítico only. +- Não recebe: board, inline edit, jobs, advanced search, completion doc WO-level. +- Schema: colunas novas Fase 0 são **nullable** — Blazor continua funcionando sem conhecer novos campos. +- Conflito Blazor vs SHOC: **SHOC vence**; Blazor não deve escrever campos board após go-live Fase 1. +- Smoke test EF obrigatório pós-migration (ver checklist smoke). + +--- + +## 4. Política Vendor Portal + +- Dispatch intacto; zero regressão checklist/signoff/verify. +- Vendor não muta WorkOrder core — apenas Dispatch e comments associados. +- Audit EventType = `Vendor` para mutações portal. + +--- + +## 5. Política Sync/Lambda + +- Upsert por `ExternalWorkOrderId` (idempotente). +- Field-level merge conforme Field Ownership Matrix. +- Campo com ManualEditFlag → skip + audit `SyncRejected`. +- Cutover Lambda → API direta: Fase 7. + +--- + +## 6. Regras de conflito (resumo) + +| Cenário | Vencedor | +|---------|----------| +| SHOC vs SHOC (mesmo campo) | 409 RowVersion; último commit válido | +| SHOC vs Sync (mesmo campo) | SHOC se ManualEditFlag | +| SHOC vs Sync (campos distintos) | Merge | +| Vendor vs Sync | Domínios separados | +| SHOC vs Vendor (mesmo conceito) | Impossível por design (vendor = Dispatch) | +| Blazor vs SHOC | SHOC vence; Blazor congelado | + +--- + +## 7. Critérios de aceite + +- [ ] Matriz por ator revisada por CTO +- [ ] PO confirma política Blazor sunset +- [ ] Sync merge referencia Field Ownership Matrix + +**Assinatura CTO:** _________________ Data: _______ diff --git a/docs/work-orders/phase-0/database-drift-report.md b/docs/work-orders/phase-0/database-drift-report.md new file mode 100644 index 0000000..8d3ce76 --- /dev/null +++ b/docs/work-orders/phase-0/database-drift-report.md @@ -0,0 +1,110 @@ +# Database Drift Report — SQL vs EF + +**Fase:** 0 +**Data:** 2026-06-24 +**EF Snapshot:** `Data.SeaHavenIndustries/Migrations/ApplicationDbContextModelSnapshot.cs` +**Script legado:** `Api.SeaHavenIndustries/db.txt` + +--- + +## 1. Metodologia + +1. Colunas EF extraídas do `ApplicationDbContextModelSnapshot` (entidade `WorkOrder`). +2. Colunas SQL legado extraídas de `db.txt` DDL `[WorkOrders]`. +3. Classificação: **mapear**, **deprecar**, **satélite**, **OK**. + +--- + +## 2. Tabela workOrders — drift conhecido + +| Coluna SQL (legado) | No EF? | Classificação | Ação Fase 0 | +|---------------------|--------|---------------|-------------| +| WorkOrderType | **Não** | mapear | Adicionar `WorkOrderType` int nullable → enum | +| AvettaTask | **Não** | inventariar | Adicionar coluna nullable; uso TBD com PO | +| AssignDate | **Não** | inventariar | Adicionar coluna nullable; possível alias AssignDate tracking | +| Nome tabela WorkOrders vs workOrders | EF usa `workOrders` | OK | Manter EF naming; SQL Server case-insensitive | + +--- + +## 3. Colunas EF (workOrders) — baseline + +Presentes no snapshot e mapeadas: + +InternalWONumber, ExternalWorkOrderId, WorkerOrderNumber, WorkerOrderTitle, Description, Customer, SiteCode, Building, Severity, DateReported, ScheduledStart, ScheduledDate, CompletedDate, Source, SourceEmailS3Key, Problem, Trade, SubTrade, VendorNTE, AssignTo, DueDate, Priority, Status, LocationId, PO, TT, Attachments, BeforPhoto*, AfterPhoto*, SignOff*, istemplate, audit fields (CreatedDate, IsDeleted, etc.) + +--- + +## 4. Novas colunas Fase 0 (migration) + +| Coluna | Tipo | Slice | +|--------|------|-------| +| LifecycleStatus | int nullable | Core | +| LegacyStatus | nvarchar (cópia Status) | Core | +| WorkOrderType | int nullable | Core | +| PrimaryDispatchId | int nullable FK | Core | +| RowVersion | rowversion | Core | +| TargetWeek | date nullable | Scheduling | +| ScheduledEnd | datetime2 nullable | Scheduling | +| ScheduleWeekOnly | bit nullable | Scheduling | +| OriginalWeek | date nullable | Tracking | +| OriginalDate | date nullable | Tracking | +| RescheduleCount | int default 0 | Analytics | +| CarriedOver | int default 0 | Analytics | +| DocStatus | int nullable | Completion | +| AvettaTask | nvarchar max nullable | Legado SQL | +| AssignDate | date nullable | Legado SQL | + +--- + +## 5. Dispatch + +| Item | EF | Ação Fase 0 | +|------|-----|-------------| +| RowVersion | Ausente | Adicionar | +| Demais colunas | OK | Manter | + +--- + +## 6. WorkOrderAuditLog + +| Coluna | EF atual | Ação Fase 0 | +|--------|----------|-------------| +| EventType | Ausente | Adicionar | +| ActorType | Ausente | Adicionar | +| DispatchId | Ausente | Adicionar nullable | +| CorrelationId | Ausente | Adicionar nullable | + +--- + +## 7. WorkOrderContacts + +| Coluna | EF | Ação Fase 0 | +|--------|-----|-------------| +| Notes | Ausente | Adicionar nvarchar nullable (pocNotes) | + +--- + +## 8. ApplicationUser (AspNetUsers) + +| Coluna | Ação Fase 0 | +|--------|-------------| +| Initials | nvarchar(8) nullable | +| Color | nvarchar(16) nullable | + +--- + +## 9. Tabelas revisadas — sem drift crítico adicional + +- `Comments` — OK +- `Locations` — OK (SiteCode via WO.SiteCode) +- `DispatchWorkOrders` — OK + +--- + +## 10. Recomendações + +1. **Eliminar dual-path:** `db.txt` catch-up não deve ser usado após migration EF Phase0; documentar em runbook. +2. **WorkOrderType:** mapear valores SQL existentes → enum na migration data script (dry-run). +3. **AvettaTask / AssignDate:** manter nullable; PO valida uso antes de exposição board. + +**Gate:** Diff assinado por Backend antes de merge migration. diff --git a/docs/work-orders/phase-0/field-ownership-matrix.md b/docs/work-orders/phase-0/field-ownership-matrix.md new file mode 100644 index 0000000..7bfe7f7 --- /dev/null +++ b/docs/work-orders/phase-0/field-ownership-matrix.md @@ -0,0 +1,86 @@ +# Field Ownership Matrix — Work Orders + +**Fase:** 0 +**Depende de:** [data-ownership-model.md](data-ownership-model.md) +**Status:** Draft para sign-off CTO + PO + +--- + +## Legenda + +- **Owner escritor:** ator autorizado a criar/alterar o valor canônico. +- **Sync sobrescreve?:** se ingest DynamoDB pode substituir após WO existir. +- **ManualEditFlag:** primeira edição SHOC bloqueia Sync no campo (ver [manual-edit-flag-design.md](manual-edit-flag-design.md)). + +--- + +## Matriz completa + +| Campo | Agregado | Owner escritor | Sync sobrescreve? | Conflito SHOC vs Sync | +|-------|----------|----------------|-------------------|----------------------| +| LifecycleStatus | Core | SHOC | Não após 1ª edição SHOC | SHOC vence | +| LegacyStatus | Core | — (read-only) | Não | N/A | +| AssignTo | Core | SHOC | Não após ManualEditFlag | SHOC vence | +| ScheduledDate | Scheduling | SHOC | Não | SHOC vence | +| ScheduledStart | Scheduling | SHOC | Parcial (ingest inicial) | SHOC após flag | +| ScheduledEnd | Scheduling | SHOC | Não | SHOC vence | +| TargetWeek | Scheduling | SHOC | Não | SHOC vence | +| ScheduleWeekOnly | Scheduling | SHOC | Não | SHOC vence | +| DueDate | Core | SHOC + Sync | Sim se nunca editado SHOC | SHOC se ManualEditFlag | +| Description | Core | SHOC + Sync | Sim se nunca editado SHOC | SHOC se ManualEditFlag | +| WorkerOrderTitle | Core | SHOC + Sync | Sim se nunca editado SHOC | SHOC se ManualEditFlag | +| SiteCode | Core | SHOC + Sync | Sim na criação; não após SHOC | SHOC após flag | +| Building | Core | SHOC + Sync | Sim na criação | SHOC após flag | +| LocationId | Core | SHOC + Sync | Sim na criação | SHOC após flag | +| WorkOrderType | Core | SHOC + Sync | Sim na criação ingest | SHOC após flag | +| InternalWONumber / WoNumber | Core | SHOC (Sync só na criação) | Não | SHOC only | +| ExternalWorkOrderId | Core | Sync | Sim (idempotente key) | Sync only | +| VendorId / VendorName | Dispatch | SHOC + Vendor | Não | Domínios separados | +| Dispatch Status | Dispatch | Vendor + SHOC | Não | Por contexto | +| Checklist / Signoff | Dispatch | Vendor | Não | Vendor vence | +| DocStatus | Completion | SHOC | Não | SHOC only | +| RescheduleCount | Analytics | Domain events | Não | N/A | +| CarriedOver | Analytics | Jobs (WeekRolled) | Não | N/A | +| OriginalWeek / OriginalDate | Tracking | SHOC (set-once) | Não | SHOC vence | +| POC / WorkOrderContacts.Notes | Contacts | SHOC | Não | SHOC vence | +| Priority / Severity | Core | SHOC + Sync | Sim se nunca editado | SHOC após flag | +| Status (legado string) | Core | — (deprecated write) | Não | Migrar para LifecycleStatus | +| Trade / Problem / SubTrade | Core | SHOC | Parcial Sync ingest | SHOC após flag | +| Customer / Source | Core | Sync + SHOC | Sim na criação | SHOC após flag | +| Attachments / Photos | Core | SHOC + Vendor | Não overwrite mútuo | Por domínio | + +--- + +## Campos board (14 colunas) — ownership resumido + +| Coluna board | Fonte | Owner | +|--------------|-------|-------| +| WO# | Core.InternalWONumber | SHOC | +| Type | Core.WorkOrderType | SHOC | +| Site | Core.SiteCode | SHOC | +| Status | Core.LifecycleStatus | SHOC | +| Assignee | Core.AssignTo | SHOC | +| Due | Core.DueDate | SHOC + Sync | +| Scheduled | Scheduling.ScheduledDate | SHOC | +| Vendor | Dispatch (primário) | SHOC via Dispatch | +| Appt | Dispatch.ScheduledDate | Dispatch | +| Past Due | Derivado | — | +| Carried | Analytics.CarriedOver | Jobs | +| Reschedule | Analytics.RescheduleCount | Domain | +| Doc | Completion.DocStatus | SHOC | +| Actions | — | SHOC UI | + +--- + +## Critérios de aceite + +- [ ] PO valida campos board vs matriz +- [ ] Sync merge implementado referencia esta matriz +- [ ] ManualEditFlag design aprovado + +**Assinaturas** + +| Papel | Data | +|-------|------| +| CTO | | +| PO | | diff --git a/docs/work-orders/phase-0/manual-edit-flag-design.md b/docs/work-orders/phase-0/manual-edit-flag-design.md new file mode 100644 index 0000000..548727f --- /dev/null +++ b/docs/work-orders/phase-0/manual-edit-flag-design.md @@ -0,0 +1,87 @@ +# ManualEditFlag — Design + +**Fase:** 0 +**Implementação:** tabela satélite `WorkOrderFieldLocks` + +--- + +## 1. Problema + +Sync/Lambda faz upsert overwrite em campos que dispatchers editam no SHOC. Precisamos saber, por campo, se houve edição manual SHOC para rejeitar ingest conflitante. + +--- + +## 2. Solução escolhida: tabela satélite + +```text +WorkOrderFieldLocks + Id (PK) + WorkOrderId (FK) + FieldName (string, max 64) + LockedAt (UTC) + LockedByUserId (nullable — null = system/bootstrap) + UNIQUE (WorkOrderId, FieldName) +``` + +**Alternativa descartada:** bitmask — difícil de auditar e estender. + +--- + +## 3. Comportamento + +| Evento | Ação | +|--------|------| +| SHOC edita campo X pela 1ª vez | INSERT lock (WorkOrderId, FieldName) | +| Sync tenta atualizar campo X com lock | Skip update; audit `SyncRejected` | +| Sync atualiza campo Y sem lock | Apply merge normal | +| WO novo (Sync create) | Sem locks; full ingest | +| Blazor edita campo legado | **Não** cria lock Fase 0 (gap conhecido; Blazor congelado) | + +--- + +## 4. Campos elegíveis a lock (SHOC writers) + +Definidos em `SyncFieldMergePolicy.ShocOwnedFields`: + +- LifecycleStatus, AssignTo, ScheduledDate, ScheduledEnd, TargetWeek, ScheduleWeekOnly +- DueDate, Description, WorkerOrderTitle, SiteCode, Building, LocationId +- WorkOrderType, DocStatus, Trade, Problem, Priority, InternalWONumber + +--- + +## 5. Audit em rejeição + +```json +{ + "eventType": "Sync", + "action": "SyncRejected", + "fieldName": "DueDate", + "oldValue": "2026-06-01", + "newValue": "2026-06-15", + "actorType": "Sync" +} +``` + +--- + +## 6. API interna + +```csharp +interface IWorkOrderFieldLockService +{ + Task LockFieldAsync(int workOrderId, string fieldName, string? userId); + Task IsLockedAsync(int workOrderId, string fieldName); +} +``` + +Implementação: `WorkOrderFieldLockService` em `SeaHaven.Services`. + +Lock criado automaticamente por `IWorkOrderAuditService` em eventos `Manual` + `FieldChanged` / `StatusChanged`. + +--- + +## 7. Critérios de aceite + +- [ ] Tabela criada na migration Phase0 +- [ ] Sync consulta locks antes de overwrite +- [ ] SyncRejected aparece no audit log diff --git a/docs/work-orders/phase-0/migration-dry-run-report.md b/docs/work-orders/phase-0/migration-dry-run-report.md new file mode 100644 index 0000000..2a815e0 --- /dev/null +++ b/docs/work-orders/phase-0/migration-dry-run-report.md @@ -0,0 +1,58 @@ +# Migration Dry-Run Report — Phase 0 + +**Migration:** `20260624163145_Phase0_DomainFoundation` +**Status:** Pronto para execução em staging + +--- + +## 1. Procedimento + +1. Restore backup produção → staging espelho +2. Snapshot pré-migration: + ```sql + SELECT Status, COUNT(*) FROM workOrders GROUP BY Status; + SELECT COUNT(*) AS Total FROM workOrders; + ``` +3. Executar: + ```powershell + dotnet ef database update --project Data.SeaHavenIndustries --startup-project Api.SeaHavenIndustries + ``` +4. Validar pós-migration: + ```sql + SELECT COUNT(*) FROM workOrders WHERE LegacyStatus IS NOT NULL; + SELECT LifecycleStatus, COUNT(*) FROM workOrders GROUP BY LifecycleStatus; + SELECT COUNT(*) FROM workOrders WHERE PrimaryDispatchId IS NOT NULL; + ``` +5. Smoke Blazor + API legado + Sync sample +6. Rollback (se falha): + ```powershell + dotnet ef database update 20260417195316_AddDispatchVerification --project Data.SeaHavenIndustries --startup-project Api.SeaHavenIndustries + ``` + +--- + +## 2. Data scripts incluídos na migration + +- `LegacyStatus` ← cópia de `Status` +- `LifecycleStatus` ← mapeamento legado (Open→Incomplete, etc.) +- `PrimaryDispatchId` ← dispatch mais recente por WO + +--- + +## 3. Resultados (preencher em staging) + +| Métrica | Antes | Depois | +|---------|-------|--------| +| Total WOs | | | +| Com LegacyStatus | | | +| Com LifecycleStatus | | | +| Com PrimaryDispatchId | | | +| Rollback testado | | Sim/Não | + +--- + +## 4. Notas + +A migration inclui reconciliação de drift em `Locations`, `Regions`, `Departments` detectada pelo EF — revisar impacto em staging antes de produção. + +**WO# normalização 11 dígitos:** documentada para Fase 2; não executada nesta migration. diff --git a/docs/work-orders/phase-0/phase-0-gates-signoff.md b/docs/work-orders/phase-0/phase-0-gates-signoff.md new file mode 100644 index 0000000..ddc4199 --- /dev/null +++ b/docs/work-orders/phase-0/phase-0-gates-signoff.md @@ -0,0 +1,46 @@ +# Phase 0 — Gates Sign-Off Checklist + +**Programa:** Work Orders Board +**Fase:** 0 — Domain Foundation + Governança + +--- + +## 9 Gates bloqueantes + +| # | Gate | Artefato | Status | +|---|------|----------|--------| +| 1 | DAR aprovado | [dar-domain-architecture-review.md](dar-domain-architecture-review.md) | Documentado | +| 2 | Data Ownership Model | [data-ownership-model.md](data-ownership-model.md) | Documentado | +| 3 | Field Ownership Matrix | [field-ownership-matrix.md](field-ownership-matrix.md) | Documentado | +| 4 | Audit Event Contract | [audit-event-contract.md](audit-event-contract.md) | Implementado baseline | +| 5 | Volume Discovery | [volume-discovery-report.md](volume-discovery-report.md) | Queries prontas; tier S provisório | +| 6 | Database drift | [database-drift-report.md](database-drift-report.md) | Documentado | +| 7 | Spike vendor/dispatch | [adr-vendor-source-of-truth.md](adr-vendor-source-of-truth.md) | ADR + query | +| 8 | Migration dry-run | [migration-dry-run-report.md](migration-dry-run-report.md) | Procedimento pronto | +| 9 | RowVersion multi-agregado | WO + Dispatch + ConcurrencyExceptionFilter | Implementado | + +--- + +## Implementação código (Fase 0) + +- Migration `Phase0_DomainFoundation` +- Enums: LifecycleStatus, WorkOrderType, DocStatus, AuditEventType, etc. +- `IWorkOrderAuditService`, `IWorkOrderFieldLockService`, `ISyncFieldMergePolicy` +- `[Authorize]` reativado em WorkOrderController +- Projeto `SeaHavenIndustries.Tests` + +--- + +## GO Fase 1 — pendente + +- [ ] Sign-off CTO/PO nos artefatos +- [ ] Dry-run staging executado +- [ ] Volume tier confirmado em staging/prod +- [ ] Smoke checklist verde + +**Assinatura GO Fase 1** + +| Papel | Nome | Data | +|-------|------|------| +| CTO | | | +| PO | | | diff --git a/docs/work-orders/phase-0/rowversion-design.md b/docs/work-orders/phase-0/rowversion-design.md new file mode 100644 index 0000000..9e80ddc --- /dev/null +++ b/docs/work-orders/phase-0/rowversion-design.md @@ -0,0 +1,36 @@ +# RowVersion Multi-Agregado — Design + +**Fase:** 0 — Gate #9 + +--- + +## Agregados + +| Agregado | Coluna | Comportamento | +|----------|--------|---------------| +| WorkOrder | `RowVersion` rowversion | Bump em mutação WO (schedule, status, assignment) | +| Dispatch | `RowVersion` rowversion | Bump em mutação vendor/checklist/signoff | + +--- + +## HTTP 409 + +`ConcurrencyExceptionFilter` captura `DbUpdateConcurrencyException` e retorna: + +```json +{ + "status": "Conflict", + "message": "The record was modified by another user. Refresh and retry.", + "code": 409 +} +``` + +--- + +## Regras (Fase 2 testes completos) + +- SHOC edita ScheduledDate → bump WO.RowVersion +- SHOC edita vendor → bump Dispatch.RowVersion +- Vendor edita checklist → bump Dispatch.RowVersion only + +Testes de concorrência paralela: Fase 2 (`Concurrency integration tests` backlog #27). diff --git a/docs/work-orders/phase-0/smoke-checklist.md b/docs/work-orders/phase-0/smoke-checklist.md new file mode 100644 index 0000000..0bad17e --- /dev/null +++ b/docs/work-orders/phase-0/smoke-checklist.md @@ -0,0 +1,57 @@ +# Smoke Test Checklist — Multi-Consumidor (Fase 0) + +**Ambiente:** Docker local (`docker compose up -d` + `scripts/setup-local-db.ps1`) + +--- + +## Blazor EF + +| # | Teste | Pass | +|---|-------|------| +| 1 | `/workorderlist` carrega sem exception | [ ] | +| 2 | Abrir detalhe WO existente | [ ] | +| 3 | Campos novos nullable não quebram binding | [ ] | + +--- + +## API REST (legado) + +| # | Teste | Pass | +|---|-------|------| +| 1 | POST login → JWT | [ ] | +| 2 | GET work order list com Bearer token | [ ] | +| 3 | POST ChangeStatus com audit no log | [ ] | +| 4 | POST ChangeAssignment.tex Assignment com audit | [ ] | +| 5 | 401 sem token (auth reativada) | [ ] | + +--- + +## Sync + +| # | Teste | Pass | +|---|-------|------| +| 1 | POST `api/Sync/WorkOrders` com JWT (mock/staging DynamoDB) | [ ] | +| 2 | Upsert idempotente por ExternalWorkOrderId | [ ] | +| 3 | Campo locked → SyncRejected no audit | [ ] | + +--- + +## Vendor Portal + +| # | Teste | Pass | +|---|-------|------| +| 1 | Checklist update flow | [ ] | +| 2 | Signoff flow | [ ] | +| 3 | Dispatch RowVersion presente (schema) | [ ] | + +--- + +## Automação local + +```powershell +dotnet test SeaHavenIndustries.Tests +dotnet build +./scripts/setup-local-db.ps1 +``` + +Testes unitários cobrem: LifecycleStatusMapper, audit baseline, sync merge rejection. diff --git a/docs/work-orders/phase-0/volume-discovery-report.md b/docs/work-orders/phase-0/volume-discovery-report.md new file mode 100644 index 0000000..12c48c3 --- /dev/null +++ b/docs/work-orders/phase-0/volume-discovery-report.md @@ -0,0 +1,96 @@ +# Volume Discovery Report — Work Orders + +**Fase:** 0 — Gate Fase 1 +**Data:** 2026-06-24 +**Ambiente:** Local dev (Docker) + queries para staging/prod + +--- + +## 1. Objetivo + +Definir tier S/M/L/XL antes de índices e estratégia de search (Fase 4). **Sem premissa de volume.** + +--- + +## 2. Queries obrigatórias + +Executar em staging ou produção read-only: + +```sql +-- Total WOs +SELECT COUNT(*) AS TotalWorkOrders +FROM workOrders +WHERE IsDeleted IS NULL OR IsDeleted = 0; + +-- WOs por semana (ISO) — p95 +WITH WeeklyCounts AS ( + SELECT DATEPART(iso_week, ScheduledDate) AS IsoWeek, + YEAR(ScheduledDate) AS IsoYear, + COUNT(*) AS Cnt + FROM workOrders + WHERE ScheduledDate IS NOT NULL + AND (IsDeleted IS NULL OR IsDeleted = 0) + GROUP BY DATEPART(iso_week, ScheduledDate), YEAR(ScheduledDate) +) +SELECT MAX(Cnt) AS P95ProxyMaxPerWeek, + AVG(Cnt * 1.0) AS AvgPerWeek +FROM WeeklyCounts; + +-- Unscheduled (não terminal) +SELECT COUNT(*) AS UnscheduledCount +FROM workOrders +WHERE ScheduledDate IS NULL + AND (IsDeleted IS NULL OR IsDeleted = 0) + AND Status NOT IN ('Completed', 'Complete', 'Cancelled', 'Canceled'); + +-- Crescimento mensal +SELECT YEAR(CreatedDate) AS Y, MONTH(CreatedDate) AS M, COUNT(*) AS Cnt +FROM workOrders +WHERE CreatedDate IS NOT NULL +GROUP BY YEAR(CreatedDate), MONTH(CreatedDate) +ORDER BY Y DESC, M DESC; +``` + +--- + +## 3. Resultados + +| Métrica | Local Docker (dev) | Staging | Produção | +|---------|-------------------|---------|----------| +| Total WOs | *executar após seed* | _pendente acesso_ | _pendente acesso_ | +| WOs/semana p95 | — | _pendente_ | _pendente_ | +| Unscheduled | — | _pendente_ | _pendente_ | +| Crescimento mensal | — | _pendente_ | _pendente_ | + +**Nota:** Ambiente local inicia vazio. Tier provisório para desenvolvimento: **S** (< 25k). Confirmar com query em staging antes do GO Fase 1. + +--- + +## 4. Classificação tier (§10.2 roadmap) + +| Tier | Total WOs | Listagem semanal | Advanced search | +|------|-----------|------------------|-----------------| +| **S** | < 25k | Índices simples | SQL filtros compostos | +| **M** | 25k–250k | Índices covering | Paginação obrigatória | +| **L** | 250k–1M | Read model candidato | Full-text | +| **XL** | > 1M | Materialized view | Search dedicado (ARB) | + +**Tier definido para Fase 1:** **S (provisório dev)** — revisar ao obter métricas staging. + +--- + +## 5. Impacto Fase 0–1 + +- Fases 1–3: índices conservadores compatíveis com qualquer tier. +- Fase 4: implementação search conforme tier confirmado + load test. + +--- + +## 6. Script local + +```powershell +./scripts/setup-local-db.ps1 +# Executar queries acima via sqlcmd ou DBCode contra localhost:1433 +``` + +**Gate:** Preencher coluna Staging/Produção antes do GO Fase 1.