# HEFESTO — Arquitetura Técnica
## Sistema de Controle Orçamentário para Facilities

**Versão:** 1.0  
**Data:** 2026-02-09  
**Stack:** NestJS · React · PostgreSQL

---

## 1. Visão Geral da Arquitetura

```
┌─────────────────────────────────────────────────────────────────────┐
│                        CLIENTE (Browser)                            │
│  ┌───────────────────────────────────────────────────────────────┐  │
│  │              React + Vite (SPA)                               │  │
│  │  ┌──────┐ ┌──────────┐ ┌──────────┐ ┌─────────┐ ┌────────┐  │  │
│  │  │ Auth │ │Dashboard │ │Demandas  │ │Propostas│ │Relat.  │  │  │
│  │  └──────┘ └──────────┘ └──────────┘ └─────────┘ └────────┘  │  │
│  │  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────────┐│  │
│  │  │Orçamento │ │Fornec.   │ │Workflow  │ │Ordens de Serviço ││  │
│  │  └──────────┘ └──────────┘ └──────────┘ └──────────────────┘│  │
│  └───────────────────────────────────────────────────────────────┘  │
└────────────────────────────┬────────────────────────────────────────┘
                             │ HTTPS / REST + JWT
┌────────────────────────────▼────────────────────────────────────────┐
│                      API GATEWAY (NestJS)                           │
│  ┌────────┐ ┌──────┐ ┌──────────┐ ┌──────────┐ ┌───────────────┐  │
│  │  Auth  │ │Users │ │  Locais  │ │Categorias│ │ Fornecedores  │  │
│  └────────┘ └──────┘ └──────────┘ └──────────┘ └───────────────┘  │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐  │
│  │Demandas  │ │Propostas │ │Orçamento │ │Workflow  │ │  OCR   │  │
│  └──────────┘ └──────────┘ └──────────┘ └──────────┘ └────────┘  │
│  ┌───────────────┐ ┌──────────┐ ┌──────────────────────────────┐  │
│  │Ordens Serviço │ │Dashboard │ │       Relatórios             │  │
│  └───────────────┘ └──────────┘ └──────────────────────────────┘  │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  Guards (JWT + RBAC) │ Interceptors │ Pipes │ Audit Logger  │   │
│  └─────────────────────────────────────────────────────────────┘   │
└────────────────────────────┬────────────────────────────────────────┘
              ┌──────────────┼──────────────┐
              ▼              ▼              ▼
     ┌────────────┐  ┌────────────┐  ┌────────────┐
     │ PostgreSQL │  │   Redis    │  │  Storage   │
     │  (dados)   │  │  (cache/   │  │  (S3/MinIO │
     │            │  │   filas)   │  │   PDFs)    │
     └────────────┘  └────────────┘  └────────────┘
                                          │
                                     ┌────▼─────┐
                                     │ OpenAI   │
                                     │GPT-4o-   │
                                     │  mini    │
                                     └──────────┘
```

---

## 2. Modelo de Dados Completo

### 2.1 Diagrama Entidade-Relacionamento (textual)

```
perfis ──1:N──> usuarios
locais ──1:N──> demandas
centros_custo ──1:N──> demandas
centros_custo ──1:N──> orcamento_planejado
categorias ──1:N──> demandas
categorias ──1:N──> orcamento_planejado
fornecedores ──1:N──> certidoes
fornecedores ──1:N──> propostas
demandas ──1:N──> itens_linha
demandas ──1:N──> propostas
demandas ──1:1──> workflow_aprovacao
demandas ──1:1──> ordens_servico
propostas ──1:N──> versoes_proposta
demandas ──1:1──> comparativo_propostas
ordens_servico ──1:1──> avaliacoes
matriz_alcada ──> centros_custo, perfis
```

### 2.2 Tabelas

#### `perfis`
| Coluna | Tipo | Restrições |
|--------|------|-----------|
| id | UUID | PK, DEFAULT gen_random_uuid() |
| nome | VARCHAR(50) | NOT NULL, UNIQUE |
| descricao | TEXT | |
| permissoes | JSONB | NOT NULL, DEFAULT '{}' |
| created_at | TIMESTAMPTZ | DEFAULT NOW() |
| updated_at | TIMESTAMPTZ | DEFAULT NOW() |

> Valores seed: `solicitante`, `gestor_facilities`, `aprovador_financeiro`, `diretoria`, `fornecedor`, `administrador`

#### `usuarios`
| Coluna | Tipo | Restrições |
|--------|------|-----------|
| id | UUID | PK |
| nome | VARCHAR(200) | NOT NULL |
| email | VARCHAR(255) | NOT NULL, UNIQUE |
| senha_hash | VARCHAR(255) | NOT NULL |
| perfil_id | UUID | FK → perfis.id, NOT NULL |
| ativo | BOOLEAN | DEFAULT TRUE |
| ultimo_acesso | TIMESTAMPTZ | |
| created_at | TIMESTAMPTZ | DEFAULT NOW() |
| updated_at | TIMESTAMPTZ | DEFAULT NOW() |

#### `locais`
| Coluna | Tipo | Restrições |
|--------|------|-----------|
| id | UUID | PK |
| nome | VARCHAR(200) | NOT NULL |
| endereco | TEXT | |
| centro_custo_id | UUID | FK → centros_custo.id, NOT NULL |
| responsavel_id | UUID | FK → usuarios.id |
| ativo | BOOLEAN | DEFAULT TRUE |
| created_at | TIMESTAMPTZ | DEFAULT NOW() |
| updated_at | TIMESTAMPTZ | DEFAULT NOW() |

#### `centros_custo`
| Coluna | Tipo | Restrições |
|--------|------|-----------|
| id | UUID | PK |
| codigo | VARCHAR(20) | NOT NULL, UNIQUE |
| nome | VARCHAR(200) | NOT NULL |
| responsavel_id | UUID | FK → usuarios.id |
| ativo | BOOLEAN | DEFAULT TRUE |
| created_at | TIMESTAMPTZ | DEFAULT NOW() |
| updated_at | TIMESTAMPTZ | DEFAULT NOW() |

