- Backend NestJS com 12 módulos - Frontend React com dashboard e gestão - Manuais técnico e de negócios (MD + PDF) - Workflow de aprovação com alçadas - RBAC com 6 perfis de acesso
1105 lines
47 KiB
Markdown
1105 lines
47 KiB
Markdown
# 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*
|