# 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*