#### `categorias`
| Coluna | Tipo | Restrições |
|--------|------|-----------|
| id | UUID | PK |
| nome | VARCHAR(200) | NOT NULL |
| subcategoria | VARCHAR(200) | |
| criticidade_padrao | VARCHAR(20) | CHECK (criticidade_padrao IN ('baixa','media','alta','critica')) |
| sla_dias | INTEGER | DEFAULT 30 |
| categoria_pai_id | UUID | FK → categorias.id (auto-ref) |
| ativo | BOOLEAN | DEFAULT TRUE |
| created_at | TIMESTAMPTZ | DEFAULT NOW() |
| updated_at | TIMESTAMPTZ | DEFAULT NOW() |

#### `fornecedores`
| Coluna | Tipo | Restrições |
|--------|------|-----------|
| id | UUID | PK |
| tipo_pessoa | VARCHAR(10) | NOT NULL, CHECK (tipo_pessoa IN ('PF','PJ')) |
| cpf_cnpj | VARCHAR(18) | NOT NULL, UNIQUE |
| razao_social | VARCHAR(300) | NOT NULL |
| nome_fantasia | VARCHAR(300) | |
| email | VARCHAR(255) | |
| telefone | VARCHAR(20) | |
| endereco | TEXT | |
| categorias_atendidas | UUID[] | (array de FK → categorias.id) |
| rating | NUMERIC(2,1) | DEFAULT 3.0, CHECK (rating >= 1 AND rating <= 5) |
| usuario_id | UUID | FK → usuarios.id (login do portal) |
| ativo | BOOLEAN | DEFAULT TRUE |
| created_at | TIMESTAMPTZ | DEFAULT NOW() |
| updated_at | TIMESTAMPTZ | DEFAULT NOW() |

#### `certidoes`
| Coluna | Tipo | Restrições |
|--------|------|-----------|
| id | UUID | PK |
| fornecedor_id | UUID | FK → fornecedores.id, NOT NULL |
| tipo | VARCHAR(100) | NOT NULL |
| numero | VARCHAR(100) | |
| data_emissao | DATE | NOT NULL |
| data_validade | DATE | NOT NULL |
| arquivo_url | TEXT | |
| status | VARCHAR(20) | CHECK (status IN ('vigente','vencida','pendente')) |
| created_at | TIMESTAMPTZ | DEFAULT NOW() |

#### `orcamento_planejado`
| Coluna | Tipo | Restrições |
|--------|------|-----------|
| id | UUID | PK |
| ano | INTEGER | NOT NULL |
| mes | INTEGER | NOT NULL, CHECK (mes BETWEEN 1 AND 12) |
| centro_custo_id | UUID | FK → centros_custo.id, NOT NULL |
| categoria_id | UUID | FK → categorias.id, NOT NULL |
| valor_planejado | NUMERIC(15,2) | NOT NULL |
| valor_comprometido | NUMERIC(15,2) | DEFAULT 0 |
| valor_realizado | NUMERIC(15,2) | DEFAULT 0 |
| created_at | TIMESTAMPTZ | DEFAULT NOW() |
| updated_at | TIMESTAMPTZ | DEFAULT NOW() |
| UNIQUE(ano, mes, centro_custo_id, categoria_id) | | |

#### `demandas`
| Coluna | Tipo | Restrições |
|--------|------|-----------|
| id | UUID | PK |
| numero | SERIAL | UNIQUE (código sequencial) |
| titulo | VARCHAR(300) | NOT NULL |
| descricao | TEXT | |
| local_id | UUID | FK → locais.id, NOT NULL |
| centro_custo_id | UUID | FK → centros_custo.id, NOT NULL |
| categoria_id | UUID | FK → categorias.id, NOT NULL |
| criticidade | VARCHAR(20) | NOT NULL, CHECK (... IN ('baixa','media','alta','critica')) |
| data_desejada | DATE | |
| status | VARCHAR(30) | NOT NULL, DEFAULT 'rascunho' |
| solicitante_id | UUID | FK → usuarios.id, NOT NULL |
| gestor_id | UUID | FK → usuarios.id |
| documentos | JSONB | DEFAULT '[]' |
| created_at | TIMESTAMPTZ | DEFAULT NOW() |
| updated_at | TIMESTAMPTZ | DEFAULT NOW() |

> **Status possíveis:** `rascunho`, `aberta`, `em_escopo`, `em_cotacao`, `propostas_recebidas`, `em_comparacao`, `em_aprovacao`, `aprovada`, `os_emitida`, `em_execucao`, `concluida`, `cancelada`

#### `itens_linha`
| Coluna | Tipo | Restrições |
|--------|------|-----------|
| id | UUID | PK |
| demanda_id | UUID | FK → demandas.id, NOT NULL |
| descricao | VARCHAR(300) | NOT NULL |
| tipo | VARCHAR(50) | CHECK (tipo IN ('mao_de_obra','material','equipamento','outros')) |
| quantidade | NUMERIC(10,2) | |
| unidade | VARCHAR(30) | |
| observacoes | TEXT | |
| ordem | INTEGER | DEFAULT 0 |
| created_at | TIMESTAMPTZ | DEFAULT NOW() |

#### `propostas`
| Coluna | Tipo | Restrições |
|--------|------|-----------|
| id | UUID | PK |
| demanda_id | UUID | FK → demandas.id, NOT NULL |
| fornecedor_id | UUID | FK → fornecedores.id, NOT NULL |
| versao_atual | INTEGER | DEFAULT 1 |
| valor_bruto | NUMERIC(15,2) | |
| valor_liquido | NUMERIC(15,2) | |
| impostos | JSONB | DEFAULT '{}' (iss, inss, pcc) |
| condicao_pagamento | TEXT | |
| prazo_execucao_dias | INTEGER | |
| data_entrega_estimada | DATE | |
| match_escopo_pct | NUMERIC(5,2) | |
| confiabilidade_ocr | NUMERIC(5,2) | |
| selecionada | BOOLEAN | DEFAULT FALSE |
| status | VARCHAR(20) | DEFAULT 'recebida' |
| created_at | TIMESTAMPTZ | DEFAULT NOW() |
| updated_at | TIMESTAMPTZ | DEFAULT NOW() |

#### `versoes_proposta`
| Coluna | Tipo | Restrições |
|--------|------|-----------|
| id | UUID | PK |
| proposta_id | UUID | FK → propostas.id, NOT NULL |
| versao | INTEGER | NOT NULL |
| arquivo_url | TEXT | NOT NULL |
| arquivo_nome | VARCHAR(300) | |
| dados_extraidos | JSONB | (resultado bruto do OCR) |
| dados_parseados | JSONB | (resultado estruturado) |
| confiabilidade | NUMERIC(5,2) | |
| uploaded_by | UUID | FK → usuarios.id |
| created_at | TIMESTAMPTZ | DEFAULT NOW() |

#### `comparativo_propostas`
| Coluna | Tipo | Restrições |
|--------|------|-----------|
| id | UUID | PK |
| demanda_id | UUID | FK → demandas.id, NOT NULL, UNIQUE |
| propostas_ids | UUID[] | NOT NULL |
| benchmark_id | UUID | FK → propostas.id |
| dados_comparativo | JSONB | NOT NULL |
| anotacoes | JSONB | DEFAULT '[]' |
| gerado_em | TIMESTAMPTZ | DEFAULT NOW() |
| atualizado_em | TIMESTAMPTZ | DEFAULT NOW() |

#### `workflow_aprovacao`
| Coluna | Tipo | Restrições |
|--------|------|-----------|
| id | UUID | PK |
| demanda_id | UUID | FK → demandas.id, NOT NULL |
| proposta_id | UUID | FK → propostas.id, NOT NULL |
| valor_total | NUMERIC(15,2) | NOT NULL |
| status | VARCHAR(30) | NOT NULL, DEFAULT 'pendente' |
| etapa_atual | INTEGER | DEFAULT 1 |
| etapas | JSONB | NOT NULL (array de etapas) |
| emergencial | BOOLEAN | DEFAULT FALSE |
| justificativa_emergencial | TEXT | |
| created_at | TIMESTAMPTZ | DEFAULT NOW() |
| updated_at | TIMESTAMPTZ | DEFAULT NOW() |

> **Status:** `pendente`, `em_andamento`, `aprovado`, `reprovado`, `aprovado_com_ressalva`, `cancelado`

> **Estrutura de `etapas` (JSONB):**
> ```json
> [
>   {
>     "ordem": 1,
>     "perfil": "gestor_facilities",
>     "aprovador_id": "uuid",
>     "status": "aprovado",
>     "data_acao": "2026-01-15T10:00:00Z",
>     "observacao": "OK",
>     "ressalva": false
>   },
>   {
>     "ordem": 2,
>     "perfil": "aprovador_financeiro",
>     "aprovador_id": null,
>     "status": "pendente",
>     "data_acao": null,
>     "observacao": null,
>     "ressalva": false
>   }
> ]
> ```

#### `matriz_alcada`
| Coluna | Tipo | Restrições |
|--------|------|-----------|
| id | UUID | PK |
| centro_custo_id | UUID | FK → centros_custo.id |
| valor_minimo | NUMERIC(15,2) | NOT NULL |
| valor_maximo | NUMERIC(15,2) | NOT NULL |
| perfil_aprovador | VARCHAR(50) | NOT NULL |
| ordem_sequencial | INTEGER | DEFAULT 1 |
| ativo | BOOLEAN | DEFAULT TRUE |
| created_at | TIMESTAMPTZ | DEFAULT NOW() |

#### `ordens_servico`
| Coluna | Tipo | Restrições |
|--------|------|-----------|
| id | UUID | PK |
| numero | SERIAL | UNIQUE |
| demanda_id | UUID | FK → demandas.id, NOT NULL, UNIQUE |
| proposta_id | UUID | FK → propostas.id, NOT NULL |
| fornecedor_id | UUID | FK → fornecedores.id, NOT NULL |
| valor | NUMERIC(15,2) | NOT NULL |
| status | VARCHAR(30) | DEFAULT 'emitida' |
| data_emissao | TIMESTAMPTZ | DEFAULT NOW() |
| data_inicio | DATE | |
| data_conclusao | DATE | |
| observacoes | TEXT | |
| created_at | TIMESTAMPTZ | DEFAULT NOW() |
| updated_at | TIMESTAMPTZ | DEFAULT NOW() |

> **Status:** `emitida`, `em_execucao`, `concluida`, `cancelada`

#### `avaliacoes`
| Coluna | Tipo | Restrições |
|--------|------|-----------|
| id | UUID | PK |
| ordem_servico_id | UUID | FK → ordens_servico.id, NOT NULL, UNIQUE |
| fornecedor_id | UUID | FK → fornecedores.id, NOT NULL |
| avaliador_id | UUID | FK → usuarios.id, NOT NULL |
| nota | NUMERIC(2,1) | NOT NULL, CHECK (nota >= 1 AND nota <= 5) |
| comentario | TEXT | |
| created_at | TIMESTAMPTZ | DEFAULT NOW() |

#### `audit_log`
| Coluna | Tipo | Restrições |
|--------|------|-----------|
| id | UUID | PK |
| usuario_id | UUID | FK → usuarios.id |
| acao | VARCHAR(50) | NOT NULL |
| entidade | VARCHAR(50) | NOT NULL |
| entidade_id | UUID | |
| dados_antes | JSONB | |
| dados_depois | JSONB | |
| ip | VARCHAR(45) | |
| user_agent | TEXT | |
| created_at | TIMESTAMPTZ | DEFAULT NOW() |

> **Índice:** `CREATE INDEX idx_audit_log_entidade ON audit_log(entidade, entidade_id);`
> **Índice:** `CREATE INDEX idx_audit_log_usuario ON audit_log(usuario_id, created_at DESC);`

#### `alertas`
| Coluna | Tipo | Restrições |
|--------|------|-----------|
| id | UUID | PK |
| usuario_id | UUID | FK → usuarios.id, NOT NULL |
| tipo | VARCHAR(50) | NOT NULL |
| titulo | VARCHAR(300) | NOT NULL |
| mensagem | TEXT | |
| entidade | VARCHAR(50) | |
| entidade_id | UUID | |
| lido | BOOLEAN | DEFAULT FALSE |
| created_at | TIMESTAMPTZ | DEFAULT NOW() |

> **Tipos de alerta:** `estouro_orcamento`, `sla_vencendo`, `proposta_pendente`, `aprovacao_pendente`, `certidao_vencendo`, `os_atrasada`

---

## 3. Módulos NestJS

### 3.1 Estrutura de Pastas

```
src/
├── main.ts
├── app.module.ts
├── common/
│   ├── decorators/          # @Roles(), @CurrentUser(), @Audit()
│   ├── guards/              # JwtAuthGuard, RolesGuard
│   ├── interceptors/        # AuditInterceptor, TransformInterceptor
│   ├── pipes/               # ParseUUIDPipe, ValidationPipe
│   ├── filters/             # HttpExceptionFilter
│   ├── dto/                 # PaginationDto, FilterDto
│   └── interfaces/          # ICurrentUser, IPaginatedResult
├── config/
│   ├── database.config.ts
│   ├── jwt.config.ts
│   ├── storage.config.ts
│   └── openai.config.ts
├── modules/
│   ├── auth/
│   │   ├── auth.module.ts
│   │   ├── auth.controller.ts
│   │   ├── auth.service.ts
│   │   ├── strategies/      # JwtStrategy, LocalStrategy
│   │   ├── dto/             # LoginDto, RegisterDto, RefreshTokenDto
│   │   └── guards/
│   ├── users/
│   │   ├── users.module.ts
│   │   ├── users.controller.ts
│   │   ├── users.service.ts
│   │   ├── entities/        # User, Perfil
│   │   └── dto/
│   ├── locais/
│   │   ├── locais.module.ts
│   │   ├── locais.controller.ts
│   │   ├── locais.service.ts
│   │   ├── entities/        # Local, CentroCusto
│   │   └── dto/
│   ├── categorias/
│   │   ├── categorias.module.ts
│   │   ├── categorias.controller.ts
│   │   ├── categorias.service.ts
│   │   ├── entities/        # Categoria
│   │   └── dto/
│   ├── fornecedores/
│   │   ├── fornecedores.module.ts
│   │   ├── fornecedores.controller.ts
│   │   ├── fornecedores.service.ts
│   │   ├── entities/        # Fornecedor, Certidao
│   │   └── dto/
│   ├── demandas/
│   │   ├── demandas.module.ts
│   │   ├── demandas.controller.ts
│   │   ├── demandas.service.ts
│   │   ├── entities/        # Demanda, ItemLinha
│   │   └── dto/
│   ├── propostas/
│   │   ├── propostas.module.ts
│   │   ├── propostas.controller.ts
│   │   ├── propostas.service.ts
│   │   ├── entities/        # Proposta, VersaoProposta, ComparativoPropostas
│   │   └── dto/
│   ├── orcamento/
│   │   ├── orcamento.module.ts
│   │   ├── orcamento.controller.ts
│   │   ├── orcamento.service.ts
│   │   ├── entities/        # OrcamentoPlanejado
│   │   └── dto/
│   ├── workflow/
│   │   ├── workflow.module.ts
│   │   ├── workflow.controller.ts
│   │   ├── workflow.service.ts
│   │   ├── entities/        # WorkflowAprovacao, MatrizAlcada
│   │   └── dto/
│   ├── ordens-servico/
│   │   ├── ordens-servico.module.ts
│   │   ├── ordens-servico.controller.ts
│   │   ├── ordens-servico.service.ts
│   │   ├── entities/        # OrdemServico, Avaliacao
│   │   └── dto/
│   ├── ocr/
│   │   ├── ocr.module.ts
│   │   ├── ocr.service.ts
│   │   ├── ocr.processor.ts       # Bull queue processor
│   │   ├── prompts/                # System prompts para GPT-4o-mini
│   │   └── interfaces/
│   ├── dashboard/
│   │   ├── dashboard.module.ts
│   │   ├── dashboard.controller.ts
│   │   └── dashboard.service.ts
│   └── relatorios/
│       ├── relatorios.module.ts
│       ├── relatorios.controller.ts
│       ├── relatorios.service.ts
│       └── templates/              # Templates PDF/Excel
└── database/
    ├── migrations/
    └── seeds/
```

### 3.2 Dependências entre Módulos

```
                    ┌──────────┐
                    │   Auth   │
                    └────┬─────┘
                         │ JWT/Guards
          ┌──────────────┼──────────────────┐
          ▼              ▼                  ▼
     ┌────────┐    ┌──────────┐       ┌──────────┐
     │ Users  │    │Dashboard │       │Relatórios│
     └────────┘    └──────────┘       └──────────┘
                        │ agrega            │ lê
     ┌──────────────────┼──────────────┐    │
     ▼         ▼        ▼             ▼    ▼
┌────────┐┌────────┐┌──────────┐┌──────────┐
│ Locais ││Categ.  ││Demandas  ││Orçamento │
└────────┘└────────┘└─────┬────┘└──────────┘
                          │
              ┌───────────┼───────────┐
              ▼           ▼           ▼
        ┌──────────┐┌──────────┐┌──────────┐
        │Propostas ││Workflow  ││  Fornec. │
        └─────┬────┘└────┬─────┘└──────────┘
              │          │
              ▼          ▼
         ┌────────┐┌──────────────┐
         │  OCR   ││Ordens Serviço│
         └────────┘└──────────────┘
```

---

## 4. API REST Completa

### 4.1 Autenticação (`/api/auth`)

| Método | Rota | Descrição | Acesso |
|--------|------|-----------|--------|
| POST | `/api/auth/login` | Login (email + senha) → JWT | Público |
| POST | `/api/auth/refresh` | Renovar token | Autenticado |
| POST | `/api/auth/logout` | Invalidar refresh token | Autenticado |
| GET | `/api/auth/me` | Dados do usuário logado | Autenticado |
| POST | `/api/auth/forgot-password` | Solicitar reset de senha | Público |
| POST | `/api/auth/reset-password` | Redefinir senha com token | Público |

### 4.2 Usuários (`/api/users`)

| Método | Rota | Descrição | Acesso |
|--------|------|-----------|--------|
| GET | `/api/users` | Listar usuários (paginado) | Admin |
| GET | `/api/users/:id` | Detalhe do usuário | Admin |
| POST | `/api/users` | Criar usuário | Admin |
| PATCH | `/api/users/:id` | Atualizar usuário | Admin |
| DELETE | `/api/users/:id` | Desativar usuário (soft) | Admin |
| GET | `/api/perfis` | Listar perfis | Admin |
| POST | `/api/perfis` | Criar perfil | Admin |
| PATCH | `/api/perfis/:id` | Atualizar permissões | Admin |

### 4.3 Locais e Centros de Custo (`/api/locais`, `/api/centros-custo`)

| Método | Rota | Descrição | Acesso |
|--------|------|-----------|--------|
| GET | `/api/locais` | Listar locais | Autenticado |
| GET | `/api/locais/:id` | Detalhe do local | Autenticado |
| POST | `/api/locais` | Criar local | Admin, Gestor |
| PATCH | `/api/locais/:id` | Atualizar local | Admin, Gestor |
| DELETE | `/api/locais/:id` | Desativar local | Admin |
| GET | `/api/centros-custo` | Listar centros de custo | Autenticado |
| GET | `/api/centros-custo/:id` | Detalhe | Autenticado |
| POST | `/api/centros-custo` | Criar CC | Admin |
| PATCH | `/api/centros-custo/:id` | Atualizar CC | Admin |

### 4.4 Categorias (`/api/categorias`)

| Método | Rota | Descrição | Acesso |
|--------|------|-----------|--------|
| GET | `/api/categorias` | Listar (árvore ou flat) | Autenticado |
| GET | `/api/categorias/:id` | Detalhe | Autenticado |
| POST | `/api/categorias` | Criar categoria | Admin |
| PATCH | `/api/categorias/:id` | Atualizar | Admin |
| DELETE | `/api/categorias/:id` | Desativar | Admin |

### 4.5 Fornecedores (`/api/fornecedores`)

| Método | Rota | Descrição | Acesso |
|--------|------|-----------|--------|
| GET | `/api/fornecedores` | Listar (paginado, filtros) | Autenticado |
| GET | `/api/fornecedores/:id` | Detalhe + certidões | Autenticado |
| POST | `/api/fornecedores` | Cadastrar fornecedor | Admin, Gestor |
| PATCH | `/api/fornecedores/:id` | Atualizar | Admin, Gestor |
| DELETE | `/api/fornecedores/:id` | Desativar | Admin |
| GET | `/api/fornecedores/:id/certidoes` | Listar certidões | Autenticado |
| POST | `/api/fornecedores/:id/certidoes` | Upload certidão | Fornecedor, Admin |
| PATCH | `/api/fornecedores/:id/certidoes/:certId` | Atualizar certidão | Admin |
| GET | `/api/fornecedores/:id/avaliacoes` | Histórico de avaliações | Autenticado |

### 4.6 Demandas (`/api/demandas`)

| Método | Rota | Descrição | Acesso |
|--------|------|-----------|--------|
| GET | `/api/demandas` | Listar (paginado, filtros por status/CC/local/categoria) | Autenticado |
| GET | `/api/demandas/:id` | Detalhe completo | Autenticado |
| POST | `/api/demandas` | Criar demanda (rascunho) | Solicitante, Gestor |
| PATCH | `/api/demandas/:id` | Atualizar demanda | Solicitante, Gestor |
| POST | `/api/demandas/:id/publicar` | Publicar (rascunho → aberta) | Solicitante, Gestor |
| POST | `/api/demandas/:id/cancelar` | Cancelar demanda | Gestor, Admin |
| POST | `/api/demandas/:id/documentos` | Upload documento | Solicitante, Gestor |
| DELETE | `/api/demandas/:id/documentos/:docId` | Remover documento | Solicitante, Gestor |
| GET | `/api/demandas/:id/itens-linha` | Listar itens de linha | Autenticado |
| POST | `/api/demandas/:id/itens-linha` | Adicionar item de linha | Gestor |
| PATCH | `/api/demandas/:id/itens-linha/:itemId` | Atualizar item | Gestor |
| DELETE | `/api/demandas/:id/itens-linha/:itemId` | Remover item | Gestor |
| POST | `/api/demandas/:id/enviar-cotacao` | Enviar p/ cotação (validações) | Gestor |
| GET | `/api/demandas/:id/timeline` | Timeline de eventos | Autenticado |

### 4.7 Propostas (`/api/propostas`)

| Método | Rota | Descrição | Acesso |
|--------|------|-----------|--------|
| GET | `/api/demandas/:demandaId/propostas` | Listar propostas da demanda | Autenticado |
| GET | `/api/propostas/:id` | Detalhe da proposta | Autenticado |
| POST | `/api/demandas/:demandaId/propostas` | Upload proposta (PDF) | Fornecedor |
| POST | `/api/propostas/:id/nova-versao` | Nova versão do PDF | Fornecedor |
| GET | `/api/propostas/:id/versoes` | Listar versões | Autenticado |
| GET | `/api/propostas/:id/versoes/:versaoId` | Detalhe da versão + dados OCR | Autenticado |
| POST | `/api/propostas/:id/selecionar` | Selecionar proposta vencedora | Gestor |
| PATCH | `/api/propostas/:id/dados-extraidos` | Corrigir dados do OCR | Gestor |
| GET | `/api/demandas/:demandaId/comparativo` | Painel comparativo | Autenticado |
| POST | `/api/demandas/:demandaId/comparativo/gerar` | Gerar/atualizar comparativo | Gestor |
| POST | `/api/demandas/:demandaId/comparativo/anotacoes` | Adicionar anotação | Autenticado |

### 4.8 Orçamento (`/api/orcamento`)

| Método | Rota | Descrição | Acesso |
|--------|------|-----------|--------|
| GET | `/api/orcamento` | Listar planejamento (filtros: ano, mês, CC, categoria) | Financeiro, Admin |
| GET | `/api/orcamento/:id` | Detalhe | Financeiro, Admin |
| POST | `/api/orcamento` | Criar linha orçamentária | Admin |
| PATCH | `/api/orcamento/:id` | Atualizar planejado | Admin |
| POST | `/api/orcamento/importar` | Importar planilha | Admin |
| GET | `/api/orcamento/resumo` | Resumo planejado x comprometido x realizado | Financeiro, Diretoria |
| GET | `/api/orcamento/verificar` | Verificar disponibilidade (CC, categoria, valor) | Sistema |

### 4.9 Workflow de Aprovação (`/api/workflow`)

| Método | Rota | Descrição | Acesso |
|--------|------|-----------|--------|
| POST | `/api/demandas/:demandaId/workflow/iniciar` | Iniciar workflow | Gestor |
| GET | `/api/workflow/:id` | Status do workflow | Autenticado |
| POST | `/api/workflow/:id/aprovar` | Aprovar etapa atual | Aprovador da etapa |
| POST | `/api/workflow/:id/reprovar` | Reprovar (com justificativa) | Aprovador da etapa |
| POST | `/api/workflow/:id/aprovar-com-ressalva` | Aprovar com ressalva | Aprovador da etapa |
| GET | `/api/workflow/pendentes` | Minhas aprovações pendentes | Autenticado |
| GET | `/api/matriz-alcada` | Listar regras de alçada | Admin |
| POST | `/api/matriz-alcada` | Criar regra | Admin |
| PATCH | `/api/matriz-alcada/:id` | Atualizar regra | Admin |
| DELETE | `/api/matriz-alcada/:id` | Desativar regra | Admin |

### 4.10 Ordens de Serviço (`/api/ordens-servico`)

| Método | Rota | Descrição | Acesso |
|--------|------|-----------|--------|
| GET | `/api/ordens-servico` | Listar OS | Autenticado |
| GET | `/api/ordens-servico/:id` | Detalhe | Autenticado |
| POST | `/api/ordens-servico/:id/iniciar` | Marcar início execução | Gestor |
| POST | `/api/ordens-servico/:id/concluir` | Marcar conclusão | Gestor |
| POST | `/api/ordens-servico/:id/cancelar` | Cancelar OS | Admin |
| POST | `/api/ordens-servico/:id/avaliacao` | Avaliar fornecedor | Gestor, Solicitante |

### 4.11 Dashboard (`/api/dashboard`)

| Método | Rota | Descrição | Acesso |
|--------|------|-----------|--------|
| GET | `/api/dashboard/indicadores` | Cards principais | Autenticado |
| GET | `/api/dashboard/demandas-por-status` | Gráfico por status | Autenticado |
| GET | `/api/dashboard/demandas-por-categoria` | Gráfico por categoria | Autenticado |
| GET | `/api/dashboard/consumo-orcamento` | Plan. x Comprom. x Realiz. | Financeiro, Diretoria |
| GET | `/api/dashboard/propostas-por-fornecedor` | Gráfico fornecedores | Gestor |
| GET | `/api/dashboard/alertas` | Alertas do usuário | Autenticado |
| PATCH | `/api/dashboard/alertas/:id/ler` | Marcar alerta como lido | Autenticado |

### 4.12 Relatórios (`/api/relatorios`)

| Método | Rota | Descrição | Acesso |
|--------|------|-----------|--------|
| GET | `/api/relatorios/consumo-orcamentario` | Por CC e categoria | Financeiro, Diretoria |
| GET | `/api/relatorios/saving` | Saving gerado | Financeiro, Diretoria |
| GET | `/api/relatorios/historico-decisoes` | Trilha de auditoria | Admin |
| GET | `/api/relatorios/lead-time` | Lead time por status | Gestor, Admin |
| GET | `/api/relatorios/gargalos` | Gargalos de aprovação | Admin |
| GET | `/api/relatorios/exportar/:tipo` | Exportar PDF ou Excel | Autenticado |

### 4.13 OCR (`/api/ocr`) — interno

| Método | Rota | Descrição | Acesso |
|--------|------|-----------|--------|
| POST | `/api/ocr/extrair` | Disparar extração de PDF | Sistema/Gestor |
| GET | `/api/ocr/status/:jobId` | Status do job de extração | Sistema/Gestor |

---

## 5. Workflow de Aprovação — Máquina de Estados

### 5.1 Diagrama de Estados da Demanda

```
                    ┌──────────┐
                    │ RASCUNHO │
                    └────┬─────┘
                         │ publicar (valida: itens_linha, CC, local)
                         ▼
                    ┌──────────┐
                    │  ABERTA  │
                    └────┬─────┘
                         │ gestor assume
                         ▼
                    ┌──────────┐
                    │ EM_ESCOPO│  ← define itens de linha
                    └────┬─────┘
                         │ enviar cotação (valida: ≥1 item)
                         ▼
                    ┌───────────┐
                    │ EM_COTAÇÃO│  ← fornecedores enviam propostas
                    └────┬──────┘
                         │ todas propostas recebidas
                         ▼
                ┌─────────────────┐
                │PROPOSTAS_RECEB. │
                └────────┬────────┘
                         │ gestor inicia comparação
                         ▼
                   ┌─────────────┐
                   │EM_COMPARAÇÃO│  ← painel comparativo, OCR
                   └──────┬──────┘
                          │ seleciona proposta → inicia workflow
                          ▼
                   ┌─────────────┐
                   │EM_APROVAÇÃO │  ← fluxo de alçadas
                   └──────┬──────┘
                     ┌────┼────┐
                     ▼    │    ▼
              ┌──────┐    │  ┌──────────┐
              │REPROV│    │  │APROVADA  │
              └──────┘    │  └────┬─────┘
                          │       │ gera OS automática
                          │       ▼
                          │  ┌──────────┐
                          │  │OS_EMITIDA│
                          │  └────┬─────┘
                          │       │
                          │       ▼
                          │  ┌────────────┐
                          │  │EM_EXECUÇÃO │
                          │  └─────┬──────┘
                          │        │
                          │        ▼
                          │  ┌──────────┐
                          │  │CONCLUÍDA │
                          │  └──────────┘
                          │
            (cancelar em qualquer ponto)
                          ▼
                    ┌──────────┐
                    │CANCELADA │
                    └──────────┘
```

### 5.2 Fluxo de Alçadas (Workflow de Aprovação)

```
Proposta Selecionada (valor R$)
         │
         ▼
┌────────────────────────┐
│ Consultar matriz_alcada│
│ (CC + valor)           │
└────────┬───────────────┘
         │
         ▼
    Valor ≤ alçada do Gestor?
    ├── SIM → Aprovação automática pelo Gestor ──→ APROVADO
    │
    └── NÃO → Fluxo Sequencial:
              │
              ▼
         ┌─────────────────────┐
     (1) │ Gestor de Facilities│──→ Aprova? ──→ Próxima etapa
         └─────────────────────┘       │
                                    Reprova? ──→ REPROVADO
              │
              ▼
         ┌─────────────────────┐
     (2) │ Aprovador Financeiro│──→ Aprova? ──→ Próxima etapa
         └─────────────────────┘       │
                                    Reprova? ──→ REPROVADO
              │
              ▼
         ┌─────────────────────┐
     (3) │ Diretoria           │──→ Aprova? ──→ APROVADO
         └─────────────────────┘       │
                                    Reprova? ──→ REPROVADO
```

### 5.3 Exceções Emergenciais

1. Demanda marcada como `criticidade = 'critica'` pode ser sinalizada como **emergencial**
2. Workflow emergencial exige `justificativa_emergencial` obrigatória
3. Permite pular etapas intermediárias, indo direto ao aprovador de maior alçada
4. Gera alerta automático para todos os aprovadores pulados
5. Registra em `audit_log` com flag `emergencial = true`

### 5.4 Aprovação com Ressalva

- Qualquer aprovador pode aprovar com ressalva (`aprovado_com_ressalva`)
- Exige campo `observacao` obrigatório
- O workflow continua normalmente, mas a OS gerada carrega flag de ressalva
- Visível no relatório de histórico de decisões

---

## 6. OCR/IA — Estratégia de Extração Inteligente

### 6.1 Pipeline de Processamento

```
PDF Upload
    │
    ▼
┌──────────────────┐
│ 1. Armazenar S3  │  (MinIO / S3-compatible)
└────────┬─────────┘
         │
         ▼
┌──────────────────┐
│ 2. Fila Bull/    │  (Redis queue: "ocr-extraction")
│    BullMQ        │
└────────┬─────────┘
         │ Worker processa
         ▼
┌──────────────────────────────────────┐
│ 3. Extrair texto do PDF             │
│    - pdf-parse (texto nativo)       │
│    - Tesseract.js (se escaneado)    │
└────────┬─────────────────────────────┘
         │
         ▼
┌──────────────────────────────────────┐
│ 4. Enviar para GPT-4o-mini          │
│    System prompt estruturado:       │
│    - Extrair: valor_bruto,          │
│      valor_liquido, impostos,       │
│      condicao_pagamento,            │
│      prazo_execucao, data_entrega   │
│    - Mapear itens ↔ itens_linha     │
│    - Retornar JSON tipado           │
│    - Indicar confiança (0-100%)     │
└────────┬─────────────────────────────┘
         │
         ▼
┌──────────────────────────────────────┐
│ 5. Salvar em versoes_proposta       │
│    - dados_extraidos (raw GPT)      │
│    - dados_parseados (estruturado)  │
│    - confiabilidade                 │
└────────┬─────────────────────────────┘
         │
         ▼
┌──────────────────────────────────────┐
│ 6. Calcular match_escopo_pct        │
│    Itens mapeados / Total itens     │
└──────────────────────────────────────┘
```

### 6.2 System Prompt para GPT-4o-mini

```
Você é um extrator de dados de propostas comerciais de serviços de Facilities.

Dado o texto de uma proposta PDF, extraia os seguintes campos em JSON:

{
  "valor_bruto": number | null,
  "valor_liquido": number | null,
  "impostos": {
    "iss": number | null,
    "inss": number | null,
    "pcc": number | null,
    "outros": number | null
  },
  "condicao_pagamento": string | null,
  "prazo_execucao_dias": number | null,
  "data_entrega_estimada": string | null,   // ISO 8601
  "itens": [
    {
      "descricao": string,
      "valor_unitario": number | null,
      "quantidade": number | null,
      "valor_total": number | null,
      "unidade": string | null
    }
  ],
  "confianca_geral": number   // 0 a 100
}

Regras:
- Se o campo não for encontrado, retorne null
- Valores monetários em BRL, sem símbolo
- confianca_geral indica % de certeza da extração
- Retorne SOMENTE o JSON, sem explicações
```

### 6.3 Match de Escopo

O serviço compara `itens` retornados pelo GPT com `itens_linha` da demanda usando similaridade textual (Levenshtein / embeddings simples). Resultado: `match_escopo_pct` na proposta.

### 6.4 Tratamento de Falhas

- **Confiabilidade < 60%**: alerta ao Gestor para revisão manual
- **PDF escaneado com baixa qualidade**: fallback para Tesseract + retry GPT
- **Timeout GPT**: retry com backoff exponencial (3 tentativas)
- **Todos os erros logados** em `audit_log`

---

## 7. Segurança

### 7.1 RBAC — Controle de Acesso por Perfil

```typescript
// Decorator customizado
@Roles('administrador', 'gestor_facilities')
@UseGuards(JwtAuthGuard, RolesGuard)
```

**Matriz de Permissões:**

| Recurso | Solicitante | Gestor | Financeiro | Diretoria | Fornecedor | Admin |
|---------|:-----------:|:------:|:----------:|:---------:|:----------:|:-----:|
| Criar demanda | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ |
| Definir escopo | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ |
| Upload proposta | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
| Comparar propostas | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ |
| Selecionar proposta | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ |
| Aprovar workflow | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ |
| Ver orçamento | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ |
| Editar orçamento | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Cadastros base | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Relatórios | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ |
| Avaliar fornecedor | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ |

### 7.2 Autenticação

- **JWT** com access token (15 min) + refresh token (7 dias)
- Refresh tokens armazenados em banco (revogáveis)
- Bcrypt para hash de senhas (salt rounds: 12)
- Rate limiting: 5 tentativas de login / 15 min por IP

### 7.3 LGPD

- Dados pessoais de fornecedores (CPF/CNPJ) criptografados em repouso (AES-256)
- Endpoint `DELETE /api/users/:id/dados-pessoais` para anonimização
- Consentimento registrado no cadastro de fornecedores
- Logs de acesso a dados sensíveis em `audit_log`
- Retenção de logs: 5 anos (configurável)

### 7.4 Audit Log

Todo `CUD` (Create, Update, Delete) é interceptado pelo `AuditInterceptor`:

```typescript
@Injectable()
export class AuditInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler) {
    const req = context.switchToHttp().getRequest();
    const before = /* snapshot antes */;
    return next.handle().pipe(
      tap(result => {
        this.auditService.log({
          usuario_id: req.user.id,
          acao: req.method,
          entidade: /* extraído da rota */,
          entidade_id: req.params.id,
          dados_antes: before,
          dados_depois: result,
          ip: req.ip,
          user_agent: req.headers['user-agent'],
        });
      }),
    );
  }
}
```

---

## 8. Infraestrutura e Deploy

### 8.1 Ambientes

| Ambiente | Descrição |
|----------|-----------|
| `development` | Local, Docker Compose |
| `staging` | Pré-produção |
| `production` | Produção |

### 8.2 Docker Compose (dev)

```yaml
services:
  api:
    build: ./backend
    ports: ["3000:3000"]
    depends_on: [postgres, redis]
    environment:
      DATABASE_URL: postgresql://hefesto:hefesto@postgres:5432/hefesto
      REDIS_URL: redis://redis:6379
      JWT_SECRET: ${JWT_SECRET}
      OPENAI_API_KEY: ${OPENAI_API_KEY}
      S3_ENDPOINT: http://minio:9000

  frontend:
    build: ./frontend
    ports: ["5173:5173"]

  postgres:
    image: postgres:16-alpine
    volumes: ["pgdata:/var/lib/postgresql/data"]
    environment:
      POSTGRES_DB: hefesto
      POSTGRES_USER: hefesto
      POSTGRES_PASSWORD: hefesto

  redis:
    image: redis:7-alpine

  minio:
    image: minio/minio
    command: server /data --console-address ":9001"
    ports: ["9000:9000", "9001:9001"]

volumes:
  pgdata:
```

### 8.3 Tecnologias e Bibliotecas

| Camada | Tecnologia |
|--------|-----------|
| Backend | NestJS 10+, TypeScript 5+ |
| ORM | TypeORM (migrations + entities) |
| Validação | class-validator, class-transformer |
| Filas | BullMQ + Redis |
| Autenticação | @nestjs/passport, passport-jwt |
| Upload | Multer + S3 SDK |
| PDF parse | pdf-parse, Tesseract.js |
| IA | OpenAI SDK (GPT-4o-mini) |
| Frontend | React 18+, Vite 5+ |
| UI Kit | shadcn/ui + Tailwind CSS |
| State | TanStack Query (React Query) |
| Gráficos | Recharts |
| Tabelas | TanStack Table |
| Exportação | jsPDF, ExcelJS |
| Testes | Jest (back), Vitest (front) |

---

## 9. Índices do Banco de Dados

```sql
-- Performance queries frequentes
CREATE INDEX idx_demandas_status ON demandas(status);
CREATE INDEX idx_demandas_solicitante ON demandas(solicitante_id);
CREATE INDEX idx_demandas_cc ON demandas(centro_custo_id);
CREATE INDEX idx_demandas_local ON demandas(local_id);
CREATE INDEX idx_demandas_created ON demandas(created_at DESC);

CREATE INDEX idx_propostas_demanda ON propostas(demanda_id);
CREATE INDEX idx_propostas_fornecedor ON propostas(fornecedor_id);

CREATE INDEX idx_workflow_status ON workflow_aprovacao(status);
CREATE INDEX idx_workflow_demanda ON workflow_aprovacao(demanda_id);

CREATE INDEX idx_os_status ON ordens_servico(status);
CREATE INDEX idx_os_fornecedor ON ordens_servico(fornecedor_id);

CREATE INDEX idx_orcamento_periodo ON orcamento_planejado(ano, mes);
CREATE INDEX idx_orcamento_cc ON orcamento_planejado(centro_custo_id);

CREATE INDEX idx_alertas_usuario ON alertas(usuario_id, lido, created_at DESC);

CREATE INDEX idx_certidoes_fornecedor ON certidoes(fornecedor_id);
CREATE INDEX idx_certidoes_validade ON certidoes(data_validade);

CREATE INDEX idx_audit_log_entidade ON audit_log(entidade, entidade_id);
CREATE INDEX idx_audit_log_usuario ON audit_log(usuario_id, created_at DESC);
```

---

## 10. Regras de Negócio Críticas

1. **Demanda sem itens de linha não pode ser publicada** — validação no `POST /demandas/:id/publicar`
2. **Demanda sem CC e Local não pode ser publicada** — validação no `POST /demandas/:id/publicar`
3. **Fornecedor com certidão vencida não pode receber OS** — verificação no momento da geração da OS
4. **Alerta automático quando proposta > 20% do orçamento planejado** — trigger no `OrcamentoService.verificar()`
5. **OS gerada automaticamente após aprovação final** — event listener no `WorkflowService`
6. **Rating do fornecedor atualizado após avaliação** — média ponderada recalculada
7. **Valor comprometido atualizado ao emitir OS** — `OrcamentoService.comprometer()`
8. **Valor realizado atualizado ao concluir OS** — `OrcamentoService.realizar()`
9. **Soft delete em todas as entidades cadastrais** — campo `ativo` ao invés de DELETE físico

---

*Documento gerado para o projeto HEFESTO — v1.0*
